diff options
Diffstat (limited to 'source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLOffScreen.cpp')
-rw-r--r-- | source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLOffScreen.cpp | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLOffScreen.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLOffScreen.cpp new file mode 100644 index 00000000000..26ece47d8b3 --- /dev/null +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLOffScreen.cpp @@ -0,0 +1,347 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2015, Blender Foundation +* All rights reserved. +* +* The Original Code is: all of this file. +* +* Contributor(s): Blender Foundation. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "glew-mx.h" + +#include <stdio.h> + +#include "RAS_OpenGLOffScreen.h" +#include "RAS_ICanvas.h" + +RAS_OpenGLOffScreen::RAS_OpenGLOffScreen(RAS_ICanvas *canvas) + :m_canvas(canvas), m_depthrb(0), m_colorrb(0), m_depthtx(0), m_colortx(0), + m_fbo(0), m_blitfbo(0), m_blitrbo(0), m_blittex(0), m_target(RAS_OFS_RENDER_BUFFER), m_bound(false) +{ + m_width = 0; + m_height = 0; + m_samples = 0; + m_color = 0; +} + +RAS_OpenGLOffScreen::~RAS_OpenGLOffScreen() +{ + Destroy(); +} + +bool RAS_OpenGLOffScreen::Create(int width, int height, int samples, RAS_OFS_RENDER_TARGET target) +{ + GLenum status; + GLuint glo[2], fbo; + GLint max_samples; + GLenum textarget; + + if (m_fbo) { + printf("RAS_OpenGLOffScreen::Create(): buffer exists already, destroy first\n"); + return false; + } + if (target != RAS_IOffScreen::RAS_OFS_RENDER_BUFFER && + target != RAS_IOffScreen::RAS_OFS_RENDER_TEXTURE) + { + printf("RAS_OpenGLOffScreen::Create(): invalid offscren target\n"); + return false; + } + if (!GLEW_EXT_framebuffer_object) { + printf("RAS_OpenGLOffScreen::Create(): frame buffer not supported\n"); + return false; + } + if (samples) { + if (!GLEW_EXT_framebuffer_multisample || + !GLEW_EXT_framebuffer_blit) + { + samples = 0; + } + } + if (samples && target == RAS_OFS_RENDER_TEXTURE) { + // we need this in addition if we use multisample textures + if (!GLEW_ARB_texture_multisample || + !GLEW_EXT_framebuffer_multisample_blit_scaled) + { + samples = 0; + } + } + if (samples) { + max_samples = 0; + glGetIntegerv(GL_MAX_SAMPLES_EXT , &max_samples); + if (samples > max_samples) + samples = max_samples; + } + m_target = target; + fbo = 0; + glGenFramebuffersEXT(1, &fbo); + if (fbo == 0) { + printf("RAS_OpenGLOffScreen::Create(): frame buffer creation failed: %d\n", (int)glGetError()); + return false; + } + m_fbo = fbo; + glo[0] = glo[1] = 0; + if (target == RAS_OFS_RENDER_TEXTURE) { + glGenTextures(2, glo); + if (glo[0] == 0 || glo[1] == 0) { + printf("RAS_OpenGLOffScreen::Create(): texture creation failed: %d\n", (int)glGetError()); + goto L_ERROR; + } + m_depthtx = glo[0]; + m_color = m_colortx = glo[1]; + if (samples) { + textarget = GL_TEXTURE_2D_MULTISAMPLE; + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_depthtx); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_DEPTH_COMPONENT, width, height, true); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_colortx); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA8, width, height, true); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); + } + else { + textarget = GL_TEXTURE_2D; + glBindTexture(GL_TEXTURE_2D, m_depthtx); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, m_colortx); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, textarget, m_depthtx, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, textarget, m_colortx, 0); + } + else { + glGenRenderbuffersEXT(2, glo); + if (glo[0] == 0 || glo[1] == 0) { + printf("RAS_OpenGLOffScreen::Create(): render buffer creation failed: %d\n", (int)glGetError()); + goto L_ERROR; + } + m_depthrb = glo[0]; + m_colorrb = glo[1]; + glBindRenderbufferEXT(GL_RENDERBUFFER, m_depthrb); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER, m_colorrb); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, GL_RGBA8, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER, 0); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER, m_depthrb); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER, m_colorrb); + } + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + printf("RAS_OpenGLOffScreen::Create(): frame buffer incomplete: %d\n", (int)status); + goto L_ERROR; + } + m_width = width; + m_height = height; + + if (samples > 0) { + GLuint blit_tex; + GLuint blit_fbo; + // create a secondary FBO to blit to before the pixel can be read + + /* write into new single-sample buffer */ + glGenFramebuffersEXT(1, &blit_fbo); + if (!blit_fbo) { + printf("RAS_OpenGLOffScreen::Create(): failed creating a FBO for multi-sample offscreen buffer\n"); + goto L_ERROR; + } + m_blitfbo = blit_fbo; + blit_tex = 0; + if (target == RAS_OFS_RENDER_TEXTURE) { + glGenTextures(1, &blit_tex); + if (!blit_tex) { + printf("RAS_OpenGLOffScreen::Create(): failed creating a texture for multi-sample offscreen buffer\n"); + goto L_ERROR; + } + // m_color is the texture where the final render goes, the blit texture in this case + m_color = m_blittex = blit_tex; + glBindTexture(GL_TEXTURE_2D, m_blittex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_blitfbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_blittex, 0); + } + else { + /* create render buffer for new 'fbo_blit' */ + glGenRenderbuffersEXT(1, &blit_tex); + if (!blit_tex) { + printf("RAS_OpenGLOffScreen::Create(): failed creating a render buffer for multi-sample offscreen buffer\n"); + goto L_ERROR; + } + m_blitrbo = blit_tex; + glBindRenderbufferEXT(GL_RENDERBUFFER, m_blitrbo); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 0, GL_RGBA8, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER, 0); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_blitfbo); + glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER, m_blitrbo); + } + status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); + if (status != GL_FRAMEBUFFER_COMPLETE) { + printf("RAS_OpenGLOffScreen::Create(): frame buffer for multi-sample offscreen buffer incomplete: %d\n", (int)status); + goto L_ERROR; + } + // remember that multisample is enabled + m_samples = 1; + } + return true; + +L_ERROR: + Destroy(); + return false; +} + +void RAS_OpenGLOffScreen::Destroy() +{ + GLuint globj; + Unbind(); + if (m_fbo) { + globj = m_fbo; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + if (m_target == RAS_OFS_RENDER_TEXTURE) { + GLenum textarget = (m_samples) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, textarget, 0, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, textarget, 0, 0); + } + else { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffersEXT(1, &globj); + m_fbo = 0; + } + if (m_depthrb) { + globj = m_depthrb; + glDeleteRenderbuffers(1, &globj); + m_depthrb = 0; + } + if (m_colorrb) { + globj = m_colorrb; + glDeleteRenderbuffers(1, &globj); + m_colorrb = 0; + } + if (m_depthtx) { + globj = m_depthtx; + glDeleteTextures(1, &globj); + m_depthtx = 0; + } + if (m_colortx) { + globj = m_colortx; + glDeleteTextures(1, &globj); + m_colortx = 0; + } + if (m_blitfbo) { + globj = m_blitfbo; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_blitfbo); + if (m_target == RAS_OFS_RENDER_TEXTURE) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); + } + else { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDeleteFramebuffersEXT(1, &globj); + m_blitfbo = 0; + } + if (m_blitrbo) { + globj = m_blitrbo; + glDeleteRenderbuffers(1, &globj); + m_blitrbo = 0; + } + if (m_blittex) { + globj = m_blittex; + glDeleteTextures(1, &globj); + m_blittex = 0; + } + m_width = 0; + m_height = 0; + m_samples = 0; + m_color = 0; + m_target = RAS_OFS_RENDER_BUFFER; +} + +void RAS_OpenGLOffScreen::Bind(RAS_OFS_BIND_MODE mode) +{ + if (m_fbo) { + if (mode == RAS_OFS_BIND_RENDER) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + glViewport(0, 0, m_width, m_height); + glDisable(GL_SCISSOR_TEST); + } + else if (!m_blitfbo) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + } + else { + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_blitfbo); + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + } + m_bound = true; + } +} + +void RAS_OpenGLOffScreen::Unbind() +{ + if (!m_bound) + return; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glEnable(GL_SCISSOR_TEST); + glReadBuffer(GL_BACK); + glDrawBuffer(GL_BACK); + m_bound = false; +} + +void RAS_OpenGLOffScreen::MipMap() +{ + if (m_color) { + glBindTexture(GL_TEXTURE_2D, m_color); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +void RAS_OpenGLOffScreen::Blit() +{ + if (m_bound && m_blitfbo) { + // set the draw target to the secondary FBO, the read target is still the multisample FBO + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, m_blitfbo); + + // sample the primary + glBlitFramebufferEXT(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + // make sure the next glReadPixels will read from the secondary buffer + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, m_blitfbo); + } +} |