Implementation of render targets, by Mason Wheeler and Gabriel Jacobo

Thanks guys!
This commit is contained in:
Sam Lantinga 2012-01-18 22:45:49 -05:00
parent 2e70e7f3cb
commit a49a88676f
11 changed files with 879 additions and 45 deletions

View file

@ -66,6 +66,7 @@ static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_Rect * dstrect);
static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 pixel_format, void * pixels, int pitch);
static int GL_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static void GL_RenderPresent(SDL_Renderer * renderer);
static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static void GL_DestroyRenderer(SDL_Renderer * renderer);
@ -82,6 +83,15 @@ SDL_RenderDriver GL_RenderDriver = {
0}
};
typedef struct GL_FBOList GL_FBOList;
struct GL_FBOList
{
Uint32 w, h;
GLuint FBO;
GL_FBOList *next;
};
typedef struct
{
SDL_GLContext context;
@ -91,6 +101,11 @@ typedef struct
Uint32 color;
int blendMode;
} current;
SDL_bool GL_EXT_framebuffer_object_supported;
GL_FBOList *framebuffers;
SDL_Texture *renderTarget;
SDL_Rect viewport_copy;
/* OpenGL functions */
#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
@ -101,6 +116,12 @@ typedef struct
SDL_bool GL_ARB_multitexture_supported;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
GLint num_texture_units;
PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
/* Shader support */
GL_ShaderContext *shaders;
@ -123,6 +144,8 @@ typedef struct
SDL_bool yuv;
GLuint utexture;
GLuint vtexture;
GL_FBOList *fbo;
} GL_TextureData;
@ -227,6 +250,29 @@ GL_ResetState(SDL_Renderer *renderer)
data->glLoadIdentity();
}
GL_FBOList *
GL_GetFBO(GL_RenderData *data, Uint32 w, Uint32 h)
{
GL_FBOList *result = data->framebuffers;
while (result && ((result->w != w) || (result->h != h))) {
result = result->next;
}
if (!result) {
result = SDL_malloc(sizeof(GL_FBOList));
if (result) {
result->w = w;
result->h = h;
data->glGenFramebuffersEXT(1, &result->FBO);
result->next = data->framebuffers;
data->framebuffers = result;
}
}
return result;
}
SDL_Renderer *
GL_CreateRenderer(SDL_Window * window, Uint32 flags)
{
@ -269,6 +315,7 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->RenderDrawLines = GL_RenderDrawLines;
renderer->RenderFillRects = GL_RenderFillRects;
renderer->RenderCopy = GL_RenderCopy;
renderer->SetTargetTexture = GL_SetTargetTexture;
renderer->RenderReadPixels = GL_RenderReadPixels;
renderer->RenderPresent = GL_RenderPresent;
renderer->DestroyTexture = GL_DestroyTexture;
@ -341,6 +388,22 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
}
if (SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object")) {
data->GL_EXT_framebuffer_object_supported = SDL_TRUE;
data->glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
SDL_GL_GetProcAddress("glGenFramebuffersEXT");
data->glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
SDL_GL_GetProcAddress("glDeleteFramebuffersEXT");
data->glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
SDL_GL_GetProcAddress("glFramebufferTexture2DEXT");
data->glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
SDL_GL_GetProcAddress("glBindFramebufferEXT");
data->glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
}
data->framebuffers = NULL;
data->renderTarget = NULL;
/* Set up parameters for rendering */
GL_ResetState(renderer);
@ -402,6 +465,74 @@ GetScaleQuality(void)
}
}
static int
GL_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
GL_TextureData *texturedata;
GLenum status;
if (!renderer) return -1;
GL_ActivateRenderer(renderer);
if (! data->GL_EXT_framebuffer_object_supported) {
SDL_Unsupported();
return -1;
}
if (texture == NULL) {
if (data->renderTarget != NULL) {
data->renderTarget = NULL;
renderer->viewport = data->viewport_copy;
data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
data->glMatrixMode(GL_PROJECTION);
data->glLoadIdentity();
data->glMatrixMode(GL_MODELVIEW);
data->glLoadIdentity();
data->glViewport(renderer->viewport.x, renderer->viewport.y, renderer->viewport.w, renderer->viewport.h);
data->glOrtho(0.0, (GLdouble) renderer->viewport.w, (GLdouble) renderer->viewport.h, 0.0, 0.0, 1.0);
}
return 0;
}
if (renderer != texture->renderer) return -1;
if (data->renderTarget==NULL) {
// Keep a copy of the default viewport to restore when texture==NULL
data->viewport_copy = renderer->viewport;
}
texturedata = (GL_TextureData *) texture->driverdata;
if (!texturedata) {
if (texture->native && texture->native->driverdata) {
texture = texture->native;
texturedata = texture->driverdata;
}
else return -1;
}
data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, texturedata->fbo->FBO);
/* TODO: check if texture pixel format allows this operation */
data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texturedata->type, texturedata->texture, 0);
/* Check FBO status */
status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
return -1;
}
data->renderTarget = texture;
renderer->viewport.x = 0;
renderer->viewport.y = 0;
renderer->viewport.w = texture->w;
renderer->viewport.h = texture->h;
data->glMatrixMode(GL_PROJECTION);
data->glLoadIdentity();
data->glOrtho(0.0, (GLdouble) texture->w, 0.0, (GLdouble) texture->h, 0.0, 1.0);
data->glMatrixMode(GL_MODELVIEW);
data->glLoadIdentity();
data->glViewport(0, 0, texture->w, texture->h);
return 0;
}
static int
GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
@ -446,10 +577,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
}
texture->driverdata = data;
if (texture->access == SDL_TEXTUREACCESS_TARGET) {
data->fbo = GL_GetFBO(renderdata, texture->w, texture->h);
} else {
data->fbo = NULL;
}
renderdata->glGetError();
renderdata->glGenTextures(1, &data->texture);
if (renderdata->GL_ARB_texture_rectangle_supported) {
if ((renderdata->GL_ARB_texture_rectangle_supported)
/*&& texture->access != SDL_TEXTUREACCESS_TARGET*/){
data->type = GL_TEXTURE_RECTANGLE_ARB;
texture_w = texture->w;
texture_h = texture->h;
@ -1013,6 +1151,13 @@ GL_DestroyRenderer(SDL_Renderer * renderer)
GL_DestroyShaderContext(data->shaders);
}
if (data->context) {
while (data->framebuffers) {
GL_FBOList *nextnode = data->framebuffers->next;
/* delete the framebuffer object */
data->glDeleteFramebuffersEXT(1, &data->framebuffers->FBO);
SDL_free(data->framebuffers);
data->framebuffers = nextnode;
}
/* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */
SDL_GL_DeleteContext(data->context);
}