Index: include/SDL_video.h =================================================================== --- include/SDL_video.h (revision 5553) +++ include/SDL_video.h (working copy) @@ -201,7 +201,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; /** @@ -1254,6 +1255,28 @@ const SDL_Rect * dstrect); /** + * \fn SDL_bool SDL_RenderTargetSupported(SDL_Window *window) + * + * \brief Determines whether a window supports the use of render targets + * + * \param window The window to check. + * + * \return SDL_TRUE if supported, SDL_FALSE if not. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_RenderTargetSupported(SDL_Window *window); + +/** + * \fn int SDL_SetTargetTexture(SDL_TextureID textureID) + * + * \brief Set a texture as the current rendering target. + * + * \param textureId The targeted texture, or 0 to target the current window. + * + * \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_Texture *texture); + +/** * \brief Read pixels from the current rendering target. * * \param rect A pointer to the rectangle to read, or NULL for the entire Index: src/video/SDL_renderer_gl.c =================================================================== --- src/video/SDL_renderer_gl.c (revision 5553) +++ src/video/SDL_renderer_gl.c (working copy) @@ -111,6 +111,7 @@ Uint32 pixel_format, void * pixels, int pitch); static int GL_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, const 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); @@ -150,6 +151,15 @@ 0} }; +typedef struct GL_FBOList GL_FBOList; + +struct GL_FBOList +{ + Uint32 w, h; + GLuint FBO; + GL_FBOList *next; +}; + typedef struct { SDL_GLContext context; @@ -159,9 +169,13 @@ SDL_bool GL_APPLE_ycbcr_422_supported; SDL_bool GL_MESA_ycbcr_texture_supported; SDL_bool GL_ARB_fragment_program_supported; - int blendMode; + SDL_bool GL_EXT_framebuffer_object_supported; + SDL_bool inRenderTarget; + int blendMode; int scaleMode; + GL_FBOList *framebuffers; + /* OpenGL functions */ #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #include "SDL_glfuncs.h" @@ -178,6 +192,11 @@ PFNGLGENPROGRAMSARBPROC glGenProgramsARB; PFNGLBINDPROGRAMARBPROC glBindProgramARB; PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; + PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; + PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; + PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; + PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; + PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; /* (optional) fragment programs */ GLuint fragment_program_mask; @@ -198,6 +217,7 @@ int pitch; SDL_DirtyRectList dirty; int HACK_RYAN_FIXME; + GL_FBOList *fbo; } GL_TextureData; @@ -262,6 +282,26 @@ return 0; } +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) { @@ -315,7 +355,8 @@ renderer->RenderDrawRects = GL_RenderDrawRects; renderer->RenderFillRects = GL_RenderFillRects; renderer->RenderCopy = GL_RenderCopy; - renderer->RenderReadPixels = GL_RenderReadPixels; + renderer->SetTargetTexture = GL_SetTargetTexture; + renderer->RenderReadPixels = GL_RenderReadPixels; renderer->RenderWritePixels = GL_RenderWritePixels; renderer->RenderPresent = GL_RenderPresent; renderer->DestroyTexture = GL_DestroyTexture; @@ -399,6 +440,21 @@ SDL_GL_GetProcAddress("glTextureRangeAPPLE"); } + 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; + /* we might use fragment programs for YUV data, etc. */ if (SDL_GL_ExtensionSupported("GL_ARB_fragment_program")) { /* !!! FIXME: this doesn't check for errors. */ @@ -444,6 +500,9 @@ GL_RenderData *data = (GL_RenderData *) renderer->driverdata; SDL_Window *window = renderer->window; + if (data->inRenderTarget) { + GL_SetTargetTexture(renderer, NULL); + } if (SDL_GL_MakeCurrent(window, data->context) < 0) { return -1; } @@ -461,6 +520,50 @@ } static int +GL_SetTargetTexture(SDL_Renderer * renderer, SDL_Texture * texture) +{ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + SDL_Window *window = renderer->window; + GL_TextureData *texturedata; + GLenum status; + + if (! data->GL_EXT_framebuffer_object_supported) { + SDL_Unsupported(); + return -1; + } + if (texture == NULL) { + data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + data->glViewport(0, 0, window->w, window->h); + data->glOrtho(0.0, (GLdouble) window->w, (GLdouble) window->h, 0.0, + 0.0, 1.0); + data->inRenderTarget = SDL_FALSE; + return 0; + } + texturedata = (GL_TextureData *) texture->driverdata; + 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, GL_TEXTURE_2D, texturedata->texture, 0); + /* Check FBO status */ + status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + return -1; + } + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + data->glViewport(0, 0, texture->w, texture->h); + data->glOrtho(0.0, (GLdouble) texture->w, 0.0, (GLdouble) texture->h, + 0.0, 1.0); + data->inRenderTarget = SDL_TRUE; + return 0; +} + +static int GL_DisplayModeChanged(SDL_Renderer * renderer) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; @@ -809,11 +912,18 @@ } } + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + data->fbo = GL_GetFBO(renderdata, texture->w, texture->h); + } else { + data->fbo = NULL; + } + texture->driverdata = data; 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; @@ -1545,7 +1655,14 @@ } } - /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */ + 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); } SDL_free(data); Index: src/video/SDL_sysvideo.h =================================================================== --- src/video/SDL_sysvideo.h (revision 5553) +++ src/video/SDL_sysvideo.h (working copy) @@ -103,7 +103,8 @@ int w, int h); int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect); - int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect, + int (*SetTargetTexture) (SDL_Renderer * renderer, SDL_Texture * texture); + int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch); int (*RenderWritePixels) (SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, const void * pixels, int pitch); Index: src/video/SDL_video.c =================================================================== --- src/video/SDL_video.c (revision 5553) +++ src/video/SDL_video.c (working copy) @@ -2680,6 +2680,40 @@ format, pixels, pitch); } +SDL_bool +SDL_RenderTargetSupported(SDL_Window *window) +{ + SDL_Renderer *renderer; + + CHECK_WINDOW_MAGIC(window, SDL_FALSE); + renderer = window->renderer; + + if ((!renderer) || (!renderer->SetTargetTexture)){ + return SDL_FALSE; + } + return SDL_TRUE; +} + +int +SDL_SetTargetTexture(SDL_Texture *texture) +{ + SDL_Renderer *renderer; + + CHECK_TEXTURE_MAGIC(texture, -1); + renderer = SDL_CurrentDisplay->current_renderer; + if ((!renderer) || (!texture)) { + return -1; + } + if (!renderer->SetTargetTexture) { + SDL_Unsupported(); + return -1; + } + if (renderer->SetTargetTexture(renderer, texture) < 0) { + return -1; + } + return 0; +} + void SDL_RenderPresent(void) {