Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2018-03-25 18:46:48 +0300
committerClément Foucault <foucault.clem@gmail.com>2018-03-25 21:06:12 +0300
commitbc15ec0896c2deba2e2a45581fd0f408d4906df4 (patch)
tree97f7f0a32a92fc441226c89fc09b80dbdc5e343f /source/blender/gpu/intern/gpu_framebuffer.c
parentf937123116abd4ba34ff858fa03415362d6c07cf (diff)
GPUFramebuffer: Refactor (Part 2)
This refactor modernise the use of framebuffers. It also touches a lot of files so breaking down changes we have: - GPUTexture: Allow textures to be attached to more than one GPUFrameBuffer. This allows to create and configure more FBO without the need to attach and detach texture at drawing time. - GPUFrameBuffer: The wrapper starts to mimic opengl a bit closer. This allows to configure the framebuffer inside a context other than the one that will be rendering the framebuffer. We do the actual configuration when binding the FBO. We also Keep track of config validity and save drawbuffers state in the FBO. We remove the different bind/unbind functions. These make little sense now that we have separate contexts. - DRWFrameBuffer: We replace DRW_framebuffer functions by GPU_framebuffer ones to avoid another layer of abstraction. We move the DRW convenience functions to GPUFramebuffer instead and even add new ones. The MACRO GPU_framebuffer_ensure_config is pretty much all you need to create and config a GPUFramebuffer. - DRWTexture: Due to the removal of DRWFrameBuffer, we needed to create functions to create textures for thoses framebuffers. Pool textures are now using default texture parameters for the texture type asked. - DRWManager: Make sure no framebuffer object is bound when doing cache filling. - GPUViewport: Add new color_only_fb and depth_only_fb along with FB API usage update. This let draw engines render to color/depth only target and without the need to attach/detach textures. - WM_window: Assert when a framebuffer is bound when changing context. This balance the fact we are not track ogl context inside GPUFramebuffer. - Eevee, Clay, Mode engines: Update to new API. This comes with a lot of code simplification. This also come with some cleanups in some engine codes.
Diffstat (limited to 'source/blender/gpu/intern/gpu_framebuffer.c')
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.c841
1 files changed, 409 insertions, 432 deletions
diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c
index dbcf0dc4568..0e27e3e0f8f 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.c
+++ b/source/blender/gpu/intern/gpu_framebuffer.c
@@ -30,6 +30,7 @@
#include "BLI_blenlib.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_math_base.h"
#include "BKE_global.h"
@@ -43,16 +44,67 @@
static ThreadLocal(GLuint) g_currentfb;
-/* Number of maximum output slots.
- * We support 5 outputs for now (usually we wouldn't need more to preserve fill rate) */
-#define GPU_FB_MAX_SLOTS 5
+typedef enum {
+ GPU_FB_DEPTH_ATTACHMENT = 0,
+ GPU_FB_DEPTH_STENCIL_ATTACHMENT,
+ GPU_FB_COLOR_ATTACHMENT0,
+ GPU_FB_COLOR_ATTACHMENT1,
+ GPU_FB_COLOR_ATTACHMENT2,
+ GPU_FB_COLOR_ATTACHMENT3,
+ GPU_FB_COLOR_ATTACHMENT4,
+ /* Number of maximum output slots.
+ * We support 5 outputs for now (usually we wouldn't need more to preserve fill rate). */
+ /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
+ * the maximum number of COLOR attachments specified by glDrawBuffers. */
+ GPU_FB_MAX_ATTACHEMENT
+} GPUAttachmentType;
+
+#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0)
+
+#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15)
+
+#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0)
+#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type))
struct GPUFrameBuffer {
GLuint object;
- GPUTexture *colortex[GPU_FB_MAX_SLOTS];
- GPUTexture *depthtex;
+ GPUAttachment attachments[GPU_FB_MAX_ATTACHEMENT];
+ uint16_t dirty_flag;
+ int width, height;
+ bool multisample;
+ /* TODO Check that we always use the right context when binding
+ * (FBOs are not shared accross ogl contexts). */
+ // void *ctx;
};
+static GLenum convert_attachment_type_to_gl(GPUAttachmentType type)
+{
+ static const GLenum table[] = {
+ [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT,
+ [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT,
+ [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0,
+ [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1,
+ [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2,
+ [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3,
+ [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4
+ };
+ return table[type];
+}
+
+static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot)
+{
+ switch (GPU_texture_format(tex)) {
+ case GPU_DEPTH_COMPONENT32F:
+ case GPU_DEPTH_COMPONENT24:
+ case GPU_DEPTH_COMPONENT16:
+ return GPU_FB_DEPTH_ATTACHMENT;
+ case GPU_DEPTH24_STENCIL8:
+ return GPU_FB_DEPTH_STENCIL_ATTACHMENT;
+ default:
+ return GPU_FB_COLOR_ATTACHMENT0 + slot;
+ }
+}
+
static GLenum convert_buffer_bits_to_gl(GPUFrameBufferBits bits)
{
GLbitfield mask = 0;
@@ -62,6 +114,19 @@ static GLenum convert_buffer_bits_to_gl(GPUFrameBufferBits bits)
return mask;
}
+static GPUTexture *framebuffer_get_depth_tex(GPUFrameBuffer *fb)
+{
+ if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex)
+ return fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex;
+ else
+ return fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex;;
+}
+
+static GPUTexture *framebuffer_get_color_tex(GPUFrameBuffer *fb, int slot)
+{
+ return fb->attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex;
+}
+
static void gpu_print_framebuffer_error(GLenum status, char err_out[256])
{
const char *format = "GPUFrameBuffer: framebuffer status %s\n";
@@ -102,303 +167,268 @@ static void gpu_print_framebuffer_error(GLenum status, char err_out[256])
GPUFrameBuffer *GPU_framebuffer_create(void)
{
- GPUFrameBuffer *fb;
+ /* We generate the FB object later at first use in order to
+ * create the framebuffer in the right opengl context. */
+ return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");;
+}
- fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
+static void gpu_framebuffer_init(GPUFrameBuffer *fb)
+{
glGenFramebuffers(1, &fb->object);
-
- if (!fb->object) {
- fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed.\n");
- GPU_framebuffer_free(fb);
- return NULL;
- }
-
- /* make sure no read buffer is enabled, so completeness check will not fail. We set those at binding time */
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- glReadBuffer(GL_NONE);
- glDrawBuffer(GL_NONE);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return fb;
}
-bool GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
+void GPU_framebuffer_free(GPUFrameBuffer *fb)
{
- GLenum attachment;
-
- if (slot >= GPU_FB_MAX_SLOTS) {
- fprintf(stderr,
- "Attaching to index %d framebuffer slot unsupported. "
- "Use at most %d\n", slot, GPU_FB_MAX_SLOTS);
- return false;
- }
-
- if ((G.debug & G_DEBUG)) {
- if (GPU_texture_bound_number(tex) != -1) {
- fprintf(stderr,
- "Feedback loop warning!: "
- "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n");
+ for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) {
+ if (fb->attachments[type].tex != NULL) {
+ GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex);
}
}
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- g_currentfb = fb->object;
-
- if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
- attachment = GL_DEPTH_STENCIL_ATTACHMENT;
- else if (GPU_texture_depth(tex))
- attachment = GL_DEPTH_ATTACHMENT;
- else
- attachment = GL_COLOR_ATTACHMENT0 + slot;
-
- glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip);
-
- if (GPU_texture_depth(tex))
- fb->depthtex = tex;
- else
- fb->colortex[slot] = tex;
+ /* This restores the framebuffer if it was bound */
+ glDeleteFramebuffers(1, &fb->object);
- GPU_texture_framebuffer_set(tex, fb, slot);
+ if (g_currentfb == fb->object) {
+ g_currentfb = 0;
+ }
- return true;
+ MEM_freeN(fb);
}
-static bool gpu_framebuffer_texture_layer_attach_ex(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip, bool cubemap)
-{
- GLenum attachment;
- GLenum facetarget;
+/* ---------- Attach ----------- */
- if (slot >= GPU_FB_MAX_SLOTS) {
+static void gpu_framebuffer_texture_attach_ex(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+{
+ if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) {
fprintf(stderr,
"Attaching to index %d framebuffer slot unsupported. "
- "Use at most %d\n", slot, GPU_FB_MAX_SLOTS);
- return false;
- }
-
- if ((G.debug & G_DEBUG)) {
- if (GPU_texture_bound_number(tex) != -1) {
- fprintf(stderr,
- "Feedback loop warning!: "
- "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n");
- }
+ "Use at most %d\n", slot, GPU_FB_MAX_COLOR_ATTACHMENT);
+ return;
}
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- g_currentfb = fb->object;
-
- if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
- attachment = GL_DEPTH_STENCIL_ATTACHMENT;
- else if (GPU_texture_depth(tex))
- attachment = GL_DEPTH_ATTACHMENT;
- else
- attachment = GL_COLOR_ATTACHMENT0 + slot;
+ GPUAttachmentType type = attachment_type_from_tex(tex, slot);
+ GPUAttachment *attachment = &fb->attachments[type];
- if (cubemap) {
- facetarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
- glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, facetarget, GPU_texture_opengl_bindcode(tex), mip);
+ if ((attachment->tex == tex) &&
+ (attachment->mip == mip) &&
+ (attachment->layer == layer))
+ {
+ return; /* Exact same texture already bound here. */
}
- else {
- glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), mip, layer);
+ else if (attachment->tex != NULL) {
+ GPU_framebuffer_texture_detach(fb, attachment->tex);
}
- if (GPU_texture_depth(tex))
- fb->depthtex = tex;
- else
- fb->colortex[slot] = tex;
-
- GPU_texture_framebuffer_set(tex, fb, slot);
+ if (attachment->tex == NULL) {
+ GPU_texture_attach_framebuffer(tex, fb, type);
+ }
- return true;
+ attachment->tex = tex;
+ attachment->mip = mip;
+ attachment->layer = layer;
+ GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
}
-bool GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
{
- return gpu_framebuffer_texture_layer_attach_ex(fb, tex, slot, layer, mip, false);
+ return gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip);
}
-bool GPU_framebuffer_texture_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
+void GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
{
- BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_CUBE_MAP);
- return gpu_framebuffer_texture_layer_attach_ex(fb, tex, slot, face, mip, true);
+ /* NOTE: We could support 1D ARRAY texture. */
+ BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY);
+ return gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip);
}
-void GPU_framebuffer_texture_detach(GPUTexture *tex)
+void GPU_framebuffer_texture_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
{
- GLenum attachment;
- GPUFrameBuffer *fb = GPU_texture_framebuffer(tex);
- int fb_attachment = GPU_texture_framebuffer_attachment(tex);
+ BLI_assert(GPU_texture_cube(tex));
+ return gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip);
+}
- if (!fb)
- return;
+/* ---------- Detach ----------- */
- if (g_currentfb != fb->object) {
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- g_currentfb = fb->object;
- }
+void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type)
+{
+ GPUAttachment *attachment = &fb->attachments[type];
- if (GPU_texture_stencil(tex) && GPU_texture_depth(tex)) {
- fb->depthtex = NULL;
- attachment = GL_DEPTH_STENCIL_ATTACHMENT;
- }
- else if (GPU_texture_depth(tex)) {
- fb->depthtex = NULL;
- attachment = GL_DEPTH_ATTACHMENT;
- }
- else {
- BLI_assert(fb->colortex[fb_attachment] == tex);
- fb->colortex[fb_attachment] = NULL;
- attachment = GL_COLOR_ATTACHMENT0 + fb_attachment;
+ if (attachment->tex != tex) {
+ fprintf(stderr,
+ "Warning, attempting to detach Texture %p from framebuffer %p "
+ "but texture is not attached.\n", tex, fb);
+ return;
}
- glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
+ attachment->tex = NULL;
+ GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
+}
- GPU_texture_framebuffer_set(tex, NULL, -1);
+void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex)
+{
+ GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb);
+ GPU_framebuffer_texture_detach_slot(fb, tex, type);
}
-void GPU_texture_bind_as_framebuffer(GPUTexture *tex)
+/* ---------- Config (Attach & Detach) ----------- */
+
+/**
+ * First GPUAttachment in *config is always the depth/depth_stencil buffer.
+ * Following GPUAttachments are color buffers.
+ * Setting GPUAttachment.mip to -1 will leave the texture in this slot.
+ * Setting GPUAttachment.tex to NULL will detach the texture in this slot.
+ **/
+void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_ct)
{
- GPUFrameBuffer *fb = GPU_texture_framebuffer(tex);
- int fb_attachment = GPU_texture_framebuffer_attachment(tex);
+ if (config[0].tex) {
+ BLI_assert(GPU_texture_depth(config[0].tex));
+ gpu_framebuffer_texture_attach_ex(fb, config[0].tex, 0, config[0].layer, config[0].mip);
+ }
+ else if (config[0].mip == -1) {
+ /* Leave texture attached */
+ }
+ else if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex != NULL) {
+ GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex);
+ }
+ else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) {
+ GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex);
+ }
- if (!fb) {
- fprintf(stderr, "Error, texture not bound to framebuffer!\n");
- return;
+ int slot = 0;
+ for (int i = 1; i < config_ct; ++i, ++slot) {
+ if (config[i].tex != NULL) {
+ BLI_assert(GPU_texture_depth(config[i].tex) == false);
+ gpu_framebuffer_texture_attach_ex(fb, config[i].tex, slot, config[i].layer, config[i].mip);
+ }
+ else if (config[i].mip != -1) {
+ GPUTexture *tex = framebuffer_get_color_tex(fb, slot);
+ if (tex != NULL) {
+ GPU_framebuffer_texture_detach(fb, tex);
+ }
+ }
}
+}
- /* push attributes */
- gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
- glDisable(GL_SCISSOR_TEST);
+/* ---------- Bind / Restore ----------- */
- /* bind framebuffer */
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
+static void gpu_framebuffer_attachment_attach(GPUAttachment *attach, GPUAttachmentType attach_type)
+{
+ int tex_bind = GPU_texture_opengl_bindcode(attach->tex);
+ GLenum gl_attachment = convert_attachment_type_to_gl(attach_type);
- if (GPU_texture_depth(tex)) {
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
+ if (attach->layer > -1) {
+ if (GPU_texture_cube(attach->tex)) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach->layer,
+ tex_bind, attach->mip);
+ }
+ else {
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip, attach->layer);
+ }
}
else {
- /* last bound prevails here, better allow explicit control here too */
- glDrawBuffer(GL_COLOR_ATTACHMENT0 + fb_attachment);
- glReadBuffer(GL_COLOR_ATTACHMENT0 + fb_attachment);
- }
-
- if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) {
- glEnable(GL_MULTISAMPLE);
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip);
}
+}
- /* set default viewport */
- glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
- g_currentfb = fb->object;
+static void gpu_framebuffer_attachment_detach(GPUAttachment *UNUSED(attachment), GPUAttachmentType attach_type)
+{
+ GLenum gl_attachment = convert_attachment_type_to_gl(attach_type);
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0);
}
-void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot)
+static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb)
{
- int numslots = 0, i;
- GLenum attachments[GPU_FB_MAX_SLOTS];
-
- if (!fb->colortex[slot]) {
- fprintf(stderr, "Error, framebuffer slot empty!\n");
- return;
- }
-
- for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
- if (fb->colortex[i]) {
- attachments[numslots] = GL_COLOR_ATTACHMENT0 + i;
+ GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT];
+ int numslots = 0;
+
+ BLI_assert(g_currentfb == fb->object);
+
+ /* Update attachments */
+ for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) {
+
+ if (type >= GPU_FB_COLOR_ATTACHMENT0) {
+ if (fb->attachments[type].tex) {
+ gl_attachments[numslots] = convert_attachment_type_to_gl(type);
+ }
+ else {
+ gl_attachments[numslots] = GL_NONE;
+ }
numslots++;
}
- }
-
- /* push attributes */
- gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
- glDisable(GL_SCISSOR_TEST);
- /* bind framebuffer */
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
+ if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) {
+ continue;
+ }
+ else if (fb->attachments[type].tex != NULL) {
+ gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
- /* last bound prevails here, better allow explicit control here too */
- glDrawBuffers(numslots, attachments);
- glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
+ fb->multisample = (GPU_texture_samples(fb->attachments[type].tex) > 0);
+ fb->width = GPU_texture_width(fb->attachments[type].tex);
+ fb->height = GPU_texture_height(fb->attachments[type].tex);
+ }
+ else {
+ gpu_framebuffer_attachment_detach(&fb->attachments[type], type);
+ }
+ }
+ fb->dirty_flag = 0;
- /* set default viewport */
- glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
- g_currentfb = fb->object;
+ /* Update draw buffers (color targets)
+ * This state is saved in the FBO */
+ if (numslots)
+ glDrawBuffers(numslots, gl_attachments);
+ else
+ glDrawBuffer(GL_NONE);
}
void GPU_framebuffer_bind(GPUFrameBuffer *fb)
{
- int numslots = 0, i;
- GLenum attachments[GPU_FB_MAX_SLOTS];
- GLenum readattachement = 0;
- GPUTexture *tex;
+ if (fb->object == 0)
+ gpu_framebuffer_init(fb);
- for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
- if (fb->colortex[i]) {
- attachments[numslots] = GL_COLOR_ATTACHMENT0 + i;
- tex = fb->colortex[i];
-
- if (!readattachement)
- readattachement = GL_COLOR_ATTACHMENT0 + i;
+ if (g_currentfb != fb->object)
+ glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- numslots++;
- }
- }
+ g_currentfb = fb->object;
- /* bind framebuffer */
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
+ if (fb->dirty_flag != 0)
+ gpu_framebuffer_update_attachments(fb);
- if (numslots == 0) {
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
- tex = fb->depthtex;
- }
- else {
- /* last bound prevails here, better allow explicit control here too */
- glDrawBuffers(numslots, attachments);
- glReadBuffer(readattachement);
+ /* TODO manually check for errors? */
+#if 0
+ char err_out[256];
+ if (!GPU_framebuffer_check_valid(fb, err_out)) {
+ printf("Invalid %s\n", err_out);
}
+#endif
- if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) {
+ if (fb->multisample)
glEnable(GL_MULTISAMPLE);
- }
- glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
- g_currentfb = fb->object;
+ glViewport(0, 0, fb->width, fb->height);
}
-void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex))
+void GPU_framebuffer_restore(void)
{
- /* Restore attributes. */
- gpuPopAttrib();
+ if (g_currentfb != 0) {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ g_currentfb = 0;
+ }
}
-void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot)
+bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
{
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- /* last bound prevails here, better allow explicit control here too */
- glDrawBuffer(GL_COLOR_ATTACHMENT0 + slot);
- glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
-
- /* push matrices and set default viewport and matrix */
- glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
- g_currentfb = fb->object;
+ return (fb->object == g_currentfb) && (fb->object != 0);
}
-bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
+unsigned int GPU_framebuffer_current_get(void)
{
- return fb->object == g_currentfb;
+ return g_currentfb;
}
bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
{
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- g_currentfb = fb->object;
-
- /* On macOS glDrawBuffer must be set when checking completeness,
- * otherwise it will return GL_FRAMEBUFFER_UNSUPPORTED when only a
- * color buffer without depth is used. */
- if (fb->colortex[0]) {
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- }
+ if (g_currentfb != fb->object)
+ GPU_framebuffer_bind(fb);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -411,42 +441,7 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
return true;
}
-void GPU_framebuffer_free(GPUFrameBuffer *fb)
-{
- int i;
- if (fb->depthtex)
- GPU_framebuffer_texture_detach(fb->depthtex);
-
- for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
- if (fb->colortex[i]) {
- GPU_framebuffer_texture_detach(fb->colortex[i]);
- }
- }
-
- if (fb->object) {
- glDeleteFramebuffers(1, &fb->object);
-
- if (g_currentfb == fb->object) {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- g_currentfb = 0;
- }
- }
-
- MEM_freeN(fb);
-}
-
-unsigned int GPU_framebuffer_current_get(void)
-{
- return g_currentfb;
-}
-
-void GPU_framebuffer_restore(void)
-{
- if (g_currentfb != 0) {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- g_currentfb = 0;
- }
-}
+/* ---------- Framebuffer Operations ----------- */
#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \
BLI_assert(GPU_framebuffer_bound(_fb)); \
@@ -482,61 +477,104 @@ void GPU_framebuffer_clear(
glClear(mask);
}
+void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data)
+{
+ CHECK_FRAMEBUFFER_IS_BOUND(fb);
+
+ GLenum type = GL_DEPTH_COMPONENT;
+ glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */
+ glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
+
+void GPU_framebuffer_read_color(
+ GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data)
+{
+ CHECK_FRAMEBUFFER_IS_BOUND(fb);
+
+ GLenum type;
+ switch (channels) {
+ case 1: type = GL_RED; break;
+ case 2: type = GL_RG; break;
+ case 3: type = GL_RGB; break;
+ case 4: type = GL_RGBA; break;
+ default:
+ BLI_assert(false && "wrong number of read channels");
+ return;
+ }
+ glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
+ glReadPixels(x, y, w, h, type, GL_FLOAT, data);
+}
+
+/* read_slot and write_slot are only used for color buffers. */
void GPU_framebuffer_blit(
- GPUFrameBuffer *fb_read, int read_slot, GPUFrameBuffer *fb_write,
- int write_slot, bool use_depth, bool use_stencil)
-{
- GPUTexture *read_tex = (use_depth || use_stencil) ? fb_read->depthtex : fb_read->colortex[read_slot];
- GPUTexture *write_tex = (use_depth || use_stencil) ? fb_write->depthtex : fb_write->colortex[write_slot];
- int read_attach = (use_depth) ? GL_DEPTH_ATTACHMENT :
- (use_stencil) ? GL_DEPTH_STENCIL_ATTACHMENT :
- GL_COLOR_ATTACHMENT0 + GPU_texture_framebuffer_attachment(read_tex);
- int write_attach = (use_depth) ? GL_DEPTH_ATTACHMENT :
- (use_stencil) ? GL_DEPTH_STENCIL_ATTACHMENT :
- GL_COLOR_ATTACHMENT0 + GPU_texture_framebuffer_attachment(write_tex);
- int read_bind = GPU_texture_opengl_bindcode(read_tex);
- int write_bind = GPU_texture_opengl_bindcode(write_tex);
- const int read_w = GPU_texture_width(read_tex);
- const int read_h = GPU_texture_height(read_tex);
- const int write_w = GPU_texture_width(write_tex);
- const int write_h = GPU_texture_height(write_tex);
-
-
- /* Never both! */
- BLI_assert(!(use_depth && use_stencil));
-
- if (use_depth) {
+ GPUFrameBuffer *fb_read, int read_slot,
+ GPUFrameBuffer *fb_write, int write_slot,
+ GPUFrameBufferBits blit_buffers)
+{
+ BLI_assert(blit_buffers != 0);
+
+ GLuint prev_fb = g_currentfb;
+
+ /* Framebuffers must be up to date. This simplify this function. */
+ if (fb_read->dirty_flag != 0 || fb_read->object == 0) {
+ GPU_framebuffer_bind(fb_read);
+ }
+ if (fb_write->dirty_flag != 0 || fb_write->object == 0) {
+ GPU_framebuffer_bind(fb_write);
+ }
+
+ const bool do_color = (blit_buffers & GPU_COLOR_BIT);
+ const bool do_depth = (blit_buffers & GPU_DEPTH_BIT);
+ const bool do_stencil = (blit_buffers & GPU_STENCIL_BIT);
+
+ GPUTexture *read_tex = (do_depth || do_stencil)
+ ? framebuffer_get_depth_tex(fb_read)
+ : framebuffer_get_color_tex(fb_read, read_slot);
+ GPUTexture *write_tex = (do_depth || do_stencil)
+ ? framebuffer_get_depth_tex(fb_write)
+ : framebuffer_get_color_tex(fb_write, read_slot);
+
+ if (do_depth) {
BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex));
BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
}
- else if (use_stencil) {
+ if (do_stencil) {
BLI_assert(GPU_texture_stencil(read_tex) && GPU_texture_stencil(write_tex));
BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
}
+ if (GPU_texture_samples(write_tex) != 0 ||
+ GPU_texture_samples(read_tex) != 0)
+ {
+ /* Can only blit multisample textures to another texture of the same size. */
+ BLI_assert((fb_read->width == fb_write->width) &&
+ (fb_read->height == fb_write->height));
+ }
- /* read from multi-sample buffer */
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object);
- glFramebufferTexture2D(
- GL_READ_FRAMEBUFFER, read_attach,
- GPU_texture_target(read_tex), read_bind, 0);
- BLI_assert(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
-
- /* write into new single-sample buffer */
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object);
- glFramebufferTexture2D(
- GL_DRAW_FRAMEBUFFER, write_attach,
- GPU_texture_target(write_tex), write_bind, 0);
- BLI_assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
- glDrawBuffer((use_depth || use_stencil) ? GL_COLOR_ATTACHMENT0 : read_attach);
- glBlitFramebuffer(0, 0, read_w, read_h, 0, 0, write_w, write_h,
- (use_depth) ? GL_DEPTH_BUFFER_BIT :
- (use_stencil) ? GL_STENCIL_BUFFER_BIT :
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ if (do_color) {
+ glReadBuffer(GL_COLOR_ATTACHMENT0 + read_slot);
+ glDrawBuffer(GL_COLOR_ATTACHMENT0 + write_slot);
+ /* XXX we messed with the glDrawBuffer, this will reset the
+ * glDrawBuffers the next time we bind fb_write. */
+ fb_write->dirty_flag = GPU_FB_DIRTY_DRAWBUFFER;
+ }
+
+ GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers);
+
+ glBlitFramebuffer(0, 0, fb_read->width, fb_read->height,
+ 0, 0, fb_write->width, fb_write->height,
+ mask, GL_NEAREST);
/* Restore previous framebuffer */
- glBindFramebuffer(GL_FRAMEBUFFER, g_currentfb);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
+ if (fb_write->object == prev_fb) {
+ GPU_framebuffer_bind(fb_write); /* To update drawbuffers */
+ }
+ else {
+ glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
+ g_currentfb = prev_fb;
+ }
}
/**
@@ -544,68 +582,63 @@ void GPU_framebuffer_blit(
* This function only takes care of the correct texture handling. It execute the callback for each texture level.
**/
void GPU_framebuffer_recursive_downsample(
- GPUFrameBuffer *fb, GPUTexture *tex, int num_iter, void (*callback)(void *userData, int level), void *userData)
+ GPUFrameBuffer *fb, int max_lvl,
+ void (*callback)(void *userData, int level), void *userData)
{
- int i;
- int current_dim[2] = {GPU_texture_width(tex), GPU_texture_height(tex)};
- GLenum attachment;
-
- /* Manually setup framebuffer to not use GPU_texture_framebuffer_set() */
- glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- g_currentfb = fb->object;
-
- if (GPU_texture_stencil(tex) && GPU_texture_depth(tex))
- attachment = GL_DEPTH_STENCIL_ATTACHMENT;
- else if (GPU_texture_depth(tex))
- attachment = GL_DEPTH_ATTACHMENT;
- else
- attachment = GL_COLOR_ATTACHMENT0;
-
- /* last bound prevails here, better allow explicit control here too */
- if (GPU_texture_depth(tex)) {
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
- }
- else {
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
- glReadBuffer(GL_COLOR_ATTACHMENT0);
+ /* Framebuffer must be up to date and bound. This simplify this function. */
+ if (g_currentfb != fb->object || fb->dirty_flag != 0 || fb->object == 0) {
+ GPU_framebuffer_bind(fb);
}
+ /* HACK: We make the framebuffer appear not bound in order to
+ * not trigger any error in GPU_texture_bind(). */
+ GLuint prev_fb = g_currentfb;
+ g_currentfb = 0;
- for (i = 1; i < num_iter + 1; i++) {
-
+ int i;
+ int current_dim[2] = {fb->width, fb->height};
+ for (i = 1; i < max_lvl + 1; i++) {
/* calculate next viewport size */
- current_dim[0] /= 2;
- current_dim[1] /= 2;
-
- if (current_dim[0] <= 2 && current_dim[1] <= 2) {
- /* Cannot reduce further. */
- break;
+ current_dim[0] = max_ii(current_dim[0] / 2, 1);
+ current_dim[1] = max_ii(current_dim[1] / 2, 1);
+
+ for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) {
+ if (fb->attachments[type].tex != NULL) {
+ /* bind next level for rendering but first restrict fetches only to previous level */
+ GPUTexture *tex = fb->attachments[type].tex;
+ GPU_texture_bind(tex, 0);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
+ GPU_texture_unbind(tex);
+ /* copy attachment and replace miplevel. */
+ GPUAttachment attachment = fb->attachments[type];
+ attachment.mip = i;
+ gpu_framebuffer_attachment_attach(&attachment, type);
+ }
}
- /* ensure that the viewport size is always at least 1x1 */
- CLAMP_MIN(current_dim[0], 1);
- CLAMP_MIN(current_dim[1], 1);
-
glViewport(0, 0, current_dim[0], current_dim[1]);
-
- /* bind next level for rendering but first restrict fetches only to previous level */
- GPU_texture_bind(tex, 0);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
- GPU_texture_unbind(tex);
-
- glFramebufferTexture(GL_FRAMEBUFFER, attachment, GPU_texture_opengl_bindcode(tex), i);
-
callback(userData, i);
+
+ if (current_dim[0] == 1 && current_dim[1] == 1)
+ break;
}
- glFramebufferTexture(GL_FRAMEBUFFER, attachment, 0, 0);
+ for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) {
+ if (fb->attachments[type].tex != NULL) {
+ /* reset mipmap level range */
+ GPUTexture *tex = fb->attachments[type].tex;
+ GPU_texture_bind(tex, 0);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
+ GPU_texture_unbind(tex);
+ /* Reattach original level */
+ /* NOTE: This is not necessary but this makes the FBO config
+ * remain in sync with the GPUFrameBuffer config. */
+ gpu_framebuffer_attachment_attach(&fb->attachments[type], type);
+ }
+ }
- /* reset mipmap level range for the depth image */
- GPU_texture_bind(tex, 0);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1);
- GPU_texture_unbind(tex);
+ g_currentfb = prev_fb;
}
/* GPUOffScreen */
@@ -622,51 +655,23 @@ GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, bool dept
ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen");
- ofs->fb = GPU_framebuffer_create();
- if (!ofs->fb) {
- GPU_offscreen_free(ofs);
- return NULL;
- }
-
- if (samples) {
- if (!GLEW_ARB_texture_multisample ||
- /* This is required when blitting from a multi-sampled buffers,
- * even though we're not scaling. */
- !GLEW_EXT_framebuffer_multisample_blit_scaled)
- {
- samples = 0;
- }
- }
+ ofs->color = GPU_texture_create_2D_custom_multisample(width, height, 4,
+ (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, samples, err_out);
if (depth) {
ofs->depth = GPU_texture_create_depth_with_stencil_multisample(width, height, samples, err_out);
- if (!ofs->depth) {
- GPU_offscreen_free(ofs);
- return NULL;
- }
-
- if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, 0)) {
- GPU_offscreen_free(ofs);
- return NULL;
- }
- }
-
- if (high_bitdepth) {
- ofs->color = GPU_texture_create_2D_custom_multisample(width, height, 4, GPU_RGBA16F, NULL, samples, err_out);
- }
- else {
- ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, samples, err_out);
- }
- if (!ofs->color) {
- GPU_offscreen_free(ofs);
- return NULL;
}
- if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, 0)) {
+ if (!ofs->depth || !ofs->color) {
GPU_offscreen_free(ofs);
return NULL;
}
+ GPU_framebuffer_ensure_config(&ofs->fb, {
+ GPU_ATTACHMENT_TEXTURE(ofs->depth),
+ GPU_ATTACHMENT_TEXTURE(ofs->color)
+ });
+
/* check validity at the very end! */
if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) {
GPU_offscreen_free(ofs);
@@ -692,68 +697,48 @@ void GPU_offscreen_free(GPUOffScreen *ofs)
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save)
{
- glDisable(GL_SCISSOR_TEST);
- if (save)
- GPU_texture_bind_as_framebuffer(ofs->color);
- else {
- GPU_framebuffer_bind_no_save(ofs->fb, 0);
+ if (save) {
+ gpuPushAttrib(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT);
}
+ glDisable(GL_SCISSOR_TEST);
+ GPU_framebuffer_bind(ofs->fb);
}
-void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore)
+void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore)
{
- if (restore)
- GPU_framebuffer_texture_unbind(ofs->fb, ofs->color);
GPU_framebuffer_restore();
- glEnable(GL_SCISSOR_TEST);
+ if (restore) {
+ gpuPopAttrib();
+ }
}
-
void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
{
const int w = GPU_texture_width(ofs->color);
const int h = GPU_texture_height(ofs->color);
+ BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT);
+
if (GPU_texture_target(ofs->color) == GL_TEXTURE_2D_MULTISAMPLE) {
/* For a multi-sample texture,
* we need to create an intermediate buffer to blit to,
* before its copied using 'glReadPixels' */
-
- /* not needed since 'ofs' needs to be bound to the framebuffer already */
-// #define USE_FBO_CTX_SWITCH
-
GLuint fbo_blit = 0;
GLuint tex_blit = 0;
- GLenum status;
/* create texture for new 'fbo_blit' */
glGenTextures(1, &tex_blit);
- if (!tex_blit) {
- goto finally;
- }
-
glBindTexture(GL_TEXTURE_2D, tex_blit);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, type, 0);
-
-#ifdef USE_FBO_CTX_SWITCH
- /* read from multi-sample buffer */
- glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs->color->fb->object);
- glFramebufferTexture2D(
- GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + ofs->color->fb_attachment,
- GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0);
- status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- goto finally;
- }
-#endif
+ glTexImage2D(GL_TEXTURE_2D, 0, (type == GL_FLOAT) ? GL_RGBA16F : GL_RGBA8,
+ w, h, 0, GL_RGBA, type, 0);
/* write into new single-sample buffer */
glGenFramebuffers(1, &fbo_blit);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit);
- glFramebufferTexture2D(
- GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, tex_blit, 0);
- status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, tex_blit, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
goto finally;
}
@@ -765,21 +750,13 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_blit);
glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
-#ifdef USE_FBO_CTX_SWITCH
/* restore the original frame-bufer */
- glBindFramebuffer(GL_FRAMEBUFFER, ofs->color->fb->object);
-#undef USE_FBO_CTX_SWITCH
-#endif
-
+ glBindFramebuffer(GL_FRAMEBUFFER, ofs->fb->object);
finally:
/* cleanup */
- if (tex_blit) {
- glDeleteTextures(1, &tex_blit);
- }
- if (fbo_blit) {
- glDeleteFramebuffers(1, &fbo_blit);
- }
+ glDeleteTextures(1, &tex_blit);
+ glDeleteFramebuffers(1, &fbo_blit);
}
else {
glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);