Files
ppsspp/gfx_es2/fbo.cpp
T
Unknown W. Brackets f1ec201dbc Cache bound fbos, allow unbinding only read.
This maintains the currently bound fbos, similar to glstate.
So long as these funcs are used, it can prevent duplicate binds.

Binding the same buffer twice comes with a performance penalty.
2014-08-27 23:01:43 -07:00

403 lines
12 KiB
C++

#include <string.h>
#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) && !defined(BLACKBERRY)
#ifndef GL_READ_FRAMEBUFFER
// Careful - our ES3 header defines these. Means that we must make sure
// to only use them if ES3 support is detected to be available and otherwise
// use GL_FRAMEBUFFER only.
#define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER
#define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER
#endif
#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;
bool native_fbo;
};
static FBO *g_overriddenBackbuffer;
static GLuint currentDrawHandle_ = 0;
static GLuint currentReadHandle_ = 0;
// 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->native_fbo = false;
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);
currentDrawHandle_ = fbo->handle;
currentReadHandle_ = fbo->handle;
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->native_fbo = false;
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_FRAMEBUFFER, fbo->handle);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);
glFramebufferRenderbuffer(GL_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_FRAMEBUFFER, fbo->handle);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer);
glFramebufferRenderbuffer(GL_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_FRAMEBUFFER, fbo->handle);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer);
glFramebufferRenderbuffer(GL_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);
currentDrawHandle_ = fbo->handle;
currentReadHandle_ = fbo->handle;
return fbo;
}
FBO *fbo_create_from_native_fbo(GLuint native_fbo, FBO *fbo)
{
if (!fbo)
fbo = new FBO();
fbo->native_fbo = true;
fbo->handle = native_fbo;
fbo->color_texture = 0;
fbo->z_stencil_buffer = 0;
fbo->z_buffer = 0;
fbo->stencil_buffer = 0;
fbo->width = 0;
fbo->height = 0;
fbo->colorDepth = FBO_8888;
return fbo;
}
static GLenum fbo_get_fb_target(bool read, GLuint **cached) {
bool supportsBlit = gl_extensions.FBO_ARB;
#ifdef USING_GLES2
supportsBlit = supportsBlit && (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit);
#endif
// Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV.
if (supportsBlit) {
if (read) {
*cached = &currentReadHandle_;
return GL_READ_FRAMEBUFFER;
} else {
*cached = &currentDrawHandle_;
return GL_DRAW_FRAMEBUFFER;
}
} else {
*cached = &currentDrawHandle_;
return GL_FRAMEBUFFER;
}
}
static void fbo_bind_fb_target(bool read, GLuint name) {
GLuint *cached;
GLenum target = fbo_get_fb_target(read, &cached);
if (*cached != name) {
glBindFramebuffer(target, name);
*cached = name;
}
}
void fbo_unbind() {
if (g_overriddenBackbuffer) {
fbo_bind_as_render_target(g_overriddenBackbuffer);
return;
}
CheckGLExtensions();
if (gl_extensions.FBO_ARB) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
} else {
#ifndef USING_GLES2
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#endif
}
#ifdef IOS
bindDefaultFBO();
#endif
currentDrawHandle_ = 0;
currentReadHandle_ = 0;
}
void fbo_override_backbuffer(FBO *fbo) {
g_overriddenBackbuffer = fbo;
}
void fbo_bind_as_render_target(FBO *fbo) {
// Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing
// in ES 2.0 that actually separate them anyway of course, so doesn't matter.
fbo_bind_fb_target(false, fbo->handle);
// Always restore viewport after render target binding
glstate.viewport.restore();
}
void fbo_unbind_render_target() {
fbo_unbind();
}
// For GL_EXT_FRAMEBUFFER_BLIT and similar.
void fbo_bind_for_read(FBO *fbo) {
fbo_bind_fb_target(true, fbo->handle);
}
void fbo_unbind_read() {
fbo_bind_fb_target(true, 0);
}
void fbo_bind_color_as_texture(FBO *fbo, int color) {
if (fbo) {
glBindTexture(GL_TEXTURE_2D, fbo->color_texture);
}
}
void fbo_destroy(FBO *fbo) {
if (fbo->native_fbo) {
delete fbo;
return;
}
if (gl_extensions.FBO_ARB) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo->handle);
glDeleteRenderbuffers(1, &fbo->z_stencil_buffer);
glDeleteRenderbuffers(1, &fbo->z_buffer);
glDeleteRenderbuffers(1, &fbo->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
}
currentDrawHandle_ = 0;
currentReadHandle_ = 0;
glDeleteTextures(1, &fbo->color_texture);
delete fbo;
}
void fbo_get_dimensions(FBO *fbo, int *w, int *h) {
*w = fbo->width;
*h = fbo->height;
}
int fbo_get_color_texture(FBO *fbo) {
return fbo->color_texture;
}
int fbo_get_depth_buffer(FBO *fbo) {
return fbo->z_buffer;
}
int fbo_get_stencil_buffer(FBO *fbo) {
return fbo->stencil_buffer;
}