diff -r af89a3a770e7 -r 4ffab1eafc5a include/SDL_render.h --- a/include/SDL_render.h lun ene 16 20:40:10 2012 -0500 +++ b/include/SDL_render.h mié ene 18 17:54:50 2012 -0300 @@ -88,7 +88,8 @@ typedef enum { SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */ - SDL_TEXTUREACCESS_STREAMING /**< Changes frequently, lockable */ + SDL_TEXTUREACCESS_STREAMING, /**< Changes frequently, lockable */ + SDL_TEXTUREACCESS_TARGET /**< Texture can be used as a render target */ } SDL_TextureAccess; /** @@ -561,6 +562,31 @@ const SDL_Rect * srcrect, const SDL_Rect * dstrect); + +/** + * \fn SDL_bool SDL_RenderTargetSupported(SDL_Renderer *renderer) + * + * \brief Determines whether a window supports the use of render targets + * + * \param renderer The renderer that will be checked + * + * \return SDL_TRUE if supported, SDL_FALSE if not. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_RenderTargetSupported(SDL_Renderer *renderer); + +/** + * \fn int SDL_SetTargetTexture(SDL_Renderer *renderer, SDL_Texture *texture) + * + * \brief Set a texture as the current rendering target. + * + * \param renderer The renderer that will be checked + * + * \param texture The targeted texture, or NULL for the default render target + * + * \return 0 on success, or -1 if there is no rendering context current, or the driver doesn't support the requested operation. + */ +extern DECLSPEC int SDLCALL SDL_SetTargetTexture(SDL_Renderer *renderer, SDL_Texture *texture); + /** * \brief Read pixels from the current rendering target. * diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/SDL_render.c --- a/src/render/SDL_render.c lun ene 16 20:40:10 2012 -0500 +++ b/src/render/SDL_render.c mié ene 18 17:54:50 2012 -0300 @@ -1150,6 +1150,35 @@ format, pixels, pitch); } +SDL_bool +SDL_RenderTargetSupported(SDL_Renderer *renderer) +{ + if ((!renderer) || (!renderer->SetTargetTexture)){ + return SDL_FALSE; + } + return SDL_TRUE; +} + +int +SDL_SetTargetTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + + if(!renderer) { + return -1; + } + if (!renderer->SetTargetTexture) { + SDL_Unsupported(); + return -1; + } + // Warning: texture==NULL is a valid parameter + if( texture ) { + CHECK_TEXTURE_MAGIC(texture, -1); + if(renderer != texture->renderer) return -1; + } + + return renderer->SetTargetTexture(renderer, texture); +} + void SDL_RenderPresent(SDL_Renderer * renderer) { diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/SDL_sysrender.h --- a/src/render/SDL_sysrender.h lun ene 16 20:40:10 2012 -0500 +++ b/src/render/SDL_sysrender.h mié ene 18 17:54:50 2012 -0300 @@ -87,6 +87,7 @@ int count); int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect); + int (*SetTargetTexture) (SDL_Renderer * renderer, SDL_Texture * texture); int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch); void (*RenderPresent) (SDL_Renderer * renderer); diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/direct3d/SDL_render_d3d.c --- a/src/render/direct3d/SDL_render_d3d.c lun ene 16 20:40:10 2012 -0500 +++ b/src/render/direct3d/SDL_render_d3d.c mié ene 18 17:54:50 2012 -0300 @@ -111,6 +111,7 @@ const SDL_Rect * srcrect, const SDL_Rect * dstrect); static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch); +static int D3D_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture); static void D3D_RenderPresent(SDL_Renderer * renderer); static void D3D_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); @@ -138,6 +139,12 @@ SDL_bool updateSize; SDL_bool beginScene; D3DTEXTUREFILTERTYPE scaleMode; + IDirect3DSurface9 *defaultRenderTarget; + IDirect3DSurface9 *currentRenderTarget; + SDL_bool renderTargetActive; + SDL_Rect viewport_copy; + + Uint32 NumSimultaneousRTs; } D3D_RenderData; typedef struct @@ -392,6 +399,7 @@ renderer->RenderFillRects = D3D_RenderFillRects; renderer->RenderCopy = D3D_RenderCopy; renderer->RenderReadPixels = D3D_RenderReadPixels; + renderer->SetTargetTexture = D3D_SetTargetTexture; renderer->RenderPresent = D3D_RenderPresent; renderer->DestroyTexture = D3D_DestroyTexture; renderer->DestroyRenderer = D3D_DestroyRenderer; @@ -478,6 +486,7 @@ IDirect3DDevice9_GetDeviceCaps(data->device, &caps); renderer->info.max_texture_width = caps.MaxTextureWidth; renderer->info.max_texture_height = caps.MaxTextureHeight; + data->NumSimultaneousRTs = caps.NumSimultaneousRTs; /* Set up parameters for rendering */ IDirect3DDevice9_SetVertexShader(data->device, NULL); @@ -507,6 +516,11 @@ IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + /* Store the default render target */ + IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget ); + data->currentRenderTarget = NULL; + data->renderTargetActive = SDL_FALSE; + /* Set an identity world and view matrix */ matrix.m[0][0] = 1.0f; matrix.m[0][1] = 0.0f; @@ -555,6 +569,80 @@ } static int +D3D_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; + D3D_TextureData *texturedata; + HRESULT result; + + if (!renderer) return -1; + D3D_ActivateRenderer(renderer); + + if (data->NumSimultaneousRTs < 2) { + SDL_Unsupported(); + return -1; + } + + // Release the previous render target if it wasn't the default one + if (data->currentRenderTarget != NULL) { + IDirect3DSurface9_Release(data->currentRenderTarget); + data->currentRenderTarget = NULL; + } + + /* Prepare an identity world and view matrix */ + D3DMATRIX matrix; + matrix.m[0][0] = 1.0f; + matrix.m[0][1] = 0.0f; + matrix.m[0][2] = 0.0f; + matrix.m[0][3] = 0.0f; + matrix.m[1][0] = 0.0f; + matrix.m[1][1] = 1.0f; + matrix.m[1][2] = 0.0f; + matrix.m[1][3] = 0.0f; + matrix.m[2][0] = 0.0f; + matrix.m[2][1] = 0.0f; + matrix.m[2][2] = 1.0f; + matrix.m[2][3] = 0.0f; + matrix.m[3][0] = 0.0f; + matrix.m[3][1] = 0.0f; + matrix.m[3][2] = 0.0f; + matrix.m[3][3] = 1.0f; + + if (texture == NULL) { + if (data->renderTargetActive) { + data->renderTargetActive = SDL_FALSE; + IDirect3DDevice9_SetRenderTarget(data->device, 0, data->defaultRenderTarget ); + renderer->viewport = data->viewport_copy; + D3D_UpdateViewport(renderer); + } + return 0; + } + if (renderer != texture->renderer) return -1; + + if ( !data->renderTargetActive ) { + data->viewport_copy = renderer->viewport; + } + + texturedata = (D3D_TextureData *) texture->driverdata; + result = IDirect3DTexture9_GetSurfaceLevel(texturedata->texture, 0, &data->currentRenderTarget ); + if(FAILED(result)) { + return -1; + } + result = IDirect3DDevice9_SetRenderTarget(data->device, 0, data->currentRenderTarget ); + if(FAILED(result)) { + return -1; + } + + data->renderTargetActive = SDL_TRUE; + renderer->viewport.x = 0; + renderer->viewport.y = 0; + renderer->viewport.w = texture->w; + renderer->viewport.h = texture->h; + D3D_UpdateViewport(renderer); + return 0; +} + +static int D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) { D3D_RenderData *renderdata = (D3D_RenderData *) renderer->driverdata; @@ -580,6 +668,11 @@ usage = D3DUSAGE_DYNAMIC; } else #endif + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + pool = D3DPOOL_DEFAULT; // D3DPOOL_MANAGED does not work with usage=D3DUSAGE_RENDERTARGET + usage = D3DUSAGE_RENDERTARGET; + } + else { pool = D3DPOOL_MANAGED; usage = 0; @@ -1187,6 +1280,13 @@ D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; if (data) { + // Release the render target + IDirect3DSurface9_Release(data->defaultRenderTarget); + if (data->currentRenderTarget != NULL) { + IDirect3DSurface9_Release(data->currentRenderTarget); + data->currentRenderTarget = NULL; + } + if (data->device) { IDirect3DDevice9_Release(data->device); } diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/opengl/SDL_render_gl.c --- a/src/render/opengl/SDL_render_gl.c lun ene 16 20:40:10 2012 -0500 +++ b/src/render/opengl/SDL_render_gl.c mié ene 18 17:54:50 2012 -0300 @@ -66,6 +66,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ SDL_bool yuv; GLuint utexture; GLuint vtexture; + + GL_FBOList *fbo; } GL_TextureData; @@ -227,6 +250,27 @@ 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 == NULL) + { + result = SDL_malloc(sizeof(GL_FBOList)); + 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 +313,7 @@ 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 +386,22 @@ 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); @@ -403,6 +464,74 @@ } 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) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; @@ -446,10 +575,17 @@ } 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 +1149,13 @@ 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); } diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/opengles/SDL_glesfuncs.h --- a/src/render/opengles/SDL_glesfuncs.h lun ene 16 20:40:10 2012 -0500 +++ b/src/render/opengles/SDL_glesfuncs.h mié ene 18 17:54:50 2012 -0300 @@ -27,5 +27,13 @@ SDL_PROC(void, glTexSubImage2D, (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *)) SDL_PROC(void, glVertexPointer, (GLint, GLenum, GLsizei, const GLvoid *)) SDL_PROC(void, glViewport, (GLint, GLint, GLsizei, GLsizei)) +SDL_PROC(void, glBindFramebufferOES, (GLenum, GLuint)) +SDL_PROC(void, glFramebufferTexture2DOES, (GLenum, GLenum, GLenum, GLuint, GLint)) +SDL_PROC(GLenum, glCheckFramebufferStatusOES, (GLenum)) +SDL_PROC(void, glPushMatrix, (void)) +SDL_PROC(void, glTranslatef, (GLfloat, GLfloat, GLfloat)) +SDL_PROC(void, glRotatef, (GLfloat, GLfloat, GLfloat, GLfloat)) +SDL_PROC(void, glPopMatrix, (void)) +SDL_PROC(void, glDeleteFramebuffersOES, (GLsizei, const GLuint*)) /* vi: set ts=4 sw=4 expandtab: */ diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/opengles/SDL_render_gles.c --- a/src/render/opengles/SDL_render_gles.c lun ene 16 20:40:10 2012 -0500 +++ b/src/render/opengles/SDL_render_gles.c mié ene 18 17:54:50 2012 -0300 @@ -73,6 +73,16 @@ static void GLES_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); static void GLES_DestroyRenderer(SDL_Renderer * renderer); +static int GLES_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture); + +typedef struct GLES_FBOList GLES_FBOList; + +struct GLES_FBOList +{ + Uint32 w, h; + GLuint FBO; + GLES_FBOList *next; +}; SDL_RenderDriver GLES_RenderDriver = { @@ -98,6 +108,10 @@ #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #include "SDL_glesfuncs.h" #undef SDL_PROC + SDL_bool GL_OES_framebuffer_object_supported; + GLES_FBOList *framebuffers; + SDL_Texture *renderTarget; + SDL_Rect viewport_copy; SDL_bool useDrawTexture; SDL_bool GL_OES_draw_texture_supported; @@ -113,6 +127,7 @@ GLenum formattype; void *pixels; int pitch; + GLES_FBOList *fbo; } GLES_TextureData; static void @@ -179,6 +194,27 @@ static SDL_GLContext SDL_CurrentContext = NULL; +GLES_FBOList * +GLES_GetFBO(GLES_RenderData *data, Uint32 w, Uint32 h) +{ + GLES_FBOList *result = data->framebuffers; + while ((result) && ((result->w != w) || (result->h != h)) ) + { + result = result->next; + } + if (result == NULL) + { + result = SDL_malloc(sizeof(GLES_FBOList)); + result->w = w; + result->h = h; + glGenFramebuffersOES(1, &result->FBO); + result->next = data->framebuffers; + data->framebuffers = result; + } + return result; +} + + static int GLES_ActivateRenderer(SDL_Renderer * renderer) { @@ -221,6 +257,71 @@ data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); } +static int +GLES_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; + int w, h; + GLES_TextureData *texturedata = NULL; + GLenum status; + + if (!renderer) return -1; + GLES_ActivateRenderer(renderer); + if (! data->GL_OES_framebuffer_object_supported) { + SDL_Unsupported(); + return -1; + } + + if (texture == NULL) { + if (data->renderTarget != NULL) { + data->renderTarget = NULL; + renderer->viewport = data->viewport_copy; + data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, 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->glOrthof(0.0, (GLfloat) renderer->viewport.w, (GLfloat) 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 = (GLES_TextureData *) texture->driverdata; + if (!texturedata) { + if (texture->native && texture->native->driverdata) { + texture = texture->native; + texturedata = texture->driverdata; + } + else return -1; + } + data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, texturedata->fbo->FBO); + /* TODO: check if texture pixel format allows this operation */ + data->glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, texturedata->type, texturedata->texture, 0); + /* Check FBO status */ + status = data->glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); + if (status != GL_FRAMEBUFFER_COMPLETE_OES) { + 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->glOrthof(0.0, (GLfloat) texture->w, 0.0, (GLfloat) texture->h, 0.0, 1.0); + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + data->glViewport(0, 0, texture->w, texture->h); + return 0; +} + SDL_Renderer * GLES_CreateRenderer(SDL_Window * window, Uint32 flags) { @@ -273,7 +374,8 @@ renderer->info = GLES_RenderDriver.info; renderer->info.flags = SDL_RENDERER_ACCELERATED; renderer->driverdata = data; - renderer->window = window; + renderer->window = window; + renderer->SetTargetTexture = GLES_SetTargetTexture; data->context = SDL_GL_CreateContext(window); if (!data->context) { @@ -317,6 +419,12 @@ data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); renderer->info.max_texture_height = value; + if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object")) { + data->GL_OES_framebuffer_object_supported = SDL_TRUE; + } + data->framebuffers = NULL; + data->renderTarget = NULL; + /* Set up parameters for rendering */ GLES_ResetState(renderer); @@ -403,6 +511,11 @@ } texture->driverdata = data; + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + data->fbo = GLES_GetFBO(renderer->driverdata, texture->w, texture->h); + } else { + data->fbo = NULL; + } renderdata->glGetError(); renderdata->glEnable(GL_TEXTURE_2D); @@ -757,7 +870,18 @@ SDL_Window *window = renderer->window; SDL_GetWindowSize(window, &w, &h); - cropRect[0] = srcrect->x; + if (data->renderTarget != NULL) { + cropRect[0] = srcrect->x; + cropRect[1] = srcrect->y; + cropRect[2] = srcrect->w; + cropRect[3] = srcrect->h; + data->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, + cropRect); + data->glDrawTexiOES(renderer->viewport.x + dstrect->x, renderer->viewport.y + dstrect->y, 0, + dstrect->w, dstrect->h); + } + else { + cropRect[0] = srcrect->x; cropRect[1] = srcrect->y + srcrect->h; cropRect[2] = srcrect->w; cropRect[3] = -srcrect->h; @@ -766,6 +890,7 @@ data->glDrawTexiOES(renderer->viewport.x + dstrect->x, h - (renderer->viewport.y + dstrect->y) - dstrect->h, 0, dstrect->w, dstrect->h); + } } else { minx = dstrect->x; @@ -901,6 +1026,12 @@ if (data) { if (data->context) { + while (data->framebuffers) { + GLES_FBOList *nextnode = data->framebuffers->next; + data->glDeleteFramebuffersOES(1, &data->framebuffers->FBO); + SDL_free(data->framebuffers); + data->framebuffers = nextnode; + } SDL_GL_DeleteContext(data->context); } SDL_free(data); diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/opengles2/SDL_gles2funcs.h --- a/src/render/opengles2/SDL_gles2funcs.h lun ene 16 20:40:10 2012 -0500 +++ b/src/render/opengles2/SDL_gles2funcs.h mié ene 18 17:54:50 2012 -0300 @@ -40,3 +40,7 @@ SDL_PROC(void, glUseProgram, (GLuint)) SDL_PROC(void, glVertexAttribPointer, (GLuint, GLint, GLenum, GLboolean, GLsizei, const void *)) SDL_PROC(void, glViewport, (GLint, GLint, GLsizei, GLsizei)) +SDL_PROC(void, glBindFramebuffer, (GLenum, GLuint)) +SDL_PROC(void, glFramebufferTexture2D, (GLenum, GLenum, GLenum, GLuint, GLint)) +SDL_PROC(GLenum, glCheckFramebufferStatus, (GLenum)) +SDL_PROC(void, glDeleteFramebuffers, (GLsizei, const GLuint *)) diff -r af89a3a770e7 -r 4ffab1eafc5a src/render/opengles2/SDL_render_gles2.c --- a/src/render/opengles2/SDL_render_gles2.c lun ene 16 20:40:10 2012 -0500 +++ b/src/render/opengles2/SDL_render_gles2.c mié ene 18 17:54:50 2012 -0300 @@ -55,6 +55,15 @@ * Context structures * *************************************************************************************************/ +typedef struct GLES2_FBOList GLES2_FBOList; + +struct GLES2_FBOList +{ + Uint32 w, h; + GLuint FBO; + GLES2_FBOList *next; +}; + typedef struct GLES2_TextureData { GLenum texture; @@ -63,6 +72,7 @@ GLenum pixel_type; void *pixel_data; size_t pitch; + GLES2_FBOList *fbo; } GLES2_TextureData; typedef struct GLES2_ShaderCacheEntry @@ -134,6 +144,9 @@ #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #include "SDL_gles2funcs.h" #undef SDL_PROC + GLES2_FBOList *framebuffers; + SDL_Texture *renderTarget; + SDL_Rect viewport_copy; int shader_format_count; GLenum *shader_formats; @@ -154,6 +167,8 @@ static int GLES2_UpdateViewport(SDL_Renderer * renderer); static void GLES2_DestroyRenderer(SDL_Renderer *renderer); +static int GLES2_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture); + static SDL_GLContext SDL_CurrentContext = NULL; static int GLES2_LoadFunctions(GLES2_DriverContext * data) @@ -176,7 +191,7 @@ SDL_SetError("Couldn't load GLES2 function %s: %s\n", #func, SDL_GetError()); \ return -1; \ } \ - } while ( 0 ); + } while ( 0 ); #endif /* _SDL_NOGETPROCADDR_ */ #include "SDL_gles2funcs.h" @@ -184,6 +199,26 @@ return 0; } +GLES2_FBOList * +GLES2_GetFBO(GLES2_DriverContext *data, Uint32 w, Uint32 h) +{ + GLES2_FBOList *result = data->framebuffers; + while ((result) && ((result->w != w) || (result->h != h)) ) + { + result = result->next; + } + if (result == NULL) + { + result = SDL_malloc(sizeof(GLES2_FBOList)); + result->w = w; + result->h = h; + glGenFramebuffers(1, &result->FBO); + result->next = data->framebuffers; + data->framebuffers = result; + } + return result; +} + static int GLES2_ActivateRenderer(SDL_Renderer * renderer) { @@ -207,7 +242,7 @@ GLES2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) { GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata; - + if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) { /* Rebind the context to the window area */ SDL_CurrentContext = NULL; @@ -267,6 +302,12 @@ } } if (rdata->context) { + while (rdata->framebuffers) { + GLES2_FBOList *nextnode = rdata->framebuffers->next; + rdata->glDeleteFramebuffers(1, &rdata->framebuffers->FBO); + SDL_free(rdata->framebuffers); + rdata->framebuffers = nextnode; + } SDL_GL_DeleteContext(rdata->context); } if (rdata->shader_formats) { @@ -371,6 +412,13 @@ return -1; } texture->driverdata = tdata; + + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + tdata->fbo = GLES2_GetFBO(renderer->driverdata, texture->w, texture->h); + } else { + tdata->fbo = NULL; + } + return 0; } @@ -433,7 +481,7 @@ int y; GLES2_ActivateRenderer(renderer); - + /* Bail out if we're supposed to update an empty rectangle */ if (rect->w <= 0 || rect->h <= 0) return 0; @@ -543,7 +591,7 @@ entry->vertex_shader = vertex; entry->fragment_shader = fragment; entry->blend_mode = blendMode; - + /* Create the program and link it */ rdata->glGetError(); entry->id = rdata->glCreateProgram(); @@ -560,7 +608,7 @@ SDL_free(entry); return NULL; } - + /* Predetermine locations of uniform variables */ entry->uniform_locations[GLES2_UNIFORM_PROJECTION] = rdata->glGetUniformLocation(entry->id, "u_projection"); @@ -625,7 +673,7 @@ SDL_SetError("No shader matching the requested characteristics was found"); return NULL; } - + /* Find a matching shader instance that's supported on this hardware */ for (i = 0; i < shader->instance_count && !instance; ++i) { @@ -872,7 +920,7 @@ GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata; GLES2_ActivateRenderer(renderer); - + rdata->glClearColor((GLfloat) renderer->r * inv255f, (GLfloat) renderer->g * inv255f, (GLfloat) renderer->b * inv255f, @@ -1080,20 +1128,83 @@ /* Activate an appropriate shader and set the projection matrix */ blendMode = texture->blendMode; - switch (texture->format) - { - case SDL_PIXELFORMAT_ABGR8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - break; - case SDL_PIXELFORMAT_ARGB8888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; - break; - case SDL_PIXELFORMAT_BGR888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; - break; - case SDL_PIXELFORMAT_RGB888: - sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB; - break; + if (rdata->renderTarget!=NULL) { + /* Check if we need to do color mapping between the source and render target textures */ + if (rdata->renderTarget->format != texture->format) { + switch (texture->format) + { + case SDL_PIXELFORMAT_ABGR8888: + switch (rdata->renderTarget->format) + { + case SDL_PIXELFORMAT_ARGB8888: + case SDL_PIXELFORMAT_RGB888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; + break; + case SDL_PIXELFORMAT_BGR888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; + break; + } + break; + case SDL_PIXELFORMAT_ARGB8888: + switch (rdata->renderTarget->format) + { + case SDL_PIXELFORMAT_ABGR8888: + case SDL_PIXELFORMAT_BGR888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; + break; + case SDL_PIXELFORMAT_RGB888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; + break; + } + break; + case SDL_PIXELFORMAT_BGR888: + switch (rdata->renderTarget->format) + { + case SDL_PIXELFORMAT_ABGR8888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; + break; + case SDL_PIXELFORMAT_ARGB8888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB; + break; + case SDL_PIXELFORMAT_RGB888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; + break; + } + break; + case SDL_PIXELFORMAT_RGB888: + switch (rdata->renderTarget->format) + { + case SDL_PIXELFORMAT_ABGR8888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; + break; + case SDL_PIXELFORMAT_ARGB8888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; + break; + case SDL_PIXELFORMAT_BGR888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; + break; + } + break; + } + } + else sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; // Texture formats match, use the non color mapping shader (even if the formats are not ABGR) + } + else { + switch (texture->format) + { + case SDL_PIXELFORMAT_ABGR8888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; + break; + case SDL_PIXELFORMAT_ARGB8888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; + break; + case SDL_PIXELFORMAT_BGR888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; + break; + case SDL_PIXELFORMAT_RGB888: + sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB; + break; + } } if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0) return -1; @@ -1119,15 +1230,29 @@ GLES2_SetTexCoords(rdata, SDL_TRUE); /* Emit the textured quad */ - vertices[0] = (GLfloat)dstrect->x; - vertices[1] = (GLfloat)dstrect->y; - vertices[2] = (GLfloat)(dstrect->x + dstrect->w); - vertices[3] = (GLfloat)dstrect->y; - vertices[4] = (GLfloat)dstrect->x; - vertices[5] = (GLfloat)(dstrect->y + dstrect->h); - vertices[6] = (GLfloat)(dstrect->x + dstrect->w); - vertices[7] = (GLfloat)(dstrect->y + dstrect->h); + if (rdata->renderTarget!=NULL) { + // Flip the texture vertically to compensate for the inversion it'll be subjected to later when it's rendered to the screen + vertices[0] = (GLfloat)dstrect->x; + vertices[1] = (GLfloat)renderer->viewport.h-dstrect->y; + vertices[2] = (GLfloat)(dstrect->x + dstrect->w); + vertices[3] = (GLfloat)renderer->viewport.h-dstrect->y; + vertices[4] = (GLfloat)dstrect->x; + vertices[5] = (GLfloat)renderer->viewport.h-(dstrect->y + dstrect->h); + vertices[6] = (GLfloat)(dstrect->x + dstrect->w); + vertices[7] = (GLfloat)renderer->viewport.h-(dstrect->y + dstrect->h); + } + else { + vertices[0] = (GLfloat)dstrect->x; + vertices[1] = (GLfloat)dstrect->y; + vertices[2] = (GLfloat)(dstrect->x + dstrect->w); + vertices[3] = (GLfloat)dstrect->y; + vertices[4] = (GLfloat)dstrect->x; + vertices[5] = (GLfloat)(dstrect->y + dstrect->h); + vertices[6] = (GLfloat)(dstrect->x + dstrect->w); + vertices[7] = (GLfloat)(dstrect->y + dstrect->h); + } rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices); + texCoords[0] = srcrect->x / (GLfloat)texture->w; texCoords[1] = srcrect->y / (GLfloat)texture->h; texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; @@ -1231,6 +1356,60 @@ rdata->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); } +static int +GLES2_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; + GLES2_TextureData *texturedata = NULL; + GLenum status; + SDL_BlendMode blendMode; + + if (!renderer) return -1; + + blendMode = texture->blendMode; + if (texture == NULL) { + if (data->renderTarget!=NULL) { + data->glBindFramebuffer(GL_FRAMEBUFFER, 0); + renderer->viewport = data->viewport_copy; + data->renderTarget = NULL; + data->glViewport(renderer->viewport.x, renderer->viewport.y, renderer->viewport.w, renderer->viewport.h); + if(data->current_program) GLES2_SetOrthographicProjection(renderer); + } + 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 = (GLES2_TextureData *) texture->driverdata; + if (!texturedata) { + if (texture->native && texture->native->driverdata) { + texture = texture->native; + texturedata = texture->driverdata; + } + else return -1; + } + data->glBindFramebuffer(GL_FRAMEBUFFER, texturedata->fbo->FBO); + /* TODO: check if texture pixel format allows this operation */ + data->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texturedata->texture_type, texturedata->texture, 0); + /* Check FBO status */ + status = data->glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + return -1; + } + + renderer->viewport.x = 0; + renderer->viewport.y = 0; + renderer->viewport.w = texture->w; + renderer->viewport.h = texture->h; + data->renderTarget = texture; + data->glViewport(0, 0, texture->w, texture->h); + if(data->current_program) GLES2_SetOrthographicProjection(renderer); + return 0; +} + static SDL_Renderer * GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) { @@ -1241,10 +1420,10 @@ GLboolean hasCompiler; #endif Uint32 windowFlags; - + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - + windowFlags = SDL_GetWindowFlags(window); if (!(windowFlags & SDL_WINDOW_OPENGL)) { if (SDL_RecreateWindow(window, windowFlags | SDL_WINDOW_OPENGL) < 0) { @@ -1331,6 +1510,9 @@ rdata->shader_formats[nFormats - 1] = (GLenum)-1; #endif /* ZUNE_HD */ + rdata->framebuffers = NULL; + rdata->renderTarget = NULL; + /* Populate the function pointers for the module */ renderer->WindowEvent = &GLES2_WindowEvent; renderer->CreateTexture = &GLES2_CreateTexture; @@ -1347,6 +1529,7 @@ renderer->RenderPresent = &GLES2_RenderPresent; renderer->DestroyTexture = &GLES2_DestroyTexture; renderer->DestroyRenderer = &GLES2_DestroyRenderer; + renderer->SetTargetTexture = &GLES2_SetTargetTexture; GLES2_ResetState(renderer);