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

@ -88,7 +88,8 @@ typedef struct SDL_RendererInfo
typedef enum typedef enum
{ {
SDL_TEXTUREACCESS_STATIC, /**< Changes rarely, not lockable */ 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; } SDL_TextureAccess;
/** /**
@ -561,6 +562,31 @@ extern DECLSPEC int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
const SDL_Rect * srcrect, const SDL_Rect * srcrect,
const SDL_Rect * dstrect); 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. * \brief Read pixels from the current rendering target.
* *

View file

@ -1014,9 +1014,9 @@ int
SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect) SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
{ {
SDL_Rect full_rect; SDL_Rect full_rect;
CHECK_RENDERER_MAGIC(renderer, -1); CHECK_RENDERER_MAGIC(renderer, -1);
/* If 'rect' == NULL, then outline the whole surface */ /* If 'rect' == NULL, then outline the whole surface */
if (!rect) { if (!rect) {
full_rect.x = 0; full_rect.x = 0;
@ -1150,6 +1150,35 @@ SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
format, pixels, pitch); 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 void
SDL_RenderPresent(SDL_Renderer * renderer) SDL_RenderPresent(SDL_Renderer * renderer)
{ {

View file

@ -87,6 +87,7 @@ struct SDL_Renderer
int count); int count);
int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture, int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_Rect * dstrect); 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, int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 format, void * pixels, int pitch); Uint32 format, void * pixels, int pitch);
void (*RenderPresent) (SDL_Renderer * renderer); void (*RenderPresent) (SDL_Renderer * renderer);

View file

@ -111,6 +111,7 @@ static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_Rect * dstrect); const SDL_Rect * srcrect, const SDL_Rect * dstrect);
static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 format, void * pixels, int pitch); 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_RenderPresent(SDL_Renderer * renderer);
static void D3D_DestroyTexture(SDL_Renderer * renderer, static void D3D_DestroyTexture(SDL_Renderer * renderer,
SDL_Texture * texture); SDL_Texture * texture);
@ -138,6 +139,12 @@ typedef struct
SDL_bool updateSize; SDL_bool updateSize;
SDL_bool beginScene; SDL_bool beginScene;
D3DTEXTUREFILTERTYPE scaleMode; D3DTEXTUREFILTERTYPE scaleMode;
IDirect3DSurface9 *defaultRenderTarget;
IDirect3DSurface9 *currentRenderTarget;
SDL_bool renderTargetActive;
SDL_Rect viewport_copy;
Uint32 NumSimultaneousRTs;
} D3D_RenderData; } D3D_RenderData;
typedef struct typedef struct
@ -392,6 +399,7 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->RenderFillRects = D3D_RenderFillRects; renderer->RenderFillRects = D3D_RenderFillRects;
renderer->RenderCopy = D3D_RenderCopy; renderer->RenderCopy = D3D_RenderCopy;
renderer->RenderReadPixels = D3D_RenderReadPixels; renderer->RenderReadPixels = D3D_RenderReadPixels;
renderer->SetTargetTexture = D3D_SetTargetTexture;
renderer->RenderPresent = D3D_RenderPresent; renderer->RenderPresent = D3D_RenderPresent;
renderer->DestroyTexture = D3D_DestroyTexture; renderer->DestroyTexture = D3D_DestroyTexture;
renderer->DestroyRenderer = D3D_DestroyRenderer; renderer->DestroyRenderer = D3D_DestroyRenderer;
@ -478,6 +486,7 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
IDirect3DDevice9_GetDeviceCaps(data->device, &caps); IDirect3DDevice9_GetDeviceCaps(data->device, &caps);
renderer->info.max_texture_width = caps.MaxTextureWidth; renderer->info.max_texture_width = caps.MaxTextureWidth;
renderer->info.max_texture_height = caps.MaxTextureHeight; renderer->info.max_texture_height = caps.MaxTextureHeight;
data->NumSimultaneousRTs = caps.NumSimultaneousRTs;
/* Set up parameters for rendering */ /* Set up parameters for rendering */
IDirect3DDevice9_SetVertexShader(data->device, NULL); IDirect3DDevice9_SetVertexShader(data->device, NULL);
@ -507,6 +516,11 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags)
IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_ALPHAOP, IDirect3DDevice9_SetTextureStageState(data->device, 1, D3DTSS_ALPHAOP,
D3DTOP_DISABLE); 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 */ /* Set an identity world and view matrix */
matrix.m[0][0] = 1.0f; matrix.m[0][0] = 1.0f;
matrix.m[0][1] = 0.0f; matrix.m[0][1] = 0.0f;
@ -554,6 +568,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 static int
D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{ {
@ -580,6 +668,11 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
usage = D3DUSAGE_DYNAMIC; usage = D3DUSAGE_DYNAMIC;
} else } else
#endif #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; pool = D3DPOOL_MANAGED;
usage = 0; usage = 0;
@ -1187,6 +1280,13 @@ D3D_DestroyRenderer(SDL_Renderer * renderer)
D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
if (data) { if (data) {
// Release the render target
IDirect3DSurface9_Release(data->defaultRenderTarget);
if (data->currentRenderTarget != NULL) {
IDirect3DSurface9_Release(data->currentRenderTarget);
data->currentRenderTarget = NULL;
}
if (data->device) { if (data->device) {
IDirect3DDevice9_Release(data->device); IDirect3DDevice9_Release(data->device);
} }

View file

@ -66,6 +66,7 @@ static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_Rect * dstrect); const SDL_Rect * srcrect, const SDL_Rect * dstrect);
static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 pixel_format, void * pixels, int pitch); 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_RenderPresent(SDL_Renderer * renderer);
static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static void GL_DestroyRenderer(SDL_Renderer * renderer); static void GL_DestroyRenderer(SDL_Renderer * renderer);
@ -82,6 +83,15 @@ SDL_RenderDriver GL_RenderDriver = {
0} 0}
}; };
typedef struct GL_FBOList GL_FBOList;
struct GL_FBOList
{
Uint32 w, h;
GLuint FBO;
GL_FBOList *next;
};
typedef struct typedef struct
{ {
SDL_GLContext context; SDL_GLContext context;
@ -91,6 +101,11 @@ typedef struct
Uint32 color; Uint32 color;
int blendMode; int blendMode;
} current; } current;
SDL_bool GL_EXT_framebuffer_object_supported;
GL_FBOList *framebuffers;
SDL_Texture *renderTarget;
SDL_Rect viewport_copy;
/* OpenGL functions */ /* OpenGL functions */
#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
@ -101,6 +116,12 @@ typedef struct
SDL_bool GL_ARB_multitexture_supported; SDL_bool GL_ARB_multitexture_supported;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB; PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
GLint num_texture_units; GLint num_texture_units;
PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
/* Shader support */ /* Shader support */
GL_ShaderContext *shaders; GL_ShaderContext *shaders;
@ -123,6 +144,8 @@ typedef struct
SDL_bool yuv; SDL_bool yuv;
GLuint utexture; GLuint utexture;
GLuint vtexture; GLuint vtexture;
GL_FBOList *fbo;
} GL_TextureData; } GL_TextureData;
@ -227,6 +250,29 @@ GL_ResetState(SDL_Renderer *renderer)
data->glLoadIdentity(); 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 * SDL_Renderer *
GL_CreateRenderer(SDL_Window * window, Uint32 flags) GL_CreateRenderer(SDL_Window * window, Uint32 flags)
{ {
@ -269,6 +315,7 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
renderer->RenderDrawLines = GL_RenderDrawLines; renderer->RenderDrawLines = GL_RenderDrawLines;
renderer->RenderFillRects = GL_RenderFillRects; renderer->RenderFillRects = GL_RenderFillRects;
renderer->RenderCopy = GL_RenderCopy; renderer->RenderCopy = GL_RenderCopy;
renderer->SetTargetTexture = GL_SetTargetTexture;
renderer->RenderReadPixels = GL_RenderReadPixels; renderer->RenderReadPixels = GL_RenderReadPixels;
renderer->RenderPresent = GL_RenderPresent; renderer->RenderPresent = GL_RenderPresent;
renderer->DestroyTexture = GL_DestroyTexture; 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_YV12;
renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; 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 */ /* Set up parameters for rendering */
GL_ResetState(renderer); 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 static int
GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{ {
@ -446,10 +577,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
} }
texture->driverdata = data; 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->glGetError();
renderdata->glGenTextures(1, &data->texture); 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; data->type = GL_TEXTURE_RECTANGLE_ARB;
texture_w = texture->w; texture_w = texture->w;
texture_h = texture->h; texture_h = texture->h;
@ -1013,6 +1151,13 @@ GL_DestroyRenderer(SDL_Renderer * renderer)
GL_DestroyShaderContext(data->shaders); GL_DestroyShaderContext(data->shaders);
} }
if (data->context) { 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_MakeCurrent(0, NULL); *//* doesn't do anything */
SDL_GL_DeleteContext(data->context); SDL_GL_DeleteContext(data->context);
} }

View file

@ -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, glTexSubImage2D, (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *))
SDL_PROC(void, glVertexPointer, (GLint, GLenum, GLsizei, const GLvoid *)) SDL_PROC(void, glVertexPointer, (GLint, GLenum, GLsizei, const GLvoid *))
SDL_PROC(void, glViewport, (GLint, GLint, GLsizei, GLsizei)) 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: */ /* vi: set ts=4 sw=4 expandtab: */

View file

@ -73,6 +73,16 @@ static void GLES_RenderPresent(SDL_Renderer * renderer);
static void GLES_DestroyTexture(SDL_Renderer * renderer, static void GLES_DestroyTexture(SDL_Renderer * renderer,
SDL_Texture * texture); SDL_Texture * texture);
static void GLES_DestroyRenderer(SDL_Renderer * renderer); 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 = { SDL_RenderDriver GLES_RenderDriver = {
@ -98,6 +108,10 @@ typedef struct
#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
#include "SDL_glesfuncs.h" #include "SDL_glesfuncs.h"
#undef SDL_PROC #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 useDrawTexture;
SDL_bool GL_OES_draw_texture_supported; SDL_bool GL_OES_draw_texture_supported;
@ -113,6 +127,7 @@ typedef struct
GLenum formattype; GLenum formattype;
void *pixels; void *pixels;
int pitch; int pitch;
GLES_FBOList *fbo;
} GLES_TextureData; } GLES_TextureData;
static void static void
@ -179,6 +194,27 @@ static int GLES_LoadFunctions(GLES_RenderData * data)
static SDL_GLContext SDL_CurrentContext = NULL; 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 static int
GLES_ActivateRenderer(SDL_Renderer * renderer) GLES_ActivateRenderer(SDL_Renderer * renderer)
{ {
@ -221,6 +257,71 @@ GLES_ResetState(SDL_Renderer *renderer)
data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); 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 * SDL_Renderer *
GLES_CreateRenderer(SDL_Window * window, Uint32 flags) 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->info.flags = SDL_RENDERER_ACCELERATED;
renderer->driverdata = data; renderer->driverdata = data;
renderer->window = window; renderer->window = window;
renderer->SetTargetTexture = GLES_SetTargetTexture;
data->context = SDL_GL_CreateContext(window); data->context = SDL_GL_CreateContext(window);
if (!data->context) { if (!data->context) {
@ -317,6 +419,12 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
renderer->info.max_texture_height = 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 */ /* Set up parameters for rendering */
GLES_ResetState(renderer); GLES_ResetState(renderer);
@ -335,7 +443,7 @@ GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
if (event->event == SDL_WINDOWEVENT_MINIMIZED) { if (event->event == SDL_WINDOWEVENT_MINIMIZED) {
/* According to Apple documentation, we need to finish drawing NOW! */ /* 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; 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->glGetError();
renderdata->glEnable(GL_TEXTURE_2D); renderdata->glEnable(GL_TEXTURE_2D);
@ -757,15 +870,26 @@ GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
SDL_Window *window = renderer->window; SDL_Window *window = renderer->window;
SDL_GetWindowSize(window, &w, &h); SDL_GetWindowSize(window, &w, &h);
cropRect[0] = srcrect->x; if (data->renderTarget != NULL) {
cropRect[1] = srcrect->y + srcrect->h; cropRect[0] = srcrect->x;
cropRect[2] = srcrect->w; cropRect[1] = srcrect->y;
cropRect[3] = -srcrect->h; cropRect[2] = srcrect->w;
data->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect[3] = srcrect->h;
cropRect); data->glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES,
data->glDrawTexiOES(renderer->viewport.x + dstrect->x, cropRect);
h - (renderer->viewport.y + dstrect->y) - dstrect->h, 0, data->glDrawTexiOES(renderer->viewport.x + dstrect->x, renderer->viewport.y + dstrect->y, 0,
dstrect->w, dstrect->h); 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 { } else {
minx = dstrect->x; minx = dstrect->x;
@ -901,6 +1025,12 @@ GLES_DestroyRenderer(SDL_Renderer * renderer)
if (data) { if (data) {
if (data->context) { 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_GL_DeleteContext(data->context);
} }
SDL_free(data); SDL_free(data);

View file

@ -40,3 +40,7 @@ SDL_PROC(void, glUniformMatrix4fv, (GLint, GLsizei, GLboolean, const GLfloat *))
SDL_PROC(void, glUseProgram, (GLuint)) SDL_PROC(void, glUseProgram, (GLuint))
SDL_PROC(void, glVertexAttribPointer, (GLuint, GLint, GLenum, GLboolean, GLsizei, const void *)) SDL_PROC(void, glVertexAttribPointer, (GLuint, GLint, GLenum, GLboolean, GLsizei, const void *))
SDL_PROC(void, glViewport, (GLint, GLint, GLsizei, GLsizei)) 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 *))

View file

@ -55,6 +55,15 @@ SDL_RenderDriver GLES2_RenderDriver = {
* Context structures * * Context structures *
*************************************************************************************************/ *************************************************************************************************/
typedef struct GLES2_FBOList GLES2_FBOList;
struct GLES2_FBOList
{
Uint32 w, h;
GLuint FBO;
GLES2_FBOList *next;
};
typedef struct GLES2_TextureData typedef struct GLES2_TextureData
{ {
GLenum texture; GLenum texture;
@ -63,6 +72,7 @@ typedef struct GLES2_TextureData
GLenum pixel_type; GLenum pixel_type;
void *pixel_data; void *pixel_data;
size_t pitch; size_t pitch;
GLES2_FBOList *fbo;
} GLES2_TextureData; } GLES2_TextureData;
typedef struct GLES2_ShaderCacheEntry typedef struct GLES2_ShaderCacheEntry
@ -134,6 +144,9 @@ typedef struct GLES2_DriverContext
#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
#include "SDL_gles2funcs.h" #include "SDL_gles2funcs.h"
#undef SDL_PROC #undef SDL_PROC
GLES2_FBOList *framebuffers;
SDL_Texture *renderTarget;
SDL_Rect viewport_copy;
int shader_format_count; int shader_format_count;
GLenum *shader_formats; GLenum *shader_formats;
@ -154,6 +167,8 @@ static void GLES2_WindowEvent(SDL_Renderer * renderer,
static int GLES2_UpdateViewport(SDL_Renderer * renderer); static int GLES2_UpdateViewport(SDL_Renderer * renderer);
static void GLES2_DestroyRenderer(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 SDL_GLContext SDL_CurrentContext = NULL;
static int GLES2_LoadFunctions(GLES2_DriverContext * data) 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()); \ SDL_SetError("Couldn't load GLES2 function %s: %s\n", #func, SDL_GetError()); \
return -1; \ return -1; \
} \ } \
} while ( 0 ); } while ( 0 );
#endif /* _SDL_NOGETPROCADDR_ */ #endif /* _SDL_NOGETPROCADDR_ */
#include "SDL_gles2funcs.h" #include "SDL_gles2funcs.h"
@ -184,6 +199,26 @@ static int GLES2_LoadFunctions(GLES2_DriverContext * data)
return 0; 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 static int
GLES2_ActivateRenderer(SDL_Renderer * renderer) GLES2_ActivateRenderer(SDL_Renderer * renderer)
{ {
@ -207,7 +242,7 @@ static void
GLES2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) GLES2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
{ {
GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata; GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) { if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
/* Rebind the context to the window area */ /* Rebind the context to the window area */
SDL_CurrentContext = NULL; SDL_CurrentContext = NULL;
@ -267,6 +302,12 @@ GLES2_DestroyRenderer(SDL_Renderer *renderer)
} }
} }
if (rdata->context) { 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); SDL_GL_DeleteContext(rdata->context);
} }
if (rdata->shader_formats) { if (rdata->shader_formats) {
@ -371,6 +412,13 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
return -1; return -1;
} }
texture->driverdata = tdata; 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; return 0;
} }
@ -433,7 +481,7 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
int y; int y;
GLES2_ActivateRenderer(renderer); GLES2_ActivateRenderer(renderer);
/* Bail out if we're supposed to update an empty rectangle */ /* Bail out if we're supposed to update an empty rectangle */
if (rect->w <= 0 || rect->h <= 0) if (rect->w <= 0 || rect->h <= 0)
return 0; return 0;
@ -543,7 +591,7 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex,
entry->vertex_shader = vertex; entry->vertex_shader = vertex;
entry->fragment_shader = fragment; entry->fragment_shader = fragment;
entry->blend_mode = blendMode; entry->blend_mode = blendMode;
/* Create the program and link it */ /* Create the program and link it */
rdata->glGetError(); rdata->glGetError();
entry->id = rdata->glCreateProgram(); entry->id = rdata->glCreateProgram();
@ -560,7 +608,7 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex,
SDL_free(entry); SDL_free(entry);
return NULL; return NULL;
} }
/* Predetermine locations of uniform variables */ /* Predetermine locations of uniform variables */
entry->uniform_locations[GLES2_UNIFORM_PROJECTION] = entry->uniform_locations[GLES2_UNIFORM_PROJECTION] =
rdata->glGetUniformLocation(entry->id, "u_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"); SDL_SetError("No shader matching the requested characteristics was found");
return NULL; return NULL;
} }
/* Find a matching shader instance that's supported on this hardware */ /* Find a matching shader instance that's supported on this hardware */
for (i = 0; i < shader->instance_count && !instance; ++i) 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_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
GLES2_ActivateRenderer(renderer); GLES2_ActivateRenderer(renderer);
rdata->glClearColor((GLfloat) renderer->r * inv255f, rdata->glClearColor((GLfloat) renderer->r * inv255f,
(GLfloat) renderer->g * inv255f, (GLfloat) renderer->g * inv255f,
(GLfloat) renderer->b * 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 */ /* Activate an appropriate shader and set the projection matrix */
blendMode = texture->blendMode; blendMode = texture->blendMode;
switch (texture->format) if (rdata->renderTarget!=NULL) {
{ /* Check if we need to do color mapping between the source and render target textures */
case SDL_PIXELFORMAT_ABGR8888: if (rdata->renderTarget->format != texture->format) {
sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; switch (texture->format)
break; {
case SDL_PIXELFORMAT_ARGB8888: case SDL_PIXELFORMAT_ABGR8888:
sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB; switch (rdata->renderTarget->format)
break; {
case SDL_PIXELFORMAT_BGR888: case SDL_PIXELFORMAT_ARGB8888:
sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR; case SDL_PIXELFORMAT_RGB888:
break; sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
case SDL_PIXELFORMAT_RGB888: break;
sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB; case SDL_PIXELFORMAT_BGR888:
break; 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) if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0)
return -1; return -1;
@ -1119,15 +1230,29 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
GLES2_SetTexCoords(rdata, SDL_TRUE); GLES2_SetTexCoords(rdata, SDL_TRUE);
/* Emit the textured quad */ /* Emit the textured quad */
vertices[0] = (GLfloat)dstrect->x; if (rdata->renderTarget!=NULL) {
vertices[1] = (GLfloat)dstrect->y; // Flip the texture vertically to compensate for the inversion it'll be subjected to later when it's rendered to the screen
vertices[2] = (GLfloat)(dstrect->x + dstrect->w); vertices[0] = (GLfloat)dstrect->x;
vertices[3] = (GLfloat)dstrect->y; vertices[1] = (GLfloat)renderer->viewport.h-dstrect->y;
vertices[4] = (GLfloat)dstrect->x; vertices[2] = (GLfloat)(dstrect->x + dstrect->w);
vertices[5] = (GLfloat)(dstrect->y + dstrect->h); vertices[3] = (GLfloat)renderer->viewport.h-dstrect->y;
vertices[6] = (GLfloat)(dstrect->x + dstrect->w); vertices[4] = (GLfloat)dstrect->x;
vertices[7] = (GLfloat)(dstrect->y + dstrect->h); 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); rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
texCoords[0] = srcrect->x / (GLfloat)texture->w; texCoords[0] = srcrect->x / (GLfloat)texture->w;
texCoords[1] = srcrect->y / (GLfloat)texture->h; texCoords[1] = srcrect->y / (GLfloat)texture->h;
texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
@ -1231,6 +1356,60 @@ GLES2_ResetState(SDL_Renderer *renderer)
rdata->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); 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 * static SDL_Renderer *
GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
{ {
@ -1241,10 +1420,10 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
GLboolean hasCompiler; GLboolean hasCompiler;
#endif #endif
Uint32 windowFlags; Uint32 windowFlags;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
windowFlags = SDL_GetWindowFlags(window); windowFlags = SDL_GetWindowFlags(window);
if (!(windowFlags & SDL_WINDOW_OPENGL)) { if (!(windowFlags & SDL_WINDOW_OPENGL)) {
if (SDL_RecreateWindow(window, windowFlags | SDL_WINDOW_OPENGL) < 0) { 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; rdata->shader_formats[nFormats - 1] = (GLenum)-1;
#endif /* ZUNE_HD */ #endif /* ZUNE_HD */
rdata->framebuffers = NULL;
rdata->renderTarget = NULL;
/* Populate the function pointers for the module */ /* Populate the function pointers for the module */
renderer->WindowEvent = &GLES2_WindowEvent; renderer->WindowEvent = &GLES2_WindowEvent;
renderer->CreateTexture = &GLES2_CreateTexture; renderer->CreateTexture = &GLES2_CreateTexture;
@ -1347,6 +1529,7 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
renderer->RenderPresent = &GLES2_RenderPresent; renderer->RenderPresent = &GLES2_RenderPresent;
renderer->DestroyTexture = &GLES2_DestroyTexture; renderer->DestroyTexture = &GLES2_DestroyTexture;
renderer->DestroyRenderer = &GLES2_DestroyRenderer; renderer->DestroyRenderer = &GLES2_DestroyRenderer;
renderer->SetTargetTexture = &GLES2_SetTargetTexture;
GLES2_ResetState(renderer); GLES2_ResetState(renderer);

View file

@ -44,6 +44,7 @@ TARGETS = \
testoverlay2$(EXE) \ testoverlay2$(EXE) \
testplatform$(EXE) \ testplatform$(EXE) \
testpower$(EXE) \ testpower$(EXE) \
testrendertarget$(EXE) \
testresample$(EXE) \ testresample$(EXE) \
testscale$(EXE) \ testscale$(EXE) \
testsem$(EXE) \ testsem$(EXE) \
@ -181,6 +182,9 @@ testplatform$(EXE): $(srcdir)/testplatform.c
testpower$(EXE): $(srcdir)/testpower.c testpower$(EXE): $(srcdir)/testpower.c
$(CC) -o $@ $? $(CFLAGS) $(LIBS) $(CC) -o $@ $? $(CFLAGS) $(LIBS)
testrendertarget$(EXE): $(srcdir)/testrendertarget.c $(srcdir)/common.c
$(CC) -o $@ $(srcdir)/testrendertarget.c $(srcdir)/common.c $(CFLAGS) $(LIBS)
testscale$(EXE): $(srcdir)/testscale.c $(srcdir)/common.c testscale$(EXE): $(srcdir)/testscale.c $(srcdir)/common.c
$(CC) -o $@ $(srcdir)/testscale.c $(srcdir)/common.c $(CFLAGS) $(LIBS) $(CC) -o $@ $(srcdir)/testscale.c $(srcdir)/common.c $(CFLAGS) $(LIBS)

204
test/testrendertarget.c Normal file
View file

@ -0,0 +1,204 @@
/*
Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
/* Simple program: Move N sprites around on the screen as fast as possible */
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "SDL.h"
#include "common.h"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
static CommonState *state;
typedef struct {
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *background;
SDL_Texture *sprite;
SDL_Rect sprite_rect;
int scale_direction;
} DrawState;
/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
static void
quit(int rc)
{
CommonQuit(state);
exit(rc);
}
SDL_Texture *
LoadTexture(SDL_Renderer *renderer, char *file, SDL_bool transparent)
{
SDL_Surface *temp;
SDL_Texture *texture;
/* Load the sprite image */
temp = SDL_LoadBMP(file);
if (temp == NULL) {
fprintf(stderr, "Couldn't load %s: %s", file, SDL_GetError());
return NULL;
}
/* Set transparent pixel as the pixel at (0,0) */
if (transparent) {
if (temp->format->palette) {
SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
} else {
switch (temp->format->BitsPerPixel) {
case 15:
SDL_SetColorKey(temp, SDL_TRUE,
(*(Uint16 *) temp->pixels) & 0x00007FFF);
break;
case 16:
SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
break;
case 24:
SDL_SetColorKey(temp, SDL_TRUE,
(*(Uint32 *) temp->pixels) & 0x00FFFFFF);
break;
case 32:
SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
break;
}
}
}
/* Create textures from the image */
texture = SDL_CreateTextureFromSurface(renderer, temp);
if (!texture) {
fprintf(stderr, "Couldn't create texture: %s\n", SDL_GetError());
SDL_FreeSurface(temp);
return NULL;
}
SDL_FreeSurface(temp);
/* We're ready to roll. :) */
return texture;
}
void
Draw(DrawState *s)
{
SDL_Rect viewport;
SDL_Texture *target;
SDL_RenderGetViewport(s->renderer, &viewport);
target = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, viewport.w, viewport.h);
SDL_SetTargetTexture(s->renderer, target);
/* Draw the background */
SDL_RenderCopy(s->renderer, s->background, NULL, NULL);
/* Scale and draw the sprite */
s->sprite_rect.w += s->scale_direction;
s->sprite_rect.h += s->scale_direction;
if (s->scale_direction > 0) {
if (s->sprite_rect.w >= viewport.w || s->sprite_rect.h >= viewport.h) {
s->scale_direction = -1;
}
} else {
if (s->sprite_rect.w <= 1 || s->sprite_rect.h <= 1) {
s->scale_direction = 1;
}
}
s->sprite_rect.x = (viewport.w - s->sprite_rect.w) / 2;
s->sprite_rect.y = (viewport.h - s->sprite_rect.h) / 2;
SDL_RenderCopy(s->renderer, s->sprite, NULL, &s->sprite_rect);
SDL_SetTargetTexture(s->renderer, NULL);
SDL_RenderCopy(s->renderer, target, NULL, NULL);
SDL_DestroyTexture(target);
/* Update the screen! */
SDL_RenderPresent(s->renderer);
}
int
main(int argc, char *argv[])
{
DrawState *drawstates;
int i, done;
SDL_Event event;
int frames;
Uint32 then, now;
/* Initialize test framework */
state = CommonCreateState(argv, SDL_INIT_VIDEO);
if (!state) {
return 1;
}
for (i = 1; i < argc;) {
int consumed;
consumed = CommonArg(state, i);
if (consumed == 0) {
fprintf(stderr, "Usage: %s %s\n", argv[0], CommonUsage(state));
return 1;
}
i += consumed;
}
if (!CommonInit(state)) {
quit(2);
}
drawstates = SDL_stack_alloc(DrawState, state->num_windows);
for (i = 0; i < state->num_windows; ++i) {
DrawState *drawstate = &drawstates[i];
drawstate->window = state->windows[i];
drawstate->renderer = state->renderers[i];
drawstate->sprite = LoadTexture(drawstate->renderer, "icon.bmp", SDL_TRUE);
drawstate->background = LoadTexture(drawstate->renderer, "sample.bmp", SDL_FALSE);
if (!drawstate->sprite || !drawstate->background) {
quit(2);
}
SDL_QueryTexture(drawstate->sprite, NULL, NULL,
&drawstate->sprite_rect.w, &drawstate->sprite_rect.h);
drawstate->scale_direction = 1;
}
/* Main render loop */
frames = 0;
then = SDL_GetTicks();
done = 0;
while (!done) {
/* Check for events */
++frames;
while (SDL_PollEvent(&event)) {
CommonEvent(state, &event, &done);
}
for (i = 0; i < state->num_windows; ++i) {
Draw(&drawstates[i]);
}
}
/* Print out some timing information */
now = SDL_GetTicks();
if (now > then) {
double fps = ((double) frames * 1000) / (now - then);
printf("%2.2f frames per second\n", fps);
}
SDL_stack_free(drawstates);
quit(0);
return 0;
}
/* vi: set ts=4 sw=4 expandtab: */