#include #include "base/logging.h" #include "gfx/gl_common.h" #include "gfx_es2/fbo.h" #include "gfx/gl_common.h" #include "gfx_es2/gl_state.h" #if defined(USING_GLES2) #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER #define GL_RGBA8 GL_RGBA #ifndef GL_DEPTH_COMPONENT24 #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES #endif #ifndef GL_DEPTH24_STENCIL8_OES #define GL_DEPTH24_STENCIL8_OES 0x88F0 #endif #endif #ifdef IOS extern void bindDefaultFBO(); #endif struct FBO { GLuint handle; GLuint color_texture; GLuint z_stencil_buffer; // Either this is set, or the two below. GLuint z_buffer; GLuint stencil_buffer; int width; int height; FBOColorDepth colorDepth; }; // On PC, we always use GL_DEPTH24_STENCIL8. // On Android, we try to use what's available. #ifndef USING_GLES2 FBO *fbo_ext_create(int width, int height, int num_color_textures, bool z_stencil, FBOColorDepth colorDepth) { FBO *fbo = new FBO(); fbo->width = width; fbo->height = height; fbo->colorDepth = colorDepth; // Color texture is same everywhere glGenFramebuffersEXT(1, &fbo->handle); glGenTextures(1, &fbo->color_texture); // Create the surfaces. glBindTexture(GL_TEXTURE_2D, fbo->color_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // TODO: We could opt to only create 16-bit render targets on slow devices. For later. switch (colorDepth) { case FBO_8888: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); break; case FBO_4444: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL); break; case FBO_5551: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); break; case FBO_565: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); break; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); fbo->stencil_buffer = 0; fbo->z_buffer = 0; // 24-bit Z, 8-bit stencil glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, width, height); //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); // Bind it all together glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); switch(status) { case GL_FRAMEBUFFER_COMPLETE_EXT: // ILOG("Framebuffer verified complete."); break; case GL_FRAMEBUFFER_UNSUPPORTED_EXT: ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); break; default: FLOG("Other framebuffer error: %i", status); break; } // Unbind state we don't need glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); glBindTexture(GL_TEXTURE_2D, 0); return fbo; } #endif FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil, FBOColorDepth colorDepth) { CheckGLExtensions(); #ifndef USING_GLES2 if(!gl_extensions.FBO_ARB) return fbo_ext_create(width, height, num_color_textures, z_stencil, colorDepth); #endif FBO *fbo = new FBO(); fbo->width = width; fbo->height = height; fbo->colorDepth = colorDepth; // Color texture is same everywhere glGenFramebuffers(1, &fbo->handle); glGenTextures(1, &fbo->color_texture); // Create the surfaces. glBindTexture(GL_TEXTURE_2D, fbo->color_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // TODO: We could opt to only create 16-bit render targets on slow devices. For later. switch (colorDepth) { case FBO_8888: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); break; case FBO_4444: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL); break; case FBO_5551: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); break; case FBO_565: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); break; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #ifdef USING_GLES2 if (gl_extensions.OES_packed_depth_stencil) { ILOG("Creating %i x %i FBO using DEPTH24_STENCIL8", width, height); // Standard method fbo->stencil_buffer = 0; fbo->z_buffer = 0; // 24-bit Z, 8-bit stencil combined glGenRenderbuffers(1, &fbo->z_stencil_buffer); glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height); // Bind it all together glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); } else { ILOG("Creating %i x %i FBO using separate stencil", width, height); // TEGRA fbo->z_stencil_buffer = 0; // 16/24-bit Z, separate 8-bit stencil glGenRenderbuffers(1, &fbo->z_buffer); glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer); glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, width, height); // 8-bit stencil buffer glGenRenderbuffers(1, &fbo->stencil_buffer); glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); // Bind it all together glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer); } #else fbo->stencil_buffer = 0; fbo->z_buffer = 0; // 24-bit Z, 8-bit stencil glGenRenderbuffers(1, &fbo->z_stencil_buffer); glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); // Bind it all together glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); #endif GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); switch(status) { case GL_FRAMEBUFFER_COMPLETE: // ILOG("Framebuffer verified complete."); break; case GL_FRAMEBUFFER_UNSUPPORTED: ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); break; default: FLOG("Other framebuffer error: %i", status); break; } // Unbind state we don't need glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); return fbo; } void fbo_unbind() { CheckGLExtensions(); if(gl_extensions.FBO_ARB){ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); }else{ #ifndef USING_GLES2 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); #endif } #ifdef IOS bindDefaultFBO(); #endif } void fbo_bind_as_render_target(FBO *fbo) { if(gl_extensions.FBO_ARB){ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); }else{ #ifndef USING_GLES2 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); #endif } } void fbo_bind_for_read(FBO *fbo) { if(gl_extensions.FBO_ARB){ glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle); }else{ #ifndef USING_GLES2 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); #endif } } void fbo_bind_color_as_texture(FBO *fbo, int color) { glBindTexture(GL_TEXTURE_2D, fbo->color_texture); } void fbo_destroy(FBO *fbo) { if(gl_extensions.FBO_ARB){ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo->handle); glDeleteRenderbuffers(1, &fbo->z_stencil_buffer); }else{ #ifndef USING_GLES2 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glDeleteFramebuffersEXT(1, &fbo->handle); glDeleteRenderbuffersEXT(1, &fbo->z_stencil_buffer); #endif } glDeleteTextures(1, &fbo->color_texture); delete fbo; } void fbo_get_dimensions(FBO *fbo, int *w, int *h) { *w = fbo->width; *h = fbo->height; }