#include #include "base/logging.h" #include "gfx_es2/fbo.h" #include "gfx/gl_common.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 #endif // TODO: Breakout this GL extension checker in its own file. struct GLExtensions { bool OES_depth24; bool OES_packed_depth_stencil; bool OES_depth_texture; }; GLExtensions gl_extensions; void CheckExtensions() { static bool done = false; if (done) return; done = true; memset(&gl_extensions, 0, sizeof(gl_extensions)); const char *extString = (const char *)glGetString(GL_EXTENSIONS); gl_extensions.OES_packed_depth_stencil = strstr(extString, "GL_OES_packed_depth_stencil"); gl_extensions.OES_depth24 = strstr(extString, "GL_OES_depth24"); gl_extensions.OES_depth_texture = strstr(extString, "GL_OES_depth_texture"); } 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; }; // On PC, we always use GL_DEPTH24_STENCIL8. // On Android, we try to use what's available. FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil) { CheckExtensions(); FBO *fbo = new FBO(); fbo->width = width; fbo->height = height; // 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. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 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 FBO using DEPTH24_STENCIL8"); // 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 FBO using separate stencil"); // TEGRA fbo->z_stencil_buffer = 0; // 16-bit Z, separate 8-bit stencil glGenRenderbuffers(1, &fbo->z_buffer); glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer); glRenderbufferStorage(GL_RENDERBUFFER, 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() { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void fbo_bind_as_render_target(FBO *fbo) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); } void fbo_bind_for_read(FBO *fbo) { glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle); } void fbo_bind_color_as_texture(FBO *fbo, int color) { glBindTexture(GL_TEXTURE_2D, fbo->color_texture); } void fbo_destroy(FBO *fbo) { 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); glDeleteTextures(1, &fbo->color_texture); glDeleteRenderbuffers(1, &fbo->z_stencil_buffer); }