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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/gpu/intern/gpu_framebuffer.c')
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.c929
1 files changed, 564 insertions, 365 deletions
diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c
index eafa29e6739..18f8c33c3ca 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.c
+++ b/source/blender/gpu/intern/gpu_framebuffer.c
@@ -28,308 +28,433 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_math_base.h"
#include "BKE_global.h"
-#include "GPU_debug.h"
-#include "GPU_glew.h"
+#include "GPU_batch.h"
+#include "GPU_draw.h"
+#include "GPU_extensions.h"
#include "GPU_framebuffer.h"
+#include "GPU_matrix.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
-static struct GPUFrameBufferGlobal {
- GLuint currentfb;
-} GG = {0};
+#include "intern/gpu_private.h"
-/* Number of maximum output slots.
- * We support 4 outputs for now (usually we wouldn't need more to preserve fill rate) */
-#define GPU_FB_MAX_SLOTS 4
+static ThreadLocal(void *) g_currentfb;
+
+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;
+ 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 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";
const char *err = "unknown";
+#define format_status(X) \
+ case GL_FRAMEBUFFER_##X: err = "GL_FRAMEBUFFER_"#X; \
+ break;
+
switch (status) {
- case GL_FRAMEBUFFER_COMPLETE_EXT:
- break;
- case GL_INVALID_OPERATION:
- err = "Invalid operation";
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
- err = "Incomplete attachment";
- break;
- case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
- err = "Unsupported framebuffer format";
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
- err = "Missing attachment";
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
- err = "Attached images must have same dimensions";
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
- err = "Attached images must have same format";
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
- err = "Missing draw buffer";
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
- err = "Missing read buffer";
- break;
+ /* 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
}
+#undef format_status
+
if (err_out) {
- BLI_snprintf(err_out, 256, "GPUFrameBuffer: framebuffer incomplete error %d '%s'",
- (int)status, err);
+ BLI_snprintf(err_out, 256, format, err);
}
else {
- fprintf(stderr, "GPUFrameBuffer: framebuffer incomplete error %d '%s'\n",
- (int)status, err);
+ fprintf(stderr, format, err);
}
}
-/* GPUFrameBuffer */
-
-GPUFrameBuffer *GPU_framebuffer_create(void)
+void gpu_framebuffer_module_init(void)
{
- GPUFrameBuffer *fb;
+ BLI_thread_local_create(g_currentfb);
+}
- if (!(GLEW_VERSION_3_0 || GLEW_ARB_framebuffer_object ||
- (GLEW_EXT_framebuffer_object && GLEW_EXT_framebuffer_blit)))
- {
- return NULL;
- }
+void gpu_framebuffer_module_exit(void)
+{
+ BLI_thread_local_delete(g_currentfb);
+}
- fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");
- glGenFramebuffersEXT(1, &fb->object);
+static uint gpu_framebuffer_current_get(void)
+{
+ return GET_UINT_FROM_POINTER(BLI_thread_local_get(g_currentfb));
+}
- if (!fb->object) {
- fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed. %d\n",
- (int)glGetError());
- GPU_framebuffer_free(fb);
- return NULL;
- }
+static void gpu_framebuffer_current_set(uint object)
+{
+ BLI_thread_local_set(g_currentfb, SET_UINT_IN_POINTER(object));
+}
- /* make sure no read buffer is enabled, so completeness check will not fail. We set those at binding time */
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
- glReadBuffer(GL_NONE);
- glDrawBuffer(GL_NONE);
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+/* GPUFrameBuffer */
- return fb;
+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 MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");;
}
-int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256])
+static void gpu_framebuffer_init(GPUFrameBuffer *fb)
{
- GLenum attachment;
- GLenum error;
+ glGenFramebuffers(1, &fb->object);
+}
- 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 0;
+void GPU_framebuffer_free(GPUFrameBuffer *fb)
+{
+ 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);
+ }
}
- 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");
- }
+ /* This restores the framebuffer if it was bound */
+ glDeleteFramebuffers(1, &fb->object);
+
+ if (gpu_framebuffer_current_get() == fb->object) {
+ gpu_framebuffer_current_set(0);
}
- if (GPU_texture_depth(tex))
- attachment = GL_DEPTH_ATTACHMENT_EXT;
- else
- attachment = GL_COLOR_ATTACHMENT0_EXT + slot;
+ MEM_freeN(fb);
+}
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
- GG.currentfb = fb->object;
+/* ---------- Attach ----------- */
- /* Clean glError buffer. */
- while (glGetError() != GL_NO_ERROR) {}
+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_COLOR_ATTACHMENT);
+ return;
+ }
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment,
- GPU_texture_target(tex), GPU_texture_opengl_bindcode(tex), 0);
+ GPUAttachmentType type = attachment_type_from_tex(tex, slot);
+ GPUAttachment *attachment = &fb->attachments[type];
- error = glGetError();
+ if ((attachment->tex == tex) &&
+ (attachment->mip == mip) &&
+ (attachment->layer == layer))
+ {
+ return; /* Exact same texture already bound here. */
+ }
+ else if (attachment->tex != NULL) {
+ GPU_framebuffer_texture_detach(fb, attachment->tex);
+ }
- if (error == GL_INVALID_OPERATION) {
- GPU_framebuffer_restore();
- gpu_print_framebuffer_error(error, err_out);
- return 0;
+ if (attachment->tex == NULL) {
+ GPU_texture_attach_framebuffer(tex, fb, type);
}
- if (GPU_texture_depth(tex))
- fb->depthtex = tex;
- else
- fb->colortex[slot] = tex;
+ attachment->tex = tex;
+ attachment->mip = mip;
+ attachment->layer = layer;
+ GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type);
+}
- GPU_texture_framebuffer_set(tex, fb, slot);
+void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
+{
+ gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip);
+}
- return 1;
+void GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip)
+{
+ /* 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);
}
-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));
+ gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip);
+}
- if (!fb)
- return;
+/* ---------- Detach ----------- */
- if (GG.currentfb != fb->object) {
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
- GG.currentfb = fb->object;
- }
+void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type)
+{
+ GPUAttachment *attachment = &fb->attachments[type];
- if (GPU_texture_depth(tex)) {
- fb->depthtex = NULL;
- attachment = GL_DEPTH_ATTACHMENT_EXT;
- }
- else {
- BLI_assert(fb->colortex[fb_attachment] == tex);
- fb->colortex[fb_attachment] = NULL;
- attachment = GL_COLOR_ATTACHMENT0_EXT + 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;
}
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, GPU_texture_target(tex), 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 */
- glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT);
- glDisable(GL_SCISSOR_TEST);
+/* ---------- Bind / Restore ----------- */
- /* bind framebuffer */
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 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_EXT + fb_attachment);
- glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + fb_attachment);
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip);
}
+}
- if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) {
- glEnable(GL_MULTISAMPLE);
- }
-
- /* push matrices and set default viewport and matrix */
- glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
- GG.currentfb = fb->object;
-
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
+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[4];
+ GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT];
+ int numslots = 0;
- if (!fb->colortex[slot]) {
- fprintf(stderr, "Error, framebuffer slot empty!\n");
- return;
- }
+ BLI_assert(gpu_framebuffer_current_get() == fb->object);
- for (i = 0; i < 4; i++) {
- if (fb->colortex[i]) {
- attachments[numslots] = GL_COLOR_ATTACHMENT0_EXT + i;
+ /* 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++;
}
+
+ 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);
+
+ 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;
- /* push attributes */
- glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT);
- glDisable(GL_SCISSOR_TEST);
+ /* Update draw buffers (color targets)
+ * This state is saved in the FBO */
+ if (numslots)
+ glDrawBuffers(numslots, gl_attachments);
+ else
+ glDrawBuffer(GL_NONE);
+}
- /* bind framebuffer */
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
+void GPU_framebuffer_bind(GPUFrameBuffer *fb)
+{
+ if (fb->object == 0)
+ gpu_framebuffer_init(fb);
- /* last bound prevails here, better allow explicit control here too */
- glDrawBuffers(numslots, attachments);
- glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot);
+ if (gpu_framebuffer_current_get() != fb->object)
+ glBindFramebuffer(GL_FRAMEBUFFER, fb->object);
- /* push matrices and set default viewport and matrix */
- glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
- GG.currentfb = fb->object;
+ gpu_framebuffer_current_set(fb->object);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
-}
+ if (fb->dirty_flag != 0)
+ gpu_framebuffer_update_attachments(fb);
+ /* 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_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex))
-{
- /* restore matrix */
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
+ if (fb->multisample)
+ glEnable(GL_MULTISAMPLE);
- /* restore attributes */
- glPopAttrib();
+ glViewport(0, 0, fb->width, fb->height);
}
-void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot)
+void GPU_framebuffer_restore(void)
{
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
- /* last bound prevails here, better allow explicit control here too */
- glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + slot);
- glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot);
-
- /* push matrices and set default viewport and matrix */
- glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot]));
- GG.currentfb = fb->object;
- GG.currentfb = fb->object;
+ if (gpu_framebuffer_current_get() != 0) {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ gpu_framebuffer_current_set(0);
+ }
}
bool GPU_framebuffer_bound(GPUFrameBuffer *fb)
{
- return fb->object == GG.currentfb;
+ return (fb->object == gpu_framebuffer_current_get()) && (fb->object != 0);
}
-bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
+unsigned int GPU_framebuffer_current_get(void)
{
- GLenum status;
-
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
- GG.currentfb = fb->object;
+ return gpu_framebuffer_current_get();
+}
- /* Clean glError buffer. */
- while (glGetError() != GL_NO_ERROR) {}
+bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
+{
+ if (!GPU_framebuffer_bound(fb))
+ GPU_framebuffer_bind(fb);
- status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
GPU_framebuffer_restore();
gpu_print_framebuffer_error(status, err_out);
return false;
@@ -338,109 +463,206 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256])
return true;
}
-void GPU_framebuffer_free(GPUFrameBuffer *fb)
+/* ---------- Framebuffer Operations ----------- */
+
+#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \
+ BLI_assert(GPU_framebuffer_bound(_fb)); \
+ UNUSED_VARS_NDEBUG(_fb);
+
+/* Needs to be done after binding. */
+void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h)
{
- int i;
- if (fb->depthtex)
- GPU_framebuffer_texture_detach(fb->depthtex);
+ CHECK_FRAMEBUFFER_IS_BOUND(fb);
- for (i = 0; i < GPU_FB_MAX_SLOTS; i++) {
- if (fb->colortex[i]) {
- GPU_framebuffer_texture_detach(fb->colortex[i]);
- }
- }
+ glViewport(x, y, w, h);
+}
- if (fb->object) {
- glDeleteFramebuffersEXT(1, &fb->object);
+void GPU_framebuffer_clear(
+ GPUFrameBuffer *fb, GPUFrameBufferBits buffers,
+ const float clear_col[4], float clear_depth, unsigned int clear_stencil)
+{
+ CHECK_FRAMEBUFFER_IS_BOUND(fb);
- if (GG.currentfb == fb->object) {
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
- GG.currentfb = 0;
- }
+ if (buffers & GPU_COLOR_BIT) {
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
+ }
+ if (buffers & GPU_DEPTH_BIT) {
+ glDepthMask(GL_TRUE);
+ glClearDepth(clear_depth);
+ }
+ if (buffers & GPU_STENCIL_BIT) {
+ glStencilMask(clear_stencil);
}
- MEM_freeN(fb);
+ GLbitfield mask = convert_buffer_bits_to_gl(buffers);
+ glClear(mask);
}
-void GPU_framebuffer_restore(void)
+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)
{
- if (GG.currentfb != 0) {
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
- GG.currentfb = 0;
+ 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);
}
-void GPU_framebuffer_blur(
- GPUFrameBuffer *fb, GPUTexture *tex,
- GPUFrameBuffer *blurfb, GPUTexture *blurtex)
+/* 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,
+ GPUFrameBufferBits blit_buffers)
{
- const float scaleh[2] = {1.0f / GPU_texture_width(blurtex), 0.0f};
- const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(tex)};
+ BLI_assert(blit_buffers != 0);
- GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR);
- int scale_uniform, texture_source_uniform;
+ GLuint prev_fb = gpu_framebuffer_current_get();
- if (!blur_shader)
- return;
+ /* 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);
+ }
- scale_uniform = GPU_shader_get_uniform(blur_shader, "ScaleU");
- texture_source_uniform = GPU_shader_get_uniform(blur_shader, "textureSource");
+ 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);
- /* Blurring horizontally */
+ 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);
- /* We do the bind ourselves rather than using GPU_framebuffer_texture_bind() to avoid
- * pushing unnecessary matrices onto the OpenGL stack. */
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, blurfb->object);
- glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ 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));
+ }
+ 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));
+ }
- /* avoid warnings from texture binding */
- GG.currentfb = blurfb->object;
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object);
- GPU_shader_bind(blur_shader);
- GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, scaleh);
- GPU_shader_uniform_texture(blur_shader, texture_source_uniform, tex);
- glViewport(0, 0, GPU_texture_width(blurtex), GPU_texture_height(blurtex));
+ 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;
+ }
- /* Preparing to draw quad */
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
+ GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers);
- glDisable(GL_DEPTH_TEST);
+ glBlitFramebuffer(0, 0, fb_read->width, fb_read->height,
+ 0, 0, fb_write->width, fb_write->height,
+ mask, GL_NEAREST);
- GPU_texture_bind(tex, 0);
+ /* Restore previous framebuffer */
+ if (fb_write->object == prev_fb) {
+ GPU_framebuffer_bind(fb_write); /* To update drawbuffers */
+ }
+ else {
+ glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
+ gpu_framebuffer_current_set(prev_fb);
+ }
+}
- /* Drawing quad */
- glBegin(GL_QUADS);
- glTexCoord2d(0, 0); glVertex2f(1, 1);
- glTexCoord2d(1, 0); glVertex2f(-1, 1);
- glTexCoord2d(1, 1); glVertex2f(-1, -1);
- glTexCoord2d(0, 1); glVertex2f(1, -1);
- glEnd();
+/**
+ * Use this if you need to custom downsample your texture and use the previous mip level as input.
+ * 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, int max_lvl,
+ void (*callback)(void *userData, int level), void *userData)
+{
+ /* Framebuffer must be up to date and bound. This simplify this function. */
+ if (gpu_framebuffer_current_get() != 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 = gpu_framebuffer_current_get();
+ gpu_framebuffer_current_set(0);
- /* Blurring vertically */
+ 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 (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);
+ }
+ }
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object);
- glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER));
- GG.currentfb = fb->object;
+ glViewport(0, 0, current_dim[0], current_dim[1]);
+ callback(userData, i);
- glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex));
- GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, scalev);
- GPU_shader_uniform_texture(blur_shader, texture_source_uniform, blurtex);
- GPU_texture_bind(blurtex, 0);
+ if (current_dim[0] == 1 && current_dim[1] == 1)
+ break;
+ }
- glBegin(GL_QUADS);
- glTexCoord2d(0, 0); glVertex2f(1, 1);
- glTexCoord2d(1, 0); glVertex2f(-1, 1);
- glTexCoord2d(1, 1); glVertex2f(-1, -1);
- glTexCoord2d(0, 1); glVertex2f(1, -1);
- glEnd();
+ 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);
+ }
+ }
- GPU_shader_unbind();
+ gpu_framebuffer_current_set(prev_fb);
}
/* GPUOffScreen */
@@ -451,63 +673,42 @@ struct GPUOffScreen {
GPUTexture *depth;
};
-GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256])
+GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, bool depth, bool high_bitdepth, char err_out[256])
{
GPUOffScreen *ofs;
ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen");
- ofs->fb = GPU_framebuffer_create();
- if (!ofs->fb) {
- GPU_offscreen_free(ofs);
- return NULL;
- }
+ ofs->color = GPU_texture_create_2D_multisample(width, height,
+ (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, samples, err_out);
- if (samples) {
- if (!GLEW_EXT_framebuffer_multisample ||
- !GLEW_ARB_texture_multisample ||
- /* Only needed for GPU_offscreen_read_pixels.
- * We could add an arg if we intend to use multi-sample
- * offscreen buffers w/o reading their pixels */
- !GLEW_EXT_framebuffer_blit ||
- /* This is required when blitting from a multi-sampled buffers,
- * even though we're not scaling. */
- !GLEW_EXT_framebuffer_multisample_blit_scaled)
- {
- samples = 0;
- }
+ if (depth) {
+ ofs->depth = GPU_texture_create_2D_multisample(width, height, GPU_DEPTH24_STENCIL8, NULL, samples, err_out);
}
- ofs->depth = GPU_texture_create_depth_multisample(width, height, samples, err_out);
- if (!ofs->depth) {
+ if ((depth && !ofs->depth) || !ofs->color) {
GPU_offscreen_free(ofs);
return NULL;
}
- if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) {
- GPU_offscreen_free(ofs);
- return NULL;
- }
+ gpuPushAttrib(GPU_VIEWPORT_BIT);
- ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, GPU_HDR_NONE, samples, err_out);
- if (!ofs->color) {
- GPU_offscreen_free(ofs);
- return NULL;
- }
-
- if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) {
- 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);
+ gpuPopAttrib();
return NULL;
}
GPU_framebuffer_restore();
+ gpuPopAttrib();
+
return ofs;
}
@@ -525,20 +726,37 @@ 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_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);
+
+ 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, 0);
}
void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels)
@@ -546,74 +764,46 @@ 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 */
- glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, ofs->color->fb->object);
- glFramebufferTexture2DEXT(
- GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + ofs->color->fb_attachment,
- GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0);
- status = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT);
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
- 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 */
- glGenFramebuffersEXT(1, &fbo_blit);
- glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit);
- glFramebufferTexture2DEXT(
- GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, tex_blit, 0);
- status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ glGenFramebuffers(1, &fbo_blit);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit);
+ 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;
}
/* perform the copy */
- glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
/* read the results */
- glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fbo_blit);
+ 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 */
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 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) {
- glDeleteFramebuffersEXT(1, &fbo_blit);
- }
-
- GPU_ASSERT_NO_GL_ERRORS("Read Multi-Sample Pixels");
+ glDeleteTextures(1, &tex_blit);
+ glDeleteFramebuffers(1, &fbo_blit);
}
else {
glReadPixels(0, 0, w, h, GL_RGBA, type, pixels);
@@ -630,8 +820,17 @@ int GPU_offscreen_height(const GPUOffScreen *ofs)
return GPU_texture_height(ofs->color);
}
-int GPU_offscreen_color_texture(const GPUOffScreen *ofs)
+GPUTexture *GPU_offscreen_color_texture(const GPUOffScreen *ofs)
{
- return GPU_texture_opengl_bindcode(ofs->color);
+ return ofs->color;
}
+/* only to be used by viewport code! */
+void GPU_offscreen_viewport_data_get(
+ GPUOffScreen *ofs,
+ GPUFrameBuffer **r_fb, GPUTexture **r_color, GPUTexture **r_depth)
+{
+ *r_fb = ofs->fb;
+ *r_color = ofs->color;
+ *r_depth = ofs->depth;
+}