diff options
Diffstat (limited to 'source/blender/gpu/intern')
-rw-r--r-- | source/blender/gpu/intern/gpu_backend.hh | 3 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_context_private.hh | 12 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_framebuffer.cc | 987 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_framebuffer_private.hh | 116 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture.cc | 24 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture_private.hh | 53 |
6 files changed, 458 insertions, 737 deletions
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index f63f3cead2b..ec60e6b5704 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -32,6 +32,7 @@ namespace gpu { class Batch; class DrawList; +class FrameBuffer; class Shader; class UniformBuf; @@ -45,7 +46,7 @@ class GPUBackend { virtual Batch *batch_alloc(void) = 0; virtual DrawList *drawlist_alloc(int list_length) = 0; - // virtual FrameBuffer *framebuffer_alloc(void) = 0; + virtual FrameBuffer *framebuffer_alloc(const char *name) = 0; virtual Shader *shader_alloc(const char *name) = 0; // virtual Texture *texture_alloc(void) = 0; virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0; diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh index d18d18dc97b..664f4e2f676 100644 --- a/source/blender/gpu/intern/gpu_context_private.hh +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -49,6 +49,18 @@ struct GPUContext { GPUMatrixState *matrix_state = NULL; blender::gpu::GPUStateManager *state_manager = NULL; + /** + * All 4 window framebuffers. + * None of them are valid in an offscreen context. + * Right framebuffers are only available if using stereo rendering. + * Front framebuffers contains (in principle, but not always) the last frame color. + * Default framebuffer is back_left. + */ + blender::gpu::FrameBuffer *back_left = NULL; + blender::gpu::FrameBuffer *front_left = NULL; + blender::gpu::FrameBuffer *back_right = NULL; + blender::gpu::FrameBuffer *front_right = NULL; + protected: /** Thread on which this context is active. */ pthread_t thread_; diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 5a575f9e5f5..3390b47b1b1 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -32,518 +32,313 @@ #include "GPU_shader.h" #include "GPU_texture.h" +#include "gpu_backend.hh" #include "gpu_context_private.hh" #include "gpu_private.h" +#include "gpu_texture_private.hh" #include "gpu_framebuffer_private.hh" -using namespace blender::gpu; +namespace blender::gpu { -static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) -{ -#define ATTACHMENT(type) \ - case GPU_FB_##type: { \ - return GL_##type; \ - } \ - ((void)0) - - switch (type) { - ATTACHMENT(DEPTH_ATTACHMENT); - ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); - ATTACHMENT(COLOR_ATTACHMENT0); - ATTACHMENT(COLOR_ATTACHMENT1); - ATTACHMENT(COLOR_ATTACHMENT2); - ATTACHMENT(COLOR_ATTACHMENT3); - ATTACHMENT(COLOR_ATTACHMENT4); - ATTACHMENT(COLOR_ATTACHMENT5); - default: - BLI_assert(0); - return GL_COLOR_ATTACHMENT0; - } -} +/* -------------------------------------------------------------------- */ +/** \name Constructor / Destructor + * \{ */ -static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) +FrameBuffer::FrameBuffer(const char *name) { - 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: - case GPU_DEPTH32F_STENCIL8: - return GPU_FB_DEPTH_STENCIL_ATTACHMENT; - default: - return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); + if (name) { + BLI_strncpy(name_, name, sizeof(name_)); } -} - -static GLenum convert_buffer_bits_to_gl(eGPUFrameBufferBits bits) -{ - GLbitfield mask = 0; - mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0; - mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0; - mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0; - return mask; -} - -static void gpu_print_framebuffer_error(GLenum status, char err_out[256]) -{ - const char *format = "GPUFrameBuffer: framebuffer status %s\n"; - const char *err = "unknown"; - -#define FORMAT_STATUS(X) \ - case GL_FRAMEBUFFER_##X: { \ - err = "GL_FRAMEBUFFER_" #X; \ - break; \ - } \ - ((void)0) - - switch (status) { - /* success */ - FORMAT_STATUS(COMPLETE); - /* errors shared by OpenGL desktop & ES */ - FORMAT_STATUS(INCOMPLETE_ATTACHMENT); - FORMAT_STATUS(INCOMPLETE_MISSING_ATTACHMENT); - FORMAT_STATUS(UNSUPPORTED); -#if 0 /* for OpenGL ES only */ - FORMAT_STATUS(INCOMPLETE_DIMENSIONS); -#else /* for desktop GL only */ - FORMAT_STATUS(INCOMPLETE_DRAW_BUFFER); - FORMAT_STATUS(INCOMPLETE_READ_BUFFER); - FORMAT_STATUS(INCOMPLETE_MULTISAMPLE); - FORMAT_STATUS(UNDEFINED); -#endif + else { + name_[0] = '\0'; } + /* Force config on first use. */ + dirty_attachments_ = true; -#undef FORMAT_STATUS - - if (err_out) { - BLI_snprintf(err_out, 256, format, err); - } - else { - fprintf(stderr, format, err); + for (int i = 0; i < ARRAY_SIZE(attachments_); i++) { + attachments_[i].tex = NULL; + attachments_[i].mip = -1; + attachments_[i].layer = -1; } } -GPUFrameBuffer *GPU_framebuffer_active_get(void) +FrameBuffer::~FrameBuffer() { - GPUContext *ctx = GPU_context_active_get(); - return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->active_fb : NULL); + GPUFrameBuffer *gpu_fb = reinterpret_cast<GPUFrameBuffer *>(this); + for (int i = 0; i < ARRAY_SIZE(attachments_); i++) { + if (attachments_[i].tex != NULL) { + GPU_texture_detach_framebuffer(attachments_[i].tex, gpu_fb); + } + } } -/* GPUFrameBuffer */ +/** \} */ -GPUFrameBuffer *GPU_framebuffer_create(void) -{ - /* We generate the FB object later at first use in order to - * create the framebuffer in the right opengl context. */ - return (GPUFrameBuffer *)new FrameBuffer(); -} - -static void gpu_framebuffer_init(FrameBuffer *fb) -{ - fb->object = GPU_fbo_alloc(); - fb->ctx = GPU_context_active_get(); - /* Not really needed for now. */ - // gpu_context_add_framebuffer(fb->ctx, fb); -} +/* -------------------------------------------------------------------- */ +/** \name Attachments managment + * \{ */ -void GPU_framebuffer_free(GPUFrameBuffer *gpu_fb) +void FrameBuffer::attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment) { - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - GPU_framebuffer_texture_detach(gpu_fb, fb->attachments[type].tex); - } - } - - if (fb->object != 0) { - /* This restores the framebuffer if it was bound */ - GPU_fbo_free(fb->object, fb->ctx); - /* Not really needed for now. */ - // gpu_context_remove_framebuffer(fb->ctx, fb); - } - - /* TODO(fclem) check if bound in its associated context context. */ - if (GPU_framebuffer_active_get() == gpu_fb) { - GPU_context_active_get()->active_fb = NULL; + if (new_attachment.mip == -1) { + return; /* GPU_ATTACHMENT_LEAVE */ } - delete fb; -} - -/* ---------- Attach ----------- */ - -static void gpu_framebuffer_texture_attach_ex( - FrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) -{ - if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) { + if (type >= GPU_FB_MAX_ATTACHEMENT) { fprintf(stderr, - "Attaching to index %d framebuffer slot unsupported. " - "Use at most %d\n", - slot, + "GPUFramebuffer: Error: Trying to attach texture to type %d but maximum slot is %d.\n", + type - GPU_FB_COLOR_ATTACHMENT0, GPU_FB_MAX_COLOR_ATTACHMENT); return; } - GPUFrameBuffer *gpufb = reinterpret_cast<GPUFrameBuffer *>(fb); - GPUAttachmentType type = attachment_type_from_tex(tex, slot); - GPUAttachment *attachment = &fb->attachments[type]; + if (new_attachment.tex) { + if (new_attachment.layer > 0) { + BLI_assert(ELEM(GPU_texture_target(new_attachment.tex), + GL_TEXTURE_2D_ARRAY, + GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_CUBE_MAP_ARRAY_ARB)); + } + if (GPU_texture_stencil(new_attachment.tex)) { + BLI_assert(ELEM(type, GPU_FB_DEPTH_STENCIL_ATTACHMENT)); + } + else if (GPU_texture_depth(new_attachment.tex)) { + BLI_assert(ELEM(type, GPU_FB_DEPTH_ATTACHMENT)); + } + } + + GPUAttachment &attachment = attachments_[type]; - if ((attachment->tex == tex) && (attachment->mip == mip) && (attachment->layer == layer)) { + if (attachment.tex == new_attachment.tex && attachment.layer == new_attachment.layer && + attachment.mip == new_attachment.mip) { return; /* Exact same texture already bound here. */ } - if (attachment->tex != NULL) { - GPU_framebuffer_texture_detach(gpufb, attachment->tex); - } - - if (attachment->tex == NULL) { - GPU_texture_attach_framebuffer(tex, gpufb, type); + /* Unbind previous and bind new. */ + /* TODO(fclem) cleanup the casts. */ + if (attachment.tex) { + GPU_texture_detach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this)); } - attachment->tex = tex; - attachment->mip = mip; - attachment->layer = layer; - GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); -} + attachment = new_attachment; -void GPU_framebuffer_texture_attach(GPUFrameBuffer *gpu_fb, GPUTexture *tex, int slot, int mip) -{ - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip); -} + /* Might be null if this is for unbinding. */ + if (attachment.tex) { + GPU_texture_attach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this), type); + } + else { + /* GPU_ATTACHMENT_NONE */ + } -void GPU_framebuffer_texture_layer_attach( - GPUFrameBuffer *gpu_fb, GPUTexture *tex, int slot, int layer, int mip) -{ - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - /* NOTE: We could support 1D ARRAY texture. */ - BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip); + dirty_attachments_ = true; } -void GPU_framebuffer_texture_cubeface_attach( - GPUFrameBuffer *gpu_fb, GPUTexture *tex, int slot, int face, int mip) +void FrameBuffer::recursive_downsample(int max_lvl, + void (*callback)(void *userData, int level), + void *userData) { - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - BLI_assert(GPU_texture_cube(tex)); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip); -} - -/* ---------- Detach ----------- */ - -void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *gpu_fb, GPUTexture *tex, int type) -{ - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - GPUAttachment *attachment = &fb->attachments[type]; + GPUContext *ctx = GPU_context_active_get(); + /* Bind to make sure the framebuffer is up to date. */ + this->bind(true); - if (attachment->tex != tex) { - fprintf(stderr, - "Warning, attempting to detach Texture %p from framebuffer %p " - "but texture is not attached.\n", - tex, - fb); + if (width_ == 1 && height_ == 1) { return; } + /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */ + ctx->active_fb = NULL; - attachment->tex = NULL; - GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); -} - -void GPU_framebuffer_texture_detach(GPUFrameBuffer *gpu_fb, GPUTexture *tex) -{ - GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, gpu_fb); - GPU_framebuffer_texture_detach_slot(gpu_fb, tex, type); -} - -/* ---------- 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 *gpu_fb, - const GPUAttachment *config, - int config_len) -{ - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - 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(gpu_fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex); - } - else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) { - GPU_framebuffer_texture_detach(gpu_fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex); - } + int levels = floor(log2(max_ii(width_, height_))); + max_lvl = min_ii(max_lvl, levels); - int slot = 0; - for (int i = 1; i < config_len; 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 = fb->color_tex(slot); + int current_dim[2] = {width_, height_}; + int mip_lvl; + for (mip_lvl = 1; mip_lvl < max_lvl + 1; mip_lvl++) { + /* calculate next viewport size */ + current_dim[0] = max_ii(current_dim[0] / 2, 1); + current_dim[1] = max_ii(current_dim[1] / 2, 1); + /* Replace attaached miplevel for each attachement. */ + for (int att = 0; att < ARRAY_SIZE(attachments_); att++) { + GPUTexture *tex = attachments_[att].tex; if (tex != NULL) { - GPU_framebuffer_texture_detach(gpu_fb, tex); + /* Some Intel HDXXX have issue with rendering to a mipmap that is below + * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case + * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */ + int map_lvl = (GPU_mip_render_workaround()) ? mip_lvl : (mip_lvl - 1); + /* Restrict fetches only to previous level. */ + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, mip_lvl - 1); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, map_lvl); + GPU_texture_unbind(tex); + /* Bind next level. */ + attachments_[att].mip = mip_lvl; } } - } -} + /* Update the internal attachments and viewport size. */ + dirty_attachments_ = true; + this->bind(true); + /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */ + ctx->active_fb = NULL; -/* ---------- Bind / Restore ----------- */ + callback(userData, mip_lvl); -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 (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); + /* This is the last mipmap level. Exit loop without incrementing mip_lvl. */ + if (current_dim[0] == 1 && current_dim[1] == 1) { + break; } } - else { - glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip); - } -} - -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); -} -static void gpu_framebuffer_update_attachments(FrameBuffer *fb) -{ - GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; - int numslots = 0; - - BLI_assert(GPU_context_active_get()->active_fb == fb); - - /* Update attachments */ - FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT) - { - 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++; - } - - if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) { - continue; - } - if (fb->attachments[type].tex != NULL) { - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - - 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); + for (int att = 0; att < ARRAY_SIZE(attachments_); att++) { + if (attachments_[att].tex != NULL) { + /* Reset mipmap level range. */ + GPUTexture *tex = attachments_[att].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, mip_lvl); + GPU_texture_unbind(tex); + /* Reset base level. NOTE: might not be the one bound at the start of this function. */ + attachments_[att].mip = 0; } } - fb->dirty_flag = 0; - - /* Update draw buffers (color targets) - * This state is saved in the FBO */ - if (numslots) { - glDrawBuffers(numslots, gl_attachments); - } - else { - glDrawBuffer(GL_NONE); - } + /* Reattach base level textures. */ + this->bind(true); } -/** - * Hack to solve the problem of some bugged AMD GPUs (see `GPU_unused_fb_slot_workaround`). - * If there is an empty color slot between the color slots, - * all textures after this slot are apparently skipped/discarded. - */ -static void gpu_framebuffer_update_attachments_and_fill_empty_slots(FrameBuffer *fb) -{ - GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; - int dummy_tex = 0; +/** \} */ - BLI_assert(GPU_context_active_get()->active_fb == fb); +} // namespace blender::gpu - /* Update attachments */ - for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - GPUTexture *tex = fb->attachments[type].tex; +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ - if (type >= GPU_FB_COLOR_ATTACHMENT0) { - int slot = type - GPU_FB_COLOR_ATTACHMENT0; - if (tex != NULL || (dummy_tex != 0)) { - gl_attachments[slot] = convert_attachment_type_to_gl(type); - - if (dummy_tex == 0) { - dummy_tex = GPU_texture_opengl_bindcode(tex); - } - } - else { - gl_attachments[slot] = GL_NONE; - } - } - else { - dummy_tex = 0; - } - - if ((dummy_tex != 0) && tex == NULL) { - /* Fill empty slot */ - glFramebufferTexture(GL_FRAMEBUFFER, convert_attachment_type_to_gl(type), dummy_tex, 0); - } - else if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type)) { - if (tex != NULL) { - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - - fb->multisample = (GPU_texture_samples(tex) > 0); - fb->width = GPU_texture_width(tex); - fb->height = GPU_texture_height(tex); - } - else { - gpu_framebuffer_attachment_detach(&fb->attachments[type], type); - } - } - } - fb->dirty_flag = 0; - - /* Update draw buffers (color targets) - * This state is saved in the FBO */ - glDrawBuffers(GPU_FB_MAX_COLOR_ATTACHMENT, gl_attachments); -} - -#define FRAMEBUFFER_STACK_DEPTH 16 - -static struct { - GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH]; - uint top; -} FrameBufferStack = {{0}}; +using namespace blender; +using namespace blender::gpu; -static void gpuPushFrameBuffer(GPUFrameBuffer *fb) +GPUFrameBuffer *GPU_framebuffer_create() { - BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH); - FrameBufferStack.framebuffers[FrameBufferStack.top] = fb; - FrameBufferStack.top++; + /* We generate the FB object later at first use in order to + * create the framebuffer in the right opengl context. */ + return (GPUFrameBuffer *)GPUBackend::get()->framebuffer_alloc("FB"); } -static GPUFrameBuffer *gpuPopFrameBuffer(void) +void GPU_framebuffer_free(GPUFrameBuffer *gpu_fb) { - BLI_assert(FrameBufferStack.top > 0); - FrameBufferStack.top--; - return FrameBufferStack.framebuffers[FrameBufferStack.top]; + delete reinterpret_cast<FrameBuffer *>(gpu_fb); } -#undef FRAMEBUFFER_STACK_DEPTH +/* ---------- Binding ----------- */ void GPU_framebuffer_bind(GPUFrameBuffer *gpu_fb) { FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + const bool enable_srgb = true; + fb->bind(enable_srgb); +} - if (fb->object == 0) { - gpu_framebuffer_init(fb); - } +/* Workaround for binding a srgb framebuffer without doing the srgb transform. */ +void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *gpu_fb) +{ + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + const bool enable_srgb = false; + fb->bind(enable_srgb); +} - if (GPU_framebuffer_active_get() != gpu_fb) { - glBindFramebuffer(GL_FRAMEBUFFER, fb->object); - glEnable(GL_FRAMEBUFFER_SRGB); +/* For stereo rendering. */ +void GPU_backbuffer_bind(eGPUBackBuffer buffer) +{ + GPUContext *ctx = GPU_context_active_get(); - GPUTexture *first_target = fb->attachments[GPU_FB_COLOR_ATTACHMENT0].tex; - const bool is_srgb_target = (first_target && - (GPU_texture_format(first_target) == GPU_SRGB8_A8)); - GPU_shader_set_framebuffer_srgb_target(is_srgb_target); + if (buffer == GPU_BACKBUFFER_LEFT) { + ctx->back_left->bind(false); } - - GPU_context_active_get()->active_fb = fb; - - if (fb->dirty_flag != 0) { - if (GPU_unused_fb_slot_workaround()) { - /* XXX: Please AMD, fix this. */ - gpu_framebuffer_update_attachments_and_fill_empty_slots(fb); - } - else { - gpu_framebuffer_update_attachments(fb); - } + else { + ctx->back_right->bind(false); } +} - /* 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 +void GPU_framebuffer_restore(void) +{ + GPU_context_active_get()->back_left->bind(false); +} - GPU_viewport(0, 0, fb->width, fb->height); +GPUFrameBuffer *GPU_framebuffer_active_get(void) +{ + GPUContext *ctx = GPU_context_active_get(); + return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->active_fb : NULL); } -/* Workaround for binding a srgb framebuffer without doing the srgb transform. */ -void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *gpu_fb) +/* Returns the default framebuffer. Will always exists even if it's just a dummy. */ +GPUFrameBuffer *GPU_framebuffer_back_get(void) { - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + GPUContext *ctx = GPU_context_active_get(); + return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->back_left : NULL); +} - GPU_framebuffer_bind(gpu_fb); +bool GPU_framebuffer_bound(GPUFrameBuffer *gpu_fb) +{ + return (gpu_fb == GPU_framebuffer_active_get()); +} - glDisable(GL_FRAMEBUFFER_SRGB); +/* ---------- Attachment Management ----------- */ - GPUTexture *first_target = fb->attachments[GPU_FB_COLOR_ATTACHMENT0].tex; - const bool is_srgb_target = (first_target && (GPU_texture_format(first_target) == GPU_SRGB8_A8)); - GPU_shader_set_framebuffer_srgb_target(!is_srgb_target); +bool GPU_framebuffer_check_valid(GPUFrameBuffer *gpu_fb, char err_out[256]) +{ + return reinterpret_cast<FrameBuffer *>(gpu_fb)->check(err_out); } -void GPU_framebuffer_restore(void) +void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb, GPUAttachment attachement, int slot) { - if (GPU_framebuffer_active_get() != NULL) { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); - GPU_context_active_get()->active_fb = NULL; - glDisable(GL_FRAMEBUFFER_SRGB); - GPU_shader_set_framebuffer_srgb_target(false); - } + GPUAttachmentType type = blender::gpu::Texture::attachment_type(attachement.tex, slot); + reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set(type, attachement); } -bool GPU_framebuffer_bound(GPUFrameBuffer *gpu_fb) +void GPU_framebuffer_texture_detach(GPUFrameBuffer *gpu_fb, GPUTexture *tex) { - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - return (gpu_fb == GPU_framebuffer_active_get()) && (fb->object != 0); + GPUAttachment attachement = GPU_ATTACHMENT_NONE; + int type = GPU_texture_framebuffer_attachement_get(tex, gpu_fb); + if (type != -1) { + reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set((GPUAttachmentType)type, attachement); + } + else { + BLI_assert(!"Error: Texture: Framebuffer is not attached"); + } } -bool GPU_framebuffer_check_valid(GPUFrameBuffer *gpu_fb, char err_out[256]) +/** + * 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 *gpu_fb, + const GPUAttachment *config, + int config_len) { - if (!GPU_framebuffer_bound(gpu_fb)) { - GPU_framebuffer_bind(gpu_fb); - } + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + const GPUAttachment &depth_attachement = config[0]; + Span<GPUAttachment> color_attachments(config + 1, config_len - 1); - if (status != GL_FRAMEBUFFER_COMPLETE) { - GPU_framebuffer_restore(); - gpu_print_framebuffer_error(status, err_out); - return false; + if (depth_attachement.mip == -1) { + /* GPU_ATTACHMENT_LEAVE */ + } + else if (depth_attachement.tex == NULL) { + /* GPU_ATTACHMENT_NONE: Need to clear both targets. */ + fb->attachment_set(GPU_FB_DEPTH_STENCIL_ATTACHMENT, depth_attachement); + fb->attachment_set(GPU_FB_DEPTH_ATTACHMENT, depth_attachement); + } + else { + GPUAttachmentType type = GPU_texture_stencil(depth_attachement.tex) ? + GPU_FB_DEPTH_STENCIL_ATTACHMENT : + GPU_FB_DEPTH_ATTACHMENT; + fb->attachment_set(type, depth_attachement); } - return true; + GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; + for (const GPUAttachment &attachement : color_attachments) { + fb->attachment_set(type, attachement); + ++type; + } } /* ---------- Framebuffer Operations ----------- */ @@ -567,121 +362,31 @@ void GPU_framebuffer_clear(GPUFrameBuffer *gpu_fb, float clear_depth, uint clear_stencil) { - CHECK_FRAMEBUFFER_IS_BOUND(gpu_fb); - - /* Save and restore the state. */ - eGPUWriteMask write_mask = GPU_write_mask_get(); - uint stencil_mask = GPU_stencil_mask_get(); - eGPUStencilTest stencil_test = GPU_stencil_test_get(); - - if (buffers & GPU_COLOR_BIT) { - GPU_color_mask(true, true, true, true); - glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); - } - if (buffers & GPU_DEPTH_BIT) { - GPU_depth_mask(true); - glClearDepth(clear_depth); - } - if (buffers & GPU_STENCIL_BIT) { - GPU_stencil_write_mask_set(0xFFu); - GPU_stencil_test(GPU_STENCIL_ALWAYS); - glClearStencil(clear_stencil); - } - - GPU_context_active_get()->state_manager->apply_state(); - - GLbitfield mask = convert_buffer_bits_to_gl(buffers); - glClear(mask); - - if (buffers & (GPU_COLOR_BIT | GPU_DEPTH_BIT)) { - GPU_write_mask(write_mask); - } - if (buffers & GPU_STENCIL_BIT) { - GPU_stencil_write_mask_set(stencil_mask); - GPU_stencil_test(stencil_test); - } + reinterpret_cast<FrameBuffer *>(gpu_fb)->clear(buffers, clear_col, clear_depth, clear_stencil); } -/* Clear all textures bound to this framebuffer with a different color. */ +/* Clear all textures attached to this framebuffer with a different color. */ void GPU_framebuffer_multi_clear(GPUFrameBuffer *gpu_fb, const float (*clear_cols)[4]) { - CHECK_FRAMEBUFFER_IS_BOUND(gpu_fb); - - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - - /* Save and restore the state. */ - eGPUWriteMask write_mask = GPU_write_mask_get(); - GPU_color_mask(true, true, true, true); - - int i_type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - glClearBufferfv(GL_COLOR, i, clear_cols[i]); - } - } - - GPU_write_mask(write_mask); -} - -void GPU_framebuffer_read_depth(GPUFrameBuffer *gpu_fb, int x, int y, int w, int h, float *data) -{ - CHECK_FRAMEBUFFER_IS_BOUND(gpu_fb); - - GLenum type = GL_DEPTH_COMPONENT; - glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */ - glReadPixels(x, y, w, h, type, GL_FLOAT, data); + reinterpret_cast<FrameBuffer *>(gpu_fb)->clear_multi(clear_cols); } -static GLenum gpu_get_gl_datatype(eGPUDataFormat format) +void GPU_clear_color(float red, float green, float blue, float alpha) { - switch (format) { - case GPU_DATA_FLOAT: - return GL_FLOAT; - case GPU_DATA_INT: - return GL_INT; - case GPU_DATA_UNSIGNED_INT: - return GL_UNSIGNED_INT; - case GPU_DATA_UNSIGNED_BYTE: - return GL_UNSIGNED_BYTE; - case GPU_DATA_UNSIGNED_INT_24_8: - return GL_UNSIGNED_INT_24_8; - case GPU_DATA_10_11_11_REV: - return GL_UNSIGNED_INT_10F_11F_11F_REV; - default: - BLI_assert(!"Unhandled data format"); - return GL_FLOAT; - } + float clear_col[4] = {red, green, blue, alpha}; + GPU_context_active_get()->active_fb->clear(GPU_COLOR_BIT, clear_col, 0.0f, 0x0); } -static GLenum gpu_get_gl_channel_type(int channels) +void GPU_clear_depth(float depth) { - switch (channels) { - case 1: - return GL_RED; - case 2: - return GL_RG; - case 3: - return GL_RGB; - case 4: - return GL_RGBA; - default: - BLI_assert(!"Wrong number of read channels"); - return GL_RED; - } + float clear_col[4] = {0}; + GPU_context_active_get()->active_fb->clear(GPU_DEPTH_BIT, clear_col, depth, 0x0); } -static void gpu_framebuffer_read_color_ex( - int x, int y, int w, int h, int channels, GLenum readfb, eGPUDataFormat format, float *data) +void GPU_framebuffer_read_depth(GPUFrameBuffer *gpu_fb, int x, int y, int w, int h, float *data) { - GLenum type = gpu_get_gl_channel_type(channels); - GLenum gl_format = gpu_get_gl_datatype(format); - /* TODO: needed for selection buffers to work properly, this should be handled better. */ - if (type == GL_RED && gl_format == GL_UNSIGNED_INT) { - type = GL_RED_INTEGER; - } - glReadBuffer(readfb); - glReadPixels(x, y, w, h, type, gl_format, data); + int rect[4] = {x, y, w, h}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_DEPTH_BIT, GPU_DATA_FLOAT, rect, 1, 1, data); } void GPU_framebuffer_read_color(GPUFrameBuffer *gpu_fb, @@ -694,12 +399,20 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *gpu_fb, eGPUDataFormat format, void *data) { - CHECK_FRAMEBUFFER_IS_BOUND(gpu_fb); - gpu_framebuffer_read_color_ex( - x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); + int rect[4] = {x, y, w, h}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_COLOR_BIT, format, rect, channels, slot, data); +} + +/* TODO(fclem) rename to read_color. */ +void GPU_frontbuffer_read_pixels( + int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) +{ + int rect[4] = {x, y, w, h}; + GPU_context_active_get()->front_left->read(GPU_COLOR_BIT, format, rect, channels, 0, data); } /* read_slot and write_slot are only used for color buffers. */ +/* TODO(fclem) port as texture operation. */ void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read, int read_slot, GPUFrameBuffer *gpufb_write, @@ -712,74 +425,36 @@ void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read, FrameBuffer *prev_fb = GPU_context_active_get()->active_fb; - /* Framebuffers must be up to date. This simplify this function. */ - if (fb_read->dirty_flag != 0 || fb_read->object == 0) { - GPU_framebuffer_bind(gpufb_read); +#ifndef NDEBUG + GPUTexture *read_tex, *write_tex; + if (blit_buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) { + read_tex = fb_read->depth_tex(); + write_tex = fb_write->depth_tex(); } - if (fb_write->dirty_flag != 0 || fb_write->object == 0) { - GPU_framebuffer_bind(gpufb_write); + else { + read_tex = fb_read->color_tex(read_slot); + write_tex = fb_write->color_tex(write_slot); } - 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) ? fb_read->depth_tex() : - fb_read->color_tex(read_slot)); - GPUTexture *write_tex = ((do_depth || do_stencil) ? fb_write->depth_tex() : - fb_write->color_tex(read_slot)); - - if (do_depth) { + if (blit_buffers & GPU_DEPTH_BIT) { BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex)); BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); } - if (do_stencil) { + if (blit_buffers & GPU_STENCIL_BIT) { 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)); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object); - - 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; + BLI_assert((GPU_texture_width(write_tex) == GPU_texture_width(read_tex)) && + (GPU_texture_height(write_tex) == GPU_texture_height(read_tex))); } +#endif - GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers); - - GPU_context_active_get()->state_manager->apply_state(); - - glBlitFramebuffer(0, - 0, - fb_read->width, - fb_read->height, - 0, - 0, - fb_write->width, - fb_write->height, - mask, - GL_NEAREST); + fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0); - /* Restore previous framebuffer */ - if (fb_write == prev_fb) { - GPU_framebuffer_bind(gpufb_write); /* To update drawbuffers */ - } - else if (prev_fb) { - glBindFramebuffer(GL_FRAMEBUFFER, prev_fb->object); - GPU_context_active_get()->active_fb = prev_fb; - } - else { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); - GPU_context_active_get()->active_fb = NULL; - } + /* FIXME(fclem) sRGB is not saved. */ + prev_fb->bind(true); } /** @@ -792,77 +467,40 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb, void (*callback)(void *userData, int level), void *userData) { - GPUContext *ctx = GPU_context_active_get(); - FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); - /* Framebuffer must be up to date and bound. This simplify this function. */ - if (ctx->active_fb != fb || fb->dirty_flag != 0 || fb->object == 0) { - GPU_framebuffer_bind(gpu_fb); - } - /* HACK: We make the framebuffer appear not bound in order to - * not trigger any error in GPU_texture_bind(). */ - FrameBuffer *prev_fb = ctx->active_fb; - ctx->active_fb = NULL; - - int levels = floor(log2(max_ii(fb->width, fb->height))); - max_lvl = min_ii(max_lvl, levels); + reinterpret_cast<FrameBuffer *>(gpu_fb)->recursive_downsample(max_lvl, callback, userData); +} - 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] = max_ii(current_dim[0] / 2, 1); - current_dim[1] = max_ii(current_dim[1] / 2, 1); +/** \} */ - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - /* Some Intel HDXXX have issue with rendering to a mipmap that is below - * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case - * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */ - int next_lvl = (GPU_mip_render_workaround()) ? i : i - 1; - /* 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, next_lvl); - GPU_texture_unbind(tex); - /* copy attachment and replace miplevel. */ - GPUAttachment attachment = fb->attachments[type]; - attachment.mip = i; - gpu_framebuffer_attachment_attach(&attachment, type); - } - } - - BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER)); +/* -------------------------------------------------------------------- */ +/** \name GPUOffScreen + * + * Container that holds a framebuffer and its textures. + * Might be bound to multiple contexts. + * \{ */ - GPU_viewport(0, 0, current_dim[0], current_dim[1]); - callback(userData, i); +#define FRAMEBUFFER_STACK_DEPTH 16 - if (current_dim[0] == 1 && current_dim[1] == 1) { - break; - } - } +static struct { + GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH]; + uint top; +} FrameBufferStack = {{0}}; - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_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); - } - } +static void gpuPushFrameBuffer(GPUFrameBuffer *fb) +{ + BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH); + FrameBufferStack.framebuffers[FrameBufferStack.top] = fb; + FrameBufferStack.top++; +} - ctx->active_fb = prev_fb; +static GPUFrameBuffer *gpuPopFrameBuffer(void) +{ + BLI_assert(FrameBufferStack.top > 0); + FrameBufferStack.top--; + return FrameBufferStack.framebuffers[FrameBufferStack.top]; } -/* GPUOffScreen */ +#undef FRAMEBUFFER_STACK_DEPTH #define MAX_CTX_FB_LEN 3 @@ -1013,35 +651,20 @@ void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) { - const int w = GPU_texture_width(ofs->color); - const int h = GPU_texture_height(ofs->color); - - GPU_context_active_get()->state_manager->apply_state(); - + GPUContext *ctx = GPU_context_active_get(); FrameBuffer *ofs_fb = reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs)); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs_fb->object); - GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - - if (status == GL_FRAMEBUFFER_COMPLETE) { - glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - gpu_print_framebuffer_error(status, NULL); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); + ofs_fb->blit_to(GPU_COLOR_BIT, 0, ctx->active_fb, 0, x, y); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *pixels) { + BLI_assert(ELEM(format, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); + const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); - GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE; - - glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); + GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); + GPU_framebuffer_read_color(ofs_fb, 0, 0, w, h, 4, 0, format, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1070,42 +693,4 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, *r_depth = ofs->depth; } -void GPU_clear_color(float red, float green, float blue, float alpha) -{ - BLI_assert((GPU_write_mask_get() & GPU_WRITE_COLOR) != 0); - - GPU_context_active_get()->state_manager->apply_state(); - - glClearColor(red, green, blue, alpha); - glClear(GL_COLOR_BUFFER_BIT); -} - -void GPU_clear_depth(float depth) -{ - BLI_assert((GPU_write_mask_get() & GPU_WRITE_DEPTH) != 0); - - GPU_context_active_get()->state_manager->apply_state(); - - glClearDepth(depth); - glClear(GL_DEPTH_BUFFER_BIT); -} - -void GPU_frontbuffer_read_pixels( - int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) -{ - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); -} - -/* For stereo rendering. */ -void GPU_backbuffer_bind(eGPUBackBuffer buffer) -{ - if (buffer == GPU_BACKBUFFER) { - glDrawBuffer(GL_BACK); - } - else if (buffer == GPU_BACKBUFFER_LEFT) { - glDrawBuffer(GL_BACK_LEFT); - } - else if (buffer == GPU_BACKBUFFER_RIGHT) { - glDrawBuffer(GL_BACK_RIGHT); - } -}
\ No newline at end of file +/** \} */
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh index cc5ae048c7c..93551b0ef8b 100644 --- a/source/blender/gpu/intern/gpu_framebuffer_private.hh +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -19,19 +19,26 @@ /** \file * \ingroup gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - 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). */ #pragma once +#include "BLI_span.hh" + #include "MEM_guardedalloc.h" #include "GPU_framebuffer.h" -#include "glew-mx.h" /* For GLuint. To remove. */ - struct GPUTexture; -typedef enum { +typedef enum GPUAttachmentType : int { GPU_FB_DEPTH_ATTACHMENT = 0, GPU_FB_DEPTH_STENCIL_ATTACHMENT, GPU_FB_COLOR_ATTACHMENT0, @@ -45,50 +52,99 @@ typedef enum { /* 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, + + GPU_FB_MAX_COLOR_ATTACHMENT = (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0), } GPUAttachmentType; -namespace blender { -namespace gpu { +inline constexpr GPUAttachmentType operator-(GPUAttachmentType a, int b) +{ + return static_cast<GPUAttachmentType>(static_cast<int>(a) - b); +} -#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \ - for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \ - att = static_cast<GPUAttachmentType>(att + 1)) +inline constexpr GPUAttachmentType operator+(GPUAttachmentType a, int b) +{ + return static_cast<GPUAttachmentType>(static_cast<int>(a) + b); +} -#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) +inline GPUAttachmentType &operator++(GPUAttachmentType &a) +{ + a = a + 1; + return a; +} -#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) +inline GPUAttachmentType &operator--(GPUAttachmentType &a) +{ + a = a - 1; + return a; +} -#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0) -#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type)) +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 64 +#else +# define DEBUG_NAME_LEN 16 +#endif class FrameBuffer { - public: - GPUContext *ctx; - GLuint object; - 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 across ogl contexts). */ - // void *ctx; + protected: + /** Set of texture attachements to render to. DEPTH and DEPTH_STENCIL are mutualy exclusive. */ + GPUAttachment attachments_[GPU_FB_MAX_ATTACHEMENT]; + /** Is true if internal representation need to be updated. */ + bool dirty_attachments_; + /** Size of attachement textures. */ + int width_, height_; + /** Debug name. */ + char name_[DEBUG_NAME_LEN]; public: - GPUTexture *depth_tex(void) const + FrameBuffer(const char *name); + virtual ~FrameBuffer(); + + virtual void bind(bool enabled_srgb) = 0; + virtual bool check(char err_out[256]) = 0; + virtual void clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) = 0; + virtual void clear_multi(const float (*clear_col)[4]) = 0; + + virtual void read(eGPUFrameBufferBits planes, + eGPUDataFormat format, + const int area[4], + int channel_len, + int slot, + void *r_data) = 0; + + virtual void blit_to(eGPUFrameBufferBits planes, + int src_slot, + FrameBuffer *dst, + int dst_slot, + int dst_offset_x, + int dst_offset_y) = 0; + + void attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment); + + void recursive_downsample(int max_lvl, + void (*callback)(void *userData, int level), + void *userData); + + inline GPUTexture *depth_tex(void) const { - if (attachments[GPU_FB_DEPTH_ATTACHMENT].tex) { - return attachments[GPU_FB_DEPTH_ATTACHMENT].tex; + if (attachments_[GPU_FB_DEPTH_ATTACHMENT].tex) { + return attachments_[GPU_FB_DEPTH_ATTACHMENT].tex; } - return attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex; + return attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex; }; - GPUTexture *color_tex(int slot) const + inline GPUTexture *color_tex(int slot) const { - return attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; + return attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; }; - - MEM_CXX_CLASS_ALLOC_FUNCS("FrameBuffer") }; +#undef DEBUG_NAME_LEN + } // namespace gpu } // namespace blender diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index e70eeece104..1b7e1d4fd6a 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -44,6 +44,7 @@ #include "GPU_texture.h" #include "gpu_context_private.hh" +#include "gpu_framebuffer_private.hh" #define WARN_NOT_BOUND(_tex) \ { \ @@ -109,6 +110,9 @@ struct GPUTexture { GPUContext *copy_fb_ctx; }; +using namespace blender; +using namespace blender::gpu; + static uint gpu_get_bytesize(eGPUTextureFormat data_type); static void gpu_texture_framebuffer_ensure(GPUTexture *tex); @@ -2020,7 +2024,8 @@ void GPU_texture_free(GPUTexture *tex) if (tex->refcount == 0) { 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]); + FrameBuffer *framebuffer = reinterpret_cast<FrameBuffer *>(tex->fb[i]); + framebuffer->attachment_set((GPUAttachmentType)tex->fb_attachment[i], GPU_ATTACHMENT_NONE); } } @@ -2132,17 +2137,26 @@ void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int att } /* Return previous attachment point */ -int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) +void GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) { 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]; + return; } } - BLI_assert(!"Error: Texture: Framebuffer is not attached"); - return 0; +} + +/* Return attachment type for the given framebuffer or -1 if not attached. */ +int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, GPUFrameBuffer *fb) +{ + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { + if (tex->fb[i] == fb) { + return tex->fb_attachment[i]; + } + } + return -1; } void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size) diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh new file mode 100644 index 00000000000..6aa2a39046e --- /dev/null +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_assert.h" + +namespace blender { +namespace gpu { + +class Texture { + public: + /** TODO(fclem): make it a non-static function. */ + static GPUAttachmentType attachment_type(GPUTexture *tex, int slot) + { + switch (GPU_texture_format(tex)) { + case GPU_DEPTH_COMPONENT32F: + case GPU_DEPTH_COMPONENT24: + case GPU_DEPTH_COMPONENT16: + BLI_assert(slot == 0); + return GPU_FB_DEPTH_ATTACHMENT; + case GPU_DEPTH24_STENCIL8: + case GPU_DEPTH32F_STENCIL8: + BLI_assert(slot == 0); + return GPU_FB_DEPTH_STENCIL_ATTACHMENT; + default: + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); + } + } +}; + +} // namespace gpu +} // namespace blender |