Files
blender/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLOffScreen.cpp
Dalai Felinto 6b9ab1f7a2 Unifying access to GLEW from the Blender Game Engine
Note: This is not about functionality, but about using the same stub file
we are using in Blender for the game engine in blender2.8.
2017-05-11 16:08:03 +02:00

348 lines
11 KiB
C++

/*
* ***** 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 "GPU_glew.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);
}
}