summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Lantinga <slouken@libsdl.org>2012-01-18 22:45:49 -0500
committerSam Lantinga <slouken@libsdl.org>2012-01-18 22:45:49 -0500
commit3d67226719567d73c0d6317e3ad2cb7d4b6c92ec (patch)
tree960101861679a576ab72308966fa9a4adf8c70c6 /src
parent4b4cbe0d115e7d39ef5dd87cdf4b312ae31ae9ff (diff)
Implementation of render targets, by Mason Wheeler and Gabriel Jacobo
Thanks guys!
Diffstat (limited to 'src')
-rwxr-xr-xsrc/render/SDL_render.c33
-rwxr-xr-xsrc/render/SDL_sysrender.h1
-rwxr-xr-xsrc/render/direct3d/SDL_render_d3d.c100
-rwxr-xr-xsrc/render/opengl/SDL_render_gl.c147
-rw-r--r--src/render/opengles/SDL_glesfuncs.h8
-rwxr-xr-xsrc/render/opengles/SDL_render_gles.c150
-rw-r--r--src/render/opengles2/SDL_gles2funcs.h4
-rwxr-xr-xsrc/render/opengles2/SDL_render_gles2.c245
8 files changed, 644 insertions, 44 deletions
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 1648db8d..4ac83c80 100755
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -1014,9 +1014,9 @@ int
SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
{
SDL_Rect full_rect;
-
+
CHECK_RENDERER_MAGIC(renderer, -1);
-
+
/* If 'rect' == NULL, then outline the whole surface */
if (!rect) {
full_rect.x = 0;
@@ -1150,6 +1150,35 @@ SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
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 --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 0afd28dc..0c34952a 100755
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -87,6 +87,7 @@ struct SDL_Renderer
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 --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c
index 69179c8e..11ce494d 100755
--- a/src/render/direct3d/SDL_render_d3d.c
+++ b/src/render/direct3d/SDL_render_d3d.c
@@ -111,6 +111,7 @@ static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
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 @@ typedef struct
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 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
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 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
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 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
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 @@ GetScaleQuality(void)
}
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 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
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_DestroyRenderer(SDL_Renderer * renderer)
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 --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c
index 5236f241..952dcc8e 100755
--- a/src/render/opengl/SDL_render_gl.c
+++ b/src/render/opengl/SDL_render_gl.c
@@ -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);
@@ -403,6 +466,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)
{
GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
@@ -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);
}
diff --git a/src/render/opengles/SDL_glesfuncs.h b/src/render/opengles/SDL_glesfuncs.h
index ebf57cfb..4f2b2ce3 100644
--- a/src/render/opengles/SDL_glesfuncs.h
+++ b/src/render/opengles/SDL_glesfuncs.h
@@ -27,5 +27,13 @@ SDL_PROC(void, glTexParameteriv, (GLenum, GLenum, const GLint *))
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 --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c
index d7641617..66f77018 100755
--- a/src/render/opengles/SDL_render_gles.c
+++ b/src/render/opengles/SDL_render_gles.c
@@ -73,6 +73,16 @@ static void GLES_RenderPresent(SDL_Renderer * renderer);
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 @@ typedef struct
#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 @@ typedef struct
GLenum formattype;
void *pixels;
int pitch;
+ GLES_FBOList *fbo;
} GLES_TextureData;
static void
@@ -179,6 +194,27 @@ static int GLES_LoadFunctions(GLES_RenderData * data)
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 @@ GLES_ResetState(SDL_Renderer *renderer)
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)
{
@@ -274,6 +375,7 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->info.flags = SDL_RENDERER_ACCELERATED;
renderer->driverdata = data;
renderer->window = window;
+ renderer->SetTargetTexture = GLES_SetTargetTexture;
data->context = SDL_GL_CreateContext(window);
if (!data->context) {
@@ -317,6 +419,12 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
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);
@@ -335,7 +443,7 @@ GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
if (event->event == SDL_WINDOWEVENT_MINIMIZED) {
/* According to Apple documentation, we need to finish drawing NOW! */
- data->glFinish();
+ data->glFinish();
}
}
@@ -403,6 +511,11 @@ GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
}
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,15 +870,26 @@ GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
SDL_Window *window = renderer->window;
SDL_GetWindowSize(window, &w, &h);
- cropRect[0] = srcrect->x;
- cropRect[1] = srcrect->y + srcrect->h;
- 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,
- h - (renderer->viewport.y + dstrect->y) - dstrect->h, 0,
- dstrect->w, dstrect->h);
+ 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;
+ data->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES,
+ cropRect);
+ 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 +1025,12 @@ GLES_DestroyRenderer(SDL_Renderer * renderer)
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 --git a/src/render/opengles2/SDL_gles2funcs.h b/src/render/opengles2/SDL_gles2funcs.h
index fe143c95..e64528a4 100644
--- a/src/render/opengles2/SDL_gles2funcs.h
+++ b/src/render/opengles2/SDL_gles2funcs.h
@@ -40,3 +40,7 @@ SDL_PROC(void, glUniformMatrix4fv, (GLint, GLsizei, GLboolean, const GLfloat *))
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 --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c
index 2e59572a..3a767644 100755
--- a/src/render/opengles2/SDL_render_gles2.c
+++ b/src/render/opengles2/SDL_render_gles2.c
@@ -55,6 +55,15 @@ SDL_RenderDriver GLES2_RenderDriver = {
* 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 @@ typedef struct GLES2_TextureData
GLenum pixel_type;
void *pixel_data;
size_t pitch;
+ GLES2_FBOList *fbo;
} GLES2_TextureData;
typedef struct GLES2_ShaderCacheEntry
@@ -134,6 +144,9 @@ typedef struct GLES2_DriverContext
#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 void GLES2_WindowEvent(SDL_Renderer * renderer,
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 @@ static int GLES2_LoadFunctions(GLES2_DriverContext * data)
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 @@ static int GLES2_LoadFunctions(GLES2_DriverContext * data)
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 @@ static void
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 @@ GLES2_DestroyRenderer(SDL_Renderer *renderer)
}
}
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 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
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 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
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 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex,
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 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex,
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 @@ GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type, SDL_BlendMode b
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_RenderClear(SDL_Renderer * renderer)
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 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
/* 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_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
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 @@ GLES2_ResetState(SDL_Renderer *renderer)
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 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
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 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
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 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
renderer->RenderPresent = &GLES2_RenderPresent;
renderer->DestroyTexture = &GLES2_DestroyTexture;
renderer->DestroyRenderer = &GLES2_DestroyRenderer;
+ renderer->SetTargetTexture = &GLES2_SetTargetTexture;
GLES2_ResetState(renderer);