diff options
Diffstat (limited to 'source/blender/gpu')
-rw-r--r-- | source/blender/gpu/GPU_framebuffer.h | 106 | ||||
-rw-r--r-- | source/blender/gpu/GPU_texture.h | 5 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_framebuffer.c | 841 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_lamp.c | 79 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture.c | 58 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_viewport.c | 154 |
6 files changed, 617 insertions, 626 deletions
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 93f16b342d1..0ab15a4ea47 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -36,6 +36,13 @@ extern "C" { #endif +struct GPUTexture; + +typedef struct GPUAttachment { + struct GPUTexture *tex; + int mip, layer; +} GPUAttachment; + typedef enum GPUFrameBufferBits{ GPU_COLOR_BIT = (1 << 0), GPU_DEPTH_BIT = (1 << 1), @@ -44,40 +51,95 @@ typedef enum GPUFrameBufferBits{ typedef struct GPUFrameBuffer GPUFrameBuffer; typedef struct GPUOffScreen GPUOffScreen; -struct GPUTexture; /* GPU Framebuffer * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice * multiple FBO's may be created, to get around limitations on the number * of attached textures and the dimension requirements. - * - after any of the GPU_framebuffer_* functions, GPU_framebuffer_restore must - * be called before rendering to the window framebuffer again */ - -void GPU_texture_bind_as_framebuffer(struct GPUTexture *tex); + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). + */ GPUFrameBuffer *GPU_framebuffer_create(void); -bool GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip); -bool GPU_framebuffer_texture_layer_attach( - GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip); -bool GPU_framebuffer_texture_cubeface_attach( - GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip); -void GPU_framebuffer_texture_detach(struct GPUTexture *tex); -void GPU_framebuffer_bind(GPUFrameBuffer *fb); -void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot); -void GPU_framebuffer_texture_unbind(GPUFrameBuffer *fb, struct GPUTexture *tex); void GPU_framebuffer_free(GPUFrameBuffer *fb); +void GPU_framebuffer_bind(GPUFrameBuffer *fb); +void GPU_framebuffer_restore(void); + +bool GPU_framebuffer_bound(GPUFrameBuffer *fb); bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]); /* internal use only */ unsigned int GPU_framebuffer_current_get(void); -void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot); -bool GPU_framebuffer_bound(GPUFrameBuffer *fb); +#define GPU_FRAMEBUFFER_FREE_SAFE(fb) do { \ + if (fb != NULL) { \ + GPU_framebuffer_free(fb); \ + fb = NULL; \ + } \ +} while (0) + +/* Framebuffer setup : You need to call GPU_framebuffer_bind for theses + * to be effective. */ + +void GPU_framebuffer_texture_attach( + GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip); +void GPU_framebuffer_texture_layer_attach( + GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip); +void GPU_framebuffer_texture_cubeface_attach( + GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip); +void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, struct GPUTexture *tex); +void GPU_framebuffer_texture_detach_slot( + GPUFrameBuffer *fb, struct GPUTexture *tex, int type); + +/** + * How to use GPU_framebuffer_ensure_config(). + * + * Example : + * GPU_framebuffer_ensure_config(&fb, { + * GPU_ATTACHMENT_TEXTURE(depth), // must be depth buffer + * GPU_ATTACHMENT_TEXTURE(tex1), + * GPU_ATTACHMENT_TEXTURE_CUBEFACE(tex2, 0), + * GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex2, 0, 0) + * }) + * + * Note : Unspecified attachements (i.e: those beyond the last + * GPU_ATTACHMENT_* in GPU_framebuffer_ensure_config list) + * are left unchanged. + * Note : Make sure that the dimensions of your textures matches + * otherwise you will have an invalid framebuffer error. + **/ +#define GPU_framebuffer_ensure_config(_fb, ...) do { \ + if (*(_fb) == NULL) { \ + *(_fb) = GPU_framebuffer_create(); \ + } \ + GPUAttachment config[] = __VA_ARGS__; \ + GPU_framebuffer_config_array(*(_fb), config, (sizeof(config) / sizeof(GPUAttachment))); \ +} while (0) + +void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_ct); + +#define GPU_ATTACHMENT_NONE \ + {.tex = NULL, .layer = -1, .mip = 0} +#define GPU_ATTACHMENT_LEAVE \ + {.tex = NULL, .layer = -1, .mip = -1} +#define GPU_ATTACHMENT_TEXTURE(_tex) \ + {.tex = _tex, .layer = -1, .mip = 0} +#define GPU_ATTACHMENT_TEXTURE_MIP(_tex, _mip) \ + {.tex = _tex, .layer = -1, .mip = _mip} +#define GPU_ATTACHMENT_TEXTURE_LAYER(_tex, _layer) \ + {.tex = _tex, .layer = _layer, .mip = 0} +#define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_tex, _layer, _mip) \ + {.tex = _tex, .layer = _layer, .mip = _mip} +#define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_tex, _face) \ + {.tex = _tex, .layer = _face, .mip = 0} +#define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_tex, _face, _mip) \ + {.tex = _tex, .layer = _face, .mip = _mip} + /* Framebuffer operations */ void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h); -void GPU_framebuffer_restore(void); void GPU_framebuffer_clear( GPUFrameBuffer *fb, GPUFrameBufferBits buffers, const float clear_col[4], float clear_depth, unsigned int clear_stencil); @@ -100,18 +162,22 @@ void GPU_framebuffer_clear( #define GPU_framebuffer_clear_color_depth_stencil(fb, col, depth, stencil) \ GPU_framebuffer_clear(fb, GPU_COLOR_BIT | GPU_DEPTH_BIT | GPU_STENCIL_BIT, col, depth, stencil) +void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data); +void GPU_framebuffer_read_color( + GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data); void GPU_framebuffer_blit( GPUFrameBuffer *fb_read, int read_slot, - GPUFrameBuffer *fb_write, int write_slot, bool use_depth, bool use_stencil); + GPUFrameBuffer *fb_write, int write_slot, + GPUFrameBufferBits blit_buffers); void GPU_framebuffer_recursive_downsample( - GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter, + GPUFrameBuffer *fb, int max_lvl, void (*callback)(void *userData, int level), void *userData); /* GPU OffScreen * - wrapper around framebuffer and texture for simple offscreen drawing - * - changes size if graphics card can't support it */ + */ GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, bool depth, bool high_bitdepth, char err_out[256]); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 72e3859a5a8..57185d2f39e 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -189,9 +189,8 @@ void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter); void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat); -struct GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex); -int GPU_texture_framebuffer_attachment(GPUTexture *tex); -void GPU_texture_framebuffer_set(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment); +void GPU_texture_attach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment); +int GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb); int GPU_texture_target(const GPUTexture *tex); int GPU_texture_width(const GPUTexture *tex); 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); diff --git a/source/blender/gpu/intern/gpu_lamp.c b/source/blender/gpu/intern/gpu_lamp.c index ffdb433bacf..8968521060d 100644 --- a/source/blender/gpu/intern/gpu_lamp.c +++ b/source/blender/gpu/intern/gpu_lamp.c @@ -267,89 +267,35 @@ GPULamp *GPU_lamp_from_blender(Scene *scene, Object *ob, Object *par) if ((la->type == LA_SPOT && (la->mode & (LA_SHAD_BUF | LA_SHAD_RAY))) || (la->type == LA_SUN && (la->mode & LA_SHAD_RAY))) { - /* opengl */ - lamp->fb = GPU_framebuffer_create(); - if (!lamp->fb) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) { - /* Shadow depth map */ lamp->depthtex = GPU_texture_create_depth(lamp->size, lamp->size, NULL); - if (!lamp->depthtex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - GPU_texture_bind(lamp->depthtex, 0); - GPU_texture_compare_mode(lamp->depthtex, true); - GPU_texture_unbind(lamp->depthtex); - - if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, 0)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - /* Shadow color map */ lamp->tex = gpu_lamp_create_vsm_shadow_map(lamp->size); - if (!lamp->tex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - /* FBO and texture for blurring */ - lamp->blurfb = GPU_framebuffer_create(); - if (!lamp->blurfb) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - lamp->blurtex = gpu_lamp_create_vsm_shadow_map(lamp->size * 0.5); - if (!lamp->blurtex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - if (!GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, 0)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } + lamp->fb = GPU_framebuffer_create(); + GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, 0); + GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0); - /* we need to properly bind to test for completeness */ - GPU_texture_bind_as_framebuffer(lamp->blurtex); + lamp->blurfb = GPU_framebuffer_create(); + GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, 0); - if (!GPU_framebuffer_check_valid(lamp->blurfb, NULL)) { + if (!GPU_framebuffer_check_valid(lamp->fb, NULL) || + !GPU_framebuffer_check_valid(lamp->blurfb, NULL)) + { gpu_lamp_shadow_free(lamp); return lamp; } - } else { lamp->tex = GPU_texture_create_depth(lamp->size, lamp->size, NULL); - if (!lamp->tex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } GPU_texture_bind(lamp->tex, 0); GPU_texture_compare_mode(lamp->tex, true); + GPU_texture_filter_mode(lamp->tex, true); GPU_texture_unbind(lamp->tex); - if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } + lamp->fb = GPU_framebuffer_create(); + GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0); if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) { gpu_lamp_shadow_free(lamp); @@ -437,7 +383,7 @@ void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsiz /* opengl */ glDisable(GL_SCISSOR_TEST); - GPU_texture_bind_as_framebuffer(lamp->tex); + GPU_framebuffer_bind(lamp->fb); if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) GPU_shader_bind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE)); @@ -486,7 +432,6 @@ void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp) gpu_lamp_shadow_blur(lamp); } - GPU_framebuffer_texture_unbind(lamp->fb, lamp->tex); GPU_framebuffer_restore(); glEnable(GL_SCISSOR_TEST); } diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index b8368343a88..2ccc9ce7ca7 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -48,6 +48,9 @@ static struct GPUTextureGlobal { GPUTexture *invalid_tex_3D; } GG = {NULL, NULL, NULL}; +/* Maximum number of FBOs a texture can be attached to. */ +#define GPU_TEX_MAX_FBO_ATTACHED 8 + typedef enum GPUTextureFormatFlag{ GPU_FORMAT_DEPTH = (1 << 0), GPU_FORMAT_STENCIL = (1 << 1), @@ -72,14 +75,15 @@ struct GPUTexture { GLuint bindcode; /* opengl identifier for texture */ int fromblender; /* we got the texture from Blender */ + GPUTextureFormat format; GPUTextureFormatFlag format_flag; - GPUFrameBuffer *fb; /* GPUFramebuffer this texture is attached to */ - int fb_attachment; /* slot the texture is attached to */ unsigned int bytesize; /* number of byte for one pixel */ - GPUTextureFormat format; int components; /* number of color/alpha channels */ int samples; /* number of samples for multisamples textures. 0 if not multisample target */ + + int fb_attachment[GPU_TEX_MAX_FBO_ATTACHED]; + GPUFrameBuffer *fb[GPU_TEX_MAX_FBO_ATTACHED]; }; /* ------ Memory Management ------- */ @@ -355,7 +359,6 @@ static GPUTexture *GPU_texture_create_nD( tex->samples = samples; tex->number = -1; tex->refcount = 1; - tex->fb_attachment = -1; tex->format = data_type; tex->components = components; tex->format_flag = 0; @@ -506,7 +509,6 @@ static GPUTexture *GPU_texture_cube_create( tex->samples = 0; tex->number = -1; tex->refcount = 1; - tex->fb_attachment = -1; tex->format = data_type; tex->components = components; tex->format_flag = GPU_FORMAT_CUBE; @@ -847,8 +849,13 @@ void GPU_texture_bind(GPUTexture *tex, int number) } if ((G.debug & G_DEBUG)) { - if (tex->fb && GPU_framebuffer_bound(tex->fb)) { - fprintf(stderr, "Feedback loop warning!: Attempting to bind texture attached to current framebuffer!\n"); + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] && GPU_framebuffer_bound(tex->fb[i])) { + fprintf(stderr, "Feedback loop warning!: Attempting to bind " + "texture attached to current framebuffer!\n"); + BLI_assert(0); /* Should never happen! */ + break; + } } } @@ -961,8 +968,12 @@ void GPU_texture_free(GPUTexture *tex) fprintf(stderr, "GPUTexture: negative refcount\n"); if (tex->refcount == 0) { - if (tex->fb) - GPU_framebuffer_texture_detach(tex); + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] != NULL) { + GPU_framebuffer_texture_detach_slot(tex->fb[i], tex, tex->fb_attachment[i]); + } + } + if (tex->bindcode && !tex->fromblender) glDeleteTextures(1, &tex->bindcode); @@ -1027,19 +1038,28 @@ int GPU_texture_opengl_bindcode(const GPUTexture *tex) return tex->bindcode; } -GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex) +void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int attachment) { - return tex->fb; -} + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] == NULL) { + tex->fb[i] = fb; + tex->fb_attachment[i] = attachment; + return; + } + } -int GPU_texture_framebuffer_attachment(GPUTexture *tex) -{ - return tex->fb_attachment; + BLI_assert(!"Error: Texture: Not enough Framebuffer slots"); } -void GPU_texture_framebuffer_set(GPUTexture *tex, GPUFrameBuffer *fb, int attachment) +/* Return previous attachment point */ +int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) { - tex->fb = fb; - tex->fb_attachment = attachment; -} + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] == fb) { + tex->fb[i] = NULL; + return tex->fb_attachment[i]; + } + } + BLI_assert(!"Error: Texture: Framebuffer is not attached"); +} diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 0f061f1c791..e635174eca2 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -48,6 +48,7 @@ #include "GPU_immediate.h" #include "GPU_texture.h" #include "GPU_viewport.h" +#include "GPU_draw.h" #include "DRW_engine.h" @@ -69,7 +70,6 @@ typedef struct ViewportTempTexture { struct GPUViewport { int size[2]; - int samples; int flag; @@ -137,12 +137,19 @@ GPUViewport *GPU_viewport_create_from_offscreen(struct GPUOffScreen *ofs) viewport->txl->multisample_depth = depth; viewport->fbl->multisample_fb = fb; gpu_viewport_default_fb_create(viewport); - GPU_framebuffer_slots_bind(viewport->fbl->default_fb, 0); } else { viewport->fbl->default_fb = fb; viewport->txl->color = color; viewport->txl->depth = depth; + GPU_framebuffer_ensure_config(&viewport->fbl->color_only_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(viewport->txl->color) + }); + GPU_framebuffer_ensure_config(&viewport->fbl->depth_only_fb, { + GPU_ATTACHMENT_TEXTURE(viewport->txl->depth), + GPU_ATTACHMENT_NONE + }); } return viewport; @@ -158,8 +165,7 @@ void GPU_viewport_clear_from_offscreen(GPUViewport *viewport) if (dfbl->multisample_fb) { /* GPUViewport expect the final result to be in default_fb but * GPUOffscreen wants it in its multisample_fb, so we sync it back. */ - GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, false, false); - GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, true, false); + GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, GPU_COLOR_BIT | GPU_DEPTH_BIT); dfbl->multisample_fb = NULL; dtxl->multisample_color = NULL; dtxl->multisample_depth = NULL; @@ -288,9 +294,9 @@ GPUTexture *GPU_viewport_texture_pool_query(GPUViewport *viewport, void *engine, GPUTexture *tex; for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex->next) { - if ((GPU_texture_width(tmp_tex->texture) == width) && - (GPU_texture_height(tmp_tex->texture) == height) && - (GPU_texture_format(tmp_tex->texture) == format)) + if ((GPU_texture_format(tmp_tex->texture) == format) && + (GPU_texture_width(tmp_tex->texture) == width) && + (GPU_texture_height(tmp_tex->texture) == height)) { /* Search if the engine is not already using this texture */ for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; ++i) { @@ -307,11 +313,16 @@ GPUTexture *GPU_viewport_texture_pool_query(GPUViewport *viewport, void *engine, } tex = GPU_texture_create_2D_custom(width, height, channels, format, NULL, NULL); + GPU_texture_bind(tex, 0); + /* Doing filtering for depth does not make sense when not doing shadow mapping, + * and enabling texture filtering on integer texture make them unreadable. */ + bool do_filter = !GPU_texture_depth(tex) && !GPU_texture_integer(tex); + GPU_texture_filter_mode(tex, do_filter); + GPU_texture_unbind(tex); ViewportTempTexture *tmp_tex = MEM_callocN(sizeof(ViewportTempTexture), "ViewportTempTexture"); tmp_tex->texture = tex; tmp_tex->user[0] = engine; - BLI_addtail(&viewport->tex_pool, tmp_tex); return tex; @@ -378,48 +389,66 @@ static void gpu_viewport_default_fb_create(GPUViewport *viewport) int *size = viewport->size; bool ok = true; - dfbl->default_fb = GPU_framebuffer_create(); - if (!dfbl->default_fb) { - ok = false; - goto cleanup; - } - - /* Color */ dtxl->color = GPU_texture_create_2D(size[0], size[1], NULL, NULL); - if (!dtxl->color) { - ok = false; - goto cleanup; - } + dtxl->depth = GPU_texture_create_depth_with_stencil(size[0], size[1], NULL); - if (!GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->color, 0, 0)) { + if (!(dtxl->depth && dtxl->color)) { ok = false; goto cleanup; } - /* Depth */ - dtxl->depth = GPU_texture_create_depth_with_stencil(size[0], size[1], NULL); + GPU_framebuffer_ensure_config(&dfbl->default_fb, { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(dtxl->color) + }); - if (dtxl->depth) { - /* Define texture parameters */ - GPU_texture_bind(dtxl->depth, 0); - GPU_texture_compare_mode(dtxl->depth, false); - GPU_texture_filter_mode(dtxl->depth, false); - GPU_texture_unbind(dtxl->depth); - } - else { - ok = false; - goto cleanup; - } + GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_NONE + }); - if (!GPU_framebuffer_texture_attach(dfbl->default_fb, dtxl->depth, 0, 0)) { - ok = false; - goto cleanup; + GPU_framebuffer_ensure_config(&dfbl->color_only_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(dtxl->color) + }); + + ok = ok && GPU_framebuffer_check_valid(dfbl->default_fb, NULL); + ok = ok && GPU_framebuffer_check_valid(dfbl->color_only_fb, NULL); + ok = ok && GPU_framebuffer_check_valid(dfbl->depth_only_fb, NULL); + +cleanup: + if (!ok) { + GPU_viewport_free(viewport); + DRW_opengl_context_disable(); + return; } - else if (!GPU_framebuffer_check_valid(dfbl->default_fb, NULL)) { + + GPU_framebuffer_restore(); +} + +static void gpu_viewport_default_multisample_fb_create(GPUViewport *viewport) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + DefaultTextureList *dtxl = viewport->txl; + int *size = viewport->size; + int samples = viewport->samples; + bool ok = true; + + dtxl->multisample_color = GPU_texture_create_2D_multisample(size[0], size[1], NULL, samples, NULL); + dtxl->multisample_depth = GPU_texture_create_depth_with_stencil_multisample(size[0], size[1], samples, NULL); + + if (!(dtxl->multisample_depth && dtxl->multisample_color)) { ok = false; goto cleanup; } + GPU_framebuffer_ensure_config(&dfbl->multisample_fb, { + GPU_ATTACHMENT_TEXTURE(dtxl->multisample_depth), + GPU_ATTACHMENT_TEXTURE(dtxl->multisample_color) + }); + + ok = ok && GPU_framebuffer_check_valid(dfbl->multisample_fb, NULL); + cleanup: if (!ok) { GPU_viewport_free(viewport); @@ -433,7 +462,6 @@ cleanup: void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect) { DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; int fbl_len, txl_len; /* add one pixel because of scissor test */ @@ -460,64 +488,20 @@ void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect) viewport->size[0] = rect_w; viewport->size[1] = rect_h; + viewport->samples = U.ogl_multisamples; gpu_viewport_texture_pool_clear_users(viewport); /* Multisample Buffer */ - if (U.ogl_multisamples > 0) { + if (viewport->samples > 0) { if (!dfbl->default_fb) { - bool ok = true; - viewport->samples = U.ogl_multisamples; - - dfbl->multisample_fb = GPU_framebuffer_create(); - if (!dfbl->multisample_fb) { - ok = false; - goto cleanup_multisample; - } - - /* Color */ - dtxl->multisample_color = GPU_texture_create_2D_multisample(rect_w, rect_h, NULL, U.ogl_multisamples, NULL); - if (!dtxl->multisample_color) { - ok = false; - goto cleanup_multisample; - } - - if (!GPU_framebuffer_texture_attach(dfbl->multisample_fb, dtxl->multisample_color, 0, 0)) { - ok = false; - goto cleanup_multisample; - } - - /* Depth */ - dtxl->multisample_depth = GPU_texture_create_depth_with_stencil_multisample(rect_w, rect_h, - U.ogl_multisamples, NULL); - - if (!dtxl->multisample_depth) { - ok = false; - goto cleanup_multisample; - } - - if (!GPU_framebuffer_texture_attach(dfbl->multisample_fb, dtxl->multisample_depth, 0, 0)) { - ok = false; - goto cleanup_multisample; - } - else if (!GPU_framebuffer_check_valid(dfbl->multisample_fb, NULL)) { - ok = false; - goto cleanup_multisample; - } - -cleanup_multisample: - if (!ok) { - GPU_viewport_free(viewport); - return; - } + gpu_viewport_default_multisample_fb_create(viewport); } } if (!dfbl->default_fb) { gpu_viewport_default_fb_create(viewport); } - - GPU_framebuffer_slots_bind(dfbl->default_fb, 0); } void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect) |