diff options
author | Ryan C. Gordon <icculus@icculus.org> | 2018-10-31 15:03:41 -0400 |
---|---|---|
committer | Ryan C. Gordon <icculus@icculus.org> | 2018-10-31 15:03:41 -0400 |
commit | d8c11a112cbb30e1bd433ca8423faf83d3889de2 (patch) | |
tree | 3ea5e35a632398e54872f860b3a74a063be0b060 | |
parent | b3ffbe64440ac62068eee70890cf65239857bea1 (diff) | |
parent | 48f849039637b543aba06a9baf61b2b549c85df4 (diff) |
Merge SDL-ryan-batching-renderer branch to default.
38 files changed, 6818 insertions, 5419 deletions
diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 4ee72e97d6..b000ee90c3 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1044,6 +1044,31 @@ extern "C" { #define SDL_HINT_AUDIO_CATEGORY "SDL_AUDIO_CATEGORY" /** + * \brief A variable controlling whether the 2D render API is compatible or efficient. + * + * This variable can be set to the following values: + * + * "0" - Don't use batching to make rendering more efficient. + * "1" - Use batching, but might cause problems if app makes its own direct OpenGL calls. + * + * Up to SDL 2.0.9, the render API would draw immediately when requested. Now + * it batches up draw requests and sends them all to the GPU only when forced + * to (during SDL_RenderPresent, when changing render targets, by updating a + * texture that the batch needs, etc). This is significantly more efficient, + * but it can cause problems for apps that expect to render on top of the + * render API's output. As such, SDL will disable batching if a specific + * render backend is requested (since this might indicate that the app is + * planning to use the underlying graphics API directly). This hint can + * be used to explicitly request batching in this instance. It is a contract + * that you will either never use the underlying graphics API directly, or + * if you do, you will call SDL_RenderFlush() before you do so any current + * batch goes to the GPU before your work begins. Not following this contract + * will result in undefined behavior. + */ +#define SDL_HINT_RENDER_BATCHING "SDL_RENDER_BATCHING" + + +/** * \brief An enumeration of hint priorities */ typedef enum diff --git a/include/SDL_rect.h b/include/SDL_rect.h index 543bb61869..986764cd69 100644 --- a/include/SDL_rect.h +++ b/include/SDL_rect.h @@ -40,7 +40,7 @@ extern "C" { #endif /** - * \brief The structure that defines a point + * \brief The structure that defines a point (integer) * * \sa SDL_EnclosePoints * \sa SDL_PointInRect @@ -52,7 +52,20 @@ typedef struct SDL_Point } SDL_Point; /** - * \brief A rectangle, with the origin at the upper left. + * \brief The structure that defines a point (floating point) + * + * \sa SDL_EnclosePoints + * \sa SDL_PointInRect + */ +typedef struct SDL_FPoint +{ + float x; + float y; +} SDL_FPoint; + + +/** + * \brief A rectangle, with the origin at the upper left (integer). * * \sa SDL_RectEmpty * \sa SDL_RectEquals @@ -67,6 +80,19 @@ typedef struct SDL_Rect int w, h; } SDL_Rect; + +/** + * \brief A rectangle, with the origin at the upper left (floating point). + */ +typedef struct SDL_FRect +{ + float x; + float y; + float w; + float h; +} SDL_FRect; + + /** * \brief Returns true if point resides inside a rectangle. */ diff --git a/include/SDL_render.h b/include/SDL_render.h index d336192974..738b7392a6 100644 --- a/include/SDL_render.h +++ b/include/SDL_render.h @@ -835,6 +835,148 @@ extern DECLSPEC int SDLCALL SDL_RenderCopyEx(SDL_Renderer * renderer, const SDL_Point *center, const SDL_RendererFlip flip); + +/** + * \brief Draw a point on the current rendering target. + * + * \param renderer The renderer which should draw a point. + * \param x The x coordinate of the point. + * \param y The y coordinate of the point. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawPointF(SDL_Renderer * renderer, + float x, float y); + +/** + * \brief Draw multiple points on the current rendering target. + * + * \param renderer The renderer which should draw multiple points. + * \param points The points to draw + * \param count The number of points to draw + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawPointsF(SDL_Renderer * renderer, + const SDL_FPoint * points, + int count); + +/** + * \brief Draw a line on the current rendering target. + * + * \param renderer The renderer which should draw a line. + * \param x1 The x coordinate of the start point. + * \param y1 The y coordinate of the start point. + * \param x2 The x coordinate of the end point. + * \param y2 The y coordinate of the end point. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawLineF(SDL_Renderer * renderer, + float x1, float y1, float x2, float y2); + +/** + * \brief Draw a series of connected lines on the current rendering target. + * + * \param renderer The renderer which should draw multiple lines. + * \param points The points along the lines + * \param count The number of points, drawing count-1 lines + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawLinesF(SDL_Renderer * renderer, + const SDL_FPoint * points, + int count); + +/** + * \brief Draw a rectangle on the current rendering target. + * + * \param renderer The renderer which should draw a rectangle. + * \param rect A pointer to the destination rectangle, or NULL to outline the entire rendering target. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawRectF(SDL_Renderer * renderer, + const SDL_FRect * rect); + +/** + * \brief Draw some number of rectangles on the current rendering target. + * + * \param renderer The renderer which should draw multiple rectangles. + * \param rects A pointer to an array of destination rectangles. + * \param count The number of rectangles. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderDrawRectsF(SDL_Renderer * renderer, + const SDL_FRect * rects, + int count); + +/** + * \brief Fill a rectangle on the current rendering target with the drawing color. + * + * \param renderer The renderer which should fill a rectangle. + * \param rect A pointer to the destination rectangle, or NULL for the entire + * rendering target. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderFillRectF(SDL_Renderer * renderer, + const SDL_FRect * rect); + +/** + * \brief Fill some number of rectangles on the current rendering target with the drawing color. + * + * \param renderer The renderer which should fill multiple rectangles. + * \param rects A pointer to an array of destination rectangles. + * \param count The number of rectangles. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderFillRectsF(SDL_Renderer * renderer, + const SDL_FRect * rects, + int count); + +/** + * \brief Copy a portion of the texture to the current rendering target. + * + * \param renderer The renderer which should copy parts of a texture. + * \param texture The source texture. + * \param srcrect A pointer to the source rectangle, or NULL for the entire + * texture. + * \param dstrect A pointer to the destination rectangle, or NULL for the + * entire rendering target. + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderCopyF(SDL_Renderer * renderer, + SDL_Texture * texture, + const SDL_Rect * srcrect, + const SDL_FRect * dstrect); + +/** + * \brief Copy a portion of the source texture to the current rendering target, rotating it by angle around the given center + * + * \param renderer The renderer which should copy parts of a texture. + * \param texture The source texture. + * \param srcrect A pointer to the source rectangle, or NULL for the entire + * texture. + * \param dstrect A pointer to the destination rectangle, or NULL for the + * entire rendering target. + * \param angle An angle in degrees that indicates the rotation that will be applied to dstrect, rotating it in a clockwise direction + * \param center A pointer to a point indicating the point around which dstrect will be rotated (if NULL, rotation will be done around dstrect.w/2, dstrect.h/2). + * \param flip An SDL_RendererFlip value stating which flipping actions should be performed on the texture + * + * \return 0 on success, or -1 on error + */ +extern DECLSPEC int SDLCALL SDL_RenderCopyExF(SDL_Renderer * renderer, + SDL_Texture * texture, + const SDL_Rect * srcrect, + const SDL_FRect * dstrect, + const double angle, + const SDL_FPoint *center, + const SDL_RendererFlip flip); + /** * \brief Read pixels from the current rendering target. * @@ -876,6 +1018,31 @@ extern DECLSPEC void SDLCALL SDL_DestroyTexture(SDL_Texture * texture); */ extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer * renderer); +/** + * \brief Force the rendering context to flush any pending commands to the + * underlying rendering API. + * + * You do not need to (and in fact, shouldn't) call this function unless + * you are planning to call into OpenGL/Direct3D/Metal/whatever directly + * in addition to using an SDL_Renderer. + * + * This is for a very-specific case: if you are using SDL's render API, + * you asked for a specific renderer backend (OpenGL, Direct3D, etc), + * you set SDL_HINT_RENDER_BATCHING to "1", and you plan to make + * OpenGL/D3D/whatever calls in addition to SDL render API calls. If all of + * this applies, you should call SDL_RenderFlush() between calls to SDL's + * render API and the low-level API you're using in cooperation. + * + * In all other cases, you can ignore this function. This is only here to + * get maximum performance out of a specific situation. In all other cases, + * SDL will do the right thing, perhaps at a performance loss. + * + * This function is first available in SDL 2.0.10, and is not needed in + * 2.0.9 and earlier, as earlier versions did not queue rendering commands + * at all, instead flushing them to the OS immediately. + */ +extern DECLSPEC int SDLCALL SDL_RenderFlush(SDL_Renderer * renderer); + /** * \brief Bind the texture to the current OpenGL/ES/ES2 context for use with diff --git a/src/SDL_assert.c b/src/SDL_assert.c index 1ca083ad4c..5a9556d4ec 100644 --- a/src/SDL_assert.c +++ b/src/SDL_assert.c @@ -178,6 +178,7 @@ SDL_PromptAssertion(const SDL_assert_data *data, void *userdata) (void) userdata; /* unused in default handler. */ + /* !!! FIXME: why is this using SDL_stack_alloc and not just "char message[SDL_MAX_LOG_MESSAGE];" ? */ message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); if (!message) { /* Uh oh, we're in real trouble now... */ diff --git a/src/SDL_internal.h b/src/SDL_internal.h index e0ba2a82c3..f8215bf34a 100644 --- a/src/SDL_internal.h +++ b/src/SDL_internal.h @@ -35,6 +35,10 @@ #define SDL_VARIABLE_LENGTH_ARRAY #endif +#define SDL_MAX_SMALL_ALLOC_STACKSIZE 128 +#define SDL_small_alloc(type, count, pisstack) ( (*(pisstack) = ((sizeof(type)*(count)) < SDL_MAX_SMALL_ALLOC_STACKSIZE)), (*(pisstack) ? SDL_stack_alloc(type, count) : (type*)SDL_malloc(sizeof(type)*(count))) ) +#define SDL_small_free(ptr, isstack) if ((isstack)) { SDL_stack_free(ptr); } else { SDL_free(ptr); } + #include "dynapi/SDL_dynapi.h" #if SDL_DYNAMIC_API diff --git a/src/SDL_log.c b/src/SDL_log.c index b1bf27d7a1..b89f7ee3a8 100644 --- a/src/SDL_log.c +++ b/src/SDL_log.c @@ -282,6 +282,7 @@ SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list return; } + /* !!! FIXME: why not just "char message[SDL_MAX_LOG_MESSAGE];" ? */ message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); if (!message) { return; @@ -321,6 +322,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, char *output; size_t length; LPTSTR tstr; + SDL_bool isstack; #if !defined(HAVE_STDIO_H) && !defined(__WINRT__) BOOL attachResult; @@ -364,7 +366,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */ length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1; - output = SDL_stack_alloc(char, length); + output = SDL_small_alloc(char, length, &isstack); SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message); tstr = WIN_UTF8ToString(output); @@ -389,7 +391,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */ SDL_free(tstr); - SDL_stack_free(output); + SDL_small_free(output, isstack); } #elif defined(__ANDROID__) { @@ -404,7 +406,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, extern void SDL_NSLog(const char *text); { char *text; - + /* !!! FIXME: why not just "char text[SDL_MAX_LOG_MESSAGE];" ? */ text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); if (text) { SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message); diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index a56575e09d..2c6115fd9e 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -481,10 +481,11 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, int argc; int len; char **argv; + SDL_bool isstack; /* Prepare the arguments. */ len = (*env)->GetArrayLength(env, array); - argv = SDL_stack_alloc(char*, 1 + len + 1); + argv = SDL_small_alloc(char*, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */ argc = 0; /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works. https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start @@ -517,7 +518,7 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, for (i = 0; i < argc; ++i) { SDL_free(argv[i]); } - SDL_stack_free(argv); + SDL_small_free(argv, isstack); } else { __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file); diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 745421382c..73767fbaef 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -701,3 +701,14 @@ #define SDL_JoystickGetDevicePlayerIndex SDL_JoystickGetDevicePlayerIndex_REAL #define SDL_JoystickGetPlayerIndex SDL_JoystickGetPlayerIndex_REAL #define SDL_GameControllerGetPlayerIndex SDL_GameControllerGetPlayerIndex_REAL +#define SDL_RenderFlush SDL_RenderFlush_REAL +#define SDL_RenderDrawPointF SDL_RenderDrawPointF_REAL +#define SDL_RenderDrawPointsF SDL_RenderDrawPointsF_REAL +#define SDL_RenderDrawLineF SDL_RenderDrawLineF_REAL +#define SDL_RenderDrawLinesF SDL_RenderDrawLinesF_REAL +#define SDL_RenderDrawRectF SDL_RenderDrawRectF_REAL +#define SDL_RenderDrawRectsF SDL_RenderDrawRectsF_REAL +#define SDL_RenderFillRectF SDL_RenderFillRectF_REAL +#define SDL_RenderFillRectsF SDL_RenderFillRectsF_REAL +#define SDL_RenderCopyF SDL_RenderCopyF_REAL +#define SDL_RenderCopyExF SDL_RenderCopyExF_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 0a1f3aefa5..076205ef09 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -755,3 +755,14 @@ SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSize,(SDL_ThreadFunction a, SDL_DYNAPI_PROC(int,SDL_JoystickGetDevicePlayerIndex,(int a),(a),return) SDL_DYNAPI_PROC(int,SDL_JoystickGetPlayerIndex,(SDL_Joystick *a),(a),return) SDL_DYNAPI_PROC(int,SDL_GameControllerGetPlayerIndex,(SDL_GameController *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_RenderFlush,(SDL_Renderer *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_RenderDrawPointF,(SDL_Renderer *a, float b, float c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_RenderDrawPointsF,(SDL_Renderer *a, const SDL_FPoint *b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_RenderDrawLineF,(SDL_Renderer *a, float b, float c, float d, float e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_RenderDrawLinesF,(SDL_Renderer *a, const SDL_FPoint *b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_RenderDrawRectF,(SDL_Renderer *a, const SDL_FRect *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_RenderDrawRectsF,(SDL_Renderer *a, const SDL_FRect *b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_RenderFillRectF,(SDL_Renderer *a, const SDL_FRect *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_RenderFillRectsF,(SDL_Renderer *a, const SDL_FRect *b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_RenderCopyF,(SDL_Renderer *a, SDL_Texture *b, const SDL_Rect *c, const SDL_FRect *d),(a,b,c,d),return) +SDL_DYNAPI_PROC(int,SDL_RenderCopyExF,(SDL_Renderer *a, SDL_Texture *b, const SDL_Rect *c, const SDL_FRect *d, const double e, const SDL_FPoint *f, const SDL_RendererFlip g),(a,b,c,d,e,f,g),return) diff --git a/src/file/SDL_rwops.c b/src/file/SDL_rwops.c index cf5d3aa0b9..5b0969d56f 100644 --- a/src/file/SDL_rwops.c +++ b/src/file/SDL_rwops.c @@ -528,6 +528,7 @@ SDL_RWFromFile(const char *file, const char *mode) char *path; FILE *fp; + /* !!! FIXME: why not just "char path[PATH_MAX];" ? */ path = SDL_stack_alloc(char, PATH_MAX); if (path) { SDL_snprintf(path, PATH_MAX, "%s/%s", diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index eb1ef06546..65645f187d 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -202,13 +202,14 @@ static void UpdateEventsForDeviceRemoval() { int i, num_events; SDL_Event *events; + SDL_bool isstack; num_events = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEADDED); if (num_events <= 0) { return; } - events = SDL_stack_alloc(SDL_Event, num_events); + events = SDL_small_alloc(SDL_Event, num_events, &isstack); if (!events) { return; } @@ -219,7 +220,7 @@ static void UpdateEventsForDeviceRemoval() } SDL_PeepEvents(events, num_events, SDL_ADDEVENT, 0, 0); - SDL_stack_free(events); + SDL_small_free(events, isstack); } static SDL_bool HasSameOutput(SDL_ExtendedGameControllerBind *a, SDL_ExtendedGameControllerBind *b) diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 02903f5f96..8c784aec7f 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -777,13 +777,14 @@ static void UpdateEventsForDeviceRemoval() { int i, num_events; SDL_Event *events; + SDL_bool isstack; num_events = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEADDED); if (num_events <= 0) { return; } - events = SDL_stack_alloc(SDL_Event, num_events); + events = SDL_small_alloc(SDL_Event, num_events, &isstack); if (!events) { return; } @@ -794,7 +795,7 @@ static void UpdateEventsForDeviceRemoval() } SDL_PeepEvents(events, num_events, SDL_ADDEVENT, 0, 0); - SDL_stack_free(events); + SDL_small_free(events, isstack); } void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance) diff --git a/src/loadso/dlopen/SDL_sysloadso.c b/src/loadso/dlopen/SDL_sysloadso.c index 18b4b84369..ed89a211d7 100644 --- a/src/loadso/dlopen/SDL_sysloadso.c +++ b/src/loadso/dlopen/SDL_sysloadso.c @@ -61,12 +61,13 @@ SDL_LoadFunction(void *handle, const char *name) void *symbol = dlsym(handle, name); if (symbol == NULL) { /* append an underscore for platforms that need that. */ + SDL_bool isstack; size_t len = 1 + SDL_strlen(name) + 1; - char *_name = SDL_stack_alloc(char, len); + char *_name = SDL_small_alloc(char, len, &isstack); _name[0] = '_'; SDL_strlcpy(&_name[1], name, len); symbol = dlsym(handle, _name); - SDL_stack_free(_name); + SDL_small_free(_name, isstack); if (symbol == NULL) { SDL_SetError("Failed loading %s: %s", name, (const char *) dlerror()); diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 4985b16ba2..6af7d10c17 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -105,6 +105,519 @@ static const SDL_RenderDriver *render_drivers[] = { static char renderer_magic; static char texture_magic; +static SDL_INLINE void +DebugLogRenderCommands(const SDL_RenderCommand *cmd) +{ +#if 0 + unsigned int i = 1; + SDL_Log("Render commands to flush:"); + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_NO_OP: + SDL_Log(" %u. no-op", i++); + break; + + case SDL_RENDERCMD_SETVIEWPORT: + SDL_Log(" %u. set viewport (first=%u, rect={(%d, %d), %dx%d})", i++, + (unsigned int) cmd->data.viewport.first, + cmd->data.viewport.rect.x, cmd->data.viewport.rect.y, + cmd->data.viewport.rect.w, cmd->data.viewport.rect.h); + break; + + case SDL_RENDERCMD_SETCLIPRECT: + SDL_Log(" %u. set cliprect (enabled=%s, rect={(%d, %d), %dx%d})", i++, + cmd->data.cliprect.enabled ? "true" : "false", + cmd->data.cliprect.rect.x, cmd->data.cliprect.rect.y, + cmd->data.cliprect.rect.w, cmd->data.cliprect.rect.h); + break; + + case SDL_RENDERCMD_SETDRAWCOLOR: + SDL_Log(" %u. set draw color (first=%u, r=%d, g=%d, b=%d, a=%d)", i++, + (unsigned int) cmd->data.color.first, + (int) cmd->data.color.r, (int) cmd->data.color.g, + (int) cmd->data.color.b, (int) cmd->data.color.a); + break; + + case SDL_RENDERCMD_CLEAR: + SDL_Log(" %u. clear (first=%u, r=%d, g=%d, b=%d, a=%d)", i++, + (unsigned int) cmd->data.color.first, + (int) cmd->data.color.r, (int) cmd->data.color.g, + (int) cmd->data.color.b, (int) cmd->data.color.a); + break; + + case SDL_RENDERCMD_DRAW_POINTS: + SDL_Log(" %u. draw points (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++, + (unsigned int) cmd->data.draw.first, + (unsigned int) cmd->data.draw.count, + (int) cmd->data.draw.r, (int) cmd->data.draw.g, + (int) cmd->data.draw.b, (int) cmd->data.draw.a, + (int) cmd->data.draw.blend); + break; + + case SDL_RENDERCMD_DRAW_LINES: + SDL_Log(" %u. draw lines (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++, + (unsigned int) cmd->data.draw.first, + (unsigned int) cmd->data.draw.count, + (int) cmd->data.draw.r, (int) cmd->data.draw.g, + (int) cmd->data.draw.b, (int) cmd->data.draw.a, + (int) cmd->data.draw.blend); + break; + + case SDL_RENDERCMD_FILL_RECTS: + SDL_Log(" %u. fill rects (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d)", i++, + (unsigned int) cmd->data.draw.first, + (unsigned int) cmd->data.draw.count, + (int) cmd->data.draw.r, (int) cmd->data.draw.g, + (int) cmd->data.draw.b, (int) cmd->data.draw.a, + (int) cmd->data.draw.blend); + break; + + case SDL_RENDERCMD_COPY: + SDL_Log(" %u. copy (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++, + (unsigned int) cmd->data.draw.first, + (unsigned int) cmd->data.draw.count, + (int) cmd->data.draw.r, (int) cmd->data.draw.g, + (int) cmd->data.draw.b, (int) cmd->data.draw.a, + (int) cmd->data.draw.blend, cmd->data.draw.texture); + break; + + + case SDL_RENDERCMD_COPY_EX: + SDL_Log(" %u. copyex (first=%u, count=%u, r=%d, g=%d, b=%d, a=%d, blend=%d, tex=%p)", i++, + (unsigned int) cmd->data.draw.first, + (unsigned int) cmd->data.draw.count, + (int) cmd->data.draw.r, (int) cmd->data.draw.g, + (int) cmd->data.draw.b, (int) cmd->data.draw.a, + (int) cmd->data.draw.blend, cmd->data.draw.texture); + break; + } + cmd = cmd->next; + } +#endif +} + +static int +FlushRenderCommands(SDL_Renderer *renderer) +{ + SDL_AllocVertGap *prevgap = &renderer->vertex_data_gaps; + SDL_AllocVertGap *gap = prevgap; + int retval; + + SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL)); + + if (renderer->render_commands == NULL) { /* nothing to do! */ + SDL_assert(renderer->vertex_data_used == 0); + return 0; + } + + DebugLogRenderCommands(renderer->render_commands); + + retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used); + + while (gap) { + prevgap = gap; + gap = gap->next; + } + prevgap->next = renderer->vertex_data_gaps_pool; + renderer->vertex_data_gaps_pool = renderer->vertex_data_gaps.next; + renderer->vertex_data_gaps.next = NULL; + + /* Move the whole render command queue to the unused pool so we can reuse them next time. */ + if (renderer->render_commands_tail != NULL) { + renderer->render_commands_tail->next = renderer->render_commands_pool; + renderer->render_commands_pool = renderer->render_commands; + renderer->render_commands_tail = NULL; + renderer->render_commands = NULL; + } + renderer->vertex_data_used = 0; + renderer->render_command_generation++; + renderer->color_queued = SDL_FALSE; + renderer->viewport_queued = SDL_FALSE; + renderer->cliprect_queued = SDL_FALSE; + return retval; +} + +static int +FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture) +{ + SDL_Renderer *renderer = texture->renderer; + if (texture->last_command_generation == renderer->render_command_generation) { + /* the current command queue depends on this texture, flush the queue now before it changes */ + return FlushRenderCommands(renderer); + } + return 0; +} + +static SDL_INLINE int +FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer) +{ + return renderer->batching ? 0 : FlushRenderCommands(renderer); +} + +int +SDL_RenderFlush(SDL_Renderer * renderer) +{ + return FlushRenderCommands(renderer); +} + +static SDL_AllocVertGap * +AllocateVertexGap(SDL_Renderer *renderer) +{ + SDL_AllocVertGap *retval = renderer->vertex_data_gaps_pool; + if (retval) { + renderer->vertex_data_gaps_pool = retval->next; + retval->next = NULL; + } else { + retval = (SDL_AllocVertGap *) SDL_malloc(sizeof (SDL_AllocVertGap)); + if (!retval) { + SDL_OutOfMemory(); + } + } + return retval; +} + + +void * +SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset) +{ + const size_t needed = renderer->vertex_data_used + numbytes + alignment; + size_t aligner, aligned; + void *retval; + + SDL_AllocVertGap *prevgap = &renderer->vertex_data_gaps; + SDL_AllocVertGap *gap = prevgap->next; + while (gap) { + const size_t gapoffset = gap->offset; + aligner = (alignment && ((gap->offset % alignment) != 0)) ? (alignment - (gap->offset % alignment)) : 0; + aligned = gapoffset + aligner; + + /* Can we use this gap? */ + if ((aligner < gap->len) && ((gap->len - aligner) >= numbytes)) { + /* we either finished this gap off, trimmed the left, trimmed the right, or split it into two gaps. */ + if (gap->len == numbytes) { /* finished it off, remove it */ + SDL_assert(aligned == gapoffset); + prevgap->next = gap->next; + gap->next = renderer->vertex_data_gaps_pool; + renderer->vertex_data_gaps_pool = gap; + } else if (aligned == gapoffset) { /* trimmed the left */ + gap->offset += numbytes; + gap->len -= numbytes; + } else if (((aligned - gapoffset) + numbytes) == gap->len) { /* trimmed the right */ + gap->len -= numbytes; + } else { /* split into two gaps */ + SDL_AllocVertGap *newgap = AllocateVertexGap(renderer); + if (!newgap) { + return NULL; + } + newgap->offset = aligned + numbytes; + newgap->len = gap->len - (aligner + numbytes); + newgap->next = gap->next; + // gap->offset doesn't change. + gap->len = aligner; + gap->next = newgap; + } + + if (offset) { + *offset = aligned; + } + return ((Uint8 *) renderer->vertex_data) + aligned; + } + + /* Try the next gap */ + prevgap = gap; + gap = gap->next; + } + + /* no gaps with enough space; get a new piece of the vertex buffer */ + while (needed > renderer->vertex_data_allocation) { + const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 1024; + const size_t newsize = current_allocation * 2; + void *ptr = SDL_realloc(renderer->vertex_data, newsize); + if (ptr == NULL) { + SDL_OutOfMemory(); + return NULL; + } + renderer->vertex_data = ptr; + renderer->vertex_data_allocation = newsize; + } + + aligner = (alignment && ((renderer->vertex_data_used % alignment) != 0)) ? (alignment - (renderer->vertex_data_used % alignment)) : 0; + aligned = renderer->vertex_data_used + aligner; + + retval = ((Uint8 *) renderer->vertex_data) + aligned; + if (offset) { + *offset = aligned; + } + + if (aligner) { /* made a new gap... */ + SDL_AllocVertGap *newgap = AllocateVertexGap(renderer); + if (newgap) { /* just let it slide as lost space if malloc fails. */ + newgap->offset = renderer->vertex_data_used; + newgap->len = aligner; + newgap->next = NULL; + prevgap->next = newgap; + } + } + + renderer->vertex_data_used += aligner + numbytes; + + return retval; +} + +static SDL_RenderCommand * +AllocateRenderCommand(SDL_Renderer *renderer) +{ + SDL_RenderCommand *retval = NULL; + + /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */ + retval = renderer->render_commands_pool; + if (retval != NULL) { + renderer->render_commands_pool = retval->next; + retval->next = NULL; + } else { + retval = SDL_calloc(1, sizeof (*retval)); + if (!retval) { + SDL_OutOfMemory(); + return NULL; + } + } + + SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL)); + if (renderer->render_commands_tail != NULL) { + renderer->render_commands_tail->next = retval; + } else { + renderer->render_commands = retval; + } + renderer->render_commands_tail = retval; + + return retval; +} + +static int +QueueCmdSetViewport(SDL_Renderer *renderer) +{ + int retval = 0; + if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) { + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + retval = -1; + if (cmd != NULL) { + cmd->command = SDL_RENDERCMD_SETVIEWPORT; + cmd->data.viewport.first = 0; /* render backend will fill this in. */ + SDL_memcpy(&cmd->data.viewport.rect, &renderer->viewport, sizeof (renderer->viewport)); + retval = renderer->QueueSetViewport(renderer, cmd); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } else { + SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect)); + renderer->viewport_queued = SDL_TRUE; + } + } + } + return retval; +} + +static int +QueueCmdSetClipRect(SDL_Renderer *renderer) +{ + int retval = 0; + if ((!renderer->cliprect_queued) || + (renderer->clipping_enabled != renderer->last_queued_cliprect_enabled) || + (SDL_memcmp(&renderer->clip_rect, &renderer->last_queued_cliprect, sizeof (SDL_Rect)) != 0)) { + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + if (cmd == NULL) { + retval = -1; + } else { + cmd->command = SDL_RENDERCMD_SETCLIPRECT; + cmd->data.cliprect.enabled = renderer->clipping_enabled; + SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect)); + SDL_memcpy(&renderer->last_queued_cliprect, &renderer->clip_rect, sizeof (SDL_Rect)); + renderer->last_queued_cliprect_enabled = renderer->clipping_enabled; + renderer->cliprect_queued = SDL_TRUE; + } + } + return retval; +} + +static int +QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a) +{ + const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + int retval = 0; + + if (!renderer->color_queued || (color != renderer->last_queued_color)) { + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + retval = -1; + + if (cmd != NULL) { + cmd->command = SDL_RENDERCMD_SETDRAWCOLOR; + cmd->data.color.first = 0; /* render backend will fill this in. */ + cmd->data.color.r = r; + cmd->data.color.g = g; + cmd->data.color.b = b; + cmd->data.color.a = a; + retval = renderer->QueueSetDrawColor(renderer, cmd); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } else { + renderer->last_queued_color = color; + renderer->color_queued = SDL_TRUE; + } + } + } + return retval; +} + +static int +QueueCmdClear(SDL_Renderer *renderer) +{ + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } + + cmd->command = SDL_RENDERCMD_CLEAR; + cmd->data.color.first = 0; + cmd->data.color.r = renderer->r; + cmd->data.color.g = renderer->g; + cmd->data.color.b = renderer->b; + cmd->data.color.a = renderer->a; + return 0; +} + +static int +PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uint8 b, const Uint8 a) +{ + int retval = 0; + if (retval == 0) { + retval = QueueCmdSetDrawColor(renderer, r, g, b, a); + } + if (retval == 0) { + retval = QueueCmdSetViewport(renderer); + } + if (retval == 0) { + retval = QueueCmdSetClipRect(renderer); + } + return retval; +} + +static SDL_RenderCommand * +PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype) +{ + /* !!! FIXME: drop this draw if viewport w or h is zero. */ + SDL_RenderCommand *cmd = NULL; + if (PrepQueueCmdDraw(renderer, renderer->r, renderer->g, renderer->b, renderer->a) == 0) { + cmd = AllocateRenderCommand(renderer); + if (cmd != NULL) { + cmd->command = cmdtype; + cmd->data.draw.first = 0; /* render backend will fill this in. */ + cmd->data.draw.count = 0; /* render backend will fill this in. */ + cmd->data.draw.r = renderer->r; + cmd->data.draw.g = renderer->g; + cmd->data.draw.b = renderer->b; + cmd->data.draw.a = renderer->a; + cmd->data.draw.blend = renderer->blendMode; + cmd->data.draw.texture = NULL; /* no texture. */ + } + } + return cmd; +} + +static int +QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count) +{ + SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS); + int retval = -1; + if (cmd != NULL) { + retval = renderer->QueueDrawPoints(renderer, cmd, points, count); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } + } + return retval; +} + +static int +QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count) +{ + SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES); + int retval = -1; + if (cmd != NULL) { + retval = renderer->QueueDrawLines(renderer, cmd, points, count); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } + } + return retval; +} + +static int +QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count) +{ + SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS); + int retval = -1; + if (cmd != NULL) { + retval = renderer->QueueFillRects(renderer, cmd, rects, count); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } + } + return retval; +} + +static SDL_RenderCommand * +PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype) +{ + /* !!! FIXME: drop this draw if viewport w or h is zero. */ + SDL_RenderCommand *cmd = NULL; + if (PrepQueueCmdDraw(renderer, texture->r, texture->g, texture->b, texture->a) == 0) { + cmd = AllocateRenderCommand(renderer); + if (cmd != NULL) { + cmd->command = cmdtype; + cmd->data.draw.first = 0; /* render backend will fill this in. */ + cmd->data.draw.count = 0; /* render backend will fill this in. */ + cmd->data.draw.r = texture->r; + cmd->data.draw.g = texture->g; + cmd->data.draw.b = texture->b; + cmd->data.draw.a = texture->a; + cmd->data.draw.blend = texture->blendMode; + cmd->data.draw.texture = texture; + } + } + return cmd; +} + +static int +QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY); + int retval = -1; + if (cmd != NULL) { + retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } + } + return retval; +} + +static int +QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture, + const SDL_Rect * srcquad, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +{ + SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX); + int retval = -1; + SDL_assert(renderer->QueueCopyEx != NULL); /* should have caught at higher level. */ + if (cmd != NULL) { + retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip); + if (retval < 0) { + cmd->command = SDL_RENDERCMD_NO_OP; + } + } + return retval; +} + + static int UpdateLogicalSize(SDL_Renderer *renderer); int @@ -183,7 +696,8 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) renderer->viewport.y = 0; renderer->viewport.w = w; renderer->viewport.h = h; - renderer->UpdateViewport(renderer); + QueueCmdSetViewport(renderer); + FlushRenderCommandsIfNotBatching(renderer); } } @@ -300,12 +814,27 @@ SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags, return 0; } +static SDL_INLINE +void VerifyDrawQueueFunctions(const SDL_Renderer *renderer) +{ + /* all of these functions are required to be implemented, even as no-ops, so we don't + have to check that they aren't NULL over and over. */ + SDL_assert(renderer->QueueSetViewport != NULL); + SDL_assert(renderer->QueueSetDrawColor != NULL); + SDL_assert(renderer->QueueDrawPoints != NULL); + SDL_assert(renderer->QueueDrawLines != NULL); + SDL_assert(renderer->QueueFillRects != NULL); + SDL_assert(renderer->QueueCopy != NULL); + SDL_assert(renderer->RunCommandQueue != NULL); +} + SDL_Renderer * SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) { #if !SDL_RENDER_DISABLED SDL_Renderer *renderer = NULL; int n = SDL_GetNumRenderDrivers(); + SDL_bool batching = SDL_TRUE; const char *hint; if (!window) { @@ -335,6 +864,9 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) if (SDL_strcasecmp(hint, driver->info.name) == 0) { /* Create a new renderer instance */ renderer = driver->CreateRenderer(window, flags); + if (renderer) { + batching = SDL_FALSE; + } break; } } @@ -366,9 +898,20 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) } /* Create a new renderer instance */ renderer = render_drivers[index]->CreateRenderer(window, flags); + batching = SDL_FALSE; } if (renderer) { + VerifyDrawQueueFunctions(renderer); + + /* let app/user override batching decisions. */ + if (renderer->always_batch) { + batching = SDL_TRUE; + } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) { + batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE); + } + + renderer->batching = batching; renderer->magic = &renderer_magic; renderer->window = window; renderer->target_mutex = SDL_CreateMutex(); @@ -377,6 +920,9 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) renderer->dpi_scale.x = 1.0f; renderer->dpi_scale.y = 1.0f; + /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */ + renderer->render_command_generation = 1; + if (window && renderer->GetOutputSize) { int window_w, window_h; int output_w, output_h; @@ -418,11 +964,15 @@ SDL_CreateSoftwareRenderer(SDL_Surface * surface) renderer = SW_CreateRendererForSurface(surface); if (renderer) { + VerifyDrawQueueFunctions(renderer); renderer->magic = &renderer_magic; renderer->target_mutex = SDL_CreateMutex(); renderer->scale.x = 1.0f; renderer->scale.y = 1.0f; + /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */ + renderer->render_command_generation = 1; + SDL_RenderSetViewport(renderer, NULL); } return renderer; @@ -744,11 +1294,8 @@ SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access, int SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b) { - SDL_Renderer *renderer; - CHECK_TEXTURE_MAGIC(texture, -1); - renderer = texture->renderer; if (r < 255 || g < 255 || b < 255) { texture->modMode |= SDL_TEXTUREMODULATE_COLOR; } else { @@ -759,11 +1306,8 @@ SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b) texture->b = b; if (texture->native) { return SDL_SetTextureColorMod(texture->native, r, g, b); - } else if (renderer->SetTextureColorMod) { - return renderer->SetTextureColorMod(renderer, texture); - } else { - return 0; } + return 0; } int @@ -787,11 +1331,8 @@ SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g, int SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha) { - SDL_Renderer *renderer; - CHECK_TEXTURE_MAGIC(texture, -1); - renderer = texture->renderer; if (alpha < 255) { texture->modMode |= SDL_TEXTUREMODULATE_ALPHA; } else { @@ -800,11 +1341,8 @@ SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha) texture->a = alpha; if (texture->native) { return SDL_SetTextureAlphaMod(texture->native, alpha); - } else if (renderer->SetTextureAlphaMod) { - return renderer->SetTextureAlphaMod(renderer, texture); - } else { - return 0; } + return 0; } int @@ -832,11 +1370,8 @@ SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode) texture->blendMode = blendMode; if (texture->native) { return SDL_SetTextureBlendMode(texture->native, blendMode); - } else if (renderer->SetTextureBlendMode) { - return renderer->SetTextureBlendMode(renderer, texture); - } else { - return 0; } + return 0; } int @@ -941,7 +1476,6 @@ int SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch) { - SDL_Renderer *renderer; SDL_Rect full_rect; CHECK_TEXTURE_MAGIC(texture, -1); @@ -968,7 +1502,10 @@ SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect, } else if (texture->native) { return SDL_UpdateTextureNative(texture, rect, pixels, pitch); } else { - renderer = texture->renderer; + SDL_Renderer *renderer = texture->renderer; + if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { + return -1; + } return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch); } } @@ -1078,6 +1615,9 @@ int SDL_UpdateYUVTexture(SDL_Texture * texture, const SDL_Rect * rect, renderer = texture->renderer; SDL_assert(renderer->UpdateTextureYUV); if (renderer->UpdateTextureYUV) { + if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { + return -1; + } return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); } else { return SDL_Unsupported(); @@ -1108,7 +1648,6 @@ int SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect, void **pixels, int *pitch) { - SDL_Renderer *renderer; SDL_Rect full_rect; CHECK_TEXTURE_MAGIC(texture, -1); @@ -1126,11 +1665,18 @@ SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect, } if (texture->yuv) { + if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { + return -1; + } return SDL_LockTextureYUV(texture, rect, pixels, pitch); } else if (texture->native) { + /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */ return SDL_LockTextureNative(texture, rect, pixels, pitch); } else { - renderer = texture->renderer; + SDL_Renderer *renderer = texture->renderer; + if (FlushRenderCommandsIfTextureNeeded(texture) < 0) { + return -1; + } return renderer->LockTexture(renderer, texture, rect, pixels, pitch); } } @@ -1180,8 +1726,6 @@ SDL_UnlockTextureNative(SDL_Texture * texture) void SDL_UnlockTexture(SDL_Texture * texture) { - SDL_Renderer *renderer; - CHECK_TEXTURE_MAGIC(texture, ); if (texture->access != SDL_TEXTUREACCESS_STREAMING) { @@ -1192,7 +1736,7 @@ SDL_UnlockTexture(SDL_Texture * texture) } else if (texture->native) { SDL_UnlockTextureNative(texture); } else { - renderer = texture->renderer; + SDL_Renderer *renderer = texture->renderer; renderer->UnlockTexture(renderer, texture); } } @@ -1217,6 +1761,8 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) return 0; } + FlushRenderCommands(renderer); /* time to send everything to the GPU! */ + /* texture == NULL is valid and means reset the target to the window */ if (texture) { CHECK_TEXTURE_MAGIC(texture, -1); @@ -1272,15 +1818,15 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) SDL_UnlockMutex(renderer->target_mutex); - if (renderer->UpdateViewport(renderer) < 0) { + if (QueueCmdSetViewport(renderer) < 0) { return -1; } - if (renderer->UpdateClipRect(renderer) < 0) { + if (QueueCmdSetClipRect(renderer) < 0) { return -1; } /* All set! */ - return 0; + return FlushRenderCommandsIfNotBatching(renderer); } SDL_Texture * @@ -1452,6 +1998,7 @@ SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer) int SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect) { + int retval; CHECK_RENDERER_MAGIC(renderer, -1); if (rect) { @@ -1466,7 +2013,8 @@ SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect) return -1; } } - return renderer->UpdateViewport(renderer); + retval = QueueCmdSetViewport(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } void @@ -1485,6 +2033,7 @@ SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect) int SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect) { + int retval; CHECK_RENDERER_MAGIC(renderer, -1) if (rect) { @@ -1497,7 +2046,9 @@ SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect) renderer->clipping_enabled = SDL_FALSE; SDL_zero(renderer->clip_rect); } - return renderer->UpdateClipRect(renderer); + + retval = QueueCmdSetClipRect(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } void @@ -1601,37 +2152,43 @@ SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode) int SDL_RenderClear(SDL_Renderer * renderer) { + int retval; CHECK_RENDERER_MAGIC(renderer, -1); - - /* Don't draw while we're hidden */ - if (renderer->hidden) { - return 0; - } - return renderer->RenderClear(renderer); + retval = QueueCmdClear(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } + +/* !!! FIXME: delete all the duplicate code for the integer versions in 2.1, + !!! FIXME: making the floating point versions the only available APIs. */ + int SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y) { - SDL_Point point; + const SDL_FPoint fpoint = { (float) x, (float) y }; + return SDL_RenderDrawPointsF(renderer, &fpoint, 1); +} - point.x = x; - point.y = y; - return SDL_RenderDrawPoints(renderer, &point, 1); +int +SDL_RenderDrawPointF(SDL_Renderer * renderer, float x, float y) +{ + const SDL_FPoint fpoint = { x, y }; + return SDL_RenderDrawPointsF(renderer, &fpoint, 1); } static int RenderDrawPointsWithRects(SDL_Renderer * renderer, - const SDL_Point * points, int count) + const SDL_Point * points, const int count) { - SDL_FRect *frects; + int retval = -1; + SDL_bool isstack; + SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack); int i; - int status; - frects = SDL_stack_alloc(SDL_FRect, count); if (!frects) { return SDL_OutOfMemory(); } + for (i = 0; i < count; ++i) { frects[i].x = points[i].x * renderer->scale.x; frects[i].y = points[i].y * renderer->scale.y; @@ -1639,11 +2196,11 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, frects[i].h = renderer->scale.y; } - status = renderer->RenderFillRects(renderer, frects, count); + retval = QueueCmdFillRects(renderer, frects, count); - SDL_stack_free(frects); + SDL_small_free(frects, isstack); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -1652,7 +2209,8 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, { SDL_FPoint *fpoints; int i; - int status; + int retval; + SDL_bool isstack; CHECK_RENDERER_MAGIC(renderer, -1); @@ -1672,7 +2230,77 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, return RenderDrawPointsWithRects(renderer, points, count); } - fpoints = SDL_stack_alloc(SDL_FPoint, count); + fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); + if (!fpoints) { + return SDL_OutOfMemory(); + } + for (i = 0; i < count; ++i) { + fpoints[i].x = points[i].x * renderer->scale.x; + fpoints[i].y = points[i].y * renderer->scale.y; + } + + retval = QueueCmdDrawPoints(renderer, fpoints, count); + + SDL_small_free(fpoints, isstack); + + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +static int +RenderDrawPointsWithRectsF(SDL_Renderer * renderer, + const SDL_FPoint * fpoints, const int count) +{ + int retval = -1; + SDL_bool isstack; + SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack); + int i; + + if (!frects) { + return SDL_OutOfMemory(); + } + + for (i = 0; i < count; ++i) { + frects[i].x = fpoints[i].x * renderer->scale.x; + frects[i].y = fpoints[i].y * renderer->scale.y; + frects[i].w = renderer->scale.x; + frects[i].h = renderer->scale.y; + } + + retval = QueueCmdFillRects(renderer, frects, count); + + SDL_small_free(frects, isstack); + + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +int +SDL_RenderDrawPointsF(SDL_Renderer * renderer, + const SDL_FPoint * points, int count) +{ + SDL_FPoint *fpoints; + int i; + int retval; + SDL_bool isstack; + + CHECK_RENDERER_MAGIC(renderer, -1); + + if (!points) { + return SDL_SetError("SDL_RenderDrawFPoints(): Passed NULL points"); + } + if (count < 1) { + return 0; + } + + /* Don't draw while we're hidden */ + if (renderer->hidden) { + return 0; + } + + if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { + return RenderDrawPointsWithRectsF(renderer, points, count); + } + + fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); if (!fpoints) { return SDL_OutOfMemory(); } @@ -1681,46 +2309,102 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, fpoints[i].y = points[i].y * renderer->scale.y; } - status = renderer->RenderDrawPoints(renderer, fpoints, count); + retval = QueueCmdDrawPoints(renderer, fpoints, count); - SDL_stack_free(fpoints); + SDL_small_free(fpoints, isstack); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2) { - SDL_Point points[2]; + const SDL_FPoint points[2] = { { (float) x1, (float) y1 }, { (float) x2, (float) y2 } }; + return SDL_RenderDrawLinesF(renderer, points, 2); +} - points[0].x = x1; - points[0].y = y1; - points[1].x = x2; - points[1].y = y2; - return SDL_RenderDrawLines(renderer, points, 2); +int +SDL_RenderDrawLineF(SDL_Renderer * renderer, float x1, float y1, float x2, float y2) +{ + const SDL_FPoint points[2] = { { x1, y1 }, { x2, y2 } }; + return SDL_RenderDrawLinesF(renderer, points, 2); } static int RenderDrawLinesWithRects(SDL_Renderer * renderer, - const SDL_Point * points, int count) + const SDL_Point * points, const int count) +{ + SDL_FRect *frect; + SDL_FRect *frects; + SDL_FPoint fpoints[2]; + int i, nrects = 0; + int retval = 0; + SDL_bool isstack; + + frects = SDL_small_alloc(SDL_FRect, count-1, &isstack); + if (!frects) { + return SDL_OutOfMemory(); + } + + for (i = 0; i < count-1; ++i) { + if (points[i].x == points[i+1].x) { + const int minY = SDL_min(points[i].y, points[i+1].y); + const int maxY = SDL_max(points[i].y, points[i+1].y); + + frect = &frects[nrects++]; + frect->x = points[i].x * renderer->scale.x; + frect->y = minY * renderer->scale.y; + frect->w = renderer->scale.x; + frect->h = (maxY - minY + 1) * renderer->scale.y; + } else if (points[i].y == points[i+1].y) { + const int minX = SDL_min(points[i].x, points[i+1].x); + const int maxX = SDL_max(points[i].x, points[i+1].x); + + frect = &frects[nrects++]; + frect->x = minX * renderer->scale.x; + frect->y = points[i].y * renderer->scale.y; + frect->w = (maxX - minX + 1) * renderer->scale.x; + frect->h = renderer->scale.y; + } else { + /* FIXME: We can't use a rect for this line... */ + fpoints[0].x = points[i].x * renderer->scale.x; + fpoints[0].y = points[i].y * renderer->scale.y; + fpoints[1].x = points[i+1].x * renderer->scale.x; + fpoints[1].y = points[i+1].y * renderer->scale.y; + retval += QueueCmdDrawLines(renderer, fpoints, 2); + } + } + + retval += QueueCmdFillRects(renderer, frects, nrects); + + SDL_small_free(frects, isstack); + + if (retval < 0) { + retval = -1; + } + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +static int +RenderDrawLinesWithRectsF(SDL_Renderer * renderer, + const SDL_FPoint * points, const int count) { SDL_FRect *frect; SDL_FRect *frects; SDL_FPoint fpoints[2]; - int i, nrects; - int status; + int i, nrects = 0; + int retval = 0; + SDL_bool isstack; - frects = SDL_stack_alloc(SDL_FRect, count-1); + frects = SDL_small_alloc(SDL_FRect, count-1, &isstack); if (!frects) { return SDL_OutOfMemory(); } - status = 0; - nrects = 0; for (i = 0; i < count-1; ++i) { if (points[i].x == points[i+1].x) { - int minY = SDL_min(points[i].y, points[i+1].y); - int maxY = SDL_max(points[i].y, points[i+1].y); + const int minY = SDL_min(points[i].y, points[i+1].y); + const int maxY = SDL_max(points[i].y, points[i+1].y); frect = &frects[nrects++]; frect->x = points[i].x * renderer->scale.x; @@ -1728,8 +2412,8 @@ RenderDrawLinesWithRects(SDL_Renderer * renderer, frect->w = renderer->scale.x; frect->h = (maxY - minY + 1) * renderer->scale.y; } else if (points[i].y == points[i+1].y) { - int minX = SDL_min(points[i].x, points[i+1].x); - int maxX = SDL_max(points[i].x, points[i+1].x); + const int minX = SDL_min(points[i].x, points[i+1].x); + const int maxX = SDL_max(points[i].x, points[i+1].x); frect = &frects[nrects++]; frect->x = minX * renderer->scale.x; @@ -1742,18 +2426,18 @@ RenderDrawLinesWithRects(SDL_Renderer * renderer, fpoints[0].y = points[i].y * renderer->scale.y; fpoints[1].x = points[i+1].x * renderer->scale.x; fpoints[1].y = points[i+1].y * renderer->scale.y; - status += renderer->RenderDrawLines(renderer, fpoints, 2); + retval += QueueCmdDrawLines(renderer, fpoints, 2); } } - status += renderer->RenderFillRects(renderer, frects, nrects); + retval += QueueCmdFillRects(renderer, frects, nrects); - SDL_stack_free(frects); + SDL_small_free(frects, isstack); - if (status < 0) { - status = -1; + if (retval < 0) { + retval = -1; } - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -1762,7 +2446,8 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, { SDL_FPoint *fpoints; int i; - int status; + int retval; + SDL_bool isstack; CHECK_RENDERER_MAGIC(renderer, -1); @@ -1782,7 +2467,50 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, return RenderDrawLinesWithRects(renderer, points, count); } - fpoints = SDL_stack_alloc(SDL_FPoint, count); + fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); + if (!fpoints) { + return SDL_OutOfMemory(); + } + for (i = 0; i < count; ++i) { + fpoints[i].x = points[i].x * renderer->scale.x; + fpoints[i].y = points[i].y * renderer->scale.y; + } + + retval = QueueCmdDrawLines(renderer, fpoints, count); + + SDL_small_free(fpoints, isstack); + + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +int +SDL_RenderDrawLinesF(SDL_Renderer * renderer, + const SDL_FPoint * points, int count) +{ + SDL_FPoint *fpoints; + int i; + int retval; + SDL_bool isstack; + + CHECK_RENDERER_MAGIC(renderer, -1); + + if (!points) { + return SDL_SetError("SDL_RenderDrawLines(): Passed NULL points"); + } + if (count < 2) { + return 0; + } + + /* Don't draw while we're hidden */ + if (renderer->hidden) { + return 0; + } + + if (renderer->scale.x != 1.0f || renderer->scale.y != 1.0f) { + return RenderDrawLinesWithRectsF(renderer, points, count); + } + + fpoints = SDL_small_alloc(SDL_FPoint, count, &isstack); if (!fpoints) { return SDL_OutOfMemory(); } @@ -1791,27 +2519,47 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, fpoints[i].y = points[i].y * renderer->scale.y; } - status = renderer->RenderDrawLines(renderer, fpoints, count); + retval = QueueCmdDrawLines(renderer, fpoints, count); - SDL_stack_free(fpoints); + SDL_small_free(fpoints, isstack); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect) { - SDL_Rect full_rect; - SDL_Point points[5]; + SDL_FRect frect; + SDL_FRect *prect = NULL; + + if (rect) { + frect.x = (float) rect->x; + frect.y = (float) rect->y; + frect.w = (float) rect->w; + frect.h = (float) rect->h; + prect = &frect; + } + + return SDL_RenderDrawRectF(renderer, prect); +} + +int +SDL_RenderDrawRectF(SDL_Renderer * renderer, const SDL_FRect * rect) +{ + SDL_FRect frect; + SDL_FPoint points[5]; CHECK_RENDERER_MAGIC(renderer, -1); /* If 'rect' == NULL, then outline the whole surface */ if (!rect) { - SDL_RenderGetViewport(renderer, &full_rect); - full_rect.x = 0; - full_rect.y = 0; - rect = &full_rect; + SDL_Rect r; + SDL_RenderGetViewport(renderer, &r); + frect.x = 0.0f; + frect.y = 0.0f; + frect.w = (float) r.w; + frect.h = (float) r.h; + rect = &frect; } points[0].x = rect->x; @@ -1824,7 +2572,7 @@ SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect) points[3].y = rect->y+rect->h-1; points[4].x = rect->x; points[4].y = rect->y; - return SDL_RenderDrawLines(renderer, points, 5); + return SDL_RenderDrawLinesF(renderer, points, 5); } int @@ -1856,20 +2604,75 @@ SDL_RenderDrawRects(SDL_Renderer * renderer, } int +SDL_RenderDrawRectsF(SDL_Renderer * renderer, + const SDL_FRect * rects, int count) +{ + int i; + + CHECK_RENDERER_MAGIC(renderer, -1); + + if (!rects) { + return SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects"); + } + if (count < 1) { + return 0; + } + + /* Don't draw while we're hidden */ + if (renderer->hidden) { + return 0; + } + + for (i = 0; i < count; ++i) { + if (SDL_RenderDrawRectF(renderer, &rects[i]) < 0) { + return -1; + } + } + return 0; +} + +int SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect) { - SDL_Rect full_rect = { 0, 0, 0, 0 }; + SDL_FRect frect; + + CHECK_RENDERER_MAGIC(renderer, -1); + + /* If 'rect' == NULL, then outline the whole surface */ + if (rect) { + frect.x = (float) rect->x; + frect.y = (float) rect->y; + frect.w = (float) rect->w; + frect.h = (float) rect->h; + } else { + SDL_Rect r; + SDL_RenderGetViewport(renderer, &r); + frect.x = 0.0f; + frect.y = 0.0f; + frect.w = (float) r.w; + frect.h = (float) r.h; + } + return SDL_RenderFillRectsF(renderer, &frect, 1); +} + +int +SDL_RenderFillRectF(SDL_Renderer * renderer, const SDL_FRect * rect) +{ + SDL_FRect frect; CHECK_RENDERER_MAGIC(renderer, -1); /* If 'rect' == NULL, then outline the whole surface */ if (!rect) { - SDL_RenderGetViewport(renderer, &full_rect); - full_rect.x = 0; - full_rect.y = 0; - rect = &full_rect; + SDL_Rect r; + SDL_RenderGetViewport(renderer, &r); + frect.x = 0.0f; + frect.y = 0.0f; + frect.w = (float) r.w; + frect.h = (float) r.h; + rect = &frect; } - return SDL_RenderFillRects(renderer, rect, 1); + return SDL_RenderFillRectsF(renderer, rect, 1); } int @@ -1878,7 +2681,8 @@ SDL_RenderFillRects(SDL_Renderer * renderer, { SDL_FRect *frects; int i; - int status; + int retval; + SDL_bool isstack; CHECK_RENDERER_MAGIC(renderer, -1); @@ -1894,7 +2698,7 @@ SDL_RenderFillRects(SDL_Renderer * renderer, return 0; } - frects = SDL_stack_alloc(SDL_FRect, count); + frects = SDL_small_alloc(SDL_FRect, count, &isstack); if (!frects) { return SDL_OutOfMemory(); } @@ -1905,20 +2709,132 @@ SDL_RenderFillRects(SDL_Renderer * renderer, frects[i].h = rects[i].h * renderer->scale.y; } - status = renderer->RenderFillRects(renderer, frects, count); + retval = QueueCmdFillRects(renderer, frects, count); - SDL_stack_free(frects); + SDL_small_free(frects, isstack); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +int +SDL_RenderFillRectsF(SDL_Renderer * renderer, + const SDL_FRect * rects, int count) +{ + SDL_FRect *frects; + int i; + int retval; + SDL_bool isstack; + + CHECK_RENDERER_MAGIC(renderer, -1); + + if (!rects) { + return SDL_SetError("SDL_RenderFillFRects(): Passed NULL rects"); + } + if (count < 1) { + return 0; + } + + /* Don't draw while we're hidden */ + if (renderer->hidden) { + return 0; + } + + frects = SDL_small_alloc(SDL_FRect, count, &isstack); + if (!frects) { + return SDL_OutOfMemory(); + } + for (i = 0; i < count; ++i) { + frects[i].x = rects[i].x * renderer->scale.x; + frects[i].y = rects[i].y * renderer->scale.y; + frects[i].w = rects[i].w * renderer->scale.x; + frects[i].h = rects[i].h * renderer->scale.y; + } + + retval = QueueCmdFillRects(renderer, frects, count); + + SDL_small_free(frects, isstack); + + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +/* !!! FIXME: move this to a public API if we want to do float versions of all of these later */ +SDL_FORCE_INLINE SDL_bool SDL_FRectEmpty(const SDL_FRect *r) +{ + return ((!r) || (r->w <= 0.0f) || (r->h <= 0.0f)) ? SDL_TRUE : SDL_FALSE; +} + +/* !!! FIXME: move this to a public API if we want to do float versions of all of these later */ +static SDL_bool +SDL_HasIntersectionF(const SDL_FRect * A, const SDL_FRect * B) +{ + float Amin, Amax, Bmin, Bmax; + + if (!A) { + SDL_InvalidParamError("A"); + return SDL_FALSE; + } + + if (!B) { + SDL_InvalidParamError("B"); + return SDL_FALSE; + } + + /* Special cases for empty rects */ + if (SDL_FRectEmpty(A) || SDL_FRectEmpty(B)) { + return SDL_FALSE; + } + + /* Horizontal intersection */ + Amin = A->x; + Amax = Amin + A->w; + Bmin = B->x; + Bmax = Bmin + B->w; + if (Bmin > Amin) + Amin = Bmin; + if (Bmax < Amax) + Amax = Bmax; + if (Amax <= Amin) + return SDL_FALSE; + + /* Vertical intersection */ + Amin = A->y; + Amax = Amin + A->h; + Bmin = B->y; + Bmax = Bmin + B->h; + if (Bmin > Amin) + Amin = Bmin; + if (Bmax < Amax) + Amax = Bmax; + if (Amax <= Amin) + return SDL_FALSE; + + return SDL_TRUE; } int SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect) { - SDL_Rect real_srcrect = { 0, 0, 0, 0 }; - SDL_Rect real_dstrect = { 0, 0, 0, 0 }; - SDL_FRect frect; + SDL_FRect dstfrect; + SDL_FRect *pdstfrect = NULL; + if (dstrect) { + dstfrect.x = (float) dstrect->x; + dstfrect.y = (float) dstrect->y; + dstfrect.w = (float) dstrect->w; + dstfrect.h = (float) dstrect->h; + pdstfrect = &dstfrect; + } + return SDL_RenderCopyF(renderer, texture, srcrect, pdstfrect); +} + +int +SDL_RenderCopyF(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + SDL_Rect real_srcrect; + SDL_FRect real_dstrect; + SDL_Rect r; + int retval; CHECK_RENDERER_MAGIC(renderer, -1); CHECK_TEXTURE_MAGIC(texture, -1); @@ -1942,11 +2858,13 @@ SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, } } - SDL_RenderGetViewport(renderer, &real_dstrect); - real_dstrect.x = 0; - real_dstrect.y = 0; + SDL_RenderGetViewport(renderer, &r); + real_dstrect.x = 0.0f; + real_dstrect.y = 0.0f; + real_dstrect.w = (float) r.w; + real_dstrect.h = (float) r.h; if (dstrect) { - if (!SDL_HasIntersection(dstrect, &real_dstrect)) { + if (!SDL_HasIntersectionF(dstrect, &real_dstrect)) { return 0; } real_dstrect = *dstrect; @@ -1956,28 +2874,56 @@ SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, texture = texture->native; } - frect.x = real_dstrect.x * renderer->scale.x; - frect.y = real_dstrect.y * renderer->scale.y; - frect.w = real_dstrect.w * renderer->scale.x; - frect.h = real_dstrect.h * renderer->scale.y; + real_dstrect.x *= renderer->scale.x; + real_dstrect.y *= renderer->scale.y; + real_dstrect.w *= renderer->scale.x; + real_dstrect.h *= renderer->scale.y; - return renderer->RenderCopy(renderer, texture, &real_srcrect, &frect); -} + texture->last_command_generation = renderer->render_command_generation; + retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} int SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect, const double angle, const SDL_Point *center, const SDL_RendererFlip flip) { - SDL_Rect real_srcrect = { 0, 0, 0, 0 }; - SDL_Rect real_dstrect = { 0, 0, 0, 0 }; - SDL_Point real_center; - SDL_FRect frect; + SDL_FRect dstfrect; + SDL_FRect *pdstfrect = NULL; SDL_FPoint fcenter; + SDL_FPoint *pfcenter = NULL; + + if (dstrect) { + dstfrect.x = (float) dstrect->x; + dstfrect.y = (float) dstrect->y; + dstfrect.w = (float) dstrect->w; + dstfrect.h = (float) dstrect->h; + pdstfrect = &dstfrect; + } + + if (center) { + fcenter.x = (float) center->x; + fcenter.y = (float) center->y; + pfcenter = &fcenter; + } + + return SDL_RenderCopyExF(renderer, texture, srcrect, pdstfrect, angle, pfcenter, flip); +} + +int +SDL_RenderCopyExF(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +{ + SDL_Rect real_srcrect; + SDL_FRect real_dstrect; + SDL_FPoint real_center; + int retval; if (flip == SDL_FLIP_NONE && (int)(angle/360) == angle/360) { /* fast path when we don't need rotation or flipping */ - return SDL_RenderCopy(renderer, texture, srcrect, dstrect); + return SDL_RenderCopyF(renderer, texture, srcrect, dstrect); } CHECK_RENDERER_MAGIC(renderer, -1); @@ -1986,7 +2932,7 @@ SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, if (renderer != texture->renderer) { return SDL_SetError("Texture was not created with this renderer"); } - if (!renderer->RenderCopyEx) { + if (!renderer->QueueCopyEx) { return SDL_SetError("Renderer does not support RenderCopyEx"); } @@ -2009,9 +2955,12 @@ SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, if (dstrect) { real_dstrect = *dstrect; } else { - SDL_RenderGetViewport(renderer, &real_dstrect); - real_dstrect.x = 0; - real_dstrect.y = 0; + SDL_Rect r; + SDL_RenderGetViewport(renderer, &r); + real_dstrect.x = 0.0f; + real_dstrect.y = 0.0f; + real_dstrect.w = (float) r.w; + real_dstrect.h = (float) r.h; } if (texture->native) { @@ -2021,19 +2970,22 @@ SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, if (center) { real_center = *center; } else { - real_center.x = real_dstrect.w/2; - real_center.y = real_dstrect.h/2; + real_center.x = real_dstrect.w / 2.0f; + real_center.y = real_dstrect.h / 2.0f; } - frect.x = real_dstrect.x * renderer->scale.x; - frect.y = real_dstrect.y * renderer->scale.y; - frect.w = real_dstrect.w * renderer->scale.x; - frect.h = real_dstrect.h * renderer->scale.y; + real_dstrect.x *= renderer->scale.x; + real_dstrect.y *= renderer->scale.y; + real_dstrect.w *= renderer->scale.x; + real_dstrect.h *= renderer->scale.y; - fcenter.x = real_center.x * renderer->scale.x; - fcenter.y = real_center.y * renderer->scale.y; + real_center.x *= renderer->scale.x; + real_center.y *= renderer->scale.y; - return renderer->RenderCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip); + texture->last_command_generation = renderer->render_command_generation; + + retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -2048,6 +3000,8 @@ SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, return SDL_Unsupported(); } + FlushRenderCommands(renderer); /* we need to render before we read the results. */ + if (!format) { format = SDL_GetWindowPixelFormat(renderer->window); } @@ -2078,7 +3032,9 @@ SDL_RenderPresent(SDL_Renderer * renderer) { CHECK_RENDERER_MAGIC(renderer, ); - /* Don't draw while we're hidden */ + FlushRenderCommands(renderer); /* time to send everything to the GPU! */ + + /* Don't present while we're hidden */ if (renderer->hidden) { return; } @@ -2094,7 +3050,9 @@ SDL_DestroyTexture(SDL_Texture * texture) renderer = texture->renderer; if (texture == renderer->target) { - SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderTarget(renderer, NULL); /* implies command queue flush */ + } else { + FlushRenderCommandsIfTextureNeeded(texture); } texture->magic = NULL; @@ -2123,10 +3081,43 @@ SDL_DestroyTexture(SDL_Texture * texture) void SDL_DestroyRenderer(SDL_Renderer * renderer) { + SDL_RenderCommand *cmd; + SDL_AllocVertGap *gap; + SDL_AllocVertGap *nextgap; + CHECK_RENDERER_MAGIC(renderer, ); SDL_DelEventWatch(SDL_RendererEventWatch, renderer); + if (renderer->render_commands_tail != NULL) { + renderer->render_commands_tail->next = renderer->render_commands_pool; + cmd = renderer->render_commands; + } else { + cmd = renderer->render_commands_pool; + } + + renderer->render_commands_pool = NULL; + renderer->render_commands_tail = NULL; + renderer->render_commands = NULL; + + while (cmd != NULL) { + SDL_RenderCommand *next = cmd->next; + SDL_free(cmd); + cmd = next; + } + + SDL_free(renderer->vertex_data); + + for (gap = renderer->vertex_data_gaps.next; gap; gap = nextgap) { + nextgap = gap->next; + SDL_free(gap); + } + + for (gap = renderer->vertex_data_gaps_pool; gap; gap = nextgap) { + nextgap = gap->next; + SDL_free(gap); + } + /* Free existing textures for this renderer */ while (renderer->textures) { SDL_Texture *tex = renderer->textures; (void) tex; @@ -2158,6 +3149,7 @@ int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh) if (texture->native) { return SDL_GL_BindTexture(texture->native, texw, texh); } else if (renderer && renderer->GL_BindTexture) { + FlushRenderCommandsIfTextureNeeded(texture); /* in case the app is going to mess with it. */ return renderer->GL_BindTexture(renderer, texture, texw, texh); } else { return SDL_Unsupported(); @@ -2173,6 +3165,7 @@ int SDL_GL_UnbindTexture(SDL_Texture *texture) if (texture->native) { return SDL_GL_UnbindTexture(texture->native); } else if (renderer && renderer->GL_UnbindTexture) { + FlushRenderCommandsIfTextureNeeded(texture); /* in case the app messed with it. */ return renderer->GL_UnbindTexture(renderer, texture); } @@ -2185,6 +3178,7 @@ SDL_RenderGetMetalLayer(SDL_Renderer * renderer) CHECK_RENDERER_MAGIC(renderer, NULL); if (renderer->GetMetalLayer) { + FlushRenderCommands(renderer); /* in case the app is going to mess with it. */ return renderer->GetMetalLayer(renderer); } return NULL; @@ -2196,6 +3190,7 @@ SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer) CHECK_RENDERER_MAGIC(renderer, NULL); if (renderer->GetMetalCommandEncoder) { + FlushRenderCommands(renderer); /* in case the app is going to mess with it. */ return renderer->GetMetalCommandEncoder(renderer); } return NULL; diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 940bebcc1d..87586f1bb9 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -39,20 +39,6 @@ typedef enum SDL_ScaleModeBest } SDL_ScaleMode; -typedef struct -{ - float x; - float y; -} SDL_FPoint; - -typedef struct -{ - float x; - float y; - float w; - float h; -} SDL_FRect; - /* Define the SDL texture structure */ struct SDL_Texture { @@ -75,12 +61,63 @@ struct SDL_Texture int pitch; SDL_Rect locked_rect; + Uint32 last_command_generation; /* last command queue generation this texture was in. */ + void *driverdata; /**< Driver specific texture representation */ SDL_Texture *prev; SDL_Texture *next; }; +typedef enum +{ + SDL_RENDERCMD_NO_OP, + SDL_RENDERCMD_SETVIEWPORT, + SDL_RENDERCMD_SETCLIPRECT, + SDL_RENDERCMD_SETDRAWCOLOR, + SDL_RENDERCMD_CLEAR, + SDL_RENDERCMD_DRAW_POINTS, + SDL_RENDERCMD_DRAW_LINES, + SDL_RENDERCMD_FILL_RECTS, + SDL_RENDERCMD_COPY, + SDL_RENDERCMD_COPY_EX +} SDL_RenderCommandType; + +typedef struct SDL_RenderCommand +{ + SDL_RenderCommandType command; + union { + struct { + size_t first; + SDL_Rect rect; + } viewport; + struct { + SDL_bool enabled; + SDL_Rect rect; + } cliprect; + struct { + size_t first; + size_t count; + Uint8 r, g, b, a; + SDL_BlendMode blend; + SDL_Texture *texture; + } draw; + struct { + size_t first; + Uint8 r, g, b, a; + } color; + } data; + struct SDL_RenderCommand *next; +} SDL_RenderCommand; + +typedef struct SDL_AllocVertGap +{ + size_t offset; + size_t len; + struct SDL_AllocVertGap *next; +} SDL_AllocVertGap; + + /* Define the SDL renderer structure */ struct SDL_Renderer { @@ -90,12 +127,20 @@ struct SDL_Renderer int (*GetOutputSize) (SDL_Renderer * renderer, int *w, int *h); SDL_bool (*SupportsBlendMode)(SDL_Renderer * renderer, SDL_BlendMode blendMode); int (*CreateTexture) (SDL_Renderer * renderer, SDL_Texture * texture); - int (*SetTextureColorMod) (SDL_Renderer * renderer, - SDL_Texture * texture); - int (*SetTextureAlphaMod) (SDL_Renderer * renderer, - SDL_Texture * texture); - int (*SetTextureBlendMode) (SDL_Renderer * renderer, - SDL_Texture * texture); + int (*QueueSetViewport) (SDL_Renderer * renderer, SDL_RenderCommand *cmd); + int (*QueueSetDrawColor) (SDL_Renderer * renderer, SDL_RenderCommand *cmd); + int (*QueueDrawPoints) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, + int count); + int (*QueueDrawLines) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, + int count); + int (*QueueFillRects) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, + int count); + int (*QueueCopy) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect); + int (*QueueCopyEx) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcquad, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); + int (*RunCommandQueue) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); int (*UpdateTexture) (SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch); @@ -108,20 +153,6 @@ struct SDL_Renderer const SDL_Rect * rect, void **pixels, int *pitch); void (*UnlockTexture) (SDL_Renderer * renderer, SDL_Texture * texture); int (*SetRenderTarget) (SDL_Renderer * renderer, SDL_Texture * texture); - int (*UpdateViewport) (SDL_Renderer * renderer); - int (*UpdateClipRect) (SDL_Renderer * renderer); - int (*RenderClear) (SDL_Renderer * renderer); - int (*RenderDrawPoints) (SDL_Renderer * renderer, const SDL_FPoint * points, - int count); - int (*RenderDrawLines) (SDL_Renderer * renderer, const SDL_FPoint * points, - int count); - int (*RenderFillRects) (SDL_Renderer * renderer, const SDL_FRect * rects, - int count); - int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); - int (*RenderCopyEx) (SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcquad, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch); void (*RenderPresent) (SDL_Renderer * renderer); @@ -178,6 +209,26 @@ struct SDL_Renderer Uint8 r, g, b, a; /**< Color for drawing operations values */ SDL_BlendMode blendMode; /**< The drawing blend mode */ + SDL_bool always_batch; + SDL_bool batching; + SDL_RenderCommand *render_commands; + SDL_RenderCommand *render_commands_tail; + SDL_RenderCommand *render_commands_pool; + Uint32 render_command_generation; + Uint32 last_queued_color; + SDL_Rect last_queued_viewport; + SDL_Rect last_queued_cliprect; + SDL_bool last_queued_cliprect_enabled; + SDL_bool color_queued; + SDL_bool viewport_queued; + SDL_bool cliprect_queued; + + void *vertex_data; + size_t vertex_data_used; + size_t vertex_data_allocation; + SDL_AllocVertGap vertex_data_gaps; + SDL_AllocVertGap *vertex_data_gaps_pool; + void *driverdata; }; @@ -209,6 +260,11 @@ extern SDL_BlendFactor SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode); extern SDL_BlendFactor SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode); extern SDL_BlendOperation SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode); +/* drivers call this during their Queue*() methods to make space in a array that are used + for a vertex buffer during RunCommandQueue(). Pointers returned here are only valid until + the next call, because it might be in an array that gets realloc()'d. */ +extern void *SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset); + #endif /* SDL_sysrender_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 69a9dff547..9d000ccdd1 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -30,6 +30,8 @@ #include "SDL_hints.h" #include "SDL_loadso.h" #include "SDL_syswm.h" +#include "SDL_log.h" +#include "SDL_assert.h" #include "../SDL_sysrender.h" #include "../SDL_d3dmath.h" #include "../../video/windows/SDL_windowsvideo.h" @@ -41,60 +43,22 @@ #include "SDL_shaders_d3d.h" - -/* Direct3D renderer implementation */ - -static SDL_Renderer *D3D_CreateRenderer(SDL_Window * window, Uint32 flags); -static void D3D_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static SDL_bool D3D_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode); -static int D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D_RecreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *pixels, - int pitch); -static int D3D_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, - const Uint8 *Yplane, int Ypitch, - const Uint8 *Uplane, int Upitch, - const Uint8 *Vplane, int Vpitch); -static int D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D_SetRenderTargetInternal(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D_UpdateViewport(SDL_Renderer * renderer); -static int D3D_UpdateClipRect(SDL_Renderer * renderer); -static int D3D_RenderClear(SDL_Renderer * renderer); -static int D3D_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int D3D_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int D3D_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); -static int D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip); -static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 format, void * pixels, int pitch); -static void D3D_RenderPresent(SDL_Renderer * renderer); -static void D3D_DestroyTexture(SDL_Renderer * renderer, - SDL_Texture * texture); -static void D3D_DestroyRenderer(SDL_Renderer * renderer); +typedef struct +{ + SDL_Rect viewport; + SDL_bool viewport_dirty; + SDL_Texture *texture; + SDL_BlendMode blend; + SDL_bool cliprect_enabled; + SDL_bool cliprect_enabled_dirty; + SDL_Rect cliprect; + SDL_bool cliprect_dirty; + SDL_bool is_copy_ex; + LPDIRECT3DPIXELSHADER9 shader; +} D3D_DrawStateCache; -SDL_RenderDriver D3D_RenderDriver = { - D3D_CreateRenderer, - { - "direct3d", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), - 1, - {SDL_PIXELFORMAT_ARGB8888}, - 0, - 0} -}; +/* Direct3D renderer implementation */ typedef struct { @@ -111,6 +75,11 @@ typedef struct IDirect3DSurface9 *currentRenderTarget; void* d3dxDLL; LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS]; + LPDIRECT3DVERTEXBUFFER9 vertexBuffers[8]; + size_t vertexBufferSize[8]; + int currentVertexBuffer; + SDL_bool reportedVboProblem; + D3D_DrawStateCache drawstate; } D3D_RenderData; typedef struct @@ -265,9 +234,8 @@ D3D_InitRenderState(D3D_RenderData *data) D3DMATRIX matrix; IDirect3DDevice9 *device = data->device; - - IDirect3DDevice9_SetVertexShader(device, NULL); IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1); + IDirect3DDevice9_SetVertexShader(device, NULL); IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE); IDirect3DDevice9_SetRenderState(device, D3DRS_CULLMODE, D3DCULL_NONE); IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE); @@ -300,21 +268,10 @@ D3D_InitRenderState(D3D_RenderData *data) D3DTOP_DISABLE); /* Set an identity world and view matrix */ + SDL_zero(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; IDirect3DDevice9_SetTransform(device, D3DTS_WORLD, &matrix); IDirect3DDevice9_SetTransform(device, D3DTS_VIEW, &matrix); @@ -326,63 +283,7 @@ D3D_InitRenderState(D3D_RenderData *data) data->beginScene = SDL_TRUE; } -static int -D3D_Reset(SDL_Renderer * renderer) -{ - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - HRESULT result; - SDL_Texture *texture; - - /* Release the default render target before reset */ - if (data->defaultRenderTarget) { - IDirect3DSurface9_Release(data->defaultRenderTarget); - data->defaultRenderTarget = NULL; - } - if (data->currentRenderTarget != NULL) { - IDirect3DSurface9_Release(data->currentRenderTarget); - data->currentRenderTarget = NULL; - } - - /* Release application render targets */ - for (texture = renderer->textures; texture; texture = texture->next) { - if (texture->access == SDL_TEXTUREACCESS_TARGET) { - D3D_DestroyTexture(renderer, texture); - } else { - D3D_RecreateTexture(renderer, texture); - } - } - - result = IDirect3DDevice9_Reset(data->device, &data->pparams); - if (FAILED(result)) { - if (result == D3DERR_DEVICELOST) { - /* Don't worry about it, we'll reset later... */ - return 0; - } else { - return D3D_SetError("Reset()", result); - } - } - - /* Allocate application render targets */ - for (texture = renderer->textures; texture; texture = texture->next) { - if (texture->access == SDL_TEXTUREACCESS_TARGET) { - D3D_CreateTexture(renderer, texture); - } - } - - IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget); - D3D_InitRenderState(data); - D3D_SetRenderTargetInternal(renderer, renderer->target); - D3D_UpdateViewport(renderer); - - /* Let the application know that render targets were reset */ - { - SDL_Event event; - event.type = SDL_RENDER_TARGETS_RESET; - SDL_PushEvent(&event); - } - - return 0; -} +static int D3D_Reset(SDL_Renderer * renderer); static int D3D_ActivateRenderer(SDL_Renderer * renderer) @@ -431,177 +332,6 @@ D3D_ActivateRenderer(SDL_Renderer * renderer) return 0; } -SDL_Renderer * -D3D_CreateRenderer(SDL_Window * window, Uint32 flags) -{ - SDL_Renderer *renderer; - D3D_RenderData *data; - SDL_SysWMinfo windowinfo; - HRESULT result; - D3DPRESENT_PARAMETERS pparams; - IDirect3DSwapChain9 *chain; - D3DCAPS9 caps; - DWORD device_flags; - Uint32 window_flags; - int w, h; - SDL_DisplayMode fullscreen_mode; - int displayIndex; - - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - return NULL; - } - - data = (D3D_RenderData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - SDL_free(renderer); - SDL_OutOfMemory(); - return NULL; - } - - if (!D3D_LoadDLL(&data->d3dDLL, &data->d3d)) { - SDL_free(renderer); - SDL_free(data); - SDL_SetError("Unable to create Direct3D interface"); - return NULL; - } - - renderer->WindowEvent = D3D_WindowEvent; - renderer->SupportsBlendMode = D3D_SupportsBlendMode; - renderer->CreateTexture = D3D_CreateTexture; - renderer->UpdateTexture = D3D_UpdateTexture; - renderer->UpdateTextureYUV = D3D_UpdateTextureYUV; - renderer->LockTexture = D3D_LockTexture; - renderer->UnlockTexture = D3D_UnlockTexture; - renderer->SetRenderTarget = D3D_SetRenderTarget; - renderer->UpdateViewport = D3D_UpdateViewport; - renderer->UpdateClipRect = D3D_UpdateClipRect; - renderer->RenderClear = D3D_RenderClear; - renderer->RenderDrawPoints = D3D_RenderDrawPoints; - renderer->RenderDrawLines = D3D_RenderDrawLines; - renderer->RenderFillRects = D3D_RenderFillRects; - renderer->RenderCopy = D3D_RenderCopy; - renderer->RenderCopyEx = D3D_RenderCopyEx; - renderer->RenderReadPixels = D3D_RenderReadPixels; - renderer->RenderPresent = D3D_RenderPresent; - renderer->DestroyTexture = D3D_DestroyTexture; - renderer->DestroyRenderer = D3D_DestroyRenderer; - renderer->info = D3D_RenderDriver.info; - renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); - renderer->driverdata = data; - - SDL_VERSION(&windowinfo.version); - SDL_GetWindowWMInfo(window, &windowinfo); - - window_flags = SDL_GetWindowFlags(window); - SDL_GetWindowSize(window, &w, &h); - SDL_GetWindowDisplayMode(window, &fullscreen_mode); - - SDL_zero(pparams); - pparams.hDeviceWindow = windowinfo.info.win.window; - pparams.BackBufferWidth = w; - pparams.BackBufferHeight = h; - pparams.BackBufferCount = 1; - pparams.SwapEffect = D3DSWAPEFFECT_DISCARD; - - if (window_flags & SDL_WINDOW_FULLSCREEN && (window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { - pparams.Windowed = FALSE; - pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode.format); - pparams.FullScreen_RefreshRateInHz = fullscreen_mode.refresh_rate; - } else { - pparams.Windowed = TRUE; - pparams.BackBufferFormat = D3DFMT_UNKNOWN; - pparams.FullScreen_RefreshRateInHz = 0; - } - if (flags & SDL_RENDERER_PRESENTVSYNC) { - pparams.PresentationInterval = D3DPRESENT_INTERVAL_ONE; - } else { - pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - } - - /* Get the adapter for the display that the window is on */ - displayIndex = SDL_GetWindowDisplayIndex(window); - data->adapter = SDL_Direct3D9GetAdapterIndex(displayIndex); - - IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps); - - device_flags = D3DCREATE_FPU_PRESERVE; - if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) { - device_flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; - } else { - device_flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; - } - - if (SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, SDL_FALSE)) { - device_flags |= D3DCREATE_MULTITHREADED; - } - - result = IDirect3D9_CreateDevice(data->d3d, data->adapter, - D3DDEVTYPE_HAL, - pparams.hDeviceWindow, - device_flags, - &pparams, &data->device); - if (FAILED(result)) { - D3D_DestroyRenderer(renderer); - D3D_SetError("CreateDevice()", result); - return NULL; - } - - /* Get presentation parameters to fill info */ - result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain); - if (FAILED(result)) { - D3D_DestroyRenderer(renderer); - D3D_SetError("GetSwapChain()", result); - return NULL; - } - result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams); - if (FAILED(result)) { - IDirect3DSwapChain9_Release(chain); - D3D_DestroyRenderer(renderer); - D3D_SetError("GetPresentParameters()", result); - return NULL; - } - IDirect3DSwapChain9_Release(chain); - if (pparams.PresentationInterval == D3DPRESENT_INTERVAL_ONE) { - renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; - } - data->pparams = pparams; - - IDirect3DDevice9_GetDeviceCaps(data->device, &caps); - renderer->info.max_texture_width = caps.MaxTextureWidth; - renderer->info.max_texture_height = caps.MaxTextureHeight; - if (caps.NumSimultaneousRTs >= 2) { - renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE; - } - - if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) { - data->enableSeparateAlphaBlend = SDL_TRUE; - } - - /* Store the default render target */ - IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget); - data->currentRenderTarget = NULL; - - /* Set up parameters for rendering */ - D3D_InitRenderState(data); - - if (caps.MaxSimultaneousTextures >= 3) { - int i; - for (i = 0; i < SDL_arraysize(data->shaders); ++i) { - result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]); - if (FAILED(result)) { - D3D_SetError("CreatePixelShader()", result); - } - } - if (data->shaders[SHADER_YUV_JPEG] && data->shaders[SHADER_YUV_BT601] && data->shaders[SHADER_YUV_BT709]) { - renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; - renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; - } - } - return renderer; -} - static void D3D_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) { @@ -702,33 +432,6 @@ D3D_CreateStagingTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture) } static int -D3D_BindTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD sampler) -{ - HRESULT result; - - if (texture->dirty && texture->staging) { - if (!texture->texture) { - result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, texture->usage, - PixelFormatToD3DFMT(texture->format), D3DPOOL_DEFAULT, &texture->texture, NULL); - if (FAILED(result)) { - return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result); - } - } - - result = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texture->staging, (IDirect3DBaseTexture9 *)texture->texture); - if (FAILED(result)) { - return D3D_SetError("UpdateTexture()", result); - } - texture->dirty = SDL_FALSE; - } - result = IDirect3DDevice9_SetTexture(device, sampler, (IDirect3DBaseTexture9 *)texture->texture); - if (FAILED(result)) { - return D3D_SetError("SetTexture()", result); - } - return 0; -} - -static int D3D_RecreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture) { if (texture->texture) { @@ -1072,330 +775,254 @@ D3D_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return D3D_SetRenderTargetInternal(renderer, texture); } + static int -D3D_UpdateViewport(SDL_Renderer * renderer) +D3D_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - D3DVIEWPORT9 viewport; - D3DMATRIX matrix; - - /* Set the viewport */ - viewport.X = renderer->viewport.x; - viewport.Y = renderer->viewport.y; - viewport.Width = renderer->viewport.w; - viewport.Height = renderer->viewport.h; - viewport.MinZ = 0.0f; - viewport.MaxZ = 1.0f; - IDirect3DDevice9_SetViewport(data->device, &viewport); - - /* Set an orthographic projection matrix */ - if (renderer->viewport.w && renderer->viewport.h) { - matrix.m[0][0] = 2.0f / renderer->viewport.w; - 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] = -2.0f / renderer->viewport.h; - 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] = -1.0f; - matrix.m[3][1] = 1.0f; - matrix.m[3][2] = 0.0f; - matrix.m[3][3] = 1.0f; - IDirect3DDevice9_SetTransform(data->device, D3DTS_PROJECTION, &matrix); - } - - return 0; + return 0; /* nothing to do in this backend. */ } static int -D3D_UpdateClipRect(SDL_Renderer * renderer) +D3D_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; + const DWORD color = D3DCOLOR_ARGB(cmd->data.draw.a, cmd->data.draw.r, cmd->data.draw.g, cmd->data.draw.b); + const size_t vertslen = count * sizeof (Vertex); + Vertex *verts = (Vertex *) SDL_AllocateRenderVertices(renderer, vertslen, 0, &cmd->data.draw.first); + size_t i; - if (renderer->clipping_enabled) { - const SDL_Rect *rect = &renderer->clip_rect; - RECT r; - HRESULT result; + if (!verts) { + return -1; + } - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, TRUE); - r.left = renderer->viewport.x + rect->x; - r.top = renderer->viewport.y + rect->y; - r.right = renderer->viewport.x + rect->x + rect->w; - r.bottom = renderer->viewport.y + rect->y + rect->h; + SDL_memset(verts, '\0', vertslen); + cmd->data.draw.count = count; - result = IDirect3DDevice9_SetScissorRect(data->device, &r); - if (result != D3D_OK) { - D3D_SetError("SetScissor()", result); - return -1; - } - } else { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); + for (i = 0; i < count; i++, verts++, points++) { + verts->x = points->x; + verts->y = points->y; + verts->color = color; } + return 0; } static int -D3D_RenderClear(SDL_Renderer * renderer) +D3D_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - DWORD color; - HRESULT result; - int BackBufferWidth, BackBufferHeight; + const DWORD color = D3DCOLOR_ARGB(cmd->data.draw.a, cmd->data.draw.r, cmd->data.draw.g, cmd->data.draw.b); + const size_t vertslen = count * sizeof (Vertex) * 4; + Vertex *verts = (Vertex *) SDL_AllocateRenderVertices(renderer, vertslen, 0, &cmd->data.draw.first); + size_t i; - if (D3D_ActivateRenderer(renderer) < 0) { + if (!verts) { return -1; } - color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b); - - if (renderer->target) { - BackBufferWidth = renderer->target->w; - BackBufferHeight = renderer->target->h; - } else { - BackBufferWidth = data->pparams.BackBufferWidth; - BackBufferHeight = data->pparams.BackBufferHeight; - } + SDL_memset(verts, '\0', vertslen); + cmd->data.draw.count = count; - if (renderer->clipping_enabled) { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); - } + for (i = 0; i < count; i++) { + const SDL_FRect *rect = &rects[i]; + const float minx = rect->x; + const float maxx = rect->x + rect->w; + const float miny = rect->y; + const float maxy = rect->y + rect->h; - /* Don't reset the viewport if we don't have to! */ - if (!renderer->viewport.x && !renderer->viewport.y && - renderer->viewport.w == BackBufferWidth && - renderer->viewport.h == BackBufferHeight) { - result = IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); - } else { - D3DVIEWPORT9 viewport; + verts->x = minx; + verts->y = miny; + verts->color = color; + verts++; - /* Clear is defined to clear the entire render target */ - viewport.X = 0; - viewport.Y = 0; - viewport.Width = BackBufferWidth; - viewport.Height = BackBufferHeight; - viewport.MinZ = 0.0f; - viewport.MaxZ = 1.0f; - IDirect3DDevice9_SetViewport(data->device, &viewport); + verts->x = maxx; + verts->y = miny; + verts->color = color; + verts++; - result = IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); + verts->x = maxx; + verts->y = maxy; + verts->color = color; + verts++; - /* Reset the viewport */ - viewport.X = renderer->viewport.x; - viewport.Y = renderer->viewport.y; - viewport.Width = renderer->viewport.w; - viewport.Height = renderer->viewport.h; - viewport.MinZ = 0.0f; - viewport.MaxZ = 1.0f; - IDirect3DDevice9_SetViewport(data->device, &viewport); + verts->x = minx; + verts->y = maxy; + verts->color = color; + verts++; } - if (renderer->clipping_enabled) { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, TRUE); - } - - if (FAILED(result)) { - return D3D_SetError("Clear()", result); - } return 0; } -static void -D3D_SetBlendMode(D3D_RenderData * data, SDL_BlendMode blendMode) -{ - if (blendMode == SDL_BLENDMODE_NONE) { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, FALSE); - } else { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, TRUE); - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND, - GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode))); - IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND, - GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode))); - if (data->enableSeparateAlphaBlend) { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA, - GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode))); - IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA, - GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode))); - } - } -} - static int -D3D_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) +D3D_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - DWORD color; - Vertex *vertices; - int i; - HRESULT result; + const DWORD color = D3DCOLOR_ARGB(cmd->data.draw.a, cmd->data.draw.r, cmd->data.draw.g, cmd->data.draw.b); + float minx, miny, maxx, maxy; + float minu, maxu, minv, maxv; + const size_t vertslen = sizeof (Vertex) * 4; + Vertex *verts = (Vertex *) SDL_AllocateRenderVertices(renderer, vertslen, 0, &cmd->data.draw.first); - if (D3D_ActivateRenderer(renderer) < 0) { + if (!verts) { return -1; } - D3D_SetBlendMode(data, renderer->blendMode); + cmd->data.draw.count = 1; - result = - IDirect3DDevice9_SetTexture(data->device, 0, - (IDirect3DBaseTexture9 *) 0); - if (FAILED(result)) { - return D3D_SetError("SetTexture()", result); - } + minx = dstrect->x - 0.5f; + miny = dstrect->y - 0.5f; + maxx = dstrect->x + dstrect->w - 0.5f; + maxy = dstrect->y + dstrect->h - 0.5f; - color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b); + minu = (float) srcrect->x / texture->w; + maxu = (float) (srcrect->x + srcrect->w) / texture->w; + minv = (float) srcrect->y / texture->h; + maxv = (float) (srcrect->y + srcrect->h) / texture->h; + + verts->x = minx; + verts->y = miny; + verts->z = 0.0f; + verts->color = color; + verts->u = minu; + verts->v = minv; + verts++; + + verts->x = maxx; + verts->y = miny; + verts->z = 0.0f; + verts->color = color; + verts->u = maxu; + verts->v = minv; + verts++; + + verts->x = maxx; + verts->y = maxy; + verts->z = 0.0f; + verts->color = color; + verts->u = maxu; + verts->v = maxv; + verts++; + + verts->x = minx; + verts->y = maxy; + verts->z = 0.0f; + verts->color = color; + verts->u = minu; + verts->v = maxv; + verts++; - vertices = SDL_stack_alloc(Vertex, count); - for (i = 0; i < count; ++i) { - vertices[i].x = points[i].x; - vertices[i].y = points[i].y; - vertices[i].z = 0.0f; - vertices[i].color = color; - vertices[i].u = 0.0f; - vertices[i].v = 0.0f; - } - result = - IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, count, - vertices, sizeof(*vertices)); - SDL_stack_free(vertices); - if (FAILED(result)) { - return D3D_SetError("DrawPrimitiveUP()", result); - } return 0; } static int -D3D_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) +D3D_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcquad, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - DWORD color; - Vertex *vertices; - int i; - HRESULT result; + const DWORD color = D3DCOLOR_ARGB(cmd->data.draw.a, cmd->data.draw.r, cmd->data.draw.g, cmd->data.draw.b); + float minx, miny, maxx, maxy; + float minu, maxu, minv, maxv; + const size_t vertslen = sizeof (Vertex) * 5; + Vertex *verts = (Vertex *) SDL_AllocateRenderVertices(renderer, vertslen, 0, &cmd->data.draw.first); - if (D3D_ActivateRenderer(renderer) < 0) { + if (!verts) { return -1; } - D3D_SetBlendMode(data, renderer->blendMode); + cmd->data.draw.count = 1; - result = - IDirect3DDevice9_SetTexture(data->device, 0, - (IDirect3DBaseTexture9 *) 0); - if (FAILED(result)) { - return D3D_SetError("SetTexture()", result); - } + minx = -center->x; + maxx = dstrect->w - center->x; + miny = -center->y; + maxy = dstrect->h - center->y; - color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b); - - vertices = SDL_stack_alloc(Vertex, count); - for (i = 0; i < count; ++i) { - vertices[i].x = points[i].x; - vertices[i].y = points[i].y; - vertices[i].z = 0.0f; - vertices[i].color = color; - vertices[i].u = 0.0f; - vertices[i].v = 0.0f; + if (flip & SDL_FLIP_HORIZONTAL) { + minu = (float) (srcquad->x + srcquad->w) / texture->w; + maxu = (float) srcquad->x / texture->w; + } else { + minu = (float) srcquad->x / texture->w; + maxu = (float) (srcquad->x + srcquad->w) / texture->w; } - result = - IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, count-1, - vertices, sizeof(*vertices)); - /* DirectX 9 has the same line rasterization semantics as GDI, - so we need to close the endpoint of the line */ - if (count == 2 || - points[0].x != points[count-1].x || points[0].y != points[count-1].y) { - vertices[0].x = points[count-1].x; - vertices[0].y = points[count-1].y; - result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, vertices, sizeof(*vertices)); - } + if (flip & SDL_FLIP_VERTICAL) { + minv = (float) (srcquad->y + srcquad->h) / texture->h; + maxv = (float) srcquad->y / texture->h; + } else { + minv = (float) srcquad->y / texture->h; + maxv = (float) (srcquad->y + srcquad->h) / texture->h; + } + + verts->x = minx; + verts->y = miny; + verts->z = 0.0f; + verts->color = color; + verts->u = minu; + verts->v = minv; + verts++; + + verts->x = maxx; + verts->y = miny; + verts->z = 0.0f; + verts->color = color; + verts->u = maxu; + verts->v = minv; + verts++; + + verts->x = maxx; + verts->y = maxy; + verts->z = 0.0f; + verts->color = color; + verts->u = maxu; + verts->v = maxv; + verts++; + + verts->x = minx; + verts->y = maxy; + verts->z = 0.0f; + verts->color = color; + verts->u = minu; + verts->v = maxv; + verts++; + + verts->x = dstrect->x + center->x - 0.5f; /* X translation */ + verts->y = dstrect->y + center->y - 0.5f; /* Y translation */ + verts->z = (float)(M_PI * (float) angle / 180.0f); /* rotation */ + verts->color = 0; + verts->u = 0.0f; + verts->v = 0.0f; + verts++; - SDL_stack_free(vertices); - if (FAILED(result)) { - return D3D_SetError("DrawPrimitiveUP()", result); - } return 0; } static int -D3D_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, - int count) +BindTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD sampler) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - DWORD color; - int i; - float minx, miny, maxx, maxy; - Vertex vertices[4]; HRESULT result; - if (D3D_ActivateRenderer(renderer) < 0) { - return -1; - } - - D3D_SetBlendMode(data, renderer->blendMode); - - result = - IDirect3DDevice9_SetTexture(data->device, 0, - (IDirect3DBaseTexture9 *) 0); - if (FAILED(result)) { - return D3D_SetError("SetTexture()", result); - } - - color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b); - - for (i = 0; i < count; ++i) { - const SDL_FRect *rect = &rects[i]; + if (texture->dirty && texture->staging) { + if (!texture->texture) { + result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, texture->usage, + PixelFormatToD3DFMT(texture->format), D3DPOOL_DEFAULT, &texture->texture, NULL); + if (FAILED(result)) { + return D3D_SetError("CreateTexture(D3DPOOL_DEFAULT)", result); + } + } - minx = rect->x; - miny = rect->y; - maxx = rect->x + rect->w; - maxy = rect->y + rect->h; - - vertices[0].x = minx; - vertices[0].y = miny; - vertices[0].z = 0.0f; - vertices[0].color = color; - vertices[0].u = 0.0f; - vertices[0].v = 0.0f; - - vertices[1].x = maxx; - vertices[1].y = miny; - vertices[1].z = 0.0f; - vertices[1].color = color; - vertices[1].u = 0.0f; - vertices[1].v = 0.0f; - - vertices[2].x = maxx; - vertices[2].y = maxy; - vertices[2].z = 0.0f; - vertices[2].color = color; - vertices[2].u = 0.0f; - vertices[2].v = 0.0f; - - vertices[3].x = minx; - vertices[3].y = maxy; - vertices[3].z = 0.0f; - vertices[3].color = color; - vertices[3].u = 0.0f; - vertices[3].v = 0.0f; - - result = - IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, - 2, vertices, sizeof(*vertices)); + result = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texture->staging, (IDirect3DBaseTexture9 *)texture->texture); if (FAILED(result)) { - return D3D_SetError("DrawPrimitiveUP()", result); + return D3D_SetError("UpdateTexture()", result); } + texture->dirty = SDL_FALSE; + } + result = IDirect3DDevice9_SetTexture(device, sampler, (IDirect3DBaseTexture9 *)texture->texture); + if (FAILED(result)) { + return D3D_SetError("SetTexture()", result); } return 0; } static void -D3D_UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *texturedata, unsigned index) +UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *texturedata, unsigned index) { if (texturedata->scaleMode != data->scaleMode[index]) { IDirect3DDevice9_SetSamplerState(data->device, index, D3DSAMP_MINFILTER, @@ -1411,22 +1038,20 @@ D3D_UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *texturedata, u } static int -D3D_RenderSetupTextureState(SDL_Renderer * renderer, SDL_Texture * texture, LPDIRECT3DPIXELSHADER9 *shader) +SetupTextureState(D3D_RenderData *data, SDL_Texture * texture, LPDIRECT3DPIXELSHADER9 *shader) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - D3D_TextureData *texturedata; + D3D_TextureData *texturedata = (D3D_TextureData *)texture->driverdata; - *shader = NULL; + SDL_assert(*shader == NULL); - texturedata = (D3D_TextureData *)texture->driverdata; if (!texturedata) { SDL_SetError("Texture is not currently available"); return -1; } - D3D_UpdateTextureScaleMode(data, texturedata, 0); + UpdateTextureScaleMode(data, texturedata, 0); - if (D3D_BindTextureRep(data->device, &texturedata->texture, 0) < 0) { + if (BindTextureRep(data->device, &texturedata->texture, 0) < 0) { return -1; } @@ -1445,13 +1070,13 @@ D3D_RenderSetupTextureState(SDL_Renderer * renderer, SDL_Texture * texture, LPDI return SDL_SetError("Unsupported YUV conversion mode"); } - D3D_UpdateTextureScaleMode(data, texturedata, 1); - D3D_UpdateTextureScaleMode(data, texturedata, 2); + UpdateTextureScaleMode(data, texturedata, 1); + UpdateTextureScaleMode(data, texturedata, 2); - if (D3D_BindTextureRep(data->device, &texturedata->utexture, 1) < 0) { + if (BindTextureRep(data->device, &texturedata->utexture, 1) < 0) { return -1; } - if (D3D_BindTextureRep(data->device, &texturedata->vtexture, 2) < 0) { + if (BindTextureRep(data->device, &texturedata->vtexture, 2) < 0) { return -1; } } @@ -1459,193 +1084,325 @@ D3D_RenderSetupTextureState(SDL_Renderer * renderer, SDL_Texture * texture, LPDI } static int -D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) +SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) { - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - LPDIRECT3DPIXELSHADER9 shader; - float minx, miny, maxx, maxy; - float minu, maxu, minv, maxv; - DWORD color; - Vertex vertices[4]; - HRESULT result; + const SDL_bool was_copy_ex = data->drawstate.is_copy_ex; + const SDL_bool is_copy_ex = (cmd->command == SDL_RENDERCMD_COPY_EX); + SDL_Texture *texture = cmd->data.draw.texture; + const SDL_BlendMode blend = cmd->data.draw.blend; + + if (texture != data->drawstate.texture) { + D3D_TextureData *oldtexturedata = data->drawstate.texture ? (D3D_TextureData *) data->drawstate.texture->driverdata : NULL; + D3D_TextureData *newtexturedata = texture ? (D3D_TextureData *) texture->driverdata : NULL; + LPDIRECT3DPIXELSHADER9 shader = NULL; + + /* disable any enabled textures we aren't going to use, let SetupTextureState() do the rest. */ + if (texture == NULL) { + IDirect3DDevice9_SetTexture(data->device, 0, NULL); + } + if ((!newtexturedata || !newtexturedata->yuv) && (oldtexturedata && oldtexturedata->yuv)) { + IDirect3DDevice9_SetTexture(data->device, 1, NULL); + IDirect3DDevice9_SetTexture(data->device, 2, NULL); + } + if (texture && SetupTextureState(data, texture, &shader) < 0) { + return -1; + } - if (D3D_ActivateRenderer(renderer) < 0) { - return -1; - } + if (shader != data->drawstate.shader) { + const HRESULT result = IDirect3DDevice9_SetPixelShader(data->device, shader); + if (FAILED(result)) { + return D3D_SetError("IDirect3DDevice9_SetPixelShader()", result); + } + data->drawstate.shader = shader; + } - minx = dstrect->x - 0.5f; - miny = dstrect->y - 0.5f; - maxx = dstrect->x + dstrect->w - 0.5f; - maxy = dstrect->y + dstrect->h - 0.5f; + data->drawstate.texture = texture; + } - minu = (float) srcrect->x / texture->w; - maxu = (float) (srcrect->x + srcrect->w) / texture->w; - minv = (float) srcrect->y / texture->h; - maxv = (float) (srcrect->y + srcrect->h) / texture->h; + if (blend != data->drawstate.blend) { + if (blend == SDL_BLENDMODE_NONE) { + IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, FALSE); + } else { + IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, TRUE); + IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND, + GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend))); + IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND, + GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend))); + if (data->enableSeparateAlphaBlend) { + IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA, + GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend))); + IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA, + GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); + } + } - color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b); - - vertices[0].x = minx; - vertices[0].y = miny; - vertices[0].z = 0.0f; - vertices[0].color = color; - vertices[0].u = minu; - vertices[0].v = minv; - - vertices[1].x = maxx; - vertices[1].y = miny; - vertices[1].z = 0.0f; - vertices[1].color = color; - vertices[1].u = maxu; - vertices[1].v = minv; - - vertices[2].x = maxx; - vertices[2].y = maxy; - vertices[2].z = 0.0f; - vertices[2].color = color; - vertices[2].u = maxu; - vertices[2].v = maxv; - - vertices[3].x = minx; - vertices[3].y = maxy; - vertices[3].z = 0.0f; - vertices[3].color = color; - vertices[3].u = minu; - vertices[3].v = maxv; - - D3D_SetBlendMode(data, texture->blendMode); - - if (D3D_RenderSetupTextureState(renderer, texture, &shader) < 0) { - return -1; + data->drawstate.blend = blend; } - - if (shader) { - result = IDirect3DDevice9_SetPixelShader(data->device, shader); - if (FAILED(result)) { - return D3D_SetError("SetShader()", result); + + if (is_copy_ex != was_copy_ex) { + if (!is_copy_ex) { /* SDL_RENDERCMD_COPY_EX will set this, we only want to reset it here if necessary. */ + const Float4X4 d3dmatrix = MatrixIdentity(); + IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*) &d3dmatrix); } + data->drawstate.is_copy_ex = is_copy_ex; + } + + if (data->drawstate.viewport_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + const D3DVIEWPORT9 d3dviewport = { viewport->x, viewport->y, viewport->w, viewport->h, 0.0f, 1.0f }; + IDirect3DDevice9_SetViewport(data->device, &d3dviewport); + + /* Set an orthographic projection matrix */ + if (viewport->w && viewport->h) { + D3DMATRIX d3dmatrix; + SDL_zero(d3dmatrix); + d3dmatrix.m[0][0] = 2.0f / viewport->w; + d3dmatrix.m[1][1] = -2.0f / viewport->h; + d3dmatrix.m[2][2] = 1.0f; + d3dmatrix.m[3][0] = -1.0f; + d3dmatrix.m[3][1] = 1.0f; + d3dmatrix.m[3][3] = 1.0f; + IDirect3DDevice9_SetTransform(data->device, D3DTS_PROJECTION, &d3dmatrix); + } + + data->drawstate.viewport_dirty = SDL_FALSE; } - result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, - vertices, sizeof(*vertices)); - if (FAILED(result)) { - D3D_SetError("DrawPrimitiveUP()", result); + + if (data->drawstate.cliprect_enabled_dirty) { + IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, data->drawstate.cliprect_enabled ? TRUE : FALSE); + data->drawstate.cliprect_enabled_dirty = SDL_FALSE; } - if (shader) { - IDirect3DDevice9_SetPixelShader(data->device, NULL); + + if (data->drawstate.cliprect_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + const SDL_Rect *rect = &cmd->data.cliprect.rect; + const RECT d3drect = { viewport->x + rect->x, viewport->y + rect->y, viewport->x + rect->x + rect->w, viewport->y + rect->y + rect->h }; + IDirect3DDevice9_SetScissorRect(data->device, &d3drect); + data->drawstate.cliprect_dirty = SDL_FALSE; } - return FAILED(result) ? -1 : 0; -} + return 0; +} static int -D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip) +D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - LPDIRECT3DPIXELSHADER9 shader = NULL; - float minx, miny, maxx, maxy; - float minu, maxu, minv, maxv; - float centerx, centery; - DWORD color; - Vertex vertices[4]; - Float4X4 modelMatrix; - HRESULT result; + const int vboidx = data->currentVertexBuffer; + IDirect3DVertexBuffer9 *vbo = NULL; + const SDL_bool istarget = renderer->target != NULL; + size_t i; if (D3D_ActivateRenderer(renderer) < 0) { return -1; } - centerx = center->x; - centery = center->y; - - minx = -centerx; - maxx = dstrect->w - centerx; - miny = -centery; - maxy = dstrect->h - centery; - - minu = (float) srcrect->x / texture->w; - maxu = (float) (srcrect->x + srcrect->w) / texture->w; - minv = (float) srcrect->y / texture->h; - maxv = (float) (srcrect->y + srcrect->h) / texture->h; + /* upload the new VBO data for this set of commands. */ + vbo = data->vertexBuffers[vboidx]; + if (!vbo || (data->vertexBufferSize[vboidx] < vertsize)) { + const DWORD usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; + const DWORD fvf = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1; + if (vbo) { + IDirect3DVertexBuffer9_Release(vbo); + } - if (flip & SDL_FLIP_HORIZONTAL) { - float tmp = maxu; - maxu = minu; - minu = tmp; - } - if (flip & SDL_FLIP_VERTICAL) { - float tmp = maxv; - maxv = minv; - minv = tmp; - } - - color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b); - - vertices[0].x = minx; - vertices[0].y = miny; - vertices[0].z = 0.0f; - vertices[0].color = color; - vertices[0].u = minu; - vertices[0].v = minv; - - vertices[1].x = maxx; - vertices[1].y = miny; - vertices[1].z = 0.0f; - vertices[1].color = color; - vertices[1].u = maxu; - vertices[1].v = minv; - - vertices[2].x = maxx; - vertices[2].y = maxy; - vertices[2].z = 0.0f; - vertices[2].color = color; - vertices[2].u = maxu; - vertices[2].v = maxv; - - vertices[3].x = minx; - vertices[3].y = maxy; - vertices[3].z = 0.0f; - vertices[3].color = color; - vertices[3].u = minu; - vertices[3].v = maxv; - - D3D_SetBlendMode(data, texture->blendMode); - - if (D3D_RenderSetupTextureState(renderer, texture, &shader) < 0) { - return -1; + if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) { + vbo = NULL; + } + data->vertexBuffers[vboidx] = vbo; + data->vertexBufferSize[vboidx] = vbo ? vertsize : 0; } - /* Rotate and translate */ - modelMatrix = MatrixMultiply( - MatrixRotationZ((float)(M_PI * (float) angle / 180.0f)), - MatrixTranslation(dstrect->x + center->x - 0.5f, dstrect->y + center->y - 0.5f, 0)); - IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&modelMatrix); - - if (shader) { - result = IDirect3DDevice9_SetPixelShader(data->device, shader); - if (FAILED(result)) { - D3D_SetError("SetShader()", result); - goto done; + if (vbo) { + void *ptr; + if (FAILED(IDirect3DVertexBuffer9_Lock(vbo, 0, vertsize, &ptr, D3DLOCK_DISCARD))) { + vbo = NULL; /* oh well, we'll do immediate mode drawing. :( */ + } else { + SDL_memcpy(ptr, vertices, vertsize); + if (FAILED(IDirect3DVertexBuffer9_Unlock(vbo))) { + vbo = NULL; /* oh well, we'll do immediate mode drawing. :( */ + } } } - result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, - vertices, sizeof(*vertices)); - if (FAILED(result)) { - D3D_SetError("DrawPrimitiveUP()", result); - } -done: - if (shader) { - IDirect3DDevice9_SetPixelShader(data->device, NULL); - } - modelMatrix = MatrixIdentity(); - IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&modelMatrix); + /* cycle through a few VBOs so D3D has some time with the data before we replace it. */ + if (vbo) { + data->currentVertexBuffer++; + if (data->currentVertexBuffer >= SDL_arraysize(data->vertexBuffers)) { + data->currentVertexBuffer = 0; + } + } else if (!data->reportedVboProblem) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL failed to get a vertex buffer for this Direct3D 9 rendering batch!"); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Dropping back to a slower method."); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This might be a brief hiccup, but if performance is bad, this is probably why."); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "This error will not be logged again for this renderer."); + data->reportedVboProblem = SDL_TRUE; + } + + IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, 0, sizeof (Vertex)); + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + /* currently this is sent with each vertex, but if we move to + shaders, we can put this in a uniform here and reduce vertex + buffer bandwidth */ + break; + } + + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_Rect *viewport = &data->drawstate.viewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); + data->drawstate.viewport_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + data->drawstate.cliprect_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_CLEAR: { + const DWORD color = D3DCOLOR_ARGB(cmd->data.color.a, cmd->data.color.r, cmd->data.color.g, cmd->data.color.b); + const SDL_Rect *viewport = &data->drawstate.viewport; + const int backw = istarget ? renderer->target->w : data->pparams.BackBufferWidth; + const int backh = istarget ? renderer->target->h : data->pparams.BackBufferHeight; + + if (data->drawstate.cliprect_enabled) { + IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + + /* Don't reset the viewport if we don't have to! */ + if (!viewport->x && !viewport->y && (viewport->w == backw) && (viewport->h == backh)) { + IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); + } else { + /* Clear is defined to clear the entire render target */ + const D3DVIEWPORT9 wholeviewport = { 0, 0, backw, backh, 0.0f, 1.0f }; + IDirect3DDevice9_SetViewport(data->device, &wholeviewport); + data->drawstate.viewport_dirty = SDL_TRUE; + IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); + } + + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + SetDrawState(data, cmd); + if (vbo) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, first / sizeof (Vertex), count); + } else { + const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, count, vertices, sizeof (Vertex)); + } + break; + } + + case SDL_RENDERCMD_DRAW_LINES: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); + + /* DirectX 9 has the same line rasterization semantics as GDI, + so we need to close the endpoint of the line with a second draw call. */ + const SDL_bool close_endpoint = ((count == 2) || (verts[0].x != verts[count-1].x) || (verts[0].y != verts[count-1].y)); + + SetDrawState(data, cmd); + + if (vbo) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, first / sizeof (Vertex), count - 1); + if (close_endpoint) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (first / sizeof (Vertex)) + (count - 1), 1); + } + } else { + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, count - 1, verts, sizeof (Vertex)); + if (close_endpoint) { + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_POINTLIST, 1, &verts[count-1], sizeof (Vertex)); + } + } + break; + } + + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + SetDrawState(data, cmd); + if (vbo) { + size_t offset = 0; + for (i = 0; i < count; ++i, offset += 4) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (first / sizeof (Vertex)) + offset, 2); + } + } else { + const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); + for (i = 0; i < count; ++i, verts += 4) { + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex)); + } + } + break; + } + + case SDL_RENDERCMD_COPY: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + SetDrawState(data, cmd); + if (vbo) { + size_t offset = 0; + for (i = 0; i < count; ++i, offset += 4) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (first / sizeof (Vertex)) + offset, 2); + } + } else { + const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); + for (i = 0; i < count; ++i, verts += 4) { + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex)); + } + } + break; + } + + case SDL_RENDERCMD_COPY_EX: { + const size_t first = cmd->data.draw.first; + const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); + const Vertex *transvert = verts + 4; + const float translatex = transvert->x; + const float translatey = transvert->y; + const float rotation = transvert->z; + const Float4X4 d3dmatrix = MatrixMultiply(MatrixRotationZ(rotation), MatrixTranslation(translatex, translatey, 0)); + SetDrawState(data, cmd); + + IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&d3dmatrix); + + if (vbo) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, first / sizeof (Vertex), 2); + } else { + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex)); + } + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } - return FAILED(result) ? -1 : 0; + cmd = cmd->next; + } + + return 0; } + static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch) @@ -1782,6 +1539,249 @@ D3D_DestroyRenderer(SDL_Renderer * renderer) } SDL_free(renderer); } + +static int +D3D_Reset(SDL_Renderer * renderer) +{ + D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; + HRESULT result; + SDL_Texture *texture; + + /* Release the default render target before reset */ + if (data->defaultRenderTarget) { + IDirect3DSurface9_Release(data->defaultRenderTarget); + data->defaultRenderTarget = NULL; + } + if (data->currentRenderTarget != NULL) { + IDirect3DSurface9_Release(data->currentRenderTarget); + data->currentRenderTarget = NULL; + } + + /* Release application render targets */ + for (texture = renderer->textures; texture; texture = texture->next) { + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + D3D_DestroyTexture(renderer, texture); + } else { + D3D_RecreateTexture(renderer, texture); + } + } + + result = IDirect3DDevice9_Reset(data->device, &data->pparams); + if (FAILED(result)) { + if (result == D3DERR_DEVICELOST) { + /* Don't worry about it, we'll reset later... */ + return 0; + } else { + return D3D_SetError("Reset()", result); + } + } + + /* Allocate application render targets */ + for (texture = renderer->textures; texture; texture = texture->next) { + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + D3D_CreateTexture(renderer, texture); + } + } + + IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget); + D3D_InitRenderState(data); + D3D_SetRenderTargetInternal(renderer, renderer->target); + data->drawstate.viewport_dirty = SDL_TRUE; + + /* Let the application know that render targets were reset */ + { + SDL_Event event; + event.type = SDL_RENDER_TARGETS_RESET; + SDL_PushEvent(&event); + } + + return 0; +} + +SDL_Renderer * +D3D_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Renderer *renderer; + D3D_RenderData *data; + SDL_SysWMinfo windowinfo; + HRESULT result; + D3DPRESENT_PARAMETERS pparams; + IDirect3DSwapChain9 *chain; + D3DCAPS9 caps; + DWORD device_flags; + Uint32 window_flags; + int w, h; + SDL_DisplayMode fullscreen_mode; + int displayIndex; + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + data = (D3D_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + SDL_free(renderer); + SDL_OutOfMemory(); + return NULL; + } + + if (!D3D_LoadDLL(&data->d3dDLL, &data->d3d)) { + SDL_free(renderer); + SDL_free(data); + SDL_SetError("Unable to create Direct3D interface"); + return NULL; + } + + renderer->WindowEvent = D3D_WindowEvent; + renderer->SupportsBlendMode = D3D_SupportsBlendMode; + renderer->CreateTexture = D3D_CreateTexture; + renderer->UpdateTexture = D3D_UpdateTexture; + renderer->UpdateTextureYUV = D3D_UpdateTextureYUV; + renderer->LockTexture = D3D_LockTexture; + renderer->UnlockTexture = D3D_UnlockTexture; + renderer->SetRenderTarget = D3D_SetRenderTarget; + renderer->QueueSetViewport = D3D_QueueSetViewport; + renderer->QueueSetDrawColor = D3D_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = D3D_QueueDrawPoints; + renderer->QueueDrawLines = D3D_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = D3D_QueueFillRects; + renderer->QueueCopy = D3D_QueueCopy; + renderer->QueueCopyEx = D3D_QueueCopyEx; + renderer->RunCommandQueue = D3D_RunCommandQueue; + renderer->RenderReadPixels = D3D_RenderReadPixels; + renderer->RenderPresent = D3D_RenderPresent; + renderer->DestroyTexture = D3D_DestroyTexture; + renderer->DestroyRenderer = D3D_DestroyRenderer; + renderer->info = D3D_RenderDriver.info; + renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + renderer->driverdata = data; + + SDL_VERSION(&windowinfo.version); + SDL_GetWindowWMInfo(window, &windowinfo); + + window_flags = SDL_GetWindowFlags(window); + SDL_GetWindowSize(window, &w, &h); + SDL_GetWindowDisplayMode(window, &fullscreen_mode); + + SDL_zero(pparams); + pparams.hDeviceWindow = windowinfo.info.win.window; + pparams.BackBufferWidth = w; + pparams.BackBufferHeight = h; + pparams.BackBufferCount = 1; + pparams.SwapEffect = D3DSWAPEFFECT_DISCARD; + + if (window_flags & SDL_WINDOW_FULLSCREEN && (window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { + pparams.Windowed = FALSE; + pparams.BackBufferFormat = PixelFormatToD3DFMT(fullscreen_mode.format); + pparams.FullScreen_RefreshRateInHz = fullscreen_mode.refresh_rate; + } else { + pparams.Windowed = TRUE; + pparams.BackBufferFormat = D3DFMT_UNKNOWN; + pparams.FullScreen_RefreshRateInHz = 0; + } + if (flags & SDL_RENDERER_PRESENTVSYNC) { + pparams.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + } else { + pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + } + + /* Get the adapter for the display that the window is on */ + displayIndex = SDL_GetWindowDisplayIndex(window); + data->adapter = SDL_Direct3D9GetAdapterIndex(displayIndex); + + IDirect3D9_GetDeviceCaps(data->d3d, data->adapter, D3DDEVTYPE_HAL, &caps); + + device_flags = D3DCREATE_FPU_PRESERVE; + if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) { + device_flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + } else { + device_flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + } + + if (SDL_GetHintBoolean(SDL_HINT_RENDER_DIRECT3D_THREADSAFE, SDL_FALSE)) { + device_flags |= D3DCREATE_MULTITHREADED; + } + + result = IDirect3D9_CreateDevice(data->d3d, data->adapter, + D3DDEVTYPE_HAL, + pparams.hDeviceWindow, + device_flags, + &pparams, &data->device); + if (FAILED(result)) { + D3D_DestroyRenderer(renderer); + D3D_SetError("CreateDevice()", result); + return NULL; + } + + /* Get presentation parameters to fill info */ + result = IDirect3DDevice9_GetSwapChain(data->device, 0, &chain); + if (FAILED(result)) { + D3D_DestroyRenderer(renderer); + D3D_SetError("GetSwapChain()", result); + return NULL; + } + result = IDirect3DSwapChain9_GetPresentParameters(chain, &pparams); + if (FAILED(result)) { + IDirect3DSwapChain9_Release(chain); + D3D_DestroyRenderer(renderer); + D3D_SetError("GetPresentParameters()", result); + return NULL; + } + IDirect3DSwapChain9_Release(chain); + if (pparams.PresentationInterval == D3DPRESENT_INTERVAL_ONE) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + data->pparams = pparams; + + IDirect3DDevice9_GetDeviceCaps(data->device, &caps); + renderer->info.max_texture_width = caps.MaxTextureWidth; + renderer->info.max_texture_height = caps.MaxTextureHeight; + if (caps.NumSimultaneousRTs >= 2) { + renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE; + } + + if (caps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND) { + data->enableSeparateAlphaBlend = SDL_TRUE; + } + + /* Store the default render target */ + IDirect3DDevice9_GetRenderTarget(data->device, 0, &data->defaultRenderTarget); + data->currentRenderTarget = NULL; + + /* Set up parameters for rendering */ + D3D_InitRenderState(data); + + if (caps.MaxSimultaneousTextures >= 3) { + int i; + for (i = 0; i < SDL_arraysize(data->shaders); ++i) { + result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]); + if (FAILED(result)) { + D3D_SetError("CreatePixelShader()", result); + } + } + if (data->shaders[SHADER_YUV_JPEG] && data->shaders[SHADER_YUV_BT601] && data->shaders[SHADER_YUV_BT709]) { + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; + } + } + + data->drawstate.blend = SDL_BLENDMODE_INVALID; + + return renderer; +} + +SDL_RenderDriver D3D_RenderDriver = { + D3D_CreateRenderer, + { + "direct3d", + (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), + 1, + {SDL_PIXELFORMAT_ARGB8888}, + 0, + 0} +}; #endif /* SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED */ #ifdef __WIN32__ diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 7a370392fa..ea2fd3a5e8 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -56,6 +56,9 @@ extern ISwapChainBackgroundPanelNative * WINRT_GlobalSwapChainBackgroundPanelNat #define SAFE_RELEASE(X) if ((X)) { IUnknown_Release(SDL_static_cast(IUnknown*, X)); X = NULL; } +/* !!! FIXME: vertex buffer bandwidth could be significantly lower; move color to a uniform, only use UV coords + !!! FIXME: when textures are needed, and don't ever pass Z, since it's always zero. */ + /* Vertex shader, common values */ typedef struct { @@ -120,7 +123,8 @@ typedef struct ID3D11RenderTargetView *mainRenderTargetView; ID3D11RenderTargetView *currentOffscreenRenderTargetView; ID3D11InputLayout *inputLayout; - ID3D11Buffer *vertexBuffer; + ID3D11Buffer *vertexBuffers[8]; + size_t vertexBufferSizes[8]; ID3D11VertexShader *vertexShader; ID3D11PixelShader *pixelShaders[NUM_SHADERS]; int blendModesCount; @@ -145,6 +149,14 @@ typedef struct ID3D11PixelShader *currentShader; ID3D11ShaderResourceView *currentShaderResource; ID3D11SamplerState *currentSampler; + SDL_bool cliprectDirty; + SDL_bool currentCliprectEnabled; + SDL_Rect currentCliprect; + SDL_Rect currentViewport; + int currentViewportRotation; + SDL_bool viewportDirty; + Float4X4 identity; + int currentVertexBuffer; } D3D11_RenderData; @@ -175,75 +187,6 @@ static const GUID SDL_IID_ID3D11Debug = { 0x79cf2233, 0x7536, 0x4948, { 0x9d, 0x #endif -/* Direct3D 11.1 renderer implementation */ -static SDL_Renderer *D3D11_CreateRenderer(SDL_Window * window, Uint32 flags); -static void D3D11_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static SDL_bool D3D11_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode); -static int D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D11_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *srcPixels, - int srcPitch); -static int D3D11_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, - const Uint8 *Yplane, int Ypitch, - const Uint8 *Uplane, int Upitch, - const Uint8 *Vplane, int Vpitch); -static int D3D11_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void D3D11_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D11_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); -static int D3D11_UpdateViewport(SDL_Renderer * renderer); -static int D3D11_UpdateClipRect(SDL_Renderer * renderer); -static int D3D11_RenderClear(SDL_Renderer * renderer); -static int D3D11_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int D3D11_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int D3D11_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); -static int D3D11_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip); -static int D3D11_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 format, void * pixels, int pitch); -static void D3D11_RenderPresent(SDL_Renderer * renderer); -static void D3D11_DestroyTexture(SDL_Renderer * renderer, - SDL_Texture * texture); -static void D3D11_DestroyRenderer(SDL_Renderer * renderer); - -/* Direct3D 11.1 Internal Functions */ -static HRESULT D3D11_CreateDeviceResources(SDL_Renderer * renderer); -static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer); -static HRESULT D3D11_UpdateForWindowSizeChange(SDL_Renderer * renderer); -static HRESULT D3D11_HandleDeviceLost(SDL_Renderer * renderer); -static void D3D11_ReleaseMainRenderTargetView(SDL_Renderer * renderer); - -SDL_RenderDriver D3D11_RenderDriver = { - D3D11_CreateRenderer, - { - "direct3d11", - ( - SDL_RENDERER_ACCELERATED | - SDL_RENDERER_PRESENTVSYNC | - SDL_RENDERER_TARGETTEXTURE - ), /* flags. see SDL_RendererFlags */ - 6, /* num_texture_formats */ - { /* texture_formats */ - SDL_PIXELFORMAT_ARGB8888, - SDL_PIXELFORMAT_RGB888, - SDL_PIXELFORMAT_YV12, - SDL_PIXELFORMAT_IYUV, - SDL_PIXELFORMAT_NV12, - SDL_PIXELFORMAT_NV21 - }, - 0, /* max_texture_width: will be filled in later */ - 0 /* max_texture_height: will be filled in later */ - } -}; - Uint32 D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) @@ -276,84 +219,7 @@ SDLPixelFormatToDXGIFormat(Uint32 sdlFormat) } } -SDL_Renderer * -D3D11_CreateRenderer(SDL_Window * window, Uint32 flags) -{ - SDL_Renderer *renderer; - D3D11_RenderData *data; - - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - return NULL; - } - - data = (D3D11_RenderData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - SDL_OutOfMemory(); - return NULL; - } - - renderer->WindowEvent = D3D11_WindowEvent; - renderer->SupportsBlendMode = D3D11_SupportsBlendMode; - renderer->CreateTexture = D3D11_CreateTexture; - renderer->UpdateTexture = D3D11_UpdateTexture; - renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV; - renderer->LockTexture = D3D11_LockTexture; - renderer->UnlockTexture = D3D11_UnlockTexture; - renderer->SetRenderTarget = D3D11_SetRenderTarget; - renderer->UpdateViewport = D3D11_UpdateViewport; - renderer->UpdateClipRect = D3D11_UpdateClipRect; - renderer->RenderClear = D3D11_RenderClear; - renderer->RenderDrawPoints = D3D11_RenderDrawPoints; - renderer->RenderDrawLines = D3D11_RenderDrawLines; - renderer->RenderFillRects = D3D11_RenderFillRects; - renderer->RenderCopy = D3D11_RenderCopy; - renderer->RenderCopyEx = D3D11_RenderCopyEx; - renderer->RenderReadPixels = D3D11_RenderReadPixels; - renderer->RenderPresent = D3D11_RenderPresent; - renderer->DestroyTexture = D3D11_DestroyTexture; - renderer->DestroyRenderer = D3D11_DestroyRenderer; - renderer->info = D3D11_RenderDriver.info; - renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); - renderer->driverdata = data; - -#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP - /* VSync is required in Windows Phone, at least for Win Phone 8.0 and 8.1. - * Failure to use it seems to either result in: - * - * - with the D3D11 debug runtime turned OFF, vsync seemingly gets turned - * off (framerate doesn't get capped), but nothing appears on-screen - * - * - with the D3D11 debug runtime turned ON, vsync gets automatically - * turned back on, and the following gets output to the debug console: - * - * DXGI ERROR: IDXGISwapChain::Present: Interval 0 is not supported, changed to Interval 1. [ UNKNOWN ERROR #1024: ] - */ - renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; -#else - if ((flags & SDL_RENDERER_PRESENTVSYNC)) { - renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; - } -#endif - - /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in - * order to give init functions access to the underlying window handle: - */ - renderer->window = window; - - /* Initialize Direct3D resources */ - if (FAILED(D3D11_CreateDeviceResources(renderer))) { - D3D11_DestroyRenderer(renderer); - return NULL; - } - if (FAILED(D3D11_CreateWindowSizeDependentResources(renderer))) { - D3D11_DestroyRenderer(renderer); - return NULL; - } - - return renderer; -} +static void D3D11_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); static void D3D11_ReleaseAll(SDL_Renderer * renderer) @@ -378,7 +244,9 @@ D3D11_ReleaseAll(SDL_Renderer * renderer) SAFE_RELEASE(data->mainRenderTargetView); SAFE_RELEASE(data->currentOffscreenRenderTargetView); SAFE_RELEASE(data->inputLayout); - SAFE_RELEASE(data->vertexBuffer); + for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) { + SAFE_RELEASE(data->vertexBuffers[i]); + } SAFE_RELEASE(data->vertexShader); for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) { SAFE_RELEASE(data->pixelShaders[i]); @@ -477,7 +345,7 @@ static D3D11_BLEND_OP GetBlendEquation(SDL_BlendOperation operation) } } -static SDL_bool +static ID3D11BlendState * D3D11_CreateBlendState(SDL_Renderer * renderer, SDL_BlendMode blendMode) { D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; @@ -506,21 +374,21 @@ D3D11_CreateBlendState(SDL_Renderer * renderer, SDL_BlendMode blendMode) result = ID3D11Device_CreateBlendState(data->d3dDevice, &blendDesc, &blendState); if (FAILED(result)) { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBlendState"), result); - return SDL_FALSE; + return NULL; } blendModes = (D3D11_BlendMode *)SDL_realloc(data->blendModes, (data->blendModesCount + 1) * sizeof(*blendModes)); if (!blendModes) { SAFE_RELEASE(blendState); SDL_OutOfMemory(); - return SDL_FALSE; + return NULL; } blendModes[data->blendModesCount].blendMode = blendMode; blendModes[data->blendModesCount].blendState = blendState; data->blendModes = blendModes; ++data->blendModesCount; - return SDL_TRUE; + return blendState; } /* Create resources that depend on the device. */ @@ -964,6 +832,45 @@ done: return result; } +static void +D3D11_ReleaseMainRenderTargetView(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; + ID3D11DeviceContext_OMSetRenderTargets(data->d3dContext, 0, NULL, NULL); + SAFE_RELEASE(data->mainRenderTargetView); +} + +static HRESULT D3D11_UpdateForWindowSizeChange(SDL_Renderer * renderer); + + +HRESULT +D3D11_HandleDeviceLost(SDL_Renderer * renderer) +{ + HRESULT result = S_OK; + + D3D11_ReleaseAll(renderer); + + result = D3D11_CreateDeviceResources(renderer); + if (FAILED(result)) { + /* D3D11_CreateDeviceResources will set the SDL error */ + return result; + } + + result = D3D11_UpdateForWindowSizeChange(renderer); + if (FAILED(result)) { + /* D3D11_UpdateForWindowSizeChange will set the SDL error */ + return result; + } + + /* Let the application know that the device has been reset */ + { + SDL_Event event; + event.type = SDL_RENDER_DEVICE_RESET; + SDL_PushEvent(&event); + } + + return S_OK; +} /* Initialize all resources that change when the window's size changes. */ static HRESULT @@ -1066,11 +973,7 @@ D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer) goto done; } - if (D3D11_UpdateViewport(renderer) != 0) { - /* D3D11_UpdateViewport will set the SDL error if it fails. */ - result = E_FAIL; - goto done; - } + data->viewportDirty = SDL_TRUE; done: SAFE_RELEASE(backBuffer); @@ -1084,35 +987,6 @@ D3D11_UpdateForWindowSizeChange(SDL_Renderer * renderer) return D3D11_CreateWindowSizeDependentResources(renderer); } -HRESULT -D3D11_HandleDeviceLost(SDL_Renderer * renderer) -{ - HRESULT result = S_OK; - - D3D11_ReleaseAll(renderer); - - result = D3D11_CreateDeviceResources(renderer); - if (FAILED(result)) { - /* D3D11_CreateDeviceResources will set the SDL error */ - return result; - } - - result = D3D11_UpdateForWindowSizeChange(renderer); - if (FAILED(result)) { - /* D3D11_UpdateForWindowSizeChange will set the SDL error */ - return result; - } - - /* Let the application know that the device has been reset */ - { - SDL_Event event; - event.type = SDL_RENDER_DEVICE_RESET; - SDL_PushEvent(&event); - } - - return S_OK; -} - void D3D11_Trim(SDL_Renderer * renderer) { @@ -1671,39 +1545,345 @@ D3D11_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return 0; } -static void -D3D11_SetModelMatrix(SDL_Renderer *renderer, const Float4X4 *matrix) +static int +D3D11_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; + return 0; /* nothing to do in this backend. */ +} + +static int +D3D11_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) +{ + VertexPositionColor *verts = (VertexPositionColor *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertexPositionColor), 0, &cmd->data.draw.first); + const float r = (float)(cmd->data.draw.r / 255.0f); + const float g = (float)(cmd->data.draw.g / 255.0f); + const float b = (float)(cmd->data.draw.b / 255.0f); + const float a = (float)(cmd->data.draw.a / 255.0f); + size_t i; + + if (!verts) { + return -1; + } + + for (i = 0; i < count; i++) { + verts->pos.x = points[i].x + 0.5f; + verts->pos.y = points[i].y + 0.5f; + verts->pos.z = 0.0f; + verts->tex.x = 0.0f; + verts->tex.y = 0.0f; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + } + + return 0; +} + +static int +D3D11_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) +{ + VertexPositionColor *verts = (VertexPositionColor *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (VertexPositionColor), 0, &cmd->data.draw.first); + const float r = (float)(cmd->data.draw.r / 255.0f); + const float g = (float)(cmd->data.draw.g / 255.0f); + const float b = (float)(cmd->data.draw.b / 255.0f); + const float a = (float)(cmd->data.draw.a / 255.0f); + size_t i; + + if (!verts) { + return -1; + } + + for (i = 0; i < count; i++) { + verts->pos.x = rects[i].x; + verts->pos.y = rects[i].y; + verts->pos.z = 0.0f; + verts->tex.x = 0.0f; + verts->tex.y = 0.0f; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = rects[i].x; + verts->pos.y = rects[i].y + rects[i].h; + verts->pos.z = 0.0f; + verts->tex.x = 0.0f; + verts->tex.y = 0.0f; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = rects[i].x + rects[i].w; + verts->pos.y = rects[i].y; + verts->pos.z = 0.0f; + verts->tex.x = 0.0f; + verts->tex.y = 0.0f; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = rects[i].x + rects[i].w; + verts->pos.y = rects[i].y + rects[i].h; + verts->pos.z = 0.0f; + verts->tex.x = 0.0f; + verts->tex.y = 0.0f; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + } + + return 0; +} + +static int +D3D11_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + VertexPositionColor *verts = (VertexPositionColor *) SDL_AllocateRenderVertices(renderer, 4 * sizeof (VertexPositionColor), 0, &cmd->data.draw.first); + const float r = (float)(cmd->data.draw.r / 255.0f); + const float g = (float)(cmd->data.draw.g / 255.0f); + const float b = (float)(cmd->data.draw.b / 255.0f); + const float a = (float)(cmd->data.draw.a / 255.0f); + const float minu = (float) srcrect->x / texture->w; + const float maxu = (float) (srcrect->x + srcrect->w) / texture->w; + const float minv = (float) srcrect->y / texture->h; + const float maxv = (float) (srcrect->y + srcrect->h) / texture->h; + + if (!verts) { + return -1; + } + + verts->pos.x = dstrect->x; + verts->pos.y = dstrect->y; + verts->pos.z = 0.0f; + verts->tex.x = minu; + verts->tex.y = minv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = dstrect->x; + verts->pos.y = dstrect->y + dstrect->h; + verts->pos.z = 0.0f; + verts->tex.x = minu; + verts->tex.y = maxv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = dstrect->x + dstrect->w; + verts->pos.y = dstrect->y; + verts->pos.z = 0.0f; + verts->tex.x = maxu; + verts->tex.y = minv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = dstrect->x + dstrect->w; + verts->pos.y = dstrect->y + dstrect->h; + verts->pos.z = 0.0f; + verts->tex.x = maxu; + verts->tex.y = maxv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + return 0; +} + +static int +D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +{ + VertexPositionColor *verts = (VertexPositionColor *) SDL_AllocateRenderVertices(renderer, 5 * sizeof (VertexPositionColor), 0, &cmd->data.draw.first); + const float r = (float)(cmd->data.draw.r / 255.0f); + const float g = (float)(cmd->data.draw.g / 255.0f); + const float b = (float)(cmd->data.draw.b / 255.0f); + const float a = (float)(cmd->data.draw.a / 255.0f); + float minx, miny, maxx, maxy; + float minu, maxu, minv, maxv; - if (matrix) { - data->vertexShaderConstantsData.model = *matrix; + if (flip & SDL_FLIP_HORIZONTAL) { + minu = (float) srcrect->x / texture->w; + maxu = (float) (srcrect->x + srcrect->w) / texture->w; } else { - data->vertexShaderConstantsData.model = MatrixIdentity(); + minu = (float) (srcrect->x + srcrect->w) / texture->w; + maxu = (float) srcrect->x / texture->w; } - ID3D11DeviceContext_UpdateSubresource(data->d3dContext, - (ID3D11Resource *)data->vertexShaderConstants, - 0, - NULL, - &data->vertexShaderConstantsData, - 0, - 0 - ); + if (flip & SDL_FLIP_VERTICAL) { + minv = (float) srcrect->y / texture->h; + maxv = (float) (srcrect->y + srcrect->h) / texture->h; + } else { + minv = (float) (srcrect->y + srcrect->h) / texture->h; + maxv = (float) srcrect->y / texture->h; + } + + minx = -center->x; + maxx = dstrect->w - center->x; + miny = -center->y; + maxy = dstrect->h - center->y; + + verts->pos.x = minx; + verts->pos.y = miny; + verts->pos.z = 0.0f; + verts->tex.x = minu; + verts->tex.y = minv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = minx; + verts->pos.y = maxy; + verts->pos.z = 0.0f; + verts->tex.x = minu; + verts->tex.y = maxv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = maxx; + verts->pos.y = miny; + verts->pos.z = 0.0f; + verts->tex.x = maxu; + verts->tex.y = minv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = maxx; + verts->pos.y = maxy; + verts->pos.z = 0.0f; + verts->tex.x = maxu; + verts->tex.y = maxv; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; + verts++; + + verts->pos.x = dstrect->x + center->x; /* X translation */ + verts->pos.y = dstrect->y + center->y; /* Y translation */ + verts->pos.z = (float)(M_PI * (float) angle / 180.0f); /* rotation */ + verts->tex.x = 0.0f; + verts->tex.y = 0.0f; + verts->color.x = 0; + verts->color.y = 0; + verts->color.z = 0; + verts->color.w = 0; + verts++; + + return 0; +} + + +static int +D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, + const void * vertexData, size_t dataSizeInBytes) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + HRESULT result = S_OK; + const int vbidx = rendererData->currentVertexBuffer; + + if (rendererData->vertexBuffers[vbidx] && rendererData->vertexBufferSizes[vbidx] >= dataSizeInBytes) { + D3D11_MAPPED_SUBRESOURCE mappedResource; + result = ID3D11DeviceContext_Map(rendererData->d3dContext, + (ID3D11Resource *)rendererData->vertexBuffers[vbidx], + 0, + D3D11_MAP_WRITE_DISCARD, + 0, + &mappedResource + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [vertex buffer]"), result); + return -1; + } + SDL_memcpy(mappedResource.pData, vertexData, dataSizeInBytes); + ID3D11DeviceContext_Unmap(rendererData->d3dContext, (ID3D11Resource *)rendererData->vertexBuffers[vbidx], 0); + } else { + D3D11_BUFFER_DESC vertexBufferDesc; + D3D11_SUBRESOURCE_DATA vertexBufferData; + const UINT stride = sizeof(VertexPositionColor); + const UINT offset = 0; + + SAFE_RELEASE(rendererData->vertexBuffers[vbidx]); + + SDL_zero(vertexBufferDesc); + vertexBufferDesc.ByteWidth = (UINT) dataSizeInBytes; + vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC; + vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + SDL_zero(vertexBufferData); + vertexBufferData.pSysMem = vertexData; + vertexBufferData.SysMemPitch = 0; + vertexBufferData.SysMemSlicePitch = 0; + + result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, + &vertexBufferDesc, + &vertexBufferData, + &rendererData->vertexBuffers[vbidx] + ); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBuffer [vertex buffer]"), result); + return -1; + } + + ID3D11DeviceContext_IASetVertexBuffers(rendererData->d3dContext, + 0, + 1, + &rendererData->vertexBuffers[vbidx], + &stride, + &offset + ); + } + + rendererData->currentVertexBuffer++; + if (rendererData->currentVertexBuffer >= SDL_arraysize(rendererData->vertexBuffers)) { + rendererData->currentVertexBuffer = 0; + } + + return 0; } static int D3D11_UpdateViewport(SDL_Renderer * renderer) { D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + const SDL_Rect *viewport = &data->currentViewport; Float4X4 projection; Float4X4 view; SDL_FRect orientationAlignedViewport; BOOL swapDimensions; - D3D11_VIEWPORT viewport; + D3D11_VIEWPORT d3dviewport; const int rotation = D3D11_GetRotationForCurrentRenderTarget(renderer); - if (renderer->viewport.w == 0 || renderer->viewport.h == 0) { + if (viewport->w == 0 || viewport->h == 0) { /* If the viewport is empty, assume that it is because * SDL_CreateRenderer is calling it, and will call it again later * with a non-empty viewport. @@ -1735,21 +1915,12 @@ D3D11_UpdateViewport(SDL_Renderer * renderer) } /* Update the view matrix */ - view.m[0][0] = 2.0f / renderer->viewport.w; - view.m[0][1] = 0.0f; - view.m[0][2] = 0.0f; - view.m[0][3] = 0.0f; - view.m[1][0] = 0.0f; - view.m[1][1] = -2.0f / renderer->viewport.h; - view.m[1][2] = 0.0f; - view.m[1][3] = 0.0f; - view.m[2][0] = 0.0f; - view.m[2][1] = 0.0f; + SDL_zero(view); + view.m[0][0] = 2.0f / viewport->w; + view.m[1][1] = -2.0f / viewport->h; view.m[2][2] = 1.0f; - view.m[2][3] = 0.0f; view.m[3][0] = -1.0f; view.m[3][1] = 1.0f; - view.m[3][2] = 0.0f; view.m[3][3] = 1.0f; /* Combine the projection + view matrix together now, as both only get @@ -1760,9 +1931,6 @@ D3D11_UpdateViewport(SDL_Renderer * renderer) view, projection); - /* Reset the model matrix */ - D3D11_SetModelMatrix(renderer, NULL); - /* Update the Direct3D viewport, which seems to be aligned to the * swap buffer's coordinate space, which is always in either * a landscape mode, for all Windows 8/RT devices, or a portrait mode, @@ -1770,158 +1938,58 @@ D3D11_UpdateViewport(SDL_Renderer * renderer) */ swapDimensions = D3D11_IsDisplayRotated90Degrees(rotation); if (swapDimensions) { - orientationAlignedViewport.x = (float) renderer->viewport.y; - orientationAlignedViewport.y = (float) renderer->viewport.x; - orientationAlignedViewport.w = (float) renderer->viewport.h; - orientationAlignedViewport.h = (float) renderer->viewport.w; + orientationAlignedViewport.x = (float) viewport->y; + orientationAlignedViewport.y = (float) viewport->x; + orientationAlignedViewport.w = (float) viewport->h; + orientationAlignedViewport.h = (float) viewport->w; } else { - orientationAlignedViewport.x = (float) renderer->viewport.x; - orientationAlignedViewport.y = (float) renderer->viewport.y; - orientationAlignedViewport.w = (float) renderer->viewport.w; - orientationAlignedViewport.h = (float) renderer->viewport.h; + orientationAlignedViewport.x = (float) viewport->x; + orientationAlignedViewport.y = (float) viewport->y; + orientationAlignedViewport.w = (float) viewport->w; + orientationAlignedViewport.h = (float) viewport->h; } /* TODO, WinRT: get custom viewports working with non-Landscape modes (Portrait, PortraitFlipped, and LandscapeFlipped) */ - viewport.TopLeftX = orientationAlignedViewport.x; - viewport.TopLeftY = orientationAlignedViewport.y; - viewport.Width = orientationAlignedViewport.w; - viewport.Height = orientationAlignedViewport.h; - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 1.0f; - /* SDL_Log("%s: D3D viewport = {%f,%f,%f,%f}\n", __FUNCTION__, viewport.TopLeftX, viewport.TopLeftY, viewport.Width, viewport.Height); */ - ID3D11DeviceContext_RSSetViewports(data->d3dContext, 1, &viewport); + d3dviewport.TopLeftX = orientationAlignedViewport.x; + d3dviewport.TopLeftY = orientationAlignedViewport.y; + d3dviewport.Width = orientationAlignedViewport.w; + d3dviewport.Height = orientationAlignedViewport.h; + d3dviewport.MinDepth = 0.0f; + d3dviewport.MaxDepth = 1.0f; + /* SDL_Log("%s: D3D viewport = {%f,%f,%f,%f}\n", __FUNCTION__, d3dviewport.TopLeftX, d3dviewport.TopLeftY, d3dviewport.Width, d3dviewport.Height); */ + ID3D11DeviceContext_RSSetViewports(data->d3dContext, 1, &d3dviewport); - return 0; -} - -static int -D3D11_UpdateClipRect(SDL_Renderer * renderer) -{ - D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; - - if (!renderer->clipping_enabled) { - ID3D11DeviceContext_RSSetScissorRects(data->d3dContext, 0, NULL); - } else { - D3D11_RECT scissorRect; - if (D3D11_GetViewportAlignedD3DRect(renderer, &renderer->clip_rect, &scissorRect, TRUE) != 0) { - /* D3D11_GetViewportAlignedD3DRect will have set the SDL error */ - return -1; - } - ID3D11DeviceContext_RSSetScissorRects(data->d3dContext, 1, &scissorRect); - } + data->viewportDirty = SDL_FALSE; return 0; } -static void -D3D11_ReleaseMainRenderTargetView(SDL_Renderer * renderer) -{ - D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; - ID3D11DeviceContext_OMSetRenderTargets(data->d3dContext, 0, NULL, NULL); - SAFE_RELEASE(data->mainRenderTargetView); -} - static ID3D11RenderTargetView * D3D11_GetCurrentRenderTargetView(SDL_Renderer * renderer) { - D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; if (data->currentOffscreenRenderTargetView) { return data->currentOffscreenRenderTargetView; - } else { + } + else { return data->mainRenderTargetView; } } static int -D3D11_RenderClear(SDL_Renderer * renderer) -{ - D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; - const float colorRGBA[] = { - (renderer->r / 255.0f), - (renderer->g / 255.0f), - (renderer->b / 255.0f), - (renderer->a / 255.0f) - }; - ID3D11DeviceContext_ClearRenderTargetView(data->d3dContext, - D3D11_GetCurrentRenderTargetView(renderer), - colorRGBA - ); - return 0; -} - -static int -D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, - const void * vertexData, size_t dataSizeInBytes) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - D3D11_BUFFER_DESC vertexBufferDesc; - HRESULT result = S_OK; - D3D11_SUBRESOURCE_DATA vertexBufferData; - const UINT stride = sizeof(VertexPositionColor); - const UINT offset = 0; - - if (rendererData->vertexBuffer) { - ID3D11Buffer_GetDesc(rendererData->vertexBuffer, &vertexBufferDesc); - } else { - SDL_zero(vertexBufferDesc); - } - - if (rendererData->vertexBuffer && vertexBufferDesc.ByteWidth >= dataSizeInBytes) { - D3D11_MAPPED_SUBRESOURCE mappedResource; - result = ID3D11DeviceContext_Map(rendererData->d3dContext, - (ID3D11Resource *)rendererData->vertexBuffer, - 0, - D3D11_MAP_WRITE_DISCARD, - 0, - &mappedResource - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11DeviceContext1::Map [vertex buffer]"), result); - return -1; - } - SDL_memcpy(mappedResource.pData, vertexData, dataSizeInBytes); - ID3D11DeviceContext_Unmap(rendererData->d3dContext, (ID3D11Resource *)rendererData->vertexBuffer, 0); - } else { - SAFE_RELEASE(rendererData->vertexBuffer); - - vertexBufferDesc.ByteWidth = (UINT) dataSizeInBytes; - vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC; - vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; - vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - - SDL_zero(vertexBufferData); - vertexBufferData.pSysMem = vertexData; - vertexBufferData.SysMemPitch = 0; - vertexBufferData.SysMemSlicePitch = 0; - - result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, - &vertexBufferDesc, - &vertexBufferData, - &rendererData->vertexBuffer - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBuffer [vertex buffer]"), result); - return -1; - } - - ID3D11DeviceContext_IASetVertexBuffers(rendererData->d3dContext, - 0, - 1, - &rendererData->vertexBuffer, - &stride, - &offset - ); - } +D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11PixelShader * shader, + const int numShaderResources, ID3D11ShaderResourceView ** shaderResources, + ID3D11SamplerState * sampler, const Float4X4 *matrix) - return 0; -} - -static void -D3D11_RenderStartDrawOp(SDL_Renderer * renderer) { D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata; + const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity; ID3D11RasterizerState *rasterizerState; ID3D11RenderTargetView *renderTargetView = D3D11_GetCurrentRenderTargetView(renderer); + ID3D11ShaderResourceView *shaderResource; + const SDL_BlendMode blendMode = cmd->data.draw.blend; + ID3D11BlendState *blendState = NULL; + if (renderTargetView != rendererData->currentRenderTargetView) { ID3D11DeviceContext_OMSetRenderTargets(rendererData->d3dContext, 1, @@ -1931,7 +1999,25 @@ D3D11_RenderStartDrawOp(SDL_Renderer * renderer) rendererData->currentRenderTargetView = renderTargetView; } - if (!renderer->clipping_enabled) { + if (rendererData->viewportDirty) { + D3D11_UpdateViewport(renderer); + } + + if (rendererData->cliprectDirty) { + if (!rendererData->currentCliprectEnabled) { + ID3D11DeviceContext_RSSetScissorRects(rendererData->d3dContext, 0, NULL); + } else { + D3D11_RECT scissorRect; + if (D3D11_GetViewportAlignedD3DRect(renderer, &rendererData->currentCliprect, &scissorRect, TRUE) != 0) { + /* D3D11_GetViewportAlignedD3DRect will have set the SDL error */ + return -1; + } + ID3D11DeviceContext_RSSetScissorRects(rendererData->d3dContext, 1, &scissorRect); + } + rendererData->cliprectDirty = SDL_FALSE; + } + + if (!rendererData->currentCliprectEnabled) { rasterizerState = rendererData->mainRasterizer; } else { rasterizerState = rendererData->clippedRasterizer; @@ -1940,13 +2026,7 @@ D3D11_RenderStartDrawOp(SDL_Renderer * renderer) ID3D11DeviceContext_RSSetState(rendererData->d3dContext, rasterizerState); rendererData->currentRasterizerState = rasterizerState; } -} -static void -D3D11_RenderSetBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata; - ID3D11BlendState *blendState = NULL; if (blendMode != SDL_BLENDMODE_NONE) { int i; for (i = 0; i < rendererData->blendModesCount; ++i) { @@ -1956,28 +2036,17 @@ D3D11_RenderSetBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) } } if (!blendState) { - if (D3D11_CreateBlendState(renderer, blendMode)) { - /* Successfully created the blend state, try again */ - D3D11_RenderSetBlendMode(renderer, blendMode); + blendState = D3D11_CreateBlendState(renderer, blendMode); + if (!blendState) { + return -1; } - return; } } if (blendState != rendererData->currentBlendState) { ID3D11DeviceContext_OMSetBlendState(rendererData->d3dContext, blendState, 0, 0xFFFFFFFF); rendererData->currentBlendState = blendState; } -} -static void -D3D11_SetPixelShader(SDL_Renderer * renderer, - ID3D11PixelShader * shader, - int numShaderResources, - ID3D11ShaderResourceView ** shaderResources, - ID3D11SamplerState * sampler) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - ID3D11ShaderResourceView *shaderResource; if (shader != rendererData->currentShader) { ID3D11DeviceContext_PSSetShader(rendererData->d3dContext, shader, NULL, 0); rendererData->currentShader = shader; @@ -1995,146 +2064,26 @@ D3D11_SetPixelShader(SDL_Renderer * renderer, ID3D11DeviceContext_PSSetSamplers(rendererData->d3dContext, 0, 1, &sampler); rendererData->currentSampler = sampler; } -} - -static void -D3D11_RenderFinishDrawOp(SDL_Renderer * renderer, - D3D11_PRIMITIVE_TOPOLOGY primitiveTopology, - UINT vertexCount) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - - ID3D11DeviceContext_IASetPrimitiveTopology(rendererData->d3dContext, primitiveTopology); - ID3D11DeviceContext_Draw(rendererData->d3dContext, vertexCount, 0); -} - -static int -D3D11_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - float r, g, b, a; - VertexPositionColor *vertices; - int i; - - r = (float)(renderer->r / 255.0f); - g = (float)(renderer->g / 255.0f); - b = (float)(renderer->b / 255.0f); - a = (float)(renderer->a / 255.0f); - - vertices = SDL_stack_alloc(VertexPositionColor, count); - for (i = 0; i < count; ++i) { - const VertexPositionColor v = { { points[i].x + 0.5f, points[i].y + 0.5f, 0.0f }, { 0.0f, 0.0f }, { r, g, b, a } }; - vertices[i] = v; - } - D3D11_RenderStartDrawOp(renderer); - D3D11_RenderSetBlendMode(renderer, renderer->blendMode); - if (D3D11_UpdateVertexBuffer(renderer, vertices, (unsigned int)count * sizeof(VertexPositionColor)) != 0) { - SDL_stack_free(vertices); - return -1; - } - - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[SHADER_SOLID], - 0, - NULL, - NULL); - - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, count); - SDL_stack_free(vertices); - return 0; -} - -static int -D3D11_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - float r, g, b, a; - VertexPositionColor *vertices; - int i; - - r = (float)(renderer->r / 255.0f); - g = (float)(renderer->g / 255.0f); - b = (float)(renderer->b / 255.0f); - a = (float)(renderer->a / 255.0f); - - vertices = SDL_stack_alloc(VertexPositionColor, count); - for (i = 0; i < count; ++i) { - const VertexPositionColor v = { { points[i].x + 0.5f, points[i].y + 0.5f, 0.0f }, { 0.0f, 0.0f }, { r, g, b, a } }; - vertices[i] = v; - } - - D3D11_RenderStartDrawOp(renderer); - D3D11_RenderSetBlendMode(renderer, renderer->blendMode); - if (D3D11_UpdateVertexBuffer(renderer, vertices, (unsigned int)count * sizeof(VertexPositionColor)) != 0) { - SDL_stack_free(vertices); - return -1; - } - - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[SHADER_SOLID], - 0, - NULL, - NULL); - - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, count); - - if (points[0].x != points[count - 1].x || points[0].y != points[count - 1].y) { - ID3D11DeviceContext_IASetPrimitiveTopology(rendererData->d3dContext, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); - ID3D11DeviceContext_Draw(rendererData->d3dContext, 1, count - 1); - } - - SDL_stack_free(vertices); - return 0; -} - -static int -D3D11_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - float r, g, b, a; - int i; - - r = (float)(renderer->r / 255.0f); - g = (float)(renderer->g / 255.0f); - b = (float)(renderer->b / 255.0f); - a = (float)(renderer->a / 255.0f); - - for (i = 0; i < count; ++i) { - VertexPositionColor vertices[] = { - { { rects[i].x, rects[i].y, 0.0f }, { 0.0f, 0.0f}, {r, g, b, a} }, - { { rects[i].x, rects[i].y + rects[i].h, 0.0f }, { 0.0f, 0.0f }, { r, g, b, a } }, - { { rects[i].x + rects[i].w, rects[i].y, 0.0f }, { 0.0f, 0.0f }, { r, g, b, a } }, - { { rects[i].x + rects[i].w, rects[i].y + rects[i].h, 0.0f }, { 0.0f, 0.0f }, { r, g, b, a } }, - }; - - D3D11_RenderStartDrawOp(renderer); - D3D11_RenderSetBlendMode(renderer, renderer->blendMode); - if (D3D11_UpdateVertexBuffer(renderer, vertices, sizeof(vertices)) != 0) { - return -1; - } - - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[SHADER_SOLID], + if (SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof (*newmatrix)) != 0) { + SDL_memcpy(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof (*newmatrix)); + ID3D11DeviceContext_UpdateSubresource(rendererData->d3dContext, + (ID3D11Resource *)rendererData->vertexShaderConstants, 0, NULL, - NULL); - - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, SDL_arraysize(vertices)); + &rendererData->vertexShaderConstantsData, + 0, + 0 + ); } return 0; } static int -D3D11_RenderSetupSampler(SDL_Renderer * renderer, SDL_Texture * texture) +D3D11_SetCopyState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, const Float4X4 *matrix) { + SDL_Texture *texture = cmd->data.draw.texture; D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; ID3D11SamplerState *textureSampler; @@ -2172,12 +2121,8 @@ D3D11_RenderSetupSampler(SDL_Renderer * renderer, SDL_Texture * texture) return SDL_SetError("Unsupported YUV conversion mode"); } - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[shader], - SDL_arraysize(shaderResources), - shaderResources, - textureSampler); + return D3D11_SetDrawState(renderer, cmd, rendererData->pixelShaders[shader], + SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix); } else if (textureData->nv12) { ID3D11ShaderResourceView *shaderResources[] = { @@ -2200,189 +2145,141 @@ D3D11_RenderSetupSampler(SDL_Renderer * renderer, SDL_Texture * texture) return SDL_SetError("Unsupported YUV conversion mode"); } - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[shader], - SDL_arraysize(shaderResources), - shaderResources, - textureSampler); + return D3D11_SetDrawState(renderer, cmd, rendererData->pixelShaders[shader], + SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix); - } else { - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[SHADER_RGB], - 1, - &textureData->mainTextureResourceView, - textureSampler); } - return 0; + return D3D11_SetDrawState(renderer, cmd, rendererData->pixelShaders[SHADER_RGB], + 1, &textureData->mainTextureResourceView, textureSampler, matrix); +} + +static void +D3D11_DrawPrimitives(SDL_Renderer * renderer, D3D11_PRIMITIVE_TOPOLOGY primitiveTopology, const size_t vertexStart, const size_t vertexCount) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + ID3D11DeviceContext_IASetPrimitiveTopology(rendererData->d3dContext, primitiveTopology); + ID3D11DeviceContext_Draw(rendererData->d3dContext, (UINT) vertexCount, (UINT) vertexStart); } static int -D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) +D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { - float minu, maxu, minv, maxv; - Float4 color; - VertexPositionColor vertices[4]; - - D3D11_RenderStartDrawOp(renderer); - D3D11_RenderSetBlendMode(renderer, texture->blendMode); - - minu = (float) srcrect->x / texture->w; - maxu = (float) (srcrect->x + srcrect->w) / texture->w; - minv = (float) srcrect->y / texture->h; - maxv = (float) (srcrect->y + srcrect->h) / texture->h; - - color.x = 1.0f; /* red */ - color.y = 1.0f; /* green */ - color.z = 1.0f; /* blue */ - color.w = 1.0f; /* alpha */ - if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) { - color.x = (float)(texture->r / 255.0f); /* red */ - color.y = (float)(texture->g / 255.0f); /* green */ - color.z = (float)(texture->b / 255.0f); /* blue */ - } - if (texture->modMode & SDL_TEXTUREMODULATE_ALPHA) { - color.w = (float)(texture->a / 255.0f); /* alpha */ - } - - vertices[0].pos.x = dstrect->x; - vertices[0].pos.y = dstrect->y; - vertices[0].pos.z = 0.0f; - vertices[0].tex.x = minu; - vertices[0].tex.y = minv; - vertices[0].color = color; - - vertices[1].pos.x = dstrect->x; - vertices[1].pos.y = dstrect->y + dstrect->h; - vertices[1].pos.z = 0.0f; - vertices[1].tex.x = minu; - vertices[1].tex.y = maxv; - vertices[1].color = color; - - vertices[2].pos.x = dstrect->x + dstrect->w; - vertices[2].pos.y = dstrect->y; - vertices[2].pos.z = 0.0f; - vertices[2].tex.x = maxu; - vertices[2].tex.y = minv; - vertices[2].color = color; - - vertices[3].pos.x = dstrect->x + dstrect->w; - vertices[3].pos.y = dstrect->y + dstrect->h; - vertices[3].pos.z = 0.0f; - vertices[3].tex.x = maxu; - vertices[3].tex.y = maxv; - vertices[3].color = color; - - if (D3D11_UpdateVertexBuffer(renderer, vertices, sizeof(vertices)) != 0) { - return -1; + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + const int viewportRotation = D3D11_GetRotationForCurrentRenderTarget(renderer); + size_t i; + + if (rendererData->currentViewportRotation != viewportRotation) { + rendererData->currentViewportRotation = viewportRotation; + rendererData->viewportDirty = SDL_TRUE; } - if (D3D11_RenderSetupSampler(renderer, texture) < 0) { + if (D3D11_UpdateVertexBuffer(renderer, vertices, vertsize) < 0) { return -1; } - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + break; /* this isn't currently used in this render backend. */ + } - return 0; -} + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_Rect *viewport = &rendererData->currentViewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); + rendererData->viewportDirty = SDL_TRUE; + } + break; + } -static int -D3D11_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip) -{ - float minu, maxu, minv, maxv; - Float4 color; - Float4X4 modelMatrix; - float minx, maxx, miny, maxy; - VertexPositionColor vertices[4]; + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) { + rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled; + rendererData->cliprectDirty = SDL_TRUE; + } + if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&rendererData->currentCliprect, rect, sizeof (SDL_Rect)); + rendererData->cliprectDirty = SDL_TRUE; + } + break; + } - D3D11_RenderStartDrawOp(renderer); - D3D11_RenderSetBlendMode(renderer, texture->blendMode); + case SDL_RENDERCMD_CLEAR: { + const float colorRGBA[] = { + (cmd->data.color.r / 255.0f), + (cmd->data.color.g / 255.0f), + (cmd->data.color.b / 255.0f), + (cmd->data.color.a / 255.0f) + }; + ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), colorRGBA); + break; + } - minu = (float) srcrect->x / texture->w; - maxu = (float) (srcrect->x + srcrect->w) / texture->w; - minv = (float) srcrect->y / texture->h; - maxv = (float) (srcrect->y + srcrect->h) / texture->h; + case SDL_RENDERCMD_DRAW_POINTS: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof (VertexPositionColor); + D3D11_SetDrawState(renderer, cmd, rendererData->pixelShaders[SHADER_SOLID], 0, NULL, NULL, NULL); + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, start, count); + break; + } - color.x = 1.0f; /* red */ - color.y = 1.0f; /* green */ - color.z = 1.0f; /* blue */ - color.w = 1.0f; /* alpha */ - if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) { - color.x = (float)(texture->r / 255.0f); /* red */ - color.y = (float)(texture->g / 255.0f); /* green */ - color.z = (float)(texture->b / 255.0f); /* blue */ - } - if (texture->modMode & SDL_TEXTUREMODULATE_ALPHA) { - color.w = (float)(texture->a / 255.0f); /* alpha */ - } + case SDL_RENDERCMD_DRAW_LINES: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof (VertexPositionColor); + const VertexPositionColor *verts = (VertexPositionColor *) (((Uint8 *) vertices) + first); + D3D11_SetDrawState(renderer, cmd, rendererData->pixelShaders[SHADER_SOLID], 0, NULL, NULL, NULL); + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, start, count); + if (verts[0].pos.x != verts[count - 1].pos.x || verts[0].pos.y != verts[count - 1].pos.y) { + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, start + (count-1), 1); + } + break; + } - if (flip & SDL_FLIP_HORIZONTAL) { - float tmp = maxu; - maxu = minu; - minu = tmp; - } - if (flip & SDL_FLIP_VERTICAL) { - float tmp = maxv; - maxv = minv; - minv = tmp; - } + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof (VertexPositionColor); + size_t offset = 0; + D3D11_SetDrawState(renderer, cmd, rendererData->pixelShaders[SHADER_SOLID], 0, NULL, NULL, NULL); + for (i = 0; i < count; i++, offset += 4) { + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start + offset, 4); + } + break; + } - modelMatrix = MatrixMultiply( - MatrixRotationZ((float)(M_PI * (float) angle / 180.0f)), - MatrixTranslation(dstrect->x + center->x, dstrect->y + center->y, 0) - ); - D3D11_SetModelMatrix(renderer, &modelMatrix); + case SDL_RENDERCMD_COPY: { + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof (VertexPositionColor); + D3D11_SetCopyState(renderer, cmd, NULL); + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start, 4); + break; + } - minx = -center->x; - maxx = dstrect->w - center->x; - miny = -center->y; - maxy = dstrect->h - center->y; + case SDL_RENDERCMD_COPY_EX: { + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof (VertexPositionColor); + const VertexPositionColor *verts = (VertexPositionColor *) (((Uint8 *) vertices) + first); + const VertexPositionColor *transvert = verts + 4; + const float translatex = transvert->pos.x; + const float translatey = transvert->pos.y; + const float rotation = transvert->pos.z; + const Float4X4 matrix = MatrixMultiply(MatrixRotationZ(rotation), MatrixTranslation(translatex, translatey, 0)); + D3D11_SetCopyState(renderer, cmd, &matrix); + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start, 4); + break; + } - vertices[0].pos.x = minx; - vertices[0].pos.y = miny; - vertices[0].pos.z = 0.0f; - vertices[0].tex.x = minu; - vertices[0].tex.y = minv; - vertices[0].color = color; - - vertices[1].pos.x = minx; - vertices[1].pos.y = maxy; - vertices[1].pos.z = 0.0f; - vertices[1].tex.x = minu; - vertices[1].tex.y = maxv; - vertices[1].color = color; - - vertices[2].pos.x = maxx; - vertices[2].pos.y = miny; - vertices[2].pos.z = 0.0f; - vertices[2].tex.x = maxu; - vertices[2].tex.y = minv; - vertices[2].color = color; - - vertices[3].pos.x = maxx; - vertices[3].pos.y = maxy; - vertices[3].pos.z = 0.0f; - vertices[3].tex.x = maxu; - vertices[3].tex.y = maxv; - vertices[3].color = color; - - if (D3D11_UpdateVertexBuffer(renderer, vertices, sizeof(vertices)) != 0) { - return -1; - } + case SDL_RENDERCMD_NO_OP: + break; + } - if (D3D11_RenderSetupSampler(renderer, texture) < 0) { - return -1; + cmd = cmd->next; } - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); - - D3D11_SetModelMatrix(renderer, NULL); - return 0; } @@ -2550,6 +2447,110 @@ D3D11_RenderPresent(SDL_Renderer * renderer) } } +SDL_Renderer * +D3D11_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Renderer *renderer; + D3D11_RenderData *data; + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + data = (D3D11_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + SDL_OutOfMemory(); + return NULL; + } + + data->identity = MatrixIdentity(); + + renderer->WindowEvent = D3D11_WindowEvent; + renderer->SupportsBlendMode = D3D11_SupportsBlendMode; + renderer->CreateTexture = D3D11_CreateTexture; + renderer->UpdateTexture = D3D11_UpdateTexture; + renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV; + renderer->LockTexture = D3D11_LockTexture; + renderer->UnlockTexture = D3D11_UnlockTexture; + renderer->SetRenderTarget = D3D11_SetRenderTarget; + renderer->QueueSetViewport = D3D11_QueueSetViewport; + renderer->QueueSetDrawColor = D3D11_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = D3D11_QueueDrawPoints; + renderer->QueueDrawLines = D3D11_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = D3D11_QueueFillRects; + renderer->QueueCopy = D3D11_QueueCopy; + renderer->QueueCopyEx = D3D11_QueueCopyEx; + renderer->RunCommandQueue = D3D11_RunCommandQueue; + renderer->RenderReadPixels = D3D11_RenderReadPixels; + renderer->RenderPresent = D3D11_RenderPresent; + renderer->DestroyTexture = D3D11_DestroyTexture; + renderer->DestroyRenderer = D3D11_DestroyRenderer; + renderer->info = D3D11_RenderDriver.info; + renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + renderer->driverdata = data; + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + /* VSync is required in Windows Phone, at least for Win Phone 8.0 and 8.1. + * Failure to use it seems to either result in: + * + * - with the D3D11 debug runtime turned OFF, vsync seemingly gets turned + * off (framerate doesn't get capped), but nothing appears on-screen + * + * - with the D3D11 debug runtime turned ON, vsync gets automatically + * turned back on, and the following gets output to the debug console: + * + * DXGI ERROR: IDXGISwapChain::Present: Interval 0 is not supported, changed to Interval 1. [ UNKNOWN ERROR #1024: ] + */ + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; +#else + if ((flags & SDL_RENDERER_PRESENTVSYNC)) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } +#endif + + /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in + * order to give init functions access to the underlying window handle: + */ + renderer->window = window; + + /* Initialize Direct3D resources */ + if (FAILED(D3D11_CreateDeviceResources(renderer))) { + D3D11_DestroyRenderer(renderer); + return NULL; + } + if (FAILED(D3D11_CreateWindowSizeDependentResources(renderer))) { + D3D11_DestroyRenderer(renderer); + return NULL; + } + + return renderer; +} + +SDL_RenderDriver D3D11_RenderDriver = { + D3D11_CreateRenderer, + { + "direct3d11", + ( + SDL_RENDERER_ACCELERATED | + SDL_RENDERER_PRESENTVSYNC | + SDL_RENDERER_TARGETTEXTURE + ), /* flags. see SDL_RendererFlags */ + 6, /* num_texture_formats */ + { /* texture_formats */ + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_RGB888, + SDL_PIXELFORMAT_YV12, + SDL_PIXELFORMAT_IYUV, + SDL_PIXELFORMAT_NV12, + SDL_PIXELFORMAT_NV21 + }, + 0, /* max_texture_width: will be filled in later */ + 0 /* max_texture_height: will be filled in later */ + } +}; + #endif /* SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 5b4d8ea27c..d8f5b4328f 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -46,64 +46,6 @@ /* Apple Metal renderer implementation */ -static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags); -static void METAL_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static int METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h); -static SDL_bool METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode); -static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *pixels, - int pitch); -static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, - const Uint8 *Yplane, int Ypitch, - const Uint8 *Uplane, int Upitch, - const Uint8 *Vplane, int Vpitch); -static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); -static int METAL_UpdateViewport(SDL_Renderer * renderer); -static int METAL_UpdateClipRect(SDL_Renderer * renderer); -static int METAL_RenderClear(SDL_Renderer * renderer); -static int METAL_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int METAL_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int METAL_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); -static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); -static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch); -static void METAL_RenderPresent(SDL_Renderer * renderer); -static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static void METAL_DestroyRenderer(SDL_Renderer * renderer); -static void *METAL_GetMetalLayer(SDL_Renderer * renderer); -static void *METAL_GetMetalCommandEncoder(SDL_Renderer * renderer); - -SDL_RenderDriver METAL_RenderDriver = { - METAL_CreateRenderer, - { - "metal", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), - 6, - { - SDL_PIXELFORMAT_ARGB8888, - SDL_PIXELFORMAT_ABGR8888, - SDL_PIXELFORMAT_YV12, - SDL_PIXELFORMAT_IYUV, - SDL_PIXELFORMAT_NV12, - SDL_PIXELFORMAT_NV21 - }, - 0, 0, - } -}; - /* macOS requires constants in a buffer to have a 256 byte alignment. */ #ifdef __MACOSX__ #define CONSTANT_ALIGN 256 @@ -113,6 +55,7 @@ SDL_RenderDriver METAL_RenderDriver = { #define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1))) +static const size_t CONSTANTS_OFFSET_INVALID = 0xFFFFFFFF; static const size_t CONSTANTS_OFFSET_IDENTITY = 0; static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16); static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16); @@ -457,248 +400,8 @@ ChoosePipelineState(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, SD return MakePipelineState(data, cache, [NSString stringWithFormat:@" (blend=custom 0x%x)", blendmode], blendmode); } -static SDL_Renderer * -METAL_CreateRenderer(SDL_Window * window, Uint32 flags) -{ @autoreleasepool { - SDL_Renderer *renderer = NULL; - METAL_RenderData *data = NULL; - id<MTLDevice> mtldevice = nil; - SDL_SysWMinfo syswm; - - SDL_VERSION(&syswm.version); - if (!SDL_GetWindowWMInfo(window, &syswm)) { - return NULL; - } - - if (IsMetalAvailable(&syswm) == -1) { - return NULL; - } - - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - return NULL; - } - - // !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS... - mtldevice = MTLCreateSystemDefaultDevice(); - - if (mtldevice == nil) { - SDL_free(renderer); - SDL_SetError("Failed to obtain Metal device"); - return NULL; - } - - // !!! FIXME: error checking on all of this. - data = [[METAL_RenderData alloc] init]; - - renderer->driverdata = (void*)CFBridgingRetain(data); - renderer->window = window; - -#ifdef __MACOSX__ - NSView *view = Cocoa_Mtl_AddMetalView(window); - CAMetalLayer *layer = (CAMetalLayer *)[view layer]; - - layer.device = mtldevice; - - //layer.colorspace = nil; - -#else - UIView *view = UIKit_Mtl_AddMetalView(window); - CAMetalLayer *layer = (CAMetalLayer *)[view layer]; -#endif - - // Necessary for RenderReadPixels. - layer.framebufferOnly = NO; - - data.mtldevice = layer.device; - data.mtllayer = layer; - id<MTLCommandQueue> mtlcmdqueue = [data.mtldevice newCommandQueue]; - data.mtlcmdqueue = mtlcmdqueue; - data.mtlcmdqueue.label = @"SDL Metal Renderer"; - data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor]; - - NSError *err = nil; - - // The compiled .metallib is embedded in a static array in a header file - // but the original shader source code is in SDL_shaders_metal.metal. - dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{}); - id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err]; - data.mtllibrary = mtllibrary; - SDL_assert(err == nil); -#if !__has_feature(objc_arc) - dispatch_release(mtllibdata); -#endif - data.mtllibrary.label = @"SDL Metal renderer shader library"; - - /* Do some shader pipeline state loading up-front rather than on demand. */ - data.pipelinescount = 0; - data.allpipelines = NULL; - ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm); - - MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init]; - - samplerdesc.minFilter = MTLSamplerMinMagFilterNearest; - samplerdesc.magFilter = MTLSamplerMinMagFilterNearest; - id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; - data.mtlsamplernearest = mtlsamplernearest; - - samplerdesc.minFilter = MTLSamplerMinMagFilterLinear; - samplerdesc.magFilter = MTLSamplerMinMagFilterLinear; - id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; - data.mtlsamplerlinear = mtlsamplerlinear; - - /* Note: matrices are column major. */ - float identitytransform[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f, - }; - - float halfpixeltransform[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.0f, 1.0f, - }; - - /* Metal pads float3s to 16 bytes. */ - float decodetransformJPEG[4*4] = { - 0.0, -0.501960814, -0.501960814, 0.0, /* offset */ - 1.0000, 0.0000, 1.4020, 0.0, /* Rcoeff */ - 1.0000, -0.3441, -0.7141, 0.0, /* Gcoeff */ - 1.0000, 1.7720, 0.0000, 0.0, /* Bcoeff */ - }; - - float decodetransformBT601[4*4] = { - -0.0627451017, -0.501960814, -0.501960814, 0.0, /* offset */ - 1.1644, 0.0000, 1.5960, 0.0, /* Rcoeff */ - 1.1644, -0.3918, -0.8130, 0.0, /* Gcoeff */ - 1.1644, 2.0172, 0.0000, 0.0, /* Bcoeff */ - }; - - float decodetransformBT709[4*4] = { - 0.0, -0.501960814, -0.501960814, 0.0, /* offset */ - 1.0000, 0.0000, 1.4020, 0.0, /* Rcoeff */ - 1.0000, -0.3441, -0.7141, 0.0, /* Gcoeff */ - 1.0000, 1.7720, 0.0000, 0.0, /* Bcoeff */ - }; - - float clearverts[6] = {0.0f, 0.0f, 0.0f, 2.0f, 2.0f, 0.0f}; - - id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared]; - mtlbufconstantstaging.label = @"SDL constant staging data"; - - id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate]; - data.mtlbufconstants = mtlbufconstants; - data.mtlbufconstants.label = @"SDL constant data"; - - char *constantdata = [mtlbufconstantstaging contents]; - SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform)); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform)); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG, sizeof(decodetransformJPEG)); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601, sizeof(decodetransformBT601)); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709, sizeof(decodetransformBT709)); - SDL_memcpy(constantdata + CONSTANTS_OFFSET_CLEAR_VERTS, clearverts, sizeof(clearverts)); - - id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer]; - id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder]; - - [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:data.mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH]; - - [blitcmd endEncoding]; - [cmdbuffer commit]; - - // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed. - - renderer->WindowEvent = METAL_WindowEvent; - renderer->GetOutputSize = METAL_GetOutputSize; - renderer->SupportsBlendMode = METAL_SupportsBlendMode; - renderer->CreateTexture = METAL_CreateTexture; - renderer->UpdateTexture = METAL_UpdateTexture; - renderer->UpdateTextureYUV = METAL_UpdateTextureYUV; - renderer->LockTexture = METAL_LockTexture; - renderer->UnlockTexture = METAL_UnlockTexture; - renderer->SetRenderTarget = METAL_SetRenderTarget; - renderer->UpdateViewport = METAL_UpdateViewport; - renderer->UpdateClipRect = METAL_UpdateClipRect; - renderer->RenderClear = METAL_RenderClear; - renderer->RenderDrawPoints = METAL_RenderDrawPoints; - renderer->RenderDrawLines = METAL_RenderDrawLines; - renderer->RenderFillRects = METAL_RenderFillRects; - renderer->RenderCopy = METAL_RenderCopy; - renderer->RenderCopyEx = METAL_RenderCopyEx; - renderer->RenderReadPixels = METAL_RenderReadPixels; - renderer->RenderPresent = METAL_RenderPresent; - renderer->DestroyTexture = METAL_DestroyTexture; - renderer->DestroyRenderer = METAL_DestroyRenderer; - renderer->GetMetalLayer = METAL_GetMetalLayer; - renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder; - - renderer->info = METAL_RenderDriver.info; - renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); - -#if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13) - if (@available(macOS 10.13, *)) { - data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0; - } else -#endif - { - renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; - } - - /* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ - int maxtexsize = 4096; -#if defined(__MACOSX__) - maxtexsize = 16384; -#elif defined(__TVOS__) - maxtexsize = 8192; -#ifdef __TVOS_11_0 - if (@available(tvOS 11.0, *)) { - if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) { - maxtexsize = 16384; - } - } -#endif -#else -#ifdef __IPHONE_11_0 - if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) { - maxtexsize = 16384; - } else -#endif -#ifdef __IPHONE_10_0 - if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) { - maxtexsize = 16384; - } else -#endif - if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) { - maxtexsize = 8192; - } else { - maxtexsize = 4096; - } -#endif - - renderer->info.max_texture_width = maxtexsize; - renderer->info.max_texture_height = maxtexsize; - -#if !__has_feature(objc_arc) - [mtlcmdqueue release]; - [mtllibrary release]; - [samplerdesc release]; - [mtlsamplernearest release]; - [mtlsamplerlinear release]; - [mtlbufconstants release]; - [view release]; - [data release]; - [mtldevice release]; -#endif - - return renderer; -}} - static void -METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load) +METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load, MTLClearColor *clear_color) { METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; @@ -725,8 +428,8 @@ METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load) SDL_assert(mtltexture); if (load == MTLLoadActionClear) { - MTLClearColor color = MTLClearColorMake(renderer->r/255.0, renderer->g/255.0, renderer->b/255.0, renderer->a/255.0); - data.mtlpassdesc.colorAttachments[0].clearColor = color; + SDL_assert(clear_color != NULL); + data.mtlpassdesc.colorAttachments[0].clearColor = *clear_color; } data.mtlpassdesc.colorAttachments[0].loadAction = load; @@ -743,9 +446,10 @@ METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load) data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat); - /* Make sure the viewport and clip rect are set on the new render pass. */ - METAL_UpdateViewport(renderer); - METAL_UpdateClipRect(renderer); + // make sure this has a definite place in the queue. This way it will + // execute reliably whether the app tries to make its own command buffers + // or whatever. This means we can _always_ batch rendering commands! + [data.mtlcmdbuffer enqueue]; } } @@ -849,7 +553,7 @@ METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) mtltexdesc.height = (texture->h + 1) / 2; } - if (yuv || nv12) { + if (yuv || nc12) { mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc]; if (mtltexture_uv == nil) { #if !__has_feature(objc_arc) @@ -1027,322 +731,466 @@ METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return 0; }} + +// normalize a value from 0.0f to len into 0.0f to 1.0f. +static inline float +normtex(const float _val, const float len) +{ + return _val / len; +} + static int -METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h) -{ @autoreleasepool { - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - float projection[4][4]; +METAL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) +{ + float projection[4][4]; /* Prepare an orthographic projection */ + const int w = cmd->data.viewport.rect.w; + const int h = cmd->data.viewport.rect.h; + const size_t matrixlen = sizeof (projection); + float *matrix = (float *) SDL_AllocateRenderVertices(renderer, matrixlen, CONSTANT_ALIGN, &cmd->data.viewport.first); + if (!matrix) { + return -1; + } - if (!w || !h) { - return 0; + SDL_memset(projection, '\0', matrixlen); + if (w && h) { + projection[0][0] = 2.0f / w; + projection[1][1] = -2.0f / h; + projection[3][0] = -1.0f; + projection[3][1] = 1.0f; + projection[3][3] = 1.0f; } + SDL_memcpy(matrix, projection, matrixlen); - /* Prepare an orthographic projection */ - projection[0][0] = 2.0f / w; - projection[0][1] = 0.0f; - projection[0][2] = 0.0f; - projection[0][3] = 0.0f; - projection[1][0] = 0.0f; - projection[1][1] = -2.0f / h; - projection[1][2] = 0.0f; - projection[1][3] = 0.0f; - projection[2][0] = 0.0f; - projection[2][1] = 0.0f; - projection[2][2] = 0.0f; - projection[2][3] = 0.0f; - projection[3][0] = -1.0f; - projection[3][1] = 1.0f; - projection[3][2] = 0.0f; - projection[3][3] = 1.0f; - - // !!! FIXME: This should be in a buffer... - [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2]; return 0; -}} +} static int -METAL_UpdateViewport(SDL_Renderer * renderer) -{ @autoreleasepool { - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - if (data.mtlcmdencoder) { - MTLViewport viewport; - viewport.originX = renderer->viewport.x; - viewport.originY = renderer->viewport.y; - viewport.width = renderer->viewport.w; - viewport.height = renderer->viewport.h; - viewport.znear = 0.0; - viewport.zfar = 1.0; - [data.mtlcmdencoder setViewport:viewport]; - METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h); +METAL_QueueSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + const size_t vertlen = sizeof (float) * 4; + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, CONSTANT_ALIGN, &cmd->data.color.first); + if (!verts) { + return -1; } + *(verts++) = ((float)cmd->data.color.r) / 255.0f; + *(verts++) = ((float)cmd->data.color.g) / 255.0f; + *(verts++) = ((float)cmd->data.color.b) / 255.0f; + *(verts++) = ((float)cmd->data.color.a) / 255.0f; return 0; -}} +} static int -METAL_UpdateClipRect(SDL_Renderer * renderer) -{ @autoreleasepool { - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - if (data.mtlcmdencoder) { - MTLScissorRect mtlrect; - // !!! FIXME: should this care about the viewport? - if (renderer->clipping_enabled) { - const SDL_Rect *rect = &renderer->clip_rect; - mtlrect.x = renderer->viewport.x + rect->x; - mtlrect.y = renderer->viewport.x + rect->y; - mtlrect.width = rect->w; - mtlrect.height = rect->h; - } else { - mtlrect.x = renderer->viewport.x; - mtlrect.y = renderer->viewport.y; - mtlrect.width = renderer->viewport.w; - mtlrect.height = renderer->viewport.h; - } - if (mtlrect.width > 0 && mtlrect.height > 0) { - [data.mtlcmdencoder setScissorRect:mtlrect]; - } +METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) +{ + const size_t vertlen = (sizeof (float) * 2) * count; + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); + if (!verts) { + return -1; } + cmd->data.draw.count = count; + SDL_memcpy(verts, points, vertlen); return 0; -}} +} static int -METAL_RenderClear(SDL_Renderer * renderer) -{ @autoreleasepool { - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; +METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) +{ + // !!! FIXME: use an index buffer + const size_t vertlen = (sizeof (float) * 8) * count; + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); + if (!verts) { + return -1; + } - /* Since we set up the render command encoder lazily when a draw is - * requested, we can do the fast path hardware clear if no draws have - * happened since the last SetRenderTarget. */ - if (data.mtlcmdencoder == nil) { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear); - } else { - // !!! FIXME: render color should live in a dedicated uniform buffer. - const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f }; + cmd->data.draw.count = count; - MTLViewport viewport; // RenderClear ignores the viewport state, though, so reset that. - viewport.originX = viewport.originY = 0.0; - viewport.width = data.mtlpassdesc.colorAttachments[0].texture.width; - viewport.height = data.mtlpassdesc.colorAttachments[0].texture.height; - viewport.znear = 0.0; - viewport.zfar = 1.0; + for (int i = 0; i < count; i++, rects++) { + if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) { + cmd->data.draw.count--; + } else { + *(verts++) = rects->x; + *(verts++) = rects->y + rects->h; + *(verts++) = rects->x; + *(verts++) = rects->y; + *(verts++) = rects->x + rects->w; + *(verts++) = rects->y + rects->h; + *(verts++) = rects->x + rects->w; + *(verts++) = rects->y; + } + } - // Slow path for clearing: draw a filled fullscreen triangle. - METAL_SetOrthographicProjection(renderer, 1, 1); - [data.mtlcmdencoder setViewport:viewport]; - [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, SDL_BLENDMODE_NONE)]; - [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_CLEAR_VERTS atIndex:0]; - [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3]; - [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0]; - [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]; - - // reset the viewport for the rest of our usual drawing work... - viewport.originX = renderer->viewport.x; - viewport.originY = renderer->viewport.y; - viewport.width = renderer->viewport.w; - viewport.height = renderer->viewport.h; - viewport.znear = 0.0; - viewport.zfar = 1.0; - [data.mtlcmdencoder setViewport:viewport]; - METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h); + if (cmd->data.draw.count == 0) { + cmd->command = SDL_RENDERCMD_NO_OP; // nothing to do, just skip this one later. } return 0; -}} - -// normalize a value from 0.0f to len into 0.0f to 1.0f. -static inline float -normtex(const float _val, const float len) -{ - return _val / len; } static int -DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count, - const MTLPrimitiveType primtype) -{ @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - - const size_t vertlen = (sizeof (float) * 2) * count; - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - - // !!! FIXME: render color should live in a dedicated uniform buffer. - const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f }; - - [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)]; - [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0]; +METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata; + const float texw = (float) texturedata.mtltexture.width; + const float texh = (float) texturedata.mtltexture.height; + // !!! FIXME: use an index buffer + const size_t vertlen = (sizeof (float) * 16); + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); + if (!verts) { + return -1; + } - [data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0]; - [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM atIndex:3]; - [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count]; + cmd->data.draw.count = 1; + + *(verts++) = dstrect->x; + *(verts++) = dstrect->y + dstrect->h; + *(verts++) = dstrect->x; + *(verts++) = dstrect->y; + *(verts++) = dstrect->x + dstrect->w; + *(verts++) = dstrect->y + dstrect->h; + *(verts++) = dstrect->x + dstrect->w; + *(verts++) = dstrect->y; + + *(verts++) = normtex(srcrect->x, texw); + *(verts++) = normtex(srcrect->y + srcrect->h, texh); + *(verts++) = normtex(srcrect->x, texw); + *(verts++) = normtex(srcrect->y, texh); + *(verts++) = normtex(srcrect->x + srcrect->w, texw); + *(verts++) = normtex(srcrect->y + srcrect->h, texh); + *(verts++) = normtex(srcrect->x + srcrect->w, texw); + *(verts++) = normtex(srcrect->y, texh); return 0; -}} - -static int -METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count) -{ - return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint); } static int -METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count) +METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcquad, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip); -} - -static int -METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count) -{ @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - - // !!! FIXME: render color should live in a dedicated uniform buffer. - const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f }; + METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata; + const float texw = (float) texturedata.mtltexture.width; + const float texh = (float) texturedata.mtltexture.height; + const float rads = (float)(M_PI * (float) angle / 180.0f); + const float c = cosf(rads), s = sinf(rads); + float minu, maxu, minv, maxv; + const size_t vertlen = (sizeof (float) * 32); + float *verts; - [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)]; - [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0]; - [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3]; + // cheat and store this offset in (count) because it needs to be aligned in ways other fields don't and we aren't using count otherwise. + verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, CONSTANT_ALIGN, &cmd->data.draw.count); + if (!verts) { + return -1; + } - for (int i = 0; i < count; i++, rects++) { - if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue; + // transform matrix + SDL_memset(verts, '\0', sizeof (*verts) * 16); + verts[10] = verts[15] = 1.0f; + // rotation + verts[0] = c; + verts[1] = s; + verts[4] = -s; + verts[5] = c; + + // translation + verts[12] = dstrect->x + center->x; + verts[13] = dstrect->y + center->y; + + // rest of the vertices don't need the aggressive alignment. Pack them in. + verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); + if (!verts) { + return -1; + } - const float verts[] = { - rects->x, rects->y + rects->h, - rects->x, rects->y, - rects->x + rects->w, rects->y + rects->h, - rects->x + rects->w, rects->y - }; + minu = normtex(srcquad->x, texw); + maxu = normtex(srcquad->x + srcquad->w, texw); + minv = normtex(srcquad->y, texh); + maxv = normtex(srcquad->y + srcquad->h, texh); - [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0]; - [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + if (flip & SDL_FLIP_HORIZONTAL) { + float tmp = maxu; + maxu = minu; + minu = tmp; + } + if (flip & SDL_FLIP_VERTICAL) { + float tmp = maxv; + maxv = minv; + minv = tmp; } + // vertices + *(verts++) = -center->x; + *(verts++) = dstrect->h - center->y; + *(verts++) = -center->x; + *(verts++) = -center->y; + *(verts++) = dstrect->w - center->x; + *(verts++) = dstrect->h - center->y; + *(verts++) = dstrect->w - center->x; + *(verts++) = -center->y; + + // texcoords + *(verts++) = minu; + *(verts++) = maxv; + *(verts++) = minu; + *(verts++) = minv; + *(verts++) = maxu; + *(verts++) = maxv; + *(verts++) = maxu; + *(verts++) = minv; + return 0; -}} +} + + +typedef struct +{ + #if __has_feature(objc_arc) + __unsafe_unretained id<MTLRenderPipelineState> pipeline; + #else + id<MTLRenderPipelineState> pipeline; + #endif + size_t constants_offset; + SDL_Texture *texture; + SDL_bool cliprect_dirty; + SDL_bool cliprect_enabled; + SDL_Rect cliprect; + SDL_bool viewport_dirty; + SDL_Rect viewport; + size_t projection_offset; + SDL_bool color_dirty; + size_t color_offset; +} METAL_DrawStateCache; static void -METAL_SetupRenderCopy(METAL_RenderData *data, SDL_Texture *texture, METAL_TextureData *texturedata) +SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_MetalFragmentFunction shader, + const size_t constants_offset, id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache) { - float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; - if (texture->modMode) { - color[0] = ((float)texture->r) / 255.0f; - color[1] = ((float)texture->g) / 255.0f; - color[2] = ((float)texture->b) / 255.0f; - color[3] = ((float)texture->a) / 255.0f; + METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; + const SDL_BlendMode blend = cmd->data.draw.blend; + size_t first = cmd->data.draw.first; + id<MTLRenderPipelineState> newpipeline; + + METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL); + + if (statecache->viewport_dirty) { + MTLViewport viewport; + viewport.originX = statecache->viewport.x; + viewport.originY = statecache->viewport.y; + viewport.width = statecache->viewport.w; + viewport.height = statecache->viewport.h; + viewport.znear = 0.0; + viewport.zfar = 1.0; + [data.mtlcmdencoder setViewport:viewport]; + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:statecache->projection_offset atIndex:2]; // projection + statecache->viewport_dirty = SDL_FALSE; + } + + if (statecache->cliprect_dirty) { + MTLScissorRect mtlrect; + if (statecache->cliprect_enabled) { + const SDL_Rect *rect = &statecache->cliprect; + mtlrect.x = statecache->viewport.x + rect->x; + mtlrect.y = statecache->viewport.y + rect->y; + mtlrect.width = rect->w; + mtlrect.height = rect->h; + } else { + mtlrect.x = statecache->viewport.x; + mtlrect.y = statecache->viewport.y; + mtlrect.width = statecache->viewport.w; + mtlrect.height = statecache->viewport.h; + } + if (mtlrect.width > 0 && mtlrect.height > 0) { + [data.mtlcmdencoder setScissorRect:mtlrect]; + } + statecache->cliprect_dirty = SDL_FALSE; } - [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, texturedata.fragmentFunction, texture->blendMode)]; - [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0]; - [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0]; + if (statecache->color_dirty) { + [data.mtlcmdencoder setFragmentBuffer:mtlbufvertex offset:statecache->color_offset atIndex:0]; + statecache->color_dirty = SDL_FALSE; + } - [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0]; + newpipeline = ChoosePipelineState(data, data.activepipelines, shader, blend); + if (newpipeline != statecache->pipeline) { + [data.mtlcmdencoder setRenderPipelineState:newpipeline]; + statecache->pipeline = newpipeline; + } - if (texturedata.yuv || texturedata.nv12) { - [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1]; - [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1]; + if (constants_offset != statecache->constants_offset) { + if (constants_offset != CONSTANTS_OFFSET_INVALID) { + [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:constants_offset atIndex:3]; + } + statecache->constants_offset = constants_offset; } + + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:first atIndex:0]; // position } -static int -METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) -{ @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); +static void +SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const size_t constants_offset, + id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache) +{ METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; + SDL_Texture *texture = cmd->data.draw.texture; METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata; - const float texw = (float) texturedata.mtltexture.width; - const float texh = (float) texturedata.mtltexture.height; - METAL_SetupRenderCopy(data, texture, texturedata); + SetDrawState(renderer, cmd, texturedata.fragmentFunction, constants_offset, mtlbufvertex, statecache); - const float xy[] = { - dstrect->x, dstrect->y + dstrect->h, - dstrect->x, dstrect->y, - dstrect->x + dstrect->w, dstrect->y + dstrect->h, - dstrect->x + dstrect->w, dstrect->y - }; - - const float uv[] = { - normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh), - normtex(srcrect->x, texw), normtex(srcrect->y, texh), - normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh), - normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh) - }; + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(8*sizeof (float)) atIndex:1]; // texcoords - [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0]; - [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1]; - [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3]; - [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + if (texture != statecache->texture) { + METAL_TextureData *oldtexturedata = NULL; + if (statecache->texture) { + oldtexturedata = (__bridge METAL_TextureData *) statecache->texture->driverdata; + } + if (!oldtexturedata || (texturedata.mtlsampler != oldtexturedata.mtlsampler)) { + [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0]; + } - return 0; -}} + [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0]; + if (texturedata.yuv || texturedata.nv12) { + [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1]; + [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1]; + } + statecache->texture = texture; + } +} static int -METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata; - const float texw = (float) texturedata.mtltexture.width; - const float texh = (float) texturedata.mtltexture.height; - float transform[16]; - float minu, maxu, minv, maxv; + METAL_DrawStateCache statecache; + id<MTLBuffer> mtlbufvertex = nil; + + statecache.pipeline = nil; + statecache.constants_offset = CONSTANTS_OFFSET_INVALID; + statecache.texture = NULL; + statecache.color_dirty = SDL_TRUE; + statecache.cliprect_dirty = SDL_TRUE; + statecache.viewport_dirty = SDL_TRUE; + statecache.projection_offset = 0; + statecache.color_offset = 0; + + // !!! FIXME: have a ring of pre-made MTLBuffers we cycle through? How expensive is creation? + if (vertsize > 0) { + id<MTLBuffer> mtlbufvertexstaging = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared]; + #if !__has_feature(objc_arc) + [mtlbufvertexstaging autorelease]; + #endif + mtlbufvertexstaging.label = @"SDL vertex staging data"; + SDL_memcpy([mtlbufvertexstaging contents], vertices, vertsize); + + // Move our new vertex buffer from system RAM to GPU memory so any draw calls can use it. + mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModePrivate]; + #if !__has_feature(objc_arc) + [mtlbufvertex autorelease]; + #endif + mtlbufvertex.label = @"SDL vertex data"; + id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer]; + id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder]; + [blitcmd copyFromBuffer:mtlbufvertexstaging sourceOffset:0 toBuffer:mtlbufvertex destinationOffset:0 size:vertsize]; + [blitcmd endEncoding]; + [cmdbuffer commit]; + } - METAL_SetupRenderCopy(data, texture, texturedata); + // If there's a command buffer here unexpectedly (app requested one?). Commit it so we can start fresh. + [data.mtlcmdencoder endEncoding]; + [data.mtlcmdbuffer commit]; + data.mtlcmdencoder = nil; + data.mtlcmdbuffer = nil; - minu = normtex(srcrect->x, texw); - maxu = normtex(srcrect->x + srcrect->w, texw); - minv = normtex(srcrect->y, texh); - maxv = normtex(srcrect->y + srcrect->h, texh); + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_memcpy(&statecache.viewport, &cmd->data.viewport.rect, sizeof (statecache.viewport)); + statecache.projection_offset = cmd->data.viewport.first; + statecache.viewport_dirty = SDL_TRUE; + break; + } - if (flip & SDL_FLIP_HORIZONTAL) { - float tmp = maxu; - maxu = minu; - minu = tmp; - } - if (flip & SDL_FLIP_VERTICAL) { - float tmp = maxv; - maxv = minv; - minv = tmp; - } + case SDL_RENDERCMD_SETCLIPRECT: { + SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)); + statecache.cliprect_enabled = cmd->data.cliprect.enabled; + statecache.cliprect_dirty = SDL_TRUE; + break; + } - const float uv[] = { - minu, maxv, - minu, minv, - maxu, maxv, - maxu, minv - }; + case SDL_RENDERCMD_SETDRAWCOLOR: { + statecache.color_offset = cmd->data.color.first; + statecache.color_dirty = SDL_TRUE; + break; + } - const float xy[] = { - -center->x, dstrect->h - center->y, - -center->x, -center->y, - dstrect->w - center->x, dstrect->h - center->y, - dstrect->w - center->x, -center->y - }; + case SDL_RENDERCMD_CLEAR: { + /* If we're already encoding a command buffer, dump it without committing it. We'd just + clear all its work anyhow, and starting a new encoder will let us use a hardware clear + operation via MTLLoadActionClear. */ + if (data.mtlcmdencoder != nil) { + [data.mtlcmdencoder endEncoding]; + + // !!! FIXME: have to commit, or an uncommitted but enqueued buffer will prevent the frame from finishing. + [data.mtlcmdbuffer commit]; + data.mtlcmdencoder = nil; + data.mtlcmdbuffer = nil; + } - { - float rads = (float)(M_PI * (float) angle / 180.0f); - float c = cosf(rads), s = sinf(rads); - SDL_memset(transform, 0, sizeof(transform)); + // force all this state to be reconfigured on next command buffer. + statecache.pipeline = nil; + statecache.constants_offset = CONSTANTS_OFFSET_INVALID; + statecache.texture = NULL; + statecache.color_dirty = SDL_TRUE; + statecache.cliprect_dirty = SDL_TRUE; + statecache.viewport_dirty = SDL_TRUE; + + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + MTLClearColor color = MTLClearColorMake(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); + + // get new command encoder, set up with an initial clear operation. + METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear, &color); + break; + } - transform[10] = transform[15] = 1.0f; + case SDL_RENDERCMD_DRAW_POINTS: + case SDL_RENDERCMD_DRAW_LINES: { + const size_t count = cmd->data.draw.count; + const MTLPrimitiveType primtype = (cmd->command == SDL_RENDERCMD_DRAW_POINTS) ? MTLPrimitiveTypePoint : MTLPrimitiveTypeLineStrip; + SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, mtlbufvertex, &statecache); + [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count]; + break; + } - /* Rotation */ - transform[0] = c; - transform[1] = s; - transform[4] = -s; - transform[5] = c; + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + size_t start = 0; + SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache); + for (size_t i = 0; i < count; i++, start += 4) { // !!! FIXME: can we do all of these this with a single draw call, using MTLPrimitiveTypeTriangle and an index buffer? + [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:start vertexCount:4]; + } + break; + } - /* Translation */ - transform[12] = dstrect->x + center->x; - transform[13] = dstrect->y + center->y; - } + case SDL_RENDERCMD_COPY: { + SetCopyState(renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache); + [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + break; + } - [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0]; - [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1]; - [data.mtlcmdencoder setVertexBytes:transform length:sizeof(transform) atIndex:3]; - [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + case SDL_RENDERCMD_COPY_EX: { + SetCopyState(renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache); + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.count atIndex:3]; // transform + [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + cmd = cmd->next; + } return 0; }} @@ -1352,21 +1200,14 @@ METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, void * pixels, int pitch) { @autoreleasepool { METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; + METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL); - /* Make sure we have a valid MTLTexture to read from, and an active command - * buffer we can wait for. */ - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - - /* Wait for the current command buffer to finish, so we don't read from the - * texture before the GPU finishes rendering to it. */ - if (data.mtlcmdencoder) { - [data.mtlcmdencoder endEncoding]; - [data.mtlcmdbuffer commit]; - [data.mtlcmdbuffer waitUntilCompleted]; - - data.mtlcmdencoder = nil; - data.mtlcmdbuffer = nil; - } + // Commit any current command buffer, and waitUntilCompleted, so any output is ready to be read. + [data.mtlcmdencoder endEncoding]; + [data.mtlcmdbuffer commit]; + [data.mtlcmdbuffer waitUntilCompleted]; + data.mtlcmdencoder = nil; + data.mtlcmdbuffer = nil; id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture; MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h); @@ -1383,13 +1224,6 @@ METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888; const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch); SDL_free(temp_pixels); - - /* Set up an active command buffer and encoder once we're done. It will use - * the same texture that was active before (even if it's part of the swap - * chain), since we didn't clear that when waiting for the command buffer to - * complete. */ - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - return status; }} @@ -1445,11 +1279,274 @@ METAL_GetMetalLayer(SDL_Renderer * renderer) static void * METAL_GetMetalCommandEncoder(SDL_Renderer * renderer) { @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); + METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL); METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; return (__bridge void*)data.mtlcmdencoder; }} +static SDL_Renderer * +METAL_CreateRenderer(SDL_Window * window, Uint32 flags) +{ @autoreleasepool { + SDL_Renderer *renderer = NULL; + METAL_RenderData *data = NULL; + id<MTLDevice> mtldevice = nil; + SDL_SysWMinfo syswm; + + SDL_VERSION(&syswm.version); + if (!SDL_GetWindowWMInfo(window, &syswm)) { + return NULL; + } + + if (IsMetalAvailable(&syswm) == -1) { + return NULL; + } + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + // !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS... + mtldevice = MTLCreateSystemDefaultDevice(); + + if (mtldevice == nil) { + SDL_free(renderer); + SDL_SetError("Failed to obtain Metal device"); + return NULL; + } + + // !!! FIXME: error checking on all of this. + data = [[METAL_RenderData alloc] init]; + + renderer->driverdata = (void*)CFBridgingRetain(data); + renderer->window = window; + +#ifdef __MACOSX__ + NSView *view = Cocoa_Mtl_AddMetalView(window); + CAMetalLayer *layer = (CAMetalLayer *)[view layer]; + + layer.device = mtldevice; + + //layer.colorspace = nil; + +#else + UIView *view = UIKit_Mtl_AddMetalView(window); + CAMetalLayer *layer = (CAMetalLayer *)[view layer]; +#endif + + // Necessary for RenderReadPixels. + layer.framebufferOnly = NO; + + data.mtldevice = layer.device; + data.mtllayer = layer; + id<MTLCommandQueue> mtlcmdqueue = [data.mtldevice newCommandQueue]; + data.mtlcmdqueue = mtlcmdqueue; + data.mtlcmdqueue.label = @"SDL Metal Renderer"; + data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor]; + + NSError *err = nil; + + // The compiled .metallib is embedded in a static array in a header file + // but the original shader source code is in SDL_shaders_metal.metal. + dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{}); + id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err]; + data.mtllibrary = mtllibrary; + SDL_assert(err == nil); +#if !__has_feature(objc_arc) + dispatch_release(mtllibdata); +#endif + data.mtllibrary.label = @"SDL Metal renderer shader library"; + + /* Do some shader pipeline state loading up-front rather than on demand. */ + data.pipelinescount = 0; + data.allpipelines = NULL; + ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm); + + MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init]; + + samplerdesc.minFilter = MTLSamplerMinMagFilterNearest; + samplerdesc.magFilter = MTLSamplerMinMagFilterNearest; + id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; + data.mtlsamplernearest = mtlsamplernearest; + + samplerdesc.minFilter = MTLSamplerMinMagFilterLinear; + samplerdesc.magFilter = MTLSamplerMinMagFilterLinear; + id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; + data.mtlsamplerlinear = mtlsamplerlinear; + + /* Note: matrices are column major. */ + float identitytransform[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }; + + float halfpixeltransform[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.0f, 1.0f, + }; + + /* Metal pads float3s to 16 bytes. */ + float decodetransformJPEG[4*4] = { + 0.0, -0.501960814, -0.501960814, 0.0, /* offset */ + 1.0000, 0.0000, 1.4020, 0.0, /* Rcoeff */ + 1.0000, -0.3441, -0.7141, 0.0, /* Gcoeff */ + 1.0000, 1.7720, 0.0000, 0.0, /* Bcoeff */ + }; + + float decodetransformBT601[4*4] = { + -0.0627451017, -0.501960814, -0.501960814, 0.0, /* offset */ + 1.1644, 0.0000, 1.5960, 0.0, /* Rcoeff */ + 1.1644, -0.3918, -0.8130, 0.0, /* Gcoeff */ + 1.1644, 2.0172, 0.0000, 0.0, /* Bcoeff */ + }; + + float decodetransformBT709[4*4] = { + 0.0, -0.501960814, -0.501960814, 0.0, /* offset */ + 1.0000, 0.0000, 1.4020, 0.0, /* Rcoeff */ + 1.0000, -0.3441, -0.7141, 0.0, /* Gcoeff */ + 1.0000, 1.7720, 0.0000, 0.0, /* Bcoeff */ + }; + + float clearverts[6] = {0.0f, 0.0f, 0.0f, 2.0f, 2.0f, 0.0f}; + + id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared]; + #if !__has_feature(objc_arc) + [mtlbufconstantstaging autorelease]; + #endif + mtlbufconstantstaging.label = @"SDL constant staging data"; + + id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate]; + data.mtlbufconstants = mtlbufconstants; + data.mtlbufconstants.label = @"SDL constant data"; + + char *constantdata = [mtlbufconstantstaging contents]; + SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform)); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform)); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG, sizeof(decodetransformJPEG)); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601, sizeof(decodetransformBT601)); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709, sizeof(decodetransformBT709)); + SDL_memcpy(constantdata + CONSTANTS_OFFSET_CLEAR_VERTS, clearverts, sizeof(clearverts)); + + id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer]; + id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder]; + + [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:data.mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH]; + + [blitcmd endEncoding]; + [cmdbuffer commit]; + + // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed. + + renderer->WindowEvent = METAL_WindowEvent; + renderer->GetOutputSize = METAL_GetOutputSize; + renderer->SupportsBlendMode = METAL_SupportsBlendMode; + renderer->CreateTexture = METAL_CreateTexture; + renderer->UpdateTexture = METAL_UpdateTexture; + renderer->UpdateTextureYUV = METAL_UpdateTextureYUV; + renderer->LockTexture = METAL_LockTexture; + renderer->UnlockTexture = METAL_UnlockTexture; + renderer->SetRenderTarget = METAL_SetRenderTarget; + renderer->QueueSetViewport = METAL_QueueSetViewport; + renderer->QueueSetDrawColor = METAL_QueueSetDrawColor; + renderer->QueueDrawPoints = METAL_QueueDrawPoints; + renderer->QueueDrawLines = METAL_QueueDrawPoints; // lines and points queue the same way. + renderer->QueueFillRects = METAL_QueueFillRects; + renderer->QueueCopy = METAL_QueueCopy; + renderer->QueueCopyEx = METAL_QueueCopyEx; + renderer->RunCommandQueue = METAL_RunCommandQueue; + renderer->RenderReadPixels = METAL_RenderReadPixels; + renderer->RenderPresent = METAL_RenderPresent; + renderer->DestroyTexture = METAL_DestroyTexture; + renderer->DestroyRenderer = METAL_DestroyRenderer; + renderer->GetMetalLayer = METAL_GetMetalLayer; + renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder; + + renderer->info = METAL_RenderDriver.info; + renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + + renderer->always_batch = SDL_TRUE; + +#if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13) + if (@available(macOS 10.13, *)) { + data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0; + } else +#endif + { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + + /* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ + int maxtexsize = 4096; +#if defined(__MACOSX__) + maxtexsize = 16384; +#elif defined(__TVOS__) + maxtexsize = 8192; +#ifdef __TVOS_11_0 + if (@available(tvOS 11.0, *)) { + if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) { + maxtexsize = 16384; + } + } +#endif +#else +#ifdef __IPHONE_11_0 + if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) { + maxtexsize = 16384; + } else +#endif +#ifdef __IPHONE_10_0 + if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) { + maxtexsize = 16384; + } else +#endif + if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) { + maxtexsize = 8192; + } else { + maxtexsize = 4096; + } +#endif + + renderer->info.max_texture_width = maxtexsize; + renderer->info.max_texture_height = maxtexsize; + +#if !__has_feature(objc_arc) + [mtlcmdqueue release]; + [mtllibrary release]; + [samplerdesc release]; + [mtlsamplernearest release]; + [mtlsamplerlinear release]; + [mtlbufconstants release]; + [view release]; + [data release]; + [mtldevice release]; +#endif + + return renderer; +}} + +SDL_RenderDriver METAL_RenderDriver = { + METAL_CreateRenderer, + { + "metal", + (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), + 6, + { + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_ABGR8888, + SDL_PIXELFORMAT_YV12, + SDL_PIXELFORMAT_IYUV, + SDL_PIXELFORMAT_NV12, + SDL_PIXELFORMAT_NV21 + }, + 0, 0, + } +}; + #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 09db12de80..01d6dadf01 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -51,57 +51,6 @@ extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); static const float inv255f = 1.0f / 255.0f; -static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags); -static void GL_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h); -static SDL_bool GL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode); -static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *pixels, - int pitch); -static int GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, - const Uint8 *Yplane, int Ypitch, - const Uint8 *Uplane, int Upitch, - const Uint8 *Vplane, int Vpitch); -static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); -static int GL_UpdateViewport(SDL_Renderer * renderer); -static int GL_UpdateClipRect(SDL_Renderer * renderer); -static int GL_RenderClear(SDL_Renderer * renderer); -static int GL_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int GL_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int GL_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); -static int GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); -static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch); -static void GL_RenderPresent(SDL_Renderer * renderer); -static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static void GL_DestroyRenderer(SDL_Renderer * renderer); -static int GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh); -static int GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture); - -SDL_RenderDriver GL_RenderDriver = { - GL_CreateRenderer, - { - "opengl", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), - 1, - {SDL_PIXELFORMAT_ARGB8888}, - 0, - 0} -}; - typedef struct GL_FBOList GL_FBOList; struct GL_FBOList @@ -113,6 +62,25 @@ struct GL_FBOList typedef struct { + SDL_bool viewport_dirty; + SDL_Rect viewport; + SDL_Texture *texture; + SDL_Texture *target; + int drawablew; + int drawableh; + SDL_BlendMode blend; + GL_Shader shader; + SDL_bool cliprect_enabled_dirty; + SDL_bool cliprect_enabled; + SDL_bool cliprect_dirty; + SDL_Rect cliprect; + SDL_bool texturing; + Uint32 color; + Uint32 clear_color; +} GL_DrawStateCache; + +typedef struct +{ SDL_GLContext context; SDL_bool debug_enabled; @@ -122,14 +90,10 @@ typedef struct GLDEBUGPROCARB next_error_callback; GLvoid *next_error_userparam; + GLenum textype; + SDL_bool GL_ARB_texture_non_power_of_two_supported; SDL_bool GL_ARB_texture_rectangle_supported; - struct { - GL_Shader shader; - Uint32 color; - SDL_BlendMode blendMode; - } current; - SDL_bool GL_EXT_framebuffer_object_supported; GL_FBOList *framebuffers; @@ -152,12 +116,12 @@ typedef struct /* Shader support */ GL_ShaderContext *shaders; + GL_DrawStateCache drawstate; } GL_RenderData; typedef struct { GLuint texture; - GLenum type; GLfloat texw; GLfloat texh; GLenum format; @@ -284,21 +248,15 @@ GL_LoadFunctions(GL_RenderData * data) return 0; } -static SDL_GLContext SDL_CurrentContext = NULL; - static int GL_ActivateRenderer(SDL_Renderer * renderer) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - if (SDL_CurrentContext != data->context || - SDL_GL_GetCurrentContext() != data->context) { + if (SDL_GL_GetCurrentContext() != data->context) { if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) { return -1; } - SDL_CurrentContext = data->context; - - GL_UpdateViewport(renderer); } GL_ClearErrors(renderer); @@ -306,33 +264,6 @@ GL_ActivateRenderer(SDL_Renderer * renderer) return 0; } -/* This is called if we need to invalidate all of the SDL OpenGL state */ -static void -GL_ResetState(SDL_Renderer *renderer) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - - if (SDL_GL_GetCurrentContext() == data->context) { - GL_UpdateViewport(renderer); - } else { - GL_ActivateRenderer(renderer); - } - - data->current.shader = SHADER_NONE; - data->current.color = 0xffffffff; - data->current.blendMode = SDL_BLENDMODE_INVALID; - - data->glDisable(GL_DEPTH_TEST); - data->glDisable(GL_CULL_FACE); - /* This ended up causing video discrepancies between OpenGL and Direct3D */ - /* data->glEnable(GL_LINE_SMOOTH); */ - - data->glMatrixMode(GL_MODELVIEW); - data->glLoadIdentity(); - - GL_CheckError("", renderer); -} - static void APIENTRY GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam) { @@ -384,215 +315,6 @@ GL_GetFBO(GL_RenderData *data, Uint32 w, Uint32 h) return result; } -SDL_Renderer * -GL_CreateRenderer(SDL_Window * window, Uint32 flags) -{ - SDL_Renderer *renderer; - GL_RenderData *data; - GLint value; - Uint32 window_flags; - int profile_mask = 0, major = 0, minor = 0; - SDL_bool changed_window = SDL_FALSE; - - SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask); - SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); - SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); - - window_flags = SDL_GetWindowFlags(window); - if (!(window_flags & SDL_WINDOW_OPENGL) || - profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) { - - changed_window = SDL_TRUE; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); - - if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { - goto error; - } - } - - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - goto error; - } - - data = (GL_RenderData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - SDL_free(renderer); - SDL_OutOfMemory(); - goto error; - } - - renderer->WindowEvent = GL_WindowEvent; - renderer->GetOutputSize = GL_GetOutputSize; - renderer->SupportsBlendMode = GL_SupportsBlendMode; - renderer->CreateTexture = GL_CreateTexture; - renderer->UpdateTexture = GL_UpdateTexture; - renderer->UpdateTextureYUV = GL_UpdateTextureYUV; - renderer->LockTexture = GL_LockTexture; - renderer->UnlockTexture = GL_UnlockTexture; - renderer->SetRenderTarget = GL_SetRenderTarget; - renderer->UpdateViewport = GL_UpdateViewport; - renderer->UpdateClipRect = GL_UpdateClipRect; - renderer->RenderClear = GL_RenderClear; - renderer->RenderDrawPoints = GL_RenderDrawPoints; - renderer->RenderDrawLines = GL_RenderDrawLines; - renderer->RenderFillRects = GL_RenderFillRects; - renderer->RenderCopy = GL_RenderCopy; - renderer->RenderCopyEx = GL_RenderCopyEx; - renderer->RenderReadPixels = GL_RenderReadPixels; - renderer->RenderPresent = GL_RenderPresent; - renderer->DestroyTexture = GL_DestroyTexture; - renderer->DestroyRenderer = GL_DestroyRenderer; - renderer->GL_BindTexture = GL_BindTexture; - renderer->GL_UnbindTexture = GL_UnbindTexture; - renderer->info = GL_RenderDriver.info; - renderer->info.flags = SDL_RENDERER_ACCELERATED; - renderer->driverdata = data; - renderer->window = window; - - data->context = SDL_GL_CreateContext(window); - if (!data->context) { - SDL_free(renderer); - SDL_free(data); - goto error; - } - if (SDL_GL_MakeCurrent(window, data->context) < 0) { - SDL_GL_DeleteContext(data->context); - SDL_free(renderer); - SDL_free(data); - goto error; - } - - if (GL_LoadFunctions(data) < 0) { - SDL_GL_DeleteContext(data->context); - SDL_free(renderer); - SDL_free(data); - goto error; - } - -#ifdef __MACOSX__ - /* Enable multi-threaded rendering */ - /* Disabled until Ryan finishes his VBO/PBO code... - CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine); - */ -#endif - - if (flags & SDL_RENDERER_PRESENTVSYNC) { - SDL_GL_SetSwapInterval(1); - } else { - SDL_GL_SetSwapInterval(0); - } - if (SDL_GL_GetSwapInterval() > 0) { - renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; - } - - /* Check for debug output support */ - if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) == 0 && - (value & SDL_GL_CONTEXT_DEBUG_FLAG)) { - data->debug_enabled = SDL_TRUE; - } - if (data->debug_enabled && SDL_GL_ExtensionSupported("GL_ARB_debug_output")) { - PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB"); - - data->GL_ARB_debug_output_supported = SDL_TRUE; - data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)(char *)&data->next_error_callback); - data->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM_ARB, &data->next_error_userparam); - glDebugMessageCallbackARBFunc(GL_HandleDebugMessage, renderer); - - /* Make sure our callback is called when errors actually happen */ - data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - } - - if (SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) { - data->GL_ARB_texture_non_power_of_two_supported = SDL_TRUE; - } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") || - SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { - data->GL_ARB_texture_rectangle_supported = SDL_TRUE; - } - if (data->GL_ARB_texture_rectangle_supported) { - data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value); - renderer->info.max_texture_width = value; - renderer->info.max_texture_height = value; - } else { - data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); - renderer->info.max_texture_width = value; - renderer->info.max_texture_height = value; - } - - /* Check for multitexture support */ - if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) { - data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB"); - if (data->glActiveTextureARB) { - data->GL_ARB_multitexture_supported = SDL_TRUE; - data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units); - } - } - - /* Check for shader support */ - if (SDL_GetHintBoolean(SDL_HINT_RENDER_OPENGL_SHADERS, SDL_TRUE)) { - data->shaders = GL_CreateShaderContext(); - } - SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s", - data->shaders ? "ENABLED" : "DISABLED"); - - /* We support YV12 textures using 3 textures and a shader */ - if (data->shaders && data->num_texture_units >= 3) { - 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_NV12; - renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21; - } - -#ifdef __MACOSX__ - renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_UYVY; -#endif - - 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"); - renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE; - } - data->framebuffers = NULL; - - /* Set up parameters for rendering */ - GL_ResetState(renderer); - - return renderer; - -error: - if (changed_window) { - /* Uh oh, better try to put it back... */ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); - SDL_RecreateWindow(window, window_flags); - } - return NULL; -} - -static void -GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) -{ - if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED || - event->event == SDL_WINDOWEVENT_SHOWN || - event->event == SDL_WINDOWEVENT_HIDDEN) { - /* Rebind the context to the window area and update matrices */ - SDL_CurrentContext = NULL; - } -} - static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) { @@ -712,6 +434,7 @@ static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; + const GLenum textype = renderdata->textype; GL_TextureData *data; GLint internalFormat; GLenum format, type; @@ -775,19 +498,16 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) texture->driverdata = data; if (renderdata->GL_ARB_texture_non_power_of_two_supported) { - data->type = GL_TEXTURE_2D; texture_w = texture->w; texture_h = texture->h; data->texw = 1.0f; data->texh = 1.0f; } else if (renderdata->GL_ARB_texture_rectangle_supported) { - data->type = GL_TEXTURE_RECTANGLE_ARB; texture_w = texture->w; texture_h = texture->h; data->texw = (GLfloat) texture_w; data->texh = (GLfloat) texture_h; } else { - data->type = GL_TEXTURE_2D; texture_w = power_of_2(texture->w); texture_h = power_of_2(texture->h); data->texw = (GLfloat) (texture->w) / texture_w; @@ -797,17 +517,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) data->format = format; data->formattype = type; scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR; - renderdata->glEnable(data->type); - renderdata->glBindTexture(data->type, data->texture); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode); + renderdata->glEnable(textype); + renderdata->glBindTexture(textype, data->texture); + renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, scaleMode); + renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, scaleMode); /* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE and setting it causes an INVALID_ENUM error in the latest NVidia drivers. */ - if (data->type != GL_TEXTURE_RECTANGLE_ARB) { - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, + if (textype != GL_TEXTURE_RECTANGLE_ARB) { + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } #ifdef __MACOSX__ @@ -821,10 +541,10 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) #define STORAGE_SHARED_APPLE 0x85BF #endif if (texture->access == SDL_TEXTUREACCESS_STREAMING) { - renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE, + renderdata->glTexParameteri(textype, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); } else { - renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE, + renderdata->glTexParameteri(textype, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE); } if (texture->access == SDL_TEXTUREACCESS_STREAMING @@ -834,17 +554,17 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (data->pitch / SDL_BYTESPERPIXEL(texture->format))); - renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w, + renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, texture_h, 0, format, type, data->pixels); renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE); } else #endif { - renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w, + renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, texture_h, 0, format, type, NULL); } - renderdata->glDisable(data->type); + renderdata->glDisable(textype); if (GL_CheckError("glTexImage2D()", renderer) < 0) { return -1; } @@ -855,33 +575,33 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) renderdata->glGenTextures(1, &data->utexture); renderdata->glGenTextures(1, &data->vtexture); - renderdata->glEnable(data->type); + renderdata->glEnable(textype); - renderdata->glBindTexture(data->type, data->utexture); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, + renderdata->glBindTexture(textype, data->utexture); + renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, + renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2, + renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w+1)/2, (texture_h+1)/2, 0, format, type, NULL); - renderdata->glBindTexture(data->type, data->vtexture); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, + renderdata->glBindTexture(textype, data->vtexture); + renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, + renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderdata->glTexImage2D(data->type, 0, internalFormat, (texture_w+1)/2, + renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w+1)/2, (texture_h+1)/2, 0, format, type, NULL); - renderdata->glDisable(data->type); + renderdata->glDisable(textype); } if (texture->format == SDL_PIXELFORMAT_NV12 || @@ -889,20 +609,20 @@ GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) data->nv12 = SDL_TRUE; renderdata->glGenTextures(1, &data->utexture); - renderdata->glEnable(data->type); + renderdata->glEnable(textype); - renderdata->glBindTexture(data->type, data->utexture); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, + renderdata->glBindTexture(textype, data->utexture); + renderdata->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, + renderdata->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, + renderdata->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, (texture_w+1)/2, + renderdata->glTexImage2D(textype, 0, GL_LUMINANCE_ALPHA, (texture_w+1)/2, (texture_h+1)/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); - renderdata->glDisable(data->type); + renderdata->glDisable(textype); } return GL_CheckError("", renderer); @@ -913,6 +633,7 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; + const GLenum textype = renderdata->textype; GL_TextureData *data = (GL_TextureData *) texture->driverdata; const int texturebpp = SDL_BYTESPERPIXEL(texture->format); @@ -920,11 +641,11 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, GL_ActivateRenderer(renderer); - renderdata->glEnable(data->type); - renderdata->glBindTexture(data->type, data->texture); + renderdata->glEnable(textype); + renderdata->glBindTexture(textype, data->texture); renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp)); - renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w, + renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h, data->format, data->formattype, pixels); if (data->yuv) { @@ -933,22 +654,22 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, /* Skip to the correct offset into the next texture */ pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); if (texture->format == SDL_PIXELFORMAT_YV12) { - renderdata->glBindTexture(data->type, data->vtexture); + renderdata->glBindTexture(textype, data->vtexture); } else { - renderdata->glBindTexture(data->type, data->utexture); + renderdata->glBindTexture(textype, data->utexture); } - renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, + renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2, (rect->w+1)/2, (rect->h+1)/2, data->format, data->formattype, pixels); /* Skip to the correct offset into the next texture */ pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); if (texture->format == SDL_PIXELFORMAT_YV12) { - renderdata->glBindTexture(data->type, data->utexture); + renderdata->glBindTexture(textype, data->utexture); } else { - renderdata->glBindTexture(data->type, data->vtexture); + renderdata->glBindTexture(textype, data->vtexture); } - renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, + renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2, (rect->w+1)/2, (rect->h+1)/2, data->format, data->formattype, pixels); } @@ -958,12 +679,12 @@ GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, /* Skip to the correct offset into the next texture */ pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); - renderdata->glBindTexture(data->type, data->utexture); - renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, + renderdata->glBindTexture(textype, data->utexture); + renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2, (rect->w + 1)/2, (rect->h + 1)/2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels); } - renderdata->glDisable(data->type); + renderdata->glDisable(textype); return GL_CheckError("glTexSubImage2D()", renderer); } @@ -976,30 +697,31 @@ GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, const Uint8 *Vplane, int Vpitch) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; + const GLenum textype = renderdata->textype; GL_TextureData *data = (GL_TextureData *) texture->driverdata; GL_ActivateRenderer(renderer); - renderdata->glEnable(data->type); - renderdata->glBindTexture(data->type, data->texture); + renderdata->glEnable(textype); + renderdata->glBindTexture(textype, data->texture); renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch); - renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w, + renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, rect->h, data->format, data->formattype, Yplane); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch); - renderdata->glBindTexture(data->type, data->utexture); - renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, + renderdata->glBindTexture(textype, data->utexture); + renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2, (rect->w + 1)/2, (rect->h + 1)/2, data->format, data->formattype, Uplane); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch); - renderdata->glBindTexture(data->type, data->vtexture); - renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2, + renderdata->glBindTexture(textype, data->vtexture); + renderdata->glTexSubImage2D(textype, 0, rect->x/2, rect->y/2, (rect->w + 1)/2, (rect->h + 1)/2, data->format, data->formattype, Vplane); - renderdata->glDisable(data->type); + renderdata->glDisable(textype); return GL_CheckError("glTexSubImage2D()", renderer); } @@ -1053,7 +775,7 @@ GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) 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, texturedata->type, texturedata->texture, 0); + data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, data->textype, texturedata->texture, 0); /* Check FBO status */ status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { @@ -1062,337 +784,71 @@ GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return 0; } +/* !!! FIXME: all these Queue* calls set up the vertex buffer the way the immediate mode + !!! FIXME: renderer wants it, but this might want to operate differently if we move to + !!! FIXME: VBOs at some point. */ static int -GL_UpdateViewport(SDL_Renderer * renderer) +GL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - - if (SDL_CurrentContext != data->context) { - /* We'll update the viewport after we rebind the context */ - return 0; - } - - if (renderer->target) { - data->glViewport(renderer->viewport.x, renderer->viewport.y, - renderer->viewport.w, renderer->viewport.h); - } else { - int w, h; - - SDL_GL_GetDrawableSize(renderer->window, &w, &h); - data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h), - renderer->viewport.w, renderer->viewport.h); - } - - data->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - if (renderer->viewport.w && renderer->viewport.h) { - if (renderer->target) { - data->glOrtho((GLdouble) 0, - (GLdouble) renderer->viewport.w, - (GLdouble) 0, - (GLdouble) renderer->viewport.h, - 0.0, 1.0); - } else { - data->glOrtho((GLdouble) 0, - (GLdouble) renderer->viewport.w, - (GLdouble) renderer->viewport.h, - (GLdouble) 0, - 0.0, 1.0); - } - } - data->glMatrixMode(GL_MODELVIEW); - - return GL_CheckError("", renderer); + return 0; /* nothing to do in this backend. */ } static int -GL_UpdateClipRect(SDL_Renderer * renderer) +GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - - if (renderer->clipping_enabled) { - const SDL_Rect *rect = &renderer->clip_rect; - data->glEnable(GL_SCISSOR_TEST); - if (renderer->target) { - data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h); - } else { - int w, h; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - SDL_GL_GetDrawableSize(renderer->window, &w, &h); - data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h); - } - } else { - data->glDisable(GL_SCISSOR_TEST); - } - return 0; -} - -static void -GL_SetShader(GL_RenderData * data, GL_Shader shader) -{ - if (data->shaders && shader != data->current.shader) { - GL_SelectShader(data->shaders, shader); - data->current.shader = shader; - } -} - -static void -GL_SetColor(GL_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); - - if (color != data->current.color) { - data->glColor4f((GLfloat) r * inv255f, - (GLfloat) g * inv255f, - (GLfloat) b * inv255f, - (GLfloat) a * inv255f); - data->current.color = color; - } -} - -static void -GL_SetBlendMode(GL_RenderData * data, SDL_BlendMode blendMode) -{ - if (blendMode != data->current.blendMode) { - if (blendMode == SDL_BLENDMODE_NONE) { - data->glDisable(GL_BLEND); - } else { - data->glEnable(GL_BLEND); - data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode))); - data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode))); - } - data->current.blendMode = blendMode; - } -} - -static void -GL_SetDrawingState(SDL_Renderer * renderer) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - - GL_ActivateRenderer(renderer); - - GL_SetColor(data, renderer->r, - renderer->g, - renderer->b, - renderer->a); - - GL_SetBlendMode(data, renderer->blendMode); - - GL_SetShader(data, SHADER_SOLID); -} - -static int -GL_RenderClear(SDL_Renderer * renderer) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - - GL_ActivateRenderer(renderer); - - data->glClearColor((GLfloat) renderer->r * inv255f, - (GLfloat) renderer->g * inv255f, - (GLfloat) renderer->b * inv255f, - (GLfloat) renderer->a * inv255f); - - if (renderer->clipping_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } - - data->glClear(GL_COLOR_BUFFER_BIT); - - if (renderer->clipping_enabled) { - data->glEnable(GL_SCISSOR_TEST); + if (!verts) { + return -1; } - return 0; -} - -static int -GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - int i; - - GL_SetDrawingState(renderer); - - data->glBegin(GL_POINTS); - for (i = 0; i < count; ++i) { - data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); + cmd->data.draw.count = count; + for (i = 0; i < count; i++) { + *(verts++) = 0.5f + points[i].x; + *(verts++) = 0.5f + points[i].y; } - data->glEnd(); return 0; } static int -GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) +GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - int i; - - GL_SetDrawingState(renderer); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - if (count > 2 && - points[0].x == points[count-1].x && points[0].y == points[count-1].y) { - data->glBegin(GL_LINE_LOOP); - /* GL_LINE_LOOP takes care of the final segment */ - --count; - for (i = 0; i < count; ++i) { - data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); - } - data->glEnd(); - } else { -#if defined(__MACOSX__) || defined(__WIN32__) -#else - int x1, y1, x2, y2; -#endif - - data->glBegin(GL_LINE_STRIP); - for (i = 0; i < count; ++i) { - data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); - } - data->glEnd(); - - /* The line is half open, so we need one more point to complete it. - * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html - * If we have to, we can use vertical line and horizontal line textures - * for vertical and horizontal lines, and then create custom textures - * for diagonal lines and software render those. It's terrible, but at - * least it would be pixel perfect. - */ - data->glBegin(GL_POINTS); -#if defined(__MACOSX__) || defined(__WIN32__) - /* Mac OS X and Windows seem to always leave the last point open */ - data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y); -#else - /* Linux seems to leave the right-most or bottom-most point open */ - x1 = points[0].x; - y1 = points[0].y; - x2 = points[count-1].x; - y2 = points[count-1].y; - - if (x1 > x2) { - data->glVertex2f(0.5f + x1, 0.5f + y1); - } else if (x2 > x1) { - data->glVertex2f(0.5f + x2, 0.5f + y2); - } - if (y1 > y2) { - data->glVertex2f(0.5f + x1, 0.5f + y1); - } else if (y2 > y1) { - data->glVertex2f(0.5f + x2, 0.5f + y2); - } -#endif - data->glEnd(); + if (!verts) { + return -1; } - return GL_CheckError("", renderer); -} -static int -GL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - int i; - - GL_SetDrawingState(renderer); - - for (i = 0; i < count; ++i) { + cmd->data.draw.count = count; + for (i = 0; i < count; i++) { const SDL_FRect *rect = &rects[i]; - - data->glRectf(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h); + *(verts++) = rect->x; + *(verts++) = rect->y; + *(verts++) = rect->x + rect->w; + *(verts++) = rect->y + rect->h; } - return GL_CheckError("", renderer); -} - -static int -GL_SetupCopy(SDL_Renderer * renderer, SDL_Texture * texture) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; - - data->glEnable(texturedata->type); - if (texturedata->yuv) { - data->glActiveTextureARB(GL_TEXTURE2_ARB); - data->glBindTexture(texturedata->type, texturedata->vtexture); - - data->glActiveTextureARB(GL_TEXTURE1_ARB); - data->glBindTexture(texturedata->type, texturedata->utexture); - data->glActiveTextureARB(GL_TEXTURE0_ARB); - } - if (texturedata->nv12) { - data->glActiveTextureARB(GL_TEXTURE1_ARB); - data->glBindTexture(texturedata->type, texturedata->utexture); - - data->glActiveTextureARB(GL_TEXTURE0_ARB); - } - data->glBindTexture(texturedata->type, texturedata->texture); - - if (texture->modMode) { - GL_SetColor(data, texture->r, texture->g, texture->b, texture->a); - } else { - GL_SetColor(data, 255, 255, 255, 255); - } - - GL_SetBlendMode(data, texture->blendMode); - - if (texturedata->yuv || texturedata->nv12) { - switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { - case SDL_YUV_CONVERSION_JPEG: - if (texturedata->yuv) { - GL_SetShader(data, SHADER_YUV_JPEG); - } else if (texture->format == SDL_PIXELFORMAT_NV12) { - GL_SetShader(data, SHADER_NV12_JPEG); - } else { - GL_SetShader(data, SHADER_NV21_JPEG); - } - break; - case SDL_YUV_CONVERSION_BT601: - if (texturedata->yuv) { - GL_SetShader(data, SHADER_YUV_BT601); - } else if (texture->format == SDL_PIXELFORMAT_NV12) { - GL_SetShader(data, SHADER_NV12_BT601); - } else { - GL_SetShader(data, SHADER_NV21_BT601); - } - break; - case SDL_YUV_CONVERSION_BT709: - if (texturedata->yuv) { - GL_SetShader(data, SHADER_YUV_BT709); - } else if (texture->format == SDL_PIXELFORMAT_NV12) { - GL_SetShader(data, SHADER_NV12_BT709); - } else { - GL_SetShader(data, SHADER_NV21_BT709); - } - break; - default: - return SDL_SetError("Unsupported YUV conversion mode"); - } - } else { - GL_SetShader(data, SHADER_RGB); - } return 0; } static int -GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) +GL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 8 * sizeof (GLfloat), 0, &cmd->data.draw.first); - GL_ActivateRenderer(renderer); - - if (GL_SetupCopy(renderer, texture) < 0) { + if (!verts) { return -1; } + cmd->data.draw.count = 1; + minx = dstrect->x; miny = dstrect->y; maxx = dstrect->x + dstrect->w; @@ -1407,36 +863,30 @@ GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; maxv *= texturedata->texh; - data->glBegin(GL_TRIANGLE_STRIP); - data->glTexCoord2f(minu, minv); - data->glVertex2f(minx, miny); - data->glTexCoord2f(maxu, minv); - data->glVertex2f(maxx, miny); - data->glTexCoord2f(minu, maxv); - data->glVertex2f(minx, maxy); - data->glTexCoord2f(maxu, maxv); - data->glVertex2f(maxx, maxy); - data->glEnd(); - - data->glDisable(texturedata->type); - - return GL_CheckError("", renderer); + cmd->data.draw.count = 1; + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = maxy; + *(verts++) = minu; + *(verts++) = maxu; + *(verts++) = minv; + *(verts++) = maxv; + return 0; } static int -GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +GL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; GLfloat centerx, centery; GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 11 * sizeof (GLfloat), 0, &cmd->data.draw.first); - GL_ActivateRenderer(renderer); - - if (GL_SetupCopy(renderer, texture) < 0) { + if (!verts) { return -1; } @@ -1470,24 +920,383 @@ GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; maxv *= texturedata->texh; - /* Translate to flip, rotate, translate to position */ - data->glPushMatrix(); - data->glTranslatef((GLfloat)dstrect->x + centerx, (GLfloat)dstrect->y + centery, (GLfloat)0.0); - data->glRotated(angle, (GLdouble)0.0, (GLdouble)0.0, (GLdouble)1.0); - - data->glBegin(GL_TRIANGLE_STRIP); - data->glTexCoord2f(minu, minv); - data->glVertex2f(minx, miny); - data->glTexCoord2f(maxu, minv); - data->glVertex2f(maxx, miny); - data->glTexCoord2f(minu, maxv); - data->glVertex2f(minx, maxy); - data->glTexCoord2f(maxu, maxv); - data->glVertex2f(maxx, maxy); - data->glEnd(); - data->glPopMatrix(); - - data->glDisable(texturedata->type); + cmd->data.draw.count = 1; + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = maxy; + *(verts++) = minu; + *(verts++) = maxu; + *(verts++) = minv; + *(verts++) = maxv; + *(verts++) = (GLfloat) dstrect->x + centerx; + *(verts++) = (GLfloat) dstrect->y + centery; + *(verts++) = (GLfloat) angle; + return 0; +} + +static void +SetDrawState(GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_Shader shader) +{ + const SDL_BlendMode blend = cmd->data.draw.blend; + + if (data->drawstate.viewport_dirty) { + const SDL_bool istarget = data->drawstate.target != NULL; + const SDL_Rect *viewport = &data->drawstate.viewport; + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport->x, + istarget ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h), + viewport->w, viewport->h); + if (viewport->w && viewport->h) { + data->glOrtho((GLdouble) 0, (GLdouble) viewport->w, + (GLdouble) istarget ? 0 : viewport->h, + (GLdouble) istarget ? viewport->h : 0, + 0.0, 1.0); + } + data->glMatrixMode(GL_MODELVIEW); + data->drawstate.viewport_dirty = SDL_FALSE; + } + + if (data->drawstate.cliprect_enabled_dirty) { + if (!data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } else { + data->glEnable(GL_SCISSOR_TEST); + } + data->drawstate.cliprect_enabled_dirty = SDL_FALSE; + } + + if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + const SDL_Rect *rect = &data->drawstate.cliprect; + data->glScissor(viewport->x + rect->x, + data->drawstate.target ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h, + rect->w, rect->h); + data->drawstate.cliprect_dirty = SDL_FALSE; + } + + if (blend != data->drawstate.blend) { + if (blend == SDL_BLENDMODE_NONE) { + data->glDisable(GL_BLEND); + } else { + data->glEnable(GL_BLEND); + data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); + data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blend))); + } + data->drawstate.blend = blend; + } + + if (data->shaders && (shader != data->drawstate.shader)) { + GL_SelectShader(data->shaders, shader); + data->drawstate.shader = shader; + } + + if ((cmd->data.draw.texture != NULL) != data->drawstate.texturing) { + if (cmd->data.draw.texture == NULL) { + data->glDisable(data->textype); + data->drawstate.texturing = SDL_FALSE; + } else { + data->glEnable(data->textype); + data->drawstate.texturing = SDL_TRUE; + } + } +} + +static void +SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd) +{ + SDL_Texture *texture = cmd->data.draw.texture; + const GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; + GL_Shader shader = SHADER_RGB; + + if (data->shaders) { + if (texturedata->yuv || texturedata->nv12) { + switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { + case SDL_YUV_CONVERSION_JPEG: + if (texturedata->yuv) { + shader = SHADER_YUV_JPEG; + } else if (texture->format == SDL_PIXELFORMAT_NV12) { + shader = SHADER_NV12_JPEG; + } else { + shader = SHADER_NV21_JPEG; + } + break; + case SDL_YUV_CONVERSION_BT601: + if (texturedata->yuv) { + shader = SHADER_YUV_BT601; + } else if (texture->format == SDL_PIXELFORMAT_NV12) { + shader = SHADER_NV12_BT601; + } else { + shader = SHADER_NV21_BT601; + } + break; + case SDL_YUV_CONVERSION_BT709: + if (texturedata->yuv) { + shader = SHADER_YUV_BT709; + } else if (texture->format == SDL_PIXELFORMAT_NV12) { + shader = SHADER_NV12_BT709; + } else { + shader = SHADER_NV21_BT709; + } + break; + default: + SDL_assert(!"unsupported YUV conversion mode"); + break; + } + } + } + + SetDrawState(data, cmd, shader); + + if (texture != data->drawstate.texture) { + const GLenum textype = data->textype; + if (texturedata->yuv) { + data->glActiveTextureARB(GL_TEXTURE2_ARB); + data->glBindTexture(textype, texturedata->vtexture); + + data->glActiveTextureARB(GL_TEXTURE1_ARB); + data->glBindTexture(textype, texturedata->utexture); + } + if (texturedata->nv12) { + data->glActiveTextureARB(GL_TEXTURE1_ARB); + data->glBindTexture(textype, texturedata->utexture); + } + data->glActiveTextureARB(GL_TEXTURE0_ARB); + data->glBindTexture(textype, texturedata->texture); + + data->drawstate.texture = texture; + } +} + +static int +GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + /* !!! FIXME: it'd be nice to use a vertex buffer instead of immediate mode... */ + GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + size_t i; + + if (GL_ActivateRenderer(renderer) < 0) { + return -1; + } + + data->drawstate.target = renderer->target; + if (!data->drawstate.target) { + SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh); + } + + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + if (color != data->drawstate.color) { + data->glColor4f((GLfloat) r * inv255f, + (GLfloat) g * inv255f, + (GLfloat) b * inv255f, + (GLfloat) a * inv255f); + data->drawstate.color = color; + } + break; + } + + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_Rect *viewport = &data->drawstate.viewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); + data->drawstate.viewport_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + data->drawstate.cliprect_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_CLEAR: { + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + if (color != data->drawstate.clear_color) { + const GLfloat fr = ((GLfloat) r) * inv255f; + const GLfloat fg = ((GLfloat) g) * inv255f; + const GLfloat fb = ((GLfloat) b) * inv255f; + const GLfloat fa = ((GLfloat) a) * inv255f; + data->glClearColor(fr, fg, fb, fa); + data->drawstate.clear_color = color; + } + + if (data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + + data->glClear(GL_COLOR_BUFFER_BIT); + + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: { + const size_t count = cmd->data.draw.count; + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + SetDrawState(data, cmd, SHADER_SOLID); + data->glBegin(GL_POINTS); + for (i = 0; i < count; i++, verts += 2) { + data->glVertex2f(verts[0], verts[1]); + } + data->glEnd(); + break; + } + + case SDL_RENDERCMD_DRAW_LINES: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + const size_t count = cmd->data.draw.count; + SetDrawState(data, cmd, SHADER_SOLID); + if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) { + data->glBegin(GL_LINE_LOOP); + /* GL_LINE_LOOP takes care of the final segment */ + for (i = 1; i < count; ++i, verts += 2) { + data->glVertex2f(verts[0], verts[1]); + } + data->glEnd(); + } else { + #if defined(__MACOSX__) || defined(__WIN32__) + #else + int x1, y1, x2, y2; + #endif + + data->glBegin(GL_LINE_STRIP); + for (i = 0; i < count; ++i, verts += 2) { + data->glVertex2f(verts[0], verts[1]); + } + data->glEnd(); + + /* The line is half open, so we need one more point to complete it. + * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html + * If we have to, we can use vertical line and horizontal line textures + * for vertical and horizontal lines, and then create custom textures + * for diagonal lines and software render those. It's terrible, but at + * least it would be pixel perfect. + */ + + data->glBegin(GL_POINTS); + #if defined(__MACOSX__) || defined(__WIN32__) + /* Mac OS X and Windows seem to always leave the last point open */ + data->glVertex2f(verts[(count-1)*2], verts[(count*2)-1]); + #else + /* Linux seems to leave the right-most or bottom-most point open */ + x1 = verts[0]; + y1 = verts[1]; + x2 = verts[(count-1)*2]; + y2 = verts[(count*2)-1]; + + if (x1 > x2) { + data->glVertex2f(x1, y1); + } else if (x2 > x1) { + data->glVertex2f(x2, y2); + } + if (y1 > y2) { + data->glVertex2f(x1, y1); + } else if (y2 > y1) { + data->glVertex2f(x2, y2); + } + #endif + data->glEnd(); + } + break; + } + + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + SetDrawState(data, cmd, SHADER_SOLID); + for (i = 0; i < count; ++i, verts += 4) { + data->glRectf(verts[0], verts[1], verts[2], verts[3]); + } + break; + } + + case SDL_RENDERCMD_COPY: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + const GLfloat minx = verts[0]; + const GLfloat miny = verts[1]; + const GLfloat maxx = verts[2]; + const GLfloat maxy = verts[3]; + const GLfloat minu = verts[4]; + const GLfloat maxu = verts[5]; + const GLfloat minv = verts[6]; + const GLfloat maxv = verts[7]; + SetCopyState(data, cmd); + data->glBegin(GL_TRIANGLE_STRIP); + data->glTexCoord2f(minu, minv); + data->glVertex2f(minx, miny); + data->glTexCoord2f(maxu, minv); + data->glVertex2f(maxx, miny); + data->glTexCoord2f(minu, maxv); + data->glVertex2f(minx, maxy); + data->glTexCoord2f(maxu, maxv); + data->glVertex2f(maxx, maxy); + data->glEnd(); + break; + } + + case SDL_RENDERCMD_COPY_EX: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + const GLfloat minx = verts[0]; + const GLfloat miny = verts[1]; + const GLfloat maxx = verts[2]; + const GLfloat maxy = verts[3]; + const GLfloat minu = verts[4]; + const GLfloat maxu = verts[5]; + const GLfloat minv = verts[6]; + const GLfloat maxv = verts[7]; + const GLfloat translatex = verts[8]; + const GLfloat translatey = verts[9]; + const GLdouble angle = verts[10]; + SetCopyState(data, cmd); + + /* Translate to flip, rotate, translate to position */ + data->glPushMatrix(); + data->glTranslatef(translatex, translatey, 0.0f); + data->glRotated(angle, 0.0, 0.0, 1.0); + data->glBegin(GL_TRIANGLE_STRIP); + data->glTexCoord2f(minu, minv); + data->glVertex2f(minx, miny); + data->glTexCoord2f(maxu, minv); + data->glVertex2f(maxx, miny); + data->glTexCoord2f(minu, maxv); + data->glVertex2f(minx, maxy); + data->glTexCoord2f(maxu, maxv); + data->glVertex2f(maxx, maxy); + data->glEnd(); + data->glPopMatrix(); + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } return GL_CheckError("", renderer); } @@ -1539,10 +1348,11 @@ GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, /* Flip the rows to be top-down if necessary */ if (!renderer->target) { + SDL_bool isstack; length = rect->w * SDL_BYTESPERPIXEL(temp_format); src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch; dst = (Uint8*)temp_pixels; - tmp = SDL_stack_alloc(Uint8, length); + tmp = SDL_small_alloc(Uint8, length, &isstack); rows = rect->h / 2; while (rows--) { SDL_memcpy(tmp, dst, length); @@ -1551,7 +1361,7 @@ GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, dst += temp_pitch; src -= temp_pitch; } - SDL_stack_free(tmp); + SDL_small_free(tmp, isstack); } status = SDL_ConvertPixels(rect->w, rect->h, @@ -1636,19 +1446,21 @@ GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, floa { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; + const GLenum textype = data->textype; + GL_ActivateRenderer(renderer); - data->glEnable(texturedata->type); + data->glEnable(textype); if (texturedata->yuv) { data->glActiveTextureARB(GL_TEXTURE2_ARB); - data->glBindTexture(texturedata->type, texturedata->vtexture); + data->glBindTexture(textype, texturedata->vtexture); data->glActiveTextureARB(GL_TEXTURE1_ARB); - data->glBindTexture(texturedata->type, texturedata->utexture); + data->glBindTexture(textype, texturedata->utexture); data->glActiveTextureARB(GL_TEXTURE0_ARB); } - data->glBindTexture(texturedata->type, texturedata->texture); + data->glBindTexture(textype, texturedata->texture); if(texw) *texw = (float)texturedata->texw; if(texh) *texh = (float)texturedata->texh; @@ -1661,23 +1473,252 @@ GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; + const GLenum textype = data->textype; + GL_ActivateRenderer(renderer); if (texturedata->yuv) { data->glActiveTextureARB(GL_TEXTURE2_ARB); - data->glDisable(texturedata->type); + data->glDisable(textype); data->glActiveTextureARB(GL_TEXTURE1_ARB); - data->glDisable(texturedata->type); + data->glDisable(textype); data->glActiveTextureARB(GL_TEXTURE0_ARB); } - data->glDisable(texturedata->type); + data->glDisable(textype); return 0; } + +SDL_Renderer * +GL_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Renderer *renderer; + GL_RenderData *data; + GLint value; + Uint32 window_flags; + int profile_mask = 0, major = 0, minor = 0; + SDL_bool changed_window = SDL_FALSE; + + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + + window_flags = SDL_GetWindowFlags(window); + if (!(window_flags & SDL_WINDOW_OPENGL) || + profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) { + + changed_window = SDL_TRUE; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); + + if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { + goto error; + } + } + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + goto error; + } + + data = (GL_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + SDL_free(renderer); + SDL_OutOfMemory(); + goto error; + } + + renderer->GetOutputSize = GL_GetOutputSize; + renderer->SupportsBlendMode = GL_SupportsBlendMode; + renderer->CreateTexture = GL_CreateTexture; + renderer->UpdateTexture = GL_UpdateTexture; + renderer->UpdateTextureYUV = GL_UpdateTextureYUV; + renderer->LockTexture = GL_LockTexture; + renderer->UnlockTexture = GL_UnlockTexture; + renderer->SetRenderTarget = GL_SetRenderTarget; + renderer->QueueSetViewport = GL_QueueSetViewport; + renderer->QueueSetDrawColor = GL_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = GL_QueueDrawPoints; + renderer->QueueDrawLines = GL_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = GL_QueueFillRects; + renderer->QueueCopy = GL_QueueCopy; + renderer->QueueCopyEx = GL_QueueCopyEx; + renderer->RunCommandQueue = GL_RunCommandQueue; + renderer->RenderReadPixels = GL_RenderReadPixels; + renderer->RenderPresent = GL_RenderPresent; + renderer->DestroyTexture = GL_DestroyTexture; + renderer->DestroyRenderer = GL_DestroyRenderer; + renderer->GL_BindTexture = GL_BindTexture; + renderer->GL_UnbindTexture = GL_UnbindTexture; + renderer->info = GL_RenderDriver.info; + renderer->info.flags = SDL_RENDERER_ACCELERATED; + renderer->driverdata = data; + renderer->window = window; + + data->context = SDL_GL_CreateContext(window); + if (!data->context) { + SDL_free(renderer); + SDL_free(data); + goto error; + } + if (SDL_GL_MakeCurrent(window, data->context) < 0) { + SDL_GL_DeleteContext(data->context); + SDL_free(renderer); + SDL_free(data); + goto error; + } + + if (GL_LoadFunctions(data) < 0) { + SDL_GL_DeleteContext(data->context); + SDL_free(renderer); + SDL_free(data); + goto error; + } + +#ifdef __MACOSX__ + /* Enable multi-threaded rendering */ + /* Disabled until Ryan finishes his VBO/PBO code... + CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine); + */ +#endif + + if (flags & SDL_RENDERER_PRESENTVSYNC) { + SDL_GL_SetSwapInterval(1); + } else { + SDL_GL_SetSwapInterval(0); + } + if (SDL_GL_GetSwapInterval() > 0) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + + /* Check for debug output support */ + if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) == 0 && + (value & SDL_GL_CONTEXT_DEBUG_FLAG)) { + data->debug_enabled = SDL_TRUE; + } + if (data->debug_enabled && SDL_GL_ExtensionSupported("GL_ARB_debug_output")) { + PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB"); + + data->GL_ARB_debug_output_supported = SDL_TRUE; + data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)(char *)&data->next_error_callback); + data->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM_ARB, &data->next_error_userparam); + glDebugMessageCallbackARBFunc(GL_HandleDebugMessage, renderer); + + /* Make sure our callback is called when errors actually happen */ + data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + } + + data->textype = GL_TEXTURE_2D; + if (SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) { + data->GL_ARB_texture_non_power_of_two_supported = SDL_TRUE; + } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") || + SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { + data->GL_ARB_texture_rectangle_supported = SDL_TRUE; + data->textype = GL_TEXTURE_RECTANGLE_ARB; + } + if (data->GL_ARB_texture_rectangle_supported) { + data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value); + renderer->info.max_texture_width = value; + renderer->info.max_texture_height = value; + } else { + data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); + renderer->info.max_texture_width = value; + renderer->info.max_texture_height = value; + } + + /* Check for multitexture support */ + if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) { + data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB"); + if (data->glActiveTextureARB) { + data->GL_ARB_multitexture_supported = SDL_TRUE; + data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units); + } + } + + /* Check for shader support */ + if (SDL_GetHintBoolean(SDL_HINT_RENDER_OPENGL_SHADERS, SDL_TRUE)) { + data->shaders = GL_CreateShaderContext(); + } + SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s", + data->shaders ? "ENABLED" : "DISABLED"); + + /* We support YV12 textures using 3 textures and a shader */ + if (data->shaders && data->num_texture_units >= 3) { + 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_NV12; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21; + } + +#ifdef __MACOSX__ + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_UYVY; +#endif + + 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"); + renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE; + } + data->framebuffers = NULL; + + /* Set up parameters for rendering */ + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + data->glDisable(GL_DEPTH_TEST); + data->glDisable(GL_CULL_FACE); + data->glDisable(GL_SCISSOR_TEST); + data->glDisable(data->textype); + data->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + data->glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + /* This ended up causing video discrepancies between OpenGL and Direct3D */ + /* data->glEnable(GL_LINE_SMOOTH); */ + + data->drawstate.blend = SDL_BLENDMODE_INVALID; + data->drawstate.shader = SHADER_INVALID; + data->drawstate.color = 0xFFFFFFFF; + data->drawstate.clear_color = 0xFFFFFFFF; + + return renderer; + +error: + if (changed_window) { + /* Uh oh, better try to put it back... */ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); + SDL_RecreateWindow(window, window_flags); + } + return NULL; +} + + +SDL_RenderDriver GL_RenderDriver = { + GL_CreateRenderer, + { + "opengl", + (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), + 1, + {SDL_PIXELFORMAT_ARGB8888}, + 0, + 0} +}; + + #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/opengl/SDL_shaders_gl.c b/src/render/opengl/SDL_shaders_gl.c index 251b54d130..650b244639 100644 --- a/src/render/opengl/SDL_shaders_gl.c +++ b/src/render/opengl/SDL_shaders_gl.c @@ -340,11 +340,12 @@ CompileShader(GL_ShaderContext *ctx, GLhandleARB shader, const char *defines, co ctx->glCompileShaderARB(shader); ctx->glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (status == 0) { + SDL_bool isstack; GLint length; char *info; ctx->glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); - info = SDL_stack_alloc(char, length+1); + info = SDL_small_alloc(char, length+1, &isstack); ctx->glGetInfoLogARB(shader, length, NULL, info); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to compile shader:\n%s%s\n%s", defines, source, info); @@ -352,7 +353,7 @@ CompileShader(GL_ShaderContext *ctx, GLhandleARB shader, const char *defines, co fprintf(stderr, "Failed to compile shader:\n%s%s\n%s", defines, source, info); #endif - SDL_stack_free(info); + SDL_small_free(info, isstack); return SDL_FALSE; } else { diff --git a/src/render/opengl/SDL_shaders_gl.h b/src/render/opengl/SDL_shaders_gl.h index 36975214d9..6f3c8672e6 100644 --- a/src/render/opengl/SDL_shaders_gl.h +++ b/src/render/opengl/SDL_shaders_gl.h @@ -27,6 +27,7 @@ /* OpenGL shader implementation */ typedef enum { + SHADER_INVALID = -1, SHADER_NONE, SHADER_SOLID, SHADER_RGB, diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 4007dff585..a410152e7b 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -52,45 +52,6 @@ extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); static const float inv255f = 1.0f / 255.0f; -static SDL_Renderer *GLES_CreateRenderer(SDL_Window * window, Uint32 flags); -static void GLES_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static int GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h); -static SDL_bool GLES_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode); -static int GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *pixels, - int pitch); -static int GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void GLES_UnlockTexture(SDL_Renderer * renderer, - SDL_Texture * texture); -static int GLES_SetRenderTarget(SDL_Renderer * renderer, - SDL_Texture * texture); -static int GLES_UpdateViewport(SDL_Renderer * renderer); -static int GLES_UpdateClipRect(SDL_Renderer * renderer); -static int GLES_RenderClear(SDL_Renderer * renderer); -static int GLES_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int GLES_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int GLES_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, - const SDL_FRect * dstrect); -static int GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); -static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch); -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_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh); -static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture); - typedef struct GLES_FBOList GLES_FBOList; struct GLES_FBOList @@ -100,26 +61,27 @@ struct GLES_FBOList GLES_FBOList *next; }; - -SDL_RenderDriver GLES_RenderDriver = { - GLES_CreateRenderer, - { - "opengles", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC), - 1, - {SDL_PIXELFORMAT_ABGR8888}, - 0, - 0} -}; +typedef struct +{ + SDL_Rect viewport; + SDL_bool viewport_dirty; + SDL_Texture *texture; + SDL_Texture *target; + int drawablew; + int drawableh; + SDL_BlendMode blend; + SDL_bool cliprect_enabled_dirty; + SDL_bool cliprect_enabled; + SDL_bool cliprect_dirty; + SDL_Rect cliprect; + SDL_bool texturing; + Uint32 color; + Uint32 clear_color; +} GLES_DrawStateCache; typedef struct { SDL_GLContext context; - struct { - Uint32 color; - SDL_BlendMode blendMode; - SDL_bool tex_coords; - } current; #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #define SDL_PROC_OES SDL_PROC @@ -133,6 +95,8 @@ typedef struct SDL_bool GL_OES_blend_func_separate_supported; SDL_bool GL_OES_blend_equation_separate_supported; SDL_bool GL_OES_blend_subtract_supported; + + GLES_DrawStateCache drawstate; } GLES_RenderData; typedef struct @@ -215,8 +179,6 @@ static int GLES_LoadFunctions(GLES_RenderData * data) return 0; } -static SDL_GLContext SDL_CurrentContext = NULL; - static GLES_FBOList * GLES_GetFBO(GLES_RenderData *data, Uint32 w, Uint32 h) { @@ -241,178 +203,13 @@ GLES_ActivateRenderer(SDL_Renderer * renderer) { GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - if (SDL_CurrentContext != data->context) { + if (SDL_GL_GetCurrentContext() != data->context) { if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) { return -1; } - SDL_CurrentContext = data->context; - - GLES_UpdateViewport(renderer); - } - return 0; -} - -/* This is called if we need to invalidate all of the SDL OpenGL state */ -static void -GLES_ResetState(SDL_Renderer *renderer) -{ - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - - if (SDL_CurrentContext == data->context) { - GLES_UpdateViewport(renderer); - } else { - GLES_ActivateRenderer(renderer); - } - - data->current.color = 0xffffffff; - data->current.blendMode = SDL_BLENDMODE_INVALID; - data->current.tex_coords = SDL_FALSE; - - data->glDisable(GL_DEPTH_TEST); - data->glDisable(GL_CULL_FACE); - - data->glMatrixMode(GL_MODELVIEW); - data->glLoadIdentity(); - - data->glEnableClientState(GL_VERTEX_ARRAY); - data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -SDL_Renderer * -GLES_CreateRenderer(SDL_Window * window, Uint32 flags) -{ - - SDL_Renderer *renderer; - GLES_RenderData *data; - GLint value; - Uint32 window_flags; - int profile_mask = 0, major = 0, minor = 0; - SDL_bool changed_window = SDL_FALSE; - - SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask); - SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); - SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); - - window_flags = SDL_GetWindowFlags(window); - if (!(window_flags & SDL_WINDOW_OPENGL) || - profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) { - - changed_window = SDL_TRUE; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); - - if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { - goto error; - } - } - - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - goto error; - } - - data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - GLES_DestroyRenderer(renderer); - SDL_OutOfMemory(); - goto error; - } - - renderer->WindowEvent = GLES_WindowEvent; - renderer->GetOutputSize = GLES_GetOutputSize; - renderer->SupportsBlendMode = GLES_SupportsBlendMode; - renderer->CreateTexture = GLES_CreateTexture; - renderer->UpdateTexture = GLES_UpdateTexture; - renderer->LockTexture = GLES_LockTexture; - renderer->UnlockTexture = GLES_UnlockTexture; - renderer->SetRenderTarget = GLES_SetRenderTarget; - renderer->UpdateViewport = GLES_UpdateViewport; - renderer->UpdateClipRect = GLES_UpdateClipRect; - renderer->RenderClear = GLES_RenderClear; - renderer->RenderDrawPoints = GLES_RenderDrawPoints; - renderer->RenderDrawLines = GLES_RenderDrawLines; - renderer->RenderFillRects = GLES_RenderFillRects; - renderer->RenderCopy = GLES_RenderCopy; - renderer->RenderCopyEx = GLES_RenderCopyEx; - renderer->RenderReadPixels = GLES_RenderReadPixels; - renderer->RenderPresent = GLES_RenderPresent; - renderer->DestroyTexture = GLES_DestroyTexture; - renderer->DestroyRenderer = GLES_DestroyRenderer; - renderer->GL_BindTexture = GLES_BindTexture; - renderer->GL_UnbindTexture = GLES_UnbindTexture; - renderer->info = GLES_RenderDriver.info; - renderer->info.flags = SDL_RENDERER_ACCELERATED; - renderer->driverdata = data; - renderer->window = window; - - data->context = SDL_GL_CreateContext(window); - if (!data->context) { - GLES_DestroyRenderer(renderer); - goto error; - } - if (SDL_GL_MakeCurrent(window, data->context) < 0) { - GLES_DestroyRenderer(renderer); - goto error; - } - - if (GLES_LoadFunctions(data) < 0) { - GLES_DestroyRenderer(renderer); - goto error; - } - - if (flags & SDL_RENDERER_PRESENTVSYNC) { - SDL_GL_SetSwapInterval(1); - } else { - SDL_GL_SetSwapInterval(0); - } - if (SDL_GL_GetSwapInterval() > 0) { - renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; - } - - value = 0; - data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); - renderer->info.max_texture_width = value; - value = 0; - data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); - renderer->info.max_texture_height = value; - - /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */ - if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) { - data->GL_OES_framebuffer_object_supported = SDL_TRUE; - renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE; - - value = 0; - data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value); - data->window_framebuffer = (GLuint)value; } - data->framebuffers = NULL; - if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) { - data->GL_OES_blend_func_separate_supported = SDL_TRUE; - } - if (SDL_GL_ExtensionSupported("GL_OES_blend_equation_separate")) { - data->GL_OES_blend_equation_separate_supported = SDL_TRUE; - } - if (SDL_GL_ExtensionSupported("GL_OES_blend_subtract")) { - data->GL_OES_blend_subtract_supported = SDL_TRUE; - } - - /* Set up parameters for rendering */ - GLES_ResetState(renderer); - - return renderer; - -error: - if (changed_window) { - /* Uh oh, better try to put it back... */ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); - SDL_RecreateWindow(window, window_flags); - } - return NULL; + return 0; } static void @@ -420,13 +217,6 @@ GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) { GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED || - event->event == SDL_WINDOWEVENT_SHOWN || - event->event == SDL_WINDOWEVENT_HIDDEN) { - /* Rebind the context to the window area and update matrices */ - SDL_CurrentContext = NULL; - } - if (event->event == SDL_WINDOWEVENT_MINIMIZED) { /* According to Apple documentation, we need to finish drawing NOW! */ data->glFinish(); @@ -725,293 +515,77 @@ GLES_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return 0; } -static int -GLES_UpdateViewport(SDL_Renderer * renderer) -{ - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - - if (SDL_CurrentContext != data->context) { - /* We'll update the viewport after we rebind the context */ - return 0; - } - - if (renderer->target) { - data->glViewport(renderer->viewport.x, renderer->viewport.y, - renderer->viewport.w, renderer->viewport.h); - } else { - int w, h; - - SDL_GL_GetDrawableSize(renderer->window, &w, &h); - data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h), - renderer->viewport.w, renderer->viewport.h); - } - - data->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - if (renderer->viewport.w && renderer->viewport.h) { - if (renderer->target) { - data->glOrthof((GLfloat) 0, - (GLfloat) renderer->viewport.w, - (GLfloat) 0, - (GLfloat) renderer->viewport.h, - 0.0, 1.0); - } else { - data->glOrthof((GLfloat) 0, - (GLfloat) renderer->viewport.w, - (GLfloat) renderer->viewport.h, - (GLfloat) 0, - 0.0, 1.0); - } - } - data->glMatrixMode(GL_MODELVIEW); - - return 0; -} static int -GLES_UpdateClipRect(SDL_Renderer * renderer) -{ - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - - if (SDL_CurrentContext != data->context) { - /* We'll update the clip rect after we rebind the context */ - return 0; - } - - if (renderer->clipping_enabled) { - const SDL_Rect *rect = &renderer->clip_rect; - data->glEnable(GL_SCISSOR_TEST); - if (renderer->target) { - data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h); - } else { - int w, h; - - SDL_GL_GetDrawableSize(renderer->window, &w, &h); - data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h); - } - } else { - data->glDisable(GL_SCISSOR_TEST); - } - return 0; -} - -static void -GLES_SetColor(GLES_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +GLES_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); - - if (color != data->current.color) { - data->glColor4f((GLfloat) r * inv255f, - (GLfloat) g * inv255f, - (GLfloat) b * inv255f, - (GLfloat) a * inv255f); - data->current.color = color; - } -} - -static void -GLES_SetBlendMode(GLES_RenderData * data, SDL_BlendMode blendMode) -{ - if (blendMode != data->current.blendMode) { - if (blendMode == SDL_BLENDMODE_NONE) { - data->glDisable(GL_BLEND); - } else { - data->glEnable(GL_BLEND); - if (data->GL_OES_blend_func_separate_supported) { - data->glBlendFuncSeparateOES(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode))); - } else { - data->glBlendFunc(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode))); - } - if (data->GL_OES_blend_equation_separate_supported) { - data->glBlendEquationSeparateOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode)), - GetBlendEquation(SDL_GetBlendModeAlphaOperation(blendMode))); - } else if (data->GL_OES_blend_subtract_supported) { - data->glBlendEquationOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode))); - } - } - data->current.blendMode = blendMode; - } -} - -static void -GLES_SetTexCoords(GLES_RenderData * data, SDL_bool enabled) -{ - if (enabled != data->current.tex_coords) { - if (enabled) { - data->glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } else { - data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - data->current.tex_coords = enabled; - } -} - -static void -GLES_SetDrawingState(SDL_Renderer * renderer) -{ - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - - GLES_ActivateRenderer(renderer); - - GLES_SetColor(data, (GLfloat) renderer->r, - (GLfloat) renderer->g, - (GLfloat) renderer->b, - (GLfloat) renderer->a); - - GLES_SetBlendMode(data, renderer->blendMode); - - GLES_SetTexCoords(data, SDL_FALSE); + return 0; /* nothing to do in this backend. */ } static int -GLES_RenderClear(SDL_Renderer * renderer) +GLES_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - - GLES_ActivateRenderer(renderer); - - data->glClearColor((GLfloat) renderer->r * inv255f, - (GLfloat) renderer->g * inv255f, - (GLfloat) renderer->b * inv255f, - (GLfloat) renderer->a * inv255f); - - if (renderer->clipping_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } - - data->glClear(GL_COLOR_BUFFER_BIT); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - if (renderer->clipping_enabled) { - data->glEnable(GL_SCISSOR_TEST); + if (!verts) { + return -1; } - return 0; -} - -static int -GLES_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) -{ - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - GLfloat *vertices; - int idx; - - GLES_SetDrawingState(renderer); - - /* Emit the specified vertices as points */ - vertices = SDL_stack_alloc(GLfloat, count * 2); - for (idx = 0; idx < count; ++idx) { - GLfloat x = points[idx].x + 0.5f; - GLfloat y = points[idx].y + 0.5f; - - vertices[idx * 2] = x; - vertices[(idx * 2) + 1] = y; + cmd->data.draw.count = count; + for (i = 0; i < count; i++) { + *(verts++) = 0.5f + points[i].x; + *(verts++) = 0.5f + points[i].y; } - data->glVertexPointer(2, GL_FLOAT, 0, vertices); - data->glDrawArrays(GL_POINTS, 0, count); - SDL_stack_free(vertices); return 0; } static int -GLES_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) +GLES_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - GLfloat *vertices; - int idx; - - GLES_SetDrawingState(renderer); - - /* Emit a line strip including the specified vertices */ - vertices = SDL_stack_alloc(GLfloat, count * 2); - for (idx = 0; idx < count; ++idx) { - GLfloat x = points[idx].x + 0.5f; - GLfloat y = points[idx].y + 0.5f; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 8 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - vertices[idx * 2] = x; - vertices[(idx * 2) + 1] = y; + if (!verts) { + return -1; } - data->glVertexPointer(2, GL_FLOAT, 0, vertices); - if (count > 2 && - points[0].x == points[count-1].x && points[0].y == points[count-1].y) { - /* GL_LINE_LOOP takes care of the final segment */ - --count; - data->glDrawArrays(GL_LINE_LOOP, 0, count); - } else { - data->glDrawArrays(GL_LINE_STRIP, 0, count); - /* We need to close the endpoint of the line */ - data->glDrawArrays(GL_POINTS, count-1, 1); - } - SDL_stack_free(vertices); + cmd->data.draw.count = count; - return 0; -} - -static int -GLES_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, - int count) -{ - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - int i; - - GLES_SetDrawingState(renderer); - - for (i = 0; i < count; ++i) { + for (i = 0; i < count; i++) { const SDL_FRect *rect = &rects[i]; - GLfloat minx = rect->x; - GLfloat maxx = rect->x + rect->w; - GLfloat miny = rect->y; - GLfloat maxy = rect->y + rect->h; - GLfloat vertices[8]; - vertices[0] = minx; - vertices[1] = miny; - vertices[2] = maxx; - vertices[3] = miny; - vertices[4] = minx; - vertices[5] = maxy; - vertices[6] = maxx; - vertices[7] = maxy; - - data->glVertexPointer(2, GL_FLOAT, 0, vertices); - data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + const GLfloat minx = rect->x; + const GLfloat maxx = rect->x + rect->w; + const GLfloat miny = rect->y; + const GLfloat maxy = rect->y + rect->h; + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = maxy; } return 0; } static int -GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) +GLES_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; GLfloat minu, maxu, minv, maxv; - GLfloat vertices[8]; - GLfloat texCoords[8]; - - GLES_ActivateRenderer(renderer); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first); - data->glEnable(GL_TEXTURE_2D); - - data->glBindTexture(texturedata->type, texturedata->texture); - - if (texture->modMode) { - GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a); - } else { - GLES_SetColor(data, 255, 255, 255, 255); + if (!verts) { + return -1; } - GLES_SetBlendMode(data, texture->blendMode); - - GLES_SetTexCoords(data, SDL_TRUE); + cmd->data.draw.count = 1; minx = dstrect->x; miny = dstrect->y; @@ -1027,126 +601,351 @@ GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; maxv *= texturedata->texh; - vertices[0] = minx; - vertices[1] = miny; - vertices[2] = maxx; - vertices[3] = miny; - vertices[4] = minx; - vertices[5] = maxy; - vertices[6] = maxx; - vertices[7] = maxy; - - texCoords[0] = minu; - texCoords[1] = minv; - texCoords[2] = maxu; - texCoords[3] = minv; - texCoords[4] = minu; - texCoords[5] = maxv; - texCoords[6] = maxu; - texCoords[7] = maxv; - - data->glVertexPointer(2, GL_FLOAT, 0, vertices); - data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords); - data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - data->glDisable(GL_TEXTURE_2D); + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = maxy; + + *(verts++) = minu; + *(verts++) = minv; + *(verts++) = maxu; + *(verts++) = minv; + *(verts++) = minu; + *(verts++) = maxv; + *(verts++) = maxu; + *(verts++) = maxv; return 0; } static int -GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +GLES_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcquad, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; - GLfloat minu, maxu, minv, maxv; GLfloat centerx, centery; - GLfloat vertices[8]; - GLfloat texCoords[8]; - - - GLES_ActivateRenderer(renderer); - - data->glEnable(GL_TEXTURE_2D); - - data->glBindTexture(texturedata->type, texturedata->texture); + GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 19 * sizeof (GLfloat), 0, &cmd->data.draw.first); - if (texture->modMode) { - GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a); - } else { - GLES_SetColor(data, 255, 255, 255, 255); + if (!verts) { + return -1; } - GLES_SetBlendMode(data, texture->blendMode); - - GLES_SetTexCoords(data, SDL_TRUE); - centerx = center->x; centery = center->y; - /* Rotate and translate */ - data->glPushMatrix(); - data->glTranslatef(dstrect->x + centerx, dstrect->y + centery, 0.0f); - data->glRotatef((GLfloat)angle, 0.0f, 0.0f, 1.0f); - if (flip & SDL_FLIP_HORIZONTAL) { minx = dstrect->w - centerx; maxx = -centerx; - } else { + } + else { minx = -centerx; - maxx = dstrect->w - centerx; + maxx = dstrect->w - centerx; } if (flip & SDL_FLIP_VERTICAL) { - miny = dstrect->h - centery; + miny = dstrect->h - centery; maxy = -centery; - } else { + } + else { miny = -centery; - maxy = dstrect->h - centery; + maxy = dstrect->h - centery; } - minu = (GLfloat) srcrect->x / texture->w; + minu = (GLfloat) srcquad->x / texture->w; minu *= texturedata->texw; - maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w; + maxu = (GLfloat) (srcquad->x + srcquad->w) / texture->w; maxu *= texturedata->texw; - minv = (GLfloat) srcrect->y / texture->h; + minv = (GLfloat) srcquad->y / texture->h; minv *= texturedata->texh; - maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; + maxv = (GLfloat) (srcquad->y + srcquad->h) / texture->h; maxv *= texturedata->texh; - vertices[0] = minx; - vertices[1] = miny; - vertices[2] = maxx; - vertices[3] = miny; - vertices[4] = minx; - vertices[5] = maxy; - vertices[6] = maxx; - vertices[7] = maxy; - - texCoords[0] = minu; - texCoords[1] = minv; - texCoords[2] = maxu; - texCoords[3] = minv; - texCoords[4] = minu; - texCoords[5] = maxv; - texCoords[6] = maxu; - texCoords[7] = maxv; - data->glVertexPointer(2, GL_FLOAT, 0, vertices); - data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords); - data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - data->glPopMatrix(); - data->glDisable(GL_TEXTURE_2D); + cmd->data.draw.count = 1; + + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = maxy; + + *(verts++) = minu; + *(verts++) = minv; + *(verts++) = maxu; + *(verts++) = minv; + *(verts++) = minu; + *(verts++) = maxv; + *(verts++) = maxu; + *(verts++) = maxv; + + *(verts++) = (GLfloat) dstrect->x + centerx; + *(verts++) = (GLfloat) dstrect->y + centery; + *(verts++) = (GLfloat) angle; + + return 0; +} + +static void +SetDrawState(GLES_RenderData *data, const SDL_RenderCommand *cmd) +{ + const SDL_BlendMode blend = cmd->data.draw.blend; + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + + if (color != data->drawstate.color) { + const GLfloat fr = ((GLfloat) r) * inv255f; + const GLfloat fg = ((GLfloat) g) * inv255f; + const GLfloat fb = ((GLfloat) b) * inv255f; + const GLfloat fa = ((GLfloat) a) * inv255f; + data->glColor4f(fr, fg, fb, fa); + data->drawstate.color = color; + } + + if (data->drawstate.viewport_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + const SDL_bool istarget = (data->drawstate.target != NULL); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport->x, + istarget ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h), + viewport->w, viewport->h); + if (viewport->w && viewport->h) { + data->glOrthof((GLfloat) 0, (GLfloat) viewport->w, + (GLfloat) istarget ? 0 : viewport->h, + (GLfloat) istarget ? viewport->h : 0, + 0.0, 1.0); + } + data->glMatrixMode(GL_MODELVIEW); + data->drawstate.viewport_dirty = SDL_FALSE; + } + + if (data->drawstate.cliprect_enabled_dirty) { + if (data->drawstate.cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); + } + data->drawstate.cliprect_enabled_dirty = SDL_FALSE; + } + + if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + const SDL_Rect *rect = &data->drawstate.cliprect; + const SDL_bool istarget = (data->drawstate.target != NULL); + data->glScissor(viewport->x + rect->x, + istarget ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h, + rect->w, rect->h); + data->drawstate.cliprect_dirty = SDL_FALSE; + } + + if (blend != data->drawstate.blend) { + if (blend == SDL_BLENDMODE_NONE) { + data->glDisable(GL_BLEND); + } else { + data->glEnable(GL_BLEND); + if (data->GL_OES_blend_func_separate_supported) { + data->glBlendFuncSeparateOES(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); + } else { + data->glBlendFunc(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend))); + } + if (data->GL_OES_blend_equation_separate_supported) { + data->glBlendEquationSeparateOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)), + GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend))); + } else if (data->GL_OES_blend_subtract_supported) { + data->glBlendEquationOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend))); + } + } + data->drawstate.blend = blend; + } + + if ((cmd->data.draw.texture != NULL) != data->drawstate.texturing) { + if (cmd->data.draw.texture == NULL) { + data->glDisable(GL_TEXTURE_2D); + data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); + data->drawstate.texturing = SDL_FALSE; + } else { + data->glEnable(GL_TEXTURE_2D); + data->glEnableClientState(GL_TEXTURE_COORD_ARRAY); + data->drawstate.texturing = SDL_TRUE; + } + } +} + +static void +SetCopyState(GLES_RenderData *data, const SDL_RenderCommand *cmd) +{ + SDL_Texture *texture = cmd->data.draw.texture; + SetDrawState(data, cmd); + + if (texture != data->drawstate.texture) { + GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; + data->glBindTexture(GL_TEXTURE_2D, texturedata->texture); + data->drawstate.texture = texture; + } +} + +static int +GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; + size_t i; + + if (GLES_ActivateRenderer(renderer) < 0) { + return -1; + } + + data->drawstate.target = renderer->target; + + if (!renderer->target) { + SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh); + } + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + break; /* not used in this render backend. */ + } + + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_Rect *viewport = &data->drawstate.viewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); + data->drawstate.viewport_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + data->drawstate.cliprect_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_CLEAR: { + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + if (color != data->drawstate.clear_color) { + const GLfloat fr = ((GLfloat) r) * inv255f; + const GLfloat fg = ((GLfloat) g) * inv255f; + const GLfloat fb = ((GLfloat) b) * inv255f; + const GLfloat fa = ((GLfloat) a) * inv255f; + data->glClearColor(fr, fg, fb, fa); + data->drawstate.clear_color = color; + } + + if (data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + + data->glClear(GL_COLOR_BUFFER_BIT); + + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: { + const size_t count = cmd->data.draw.count; + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + SetDrawState(data, cmd); + data->glVertexPointer(2, GL_FLOAT, 0, verts); + data->glDrawArrays(GL_POINTS, 0, count); + break; + } + + case SDL_RENDERCMD_DRAW_LINES: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + const size_t count = cmd->data.draw.count; + SetDrawState(data, cmd); + data->glVertexPointer(2, GL_FLOAT, 0, verts); + if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) { + /* GL_LINE_LOOP takes care of the final segment */ + data->glDrawArrays(GL_LINE_LOOP, 0, count - 1); + } else { + data->glDrawArrays(GL_LINE_STRIP, 0, count); + /* We need to close the endpoint of the line */ + data->glDrawArrays(GL_POINTS, count - 1, 1); + } + break; + } + + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + size_t offset = 0; + SetDrawState(data, cmd); + data->glVertexPointer(2, GL_FLOAT, 0, verts); + for (i = 0; i < count; ++i, offset += 4) { + data->glDrawArrays(GL_TRIANGLE_STRIP, offset, 4); + } + break; + } + + case SDL_RENDERCMD_COPY: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + SetCopyState(data, cmd); + data->glVertexPointer(2, GL_FLOAT, 0, verts); + data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8); + data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + break; + } + + case SDL_RENDERCMD_COPY_EX: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + const GLfloat translatex = verts[16]; + const GLfloat translatey = verts[17]; + const GLfloat angle = verts[18]; + SetCopyState(data, cmd); + data->glVertexPointer(2, GL_FLOAT, 0, verts); + data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8); + + /* Translate to flip, rotate, translate to position */ + data->glPushMatrix(); + data->glTranslatef(translatex, translatey, 0.0f); + data->glRotatef(angle, 0.0, 0.0, 1.0); + data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + data->glPopMatrix(); + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } return 0; } static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch) + Uint32 pixel_format, void * pixels, int pitch) { GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888; @@ -1173,10 +972,11 @@ GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, /* Flip the rows to be top-down if necessary */ if (!renderer->target) { + SDL_bool isstack; length = rect->w * SDL_BYTESPERPIXEL(temp_format); src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch; dst = (Uint8*)temp_pixels; - tmp = SDL_stack_alloc(Uint8, length); + tmp = SDL_small_alloc(Uint8, length, &isstack); rows = rect->h / 2; while (rows--) { SDL_memcpy(tmp, dst, length); @@ -1185,7 +985,7 @@ GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, dst += temp_pitch; src -= temp_pitch; } - SDL_stack_free(tmp); + SDL_small_free(tmp, isstack); } status = SDL_ConvertPixels(rect->w, rect->h, @@ -1273,6 +1073,164 @@ static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture) return 0; } +SDL_Renderer * +GLES_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Renderer *renderer; + GLES_RenderData *data; + GLint value; + Uint32 window_flags; + int profile_mask = 0, major = 0, minor = 0; + SDL_bool changed_window = SDL_FALSE; + + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + + window_flags = SDL_GetWindowFlags(window); + if (!(window_flags & SDL_WINDOW_OPENGL) || + profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) { + + changed_window = SDL_TRUE; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); + + if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) { + goto error; + } + } + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + goto error; + } + + data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + GLES_DestroyRenderer(renderer); + SDL_OutOfMemory(); + goto error; + } + + renderer->WindowEvent = GLES_WindowEvent; + renderer->GetOutputSize = GLES_GetOutputSize; + renderer->SupportsBlendMode = GLES_SupportsBlendMode; + renderer->CreateTexture = GLES_CreateTexture; + renderer->UpdateTexture = GLES_UpdateTexture; + renderer->LockTexture = GLES_LockTexture; + renderer->UnlockTexture = GLES_UnlockTexture; + renderer->SetRenderTarget = GLES_SetRenderTarget; + renderer->QueueSetViewport = GLES_QueueSetViewport; + renderer->QueueSetDrawColor = GLES_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = GLES_QueueDrawPoints; + renderer->QueueDrawLines = GLES_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = GLES_QueueFillRects; + renderer->QueueCopy = GLES_QueueCopy; + renderer->QueueCopyEx = GLES_QueueCopyEx; + renderer->RunCommandQueue = GLES_RunCommandQueue; + renderer->RenderReadPixels = GLES_RenderReadPixels; + renderer->RenderPresent = GLES_RenderPresent; + renderer->DestroyTexture = GLES_DestroyTexture; + renderer->DestroyRenderer = GLES_DestroyRenderer; + renderer->GL_BindTexture = GLES_BindTexture; + renderer->GL_UnbindTexture = GLES_UnbindTexture; + renderer->info = GLES_RenderDriver.info; + renderer->info.flags = SDL_RENDERER_ACCELERATED; + renderer->driverdata = data; + renderer->window = window; + + data->context = SDL_GL_CreateContext(window); + if (!data->context) { + GLES_DestroyRenderer(renderer); + goto error; + } + if (SDL_GL_MakeCurrent(window, data->context) < 0) { + GLES_DestroyRenderer(renderer); + goto error; + } + + if (GLES_LoadFunctions(data) < 0) { + GLES_DestroyRenderer(renderer); + goto error; + } + + if (flags & SDL_RENDERER_PRESENTVSYNC) { + SDL_GL_SetSwapInterval(1); + } else { + SDL_GL_SetSwapInterval(0); + } + if (SDL_GL_GetSwapInterval() > 0) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + + value = 0; + data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); + renderer->info.max_texture_width = value; + value = 0; + data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); + renderer->info.max_texture_height = value; + + /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */ + if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) { + data->GL_OES_framebuffer_object_supported = SDL_TRUE; + renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE; + + value = 0; + data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value); + data->window_framebuffer = (GLuint)value; + } + data->framebuffers = NULL; + + if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) { + data->GL_OES_blend_func_separate_supported = SDL_TRUE; + } + if (SDL_GL_ExtensionSupported("GL_OES_blend_equation_separate")) { + data->GL_OES_blend_equation_separate_supported = SDL_TRUE; + } + if (SDL_GL_ExtensionSupported("GL_OES_blend_subtract")) { + data->GL_OES_blend_subtract_supported = SDL_TRUE; + } + + /* Set up parameters for rendering */ + data->glDisable(GL_DEPTH_TEST); + data->glDisable(GL_CULL_FACE); + + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + + data->glEnableClientState(GL_VERTEX_ARRAY); + data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + data->drawstate.blend = SDL_BLENDMODE_INVALID; + data->drawstate.color = 0xFFFFFFFF; + data->drawstate.clear_color = 0xFFFFFFFF; + + return renderer; + +error: + if (changed_window) { + /* Uh oh, better try to put it back... */ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); + SDL_RecreateWindow(window, window_flags); + } + return NULL; +} + +SDL_RenderDriver GLES_RenderDriver = { + GLES_CreateRenderer, + { + "opengles", + (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC), + 1, + {SDL_PIXELFORMAT_ABGR8888}, + 0, + 0} +}; + #endif /* SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/opengles2/SDL_gles2funcs.h b/src/render/opengles2/SDL_gles2funcs.h index b6a1436186..68096aaaf0 100644 --- a/src/render/opengles2/SDL_gles2funcs.h +++ b/src/render/opengles2/SDL_gles2funcs.h @@ -75,6 +75,7 @@ SDL_PROC(void, glDeleteFramebuffers, (GLsizei, const GLuint *)) SDL_PROC(GLint, glGetAttribLocation, (GLuint, const GLchar *)) SDL_PROC(void, glGetProgramInfoLog, (GLuint, GLsizei, GLsizei*, GLchar*)) SDL_PROC(void, glGenBuffers, (GLsizei, GLuint *)) +SDL_PROC(void, glDeleteBuffers, (GLsizei, GLuint *)) SDL_PROC(void, glBindBuffer, (GLenum, GLuint)) SDL_PROC(void, glBufferData, (GLenum, GLsizeiptr, const GLvoid *, GLenum)) SDL_PROC(void, glBufferSubData, (GLenum, GLintptr, GLsizeiptr, const GLvoid *)) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index f21a957625..fe2ce1ab8a 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -29,19 +29,6 @@ #include "../../video/SDL_blit.h" #include "SDL_shaders_gles2.h" -/* !!! FIXME: Emscripten makes these into WebGL calls, and WebGL doesn't offer - !!! FIXME: client-side arrays (without an Emscripten compatibility hack, - !!! FIXME: at least), but the current VBO code here is dramatically - !!! FIXME: slower on actual iOS devices, even though the iOS Simulator - !!! FIXME: is okay. Some time after 2.0.4 ships, we should revisit this, - !!! FIXME: fix the performance bottleneck, and make everything use VBOs. -*/ -#ifdef __EMSCRIPTEN__ -#define SDL_GLES2_USE_VBOS 1 -#else -#define SDL_GLES2_USE_VBOS 0 -#endif - /* To prevent unnecessary window recreation, * these should match the defaults selected in SDL_GL_ResetAttributes */ @@ -52,29 +39,6 @@ extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); /************************************************************************************************* - * Bootstrap data * - *************************************************************************************************/ - -static SDL_Renderer *GLES2_CreateRenderer(SDL_Window *window, Uint32 flags); - -SDL_RenderDriver GLES2_RenderDriver = { - GLES2_CreateRenderer, - { - "opengles2", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), - 4, - { - SDL_PIXELFORMAT_ARGB8888, - SDL_PIXELFORMAT_ABGR8888, - SDL_PIXELFORMAT_RGB888, - SDL_PIXELFORMAT_BGR888 - }, - 0, - 0 - } -}; - -/************************************************************************************************* * Context structures * *************************************************************************************************/ @@ -109,7 +73,6 @@ typedef struct GLES2_ShaderCacheEntry GLES2_ShaderType type; const GLES2_ShaderInstance *instance; int references; - Uint8 modulation_r, modulation_g, modulation_b, modulation_a; struct GLES2_ShaderCacheEntry *prev; struct GLES2_ShaderCacheEntry *next; } GLES2_ShaderCacheEntry; @@ -126,8 +89,7 @@ typedef struct GLES2_ProgramCacheEntry GLES2_ShaderCacheEntry *vertex_shader; GLES2_ShaderCacheEntry *fragment_shader; GLuint uniform_locations[16]; - Uint8 color_r, color_g, color_b, color_a; - Uint8 modulation_r, modulation_g, modulation_b, modulation_a; + Uint32 color; GLfloat projection[4][4]; struct GLES2_ProgramCacheEntry *prev; struct GLES2_ProgramCacheEntry *next; @@ -152,7 +114,6 @@ typedef enum { GLES2_UNIFORM_PROJECTION, GLES2_UNIFORM_TEXTURE, - GLES2_UNIFORM_MODULATION, GLES2_UNIFORM_COLOR, GLES2_UNIFORM_TEXTURE_U, GLES2_UNIFORM_TEXTURE_V @@ -160,6 +121,7 @@ typedef enum typedef enum { + GLES2_IMAGESOURCE_INVALID, GLES2_IMAGESOURCE_SOLID, GLES2_IMAGESOURCE_TEXTURE_ABGR, GLES2_IMAGESOURCE_TEXTURE_ARGB, @@ -171,17 +133,33 @@ typedef enum GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES } GLES2_ImageSource; -typedef struct GLES2_DriverContext +typedef struct +{ + SDL_Rect viewport; + SDL_bool viewport_dirty; + SDL_Texture *texture; + SDL_Texture *target; + SDL_BlendMode blend; + SDL_bool cliprect_enabled_dirty; + SDL_bool cliprect_enabled; + SDL_bool cliprect_dirty; + SDL_Rect cliprect; + SDL_bool texturing; + SDL_bool is_copy_ex; + Uint32 color; + Uint32 clear_color; + int drawablew; + int drawableh; + GLES2_ProgramCacheEntry *program; + GLfloat projection[4][4]; +} GLES2_DrawStateCache; + +typedef struct GLES2_RenderData { SDL_GLContext *context; SDL_bool debug_enabled; - struct { - SDL_BlendMode blendMode; - SDL_bool tex_coords; - } current; - #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #include "SDL_gles2funcs.h" #undef SDL_PROC @@ -192,17 +170,18 @@ typedef struct GLES2_DriverContext GLenum *shader_formats; GLES2_ShaderCache shader_cache; GLES2_ProgramCache program_cache; - GLES2_ProgramCacheEntry *current_program; Uint8 clear_r, clear_g, clear_b, clear_a; -#if SDL_GLES2_USE_VBOS - GLuint vertex_buffers[4]; - GLsizeiptr vertex_buffer_size[4]; -#endif -} GLES2_DriverContext; + GLuint vertex_buffers[8]; + GLsizeiptr vertex_buffer_size[8]; + int current_vertex_buffer; + GLES2_DrawStateCache drawstate; +} GLES2_RenderData; #define GLES2_MAX_CACHED_PROGRAMS 8 +static const float inv255f = 1.0f / 255.0f; + SDL_FORCE_INLINE const char* GL_TranslateError (GLenum error) @@ -223,7 +202,7 @@ GL_TranslateError (GLenum error) SDL_FORCE_INLINE void GL_ClearErrors(SDL_Renderer *renderer) { - GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; if (!data->debug_enabled) { return; @@ -236,7 +215,7 @@ GL_ClearErrors(SDL_Renderer *renderer) SDL_FORCE_INLINE int GL_CheckAllErrors (const char *prefix, SDL_Renderer *renderer, const char *file, int line, const char *function) { - GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; int ret = 0; if (!data->debug_enabled) { @@ -269,17 +248,7 @@ GL_CheckAllErrors (const char *prefix, SDL_Renderer *renderer, const char *file, * Renderer state APIs * *************************************************************************************************/ -static int GLES2_ActivateRenderer(SDL_Renderer *renderer); -static void GLES2_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static int GLES2_UpdateViewport(SDL_Renderer * renderer); -static void GLES2_DestroyRenderer(SDL_Renderer *renderer); -static int GLES2_SetOrthographicProjection(SDL_Renderer *renderer); - - -static SDL_GLContext SDL_CurrentContext = NULL; - -static int GLES2_LoadFunctions(GLES2_DriverContext * data) +static int GLES2_LoadFunctions(GLES2_RenderData * data) { #if SDL_VIDEO_DRIVER_UIKIT #define __SDL_NOGETPROCADDR__ @@ -307,7 +276,7 @@ static int GLES2_LoadFunctions(GLES2_DriverContext * data) } static GLES2_FBOList * -GLES2_GetFBO(GLES2_DriverContext *data, Uint32 w, Uint32 h) +GLES2_GetFBO(GLES2_RenderData *data, Uint32 w, Uint32 h) { GLES2_FBOList *result = data->framebuffers; while ((result) && ((result->w != w) || (result->h != h)) ) { @@ -327,18 +296,15 @@ GLES2_GetFBO(GLES2_DriverContext *data, Uint32 w, Uint32 h) static int GLES2_ActivateRenderer(SDL_Renderer * renderer) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; - if (SDL_CurrentContext != data->context) { + if (SDL_GL_GetCurrentContext() != data->context) { /* Null out the current program to ensure we set it again */ - data->current_program = NULL; + data->drawstate.program = NULL; if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) { return -1; } - SDL_CurrentContext = data->context; - - GLES2_UpdateViewport(renderer); } GL_ClearErrors(renderer); @@ -349,14 +315,7 @@ GLES2_ActivateRenderer(SDL_Renderer * renderer) static void GLES2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED || - event->event == SDL_WINDOWEVENT_SHOWN || - event->event == SDL_WINDOWEVENT_HIDDEN) { - /* Rebind the context to the window area */ - SDL_CurrentContext = NULL; - } + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; if (event->event == SDL_WINDOWEVENT_MINIMIZED) { /* According to Apple documentation, we need to finish drawing NOW! */ @@ -434,538 +393,31 @@ GLES2_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) return SDL_TRUE; } -static int -GLES2_UpdateViewport(SDL_Renderer * renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - if (SDL_CurrentContext != data->context) { - /* We'll update the viewport after we rebind the context */ - return 0; - } - - if (renderer->target) { - data->glViewport(renderer->viewport.x, renderer->viewport.y, - renderer->viewport.w, renderer->viewport.h); - } else { - int w, h; - - SDL_GL_GetDrawableSize(renderer->window, &w, &h); - data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h), - renderer->viewport.w, renderer->viewport.h); - } - - if (data->current_program) { - GLES2_SetOrthographicProjection(renderer); - } - return GL_CheckError("", renderer); -} - -static int -GLES2_UpdateClipRect(SDL_Renderer * renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - if (SDL_CurrentContext != data->context) { - /* We'll update the clip rect after we rebind the context */ - return 0; - } - - if (renderer->clipping_enabled) { - const SDL_Rect *rect = &renderer->clip_rect; - data->glEnable(GL_SCISSOR_TEST); - if (renderer->target) { - data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h); - } else { - int w, h; - - SDL_GL_GetDrawableSize(renderer->window, &w, &h); - data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h); - } - } else { - data->glDisable(GL_SCISSOR_TEST); - } - return 0; -} static void -GLES2_DestroyRenderer(SDL_Renderer *renderer) +GLES2_EvictShader(GLES2_RenderData *data, GLES2_ShaderCacheEntry *entry) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - /* Deallocate everything */ - if (data) { - GLES2_ActivateRenderer(renderer); - - { - GLES2_ShaderCacheEntry *entry; - GLES2_ShaderCacheEntry *next; - entry = data->shader_cache.head; - while (entry) { - data->glDeleteShader(entry->id); - next = entry->next; - SDL_free(entry); - entry = next; - } - } - { - GLES2_ProgramCacheEntry *entry; - GLES2_ProgramCacheEntry *next; - entry = data->program_cache.head; - while (entry) { - data->glDeleteProgram(entry->id); - next = entry->next; - SDL_free(entry); - entry = next; - } - } - if (data->context) { - while (data->framebuffers) { - GLES2_FBOList *nextnode = data->framebuffers->next; - data->glDeleteFramebuffers(1, &data->framebuffers->FBO); - GL_CheckError("", renderer); - SDL_free(data->framebuffers); - data->framebuffers = nextnode; - } - SDL_GL_DeleteContext(data->context); - } - SDL_free(data->shader_formats); - SDL_free(data); - } - SDL_free(renderer); -} - -/************************************************************************************************* - * Texture APIs * - *************************************************************************************************/ - -static int GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture); -static int GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, - const void *pixels, int pitch); -static int GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, - const Uint8 *Yplane, int Ypitch, - const Uint8 *Uplane, int Upitch, - const Uint8 *Vplane, int Vpitch); -static int GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, - void **pixels, int *pitch); -static void GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture); -static int GLES2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); -static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture); - -static int -GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GLES2_DriverContext *renderdata = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *data; - GLenum format; - GLenum type; - GLenum scaleMode; - - GLES2_ActivateRenderer(renderer); - - /* Determine the corresponding GLES texture format params */ - switch (texture->format) - { - case SDL_PIXELFORMAT_ARGB8888: - case SDL_PIXELFORMAT_ABGR8888: - case SDL_PIXELFORMAT_RGB888: - case SDL_PIXELFORMAT_BGR888: - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - case SDL_PIXELFORMAT_IYUV: - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_NV12: - case SDL_PIXELFORMAT_NV21: - format = GL_LUMINANCE; - type = GL_UNSIGNED_BYTE; - break; -#ifdef GL_TEXTURE_EXTERNAL_OES - case SDL_PIXELFORMAT_EXTERNAL_OES: - format = GL_NONE; - type = GL_NONE; - break; -#endif - default: - return SDL_SetError("Texture format not supported"); - } - - if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES && - texture->access != SDL_TEXTUREACCESS_STATIC) { - return SDL_SetError("Unsupported texture access for SDL_PIXELFORMAT_EXTERNAL_OES"); - } - - /* Allocate a texture struct */ - data = (GLES2_TextureData *)SDL_calloc(1, sizeof(GLES2_TextureData)); - if (!data) { - return SDL_OutOfMemory(); - } - data->texture = 0; -#ifdef GL_TEXTURE_EXTERNAL_OES - data->texture_type = (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; -#else - data->texture_type = GL_TEXTURE_2D; -#endif - data->pixel_format = format; - data->pixel_type = type; - data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12)); - data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21)); - data->texture_u = 0; - data->texture_v = 0; - scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR; - - /* Allocate a blob for image renderdata */ - if (texture->access == SDL_TEXTUREACCESS_STREAMING) { - size_t size; - data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); - size = texture->h * data->pitch; - if (data->yuv) { - /* Need to add size for the U and V planes */ - size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); - } - if (data->nv12) { - /* Need to add size for the U/V plane */ - size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); - } - data->pixel_data = SDL_calloc(1, size); - if (!data->pixel_data) { - SDL_free(data); - return SDL_OutOfMemory(); - } - } - - /* Allocate the texture */ - GL_CheckError("", renderer); - - if (data->yuv) { - renderdata->glGenTextures(1, &data->texture_v); - if (GL_CheckError("glGenTexures()", renderer) < 0) { - return -1; - } - renderdata->glActiveTexture(GL_TEXTURE2); - renderdata->glBindTexture(data->texture_type, data->texture_v); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL); - - renderdata->glGenTextures(1, &data->texture_u); - if (GL_CheckError("glGenTexures()", renderer) < 0) { - return -1; - } - renderdata->glActiveTexture(GL_TEXTURE1); - renderdata->glBindTexture(data->texture_type, data->texture_u); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL); - if (GL_CheckError("glTexImage2D()", renderer) < 0) { - return -1; - } - } - - if (data->nv12) { - renderdata->glGenTextures(1, &data->texture_u); - if (GL_CheckError("glGenTexures()", renderer) < 0) { - return -1; - } - renderdata->glActiveTexture(GL_TEXTURE1); - renderdata->glBindTexture(data->texture_type, data->texture_u); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); - if (GL_CheckError("glTexImage2D()", renderer) < 0) { - return -1; - } - } - - renderdata->glGenTextures(1, &data->texture); - if (GL_CheckError("glGenTexures()", renderer) < 0) { - return -1; - } - texture->driverdata = data; - renderdata->glActiveTexture(GL_TEXTURE0); - renderdata->glBindTexture(data->texture_type, data->texture); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (texture->format != SDL_PIXELFORMAT_EXTERNAL_OES) { - renderdata->glTexImage2D(data->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL); - if (GL_CheckError("glTexImage2D()", renderer) < 0) { - return -1; - } - } - - if (texture->access == SDL_TEXTUREACCESS_TARGET) { - data->fbo = GLES2_GetFBO(renderer->driverdata, texture->w, texture->h); - } else { - data->fbo = NULL; - } - - return GL_CheckError("", renderer); -} - -static int -GLES2_TexSubImage2D(GLES2_DriverContext *data, GLenum target, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, GLint pitch, GLint bpp) -{ - Uint8 *blob = NULL; - Uint8 *src; - int src_pitch; - int y; - - if ((width == 0) || (height == 0) || (bpp == 0)) { - return 0; /* nothing to do */ - } - - /* Reformat the texture data into a tightly packed array */ - src_pitch = width * bpp; - src = (Uint8 *)pixels; - if (pitch != src_pitch) { - blob = (Uint8 *)SDL_malloc(src_pitch * height); - if (!blob) { - return SDL_OutOfMemory(); - } - src = blob; - for (y = 0; y < height; ++y) - { - SDL_memcpy(src, pixels, src_pitch); - src += src_pitch; - pixels = (Uint8 *)pixels + pitch; - } - src = blob; - } - - data->glTexSubImage2D(target, 0, xoffset, yoffset, width, height, format, type, src); - if (blob) { - SDL_free(blob); - } - return 0; -} - -static int -GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, - const void *pixels, int pitch) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - - GLES2_ActivateRenderer(renderer); - - /* Bail out if we're supposed to update an empty rectangle */ - if (rect->w <= 0 || rect->h <= 0) { - return 0; - } - - /* Create a texture subimage with the supplied data */ - data->glBindTexture(tdata->texture_type, tdata->texture); - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x, - rect->y, - rect->w, - rect->h, - tdata->pixel_format, - tdata->pixel_type, - pixels, pitch, SDL_BYTESPERPIXEL(texture->format)); - - if (tdata->yuv) { - /* Skip to the correct offset into the next texture */ - pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); - if (texture->format == SDL_PIXELFORMAT_YV12) { - data->glBindTexture(tdata->texture_type, tdata->texture_v); - } else { - data->glBindTexture(tdata->texture_type, tdata->texture_u); - } - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - tdata->pixel_format, - tdata->pixel_type, - pixels, (pitch + 1) / 2, 1); - - - /* Skip to the correct offset into the next texture */ - pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1)/2)); - if (texture->format == SDL_PIXELFORMAT_YV12) { - data->glBindTexture(tdata->texture_type, tdata->texture_u); - } else { - data->glBindTexture(tdata->texture_type, tdata->texture_v); - } - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - tdata->pixel_format, - tdata->pixel_type, - pixels, (pitch + 1) / 2, 1); - } - - if (tdata->nv12) { - /* Skip to the correct offset into the next texture */ - pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); - data->glBindTexture(tdata->texture_type, tdata->texture_u); - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - GL_LUMINANCE_ALPHA, - GL_UNSIGNED_BYTE, - pixels, 2 * ((pitch + 1) / 2), 2); + /* Unlink the shader from the cache */ + if (entry->next) { + entry->next->prev = entry->prev; } - - return GL_CheckError("glTexSubImage2D()", renderer); -} - -static int -GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, - const Uint8 *Yplane, int Ypitch, - const Uint8 *Uplane, int Upitch, - const Uint8 *Vplane, int Vpitch) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - - GLES2_ActivateRenderer(renderer); - - /* Bail out if we're supposed to update an empty rectangle */ - if (rect->w <= 0 || rect->h <= 0) { - return 0; + if (entry->prev) { + entry->prev->next = entry->next; } - - data->glBindTexture(tdata->texture_type, tdata->texture_v); - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - tdata->pixel_format, - tdata->pixel_type, - Vplane, Vpitch, 1); - - data->glBindTexture(tdata->texture_type, tdata->texture_u); - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - tdata->pixel_format, - tdata->pixel_type, - Uplane, Upitch, 1); - - data->glBindTexture(tdata->texture_type, tdata->texture); - GLES2_TexSubImage2D(data, tdata->texture_type, - rect->x, - rect->y, - rect->w, - rect->h, - tdata->pixel_format, - tdata->pixel_type, - Yplane, Ypitch, 1); - - return GL_CheckError("glTexSubImage2D()", renderer); -} - -static int -GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, - void **pixels, int *pitch) -{ - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - - /* Retrieve the buffer/pitch for the specified region */ - *pixels = (Uint8 *)tdata->pixel_data + - (tdata->pitch * rect->y) + - (rect->x * SDL_BYTESPERPIXEL(texture->format)); - *pitch = tdata->pitch; - - return 0; -} - -static void -GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - SDL_Rect rect; - - /* We do whole texture updates, at least for now */ - rect.x = 0; - rect.y = 0; - rect.w = texture->w; - rect.h = texture->h; - GLES2_UpdateTexture(renderer, texture, &rect, tdata->pixel_data, tdata->pitch); -} - -static int -GLES2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; - GLES2_TextureData *texturedata = NULL; - GLenum status; - - if (texture == NULL) { - data->glBindFramebuffer(GL_FRAMEBUFFER, data->window_framebuffer); - } else { - texturedata = (GLES2_TextureData *) texture->driverdata; - 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 SDL_SetError("glFramebufferTexture2D() failed"); - } + if (data->shader_cache.head == entry) { + data->shader_cache.head = entry->next; } - return 0; -} - -static void -GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - - GLES2_ActivateRenderer(renderer); + --data->shader_cache.count; - /* Destroy the texture */ - if (tdata) { - data->glDeleteTextures(1, &tdata->texture); - if (tdata->texture_v) { - data->glDeleteTextures(1, &tdata->texture_v); - } - if (tdata->texture_u) { - data->glDeleteTextures(1, &tdata->texture_u); - } - SDL_free(tdata->pixel_data); - SDL_free(tdata); - texture->driverdata = NULL; - } + /* Deallocate the shader */ + data->glDeleteShader(entry->id); + SDL_free(entry); } -/************************************************************************************************* - * Shader management functions * - *************************************************************************************************/ - -static GLES2_ShaderCacheEntry *GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type); -static void GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *entry); -static GLES2_ProgramCacheEntry *GLES2_CacheProgram(SDL_Renderer *renderer, - GLES2_ShaderCacheEntry *vertex, - GLES2_ShaderCacheEntry *fragment); -static int GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int h); - static GLES2_ProgramCacheEntry * -GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, +GLES2_CacheProgram(GLES2_RenderData *data, GLES2_ShaderCacheEntry *vertex, GLES2_ShaderCacheEntry *fragment) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; GLES2_ProgramCacheEntry *entry; GLES2_ShaderCacheEntry *shaderEntry; GLint linkSuccessful; @@ -1029,21 +481,27 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, data->glGetUniformLocation(entry->id, "u_texture_u"); entry->uniform_locations[GLES2_UNIFORM_TEXTURE] = data->glGetUniformLocation(entry->id, "u_texture"); - entry->uniform_locations[GLES2_UNIFORM_MODULATION] = - data->glGetUniformLocation(entry->id, "u_modulation"); entry->uniform_locations[GLES2_UNIFORM_COLOR] = data->glGetUniformLocation(entry->id, "u_color"); - entry->modulation_r = entry->modulation_g = entry->modulation_b = entry->modulation_a = 255; - entry->color_r = entry->color_g = entry->color_b = entry->color_a = 255; + entry->color = 0; data->glUseProgram(entry->id); - data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V], 2); /* always texture unit 2. */ - data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U], 1); /* always texture unit 1. */ - data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE], 0); /* always texture unit 0. */ - data->glUniformMatrix4fv(entry->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)entry->projection); - data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_MODULATION], 1.0f, 1.0f, 1.0f, 1.0f); - data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_COLOR], 1.0f, 1.0f, 1.0f, 1.0f); + if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V] != -1) { + data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V], 2); /* always texture unit 2. */ + } + if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U] != -1) { + data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U], 1); /* always texture unit 1. */ + } + if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE] != -1) { + data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE], 0); /* always texture unit 0. */ + } + if (entry->uniform_locations[GLES2_UNIFORM_PROJECTION] != -1) { + data->glUniformMatrix4fv(entry->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)entry->projection); + } + if (entry->uniform_locations[GLES2_UNIFORM_COLOR] != -1) { + data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_COLOR], 0.0f, 0.0f, 0.0f, 0.0f); + } /* Cache the linked program */ if (data->program_cache.head) { @@ -1063,11 +521,11 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, if (data->program_cache.count > GLES2_MAX_CACHED_PROGRAMS) { shaderEntry = data->program_cache.tail->vertex_shader; if (--shaderEntry->references <= 0) { - GLES2_EvictShader(renderer, shaderEntry); + GLES2_EvictShader(data, shaderEntry); } shaderEntry = data->program_cache.tail->fragment_shader; if (--shaderEntry->references <= 0) { - GLES2_EvictShader(renderer, shaderEntry); + GLES2_EvictShader(data, shaderEntry); } data->glDeleteProgram(data->program_cache.tail->id); data->program_cache.tail = data->program_cache.tail->prev; @@ -1079,9 +537,8 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, } static GLES2_ShaderCacheEntry * -GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type) +GLES2_CacheShader(GLES2_RenderData *data, GLES2_ShaderType type) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; const GLES2_Shader *shader; const GLES2_ShaderInstance *instance = NULL; GLES2_ShaderCacheEntry *entry = NULL; @@ -1144,19 +601,20 @@ GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type) compileSuccessful = GL_TRUE; } if (!compileSuccessful) { + SDL_bool isstack = SDL_FALSE; char *info = NULL; int length = 0; data->glGetShaderiv(entry->id, GL_INFO_LOG_LENGTH, &length); if (length > 0) { - info = SDL_stack_alloc(char, length); + info = SDL_small_alloc(char, length, &isstack); if (info) { data->glGetShaderInfoLog(entry->id, length, &length, info); } } if (info) { SDL_SetError("Failed to load the shader: %s", info); - SDL_stack_free(info); + SDL_small_free(info, isstack); } else { SDL_SetError("Failed to load the shader"); } @@ -1175,32 +633,9 @@ GLES2_CacheShader(SDL_Renderer *renderer, GLES2_ShaderType type) return entry; } -static void -GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *entry) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - /* Unlink the shader from the cache */ - if (entry->next) { - entry->next->prev = entry->prev; - } - if (entry->prev) { - entry->prev->next = entry->next; - } - if (data->shader_cache.head == entry) { - data->shader_cache.head = entry->next; - } - --data->shader_cache.count; - - /* Deallocate the shader */ - data->glDeleteShader(entry->id); - SDL_free(entry); -} - static int -GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int h) +GLES2_SelectProgram(GLES2_RenderData *data, GLES2_ImageSource source, int w, int h) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; GLES2_ShaderCacheEntry *vertex = NULL; GLES2_ShaderCacheEntry *fragment = NULL; GLES2_ShaderType vtype, ftype; @@ -1280,24 +715,24 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int } /* Load the requested shaders */ - vertex = GLES2_CacheShader(renderer, vtype); + vertex = GLES2_CacheShader(data, vtype); if (!vertex) { goto fault; } - fragment = GLES2_CacheShader(renderer, ftype); + fragment = GLES2_CacheShader(data, ftype); if (!fragment) { goto fault; } /* Check if we need to change programs at all */ - if (data->current_program && - data->current_program->vertex_shader == vertex && - data->current_program->fragment_shader == fragment) { + if (data->drawstate.program && + data->drawstate.program->vertex_shader == vertex && + data->drawstate.program->fragment_shader == fragment) { return 0; } /* Generate a matching program */ - program = GLES2_CacheProgram(renderer, vertex, fragment); + program = GLES2_CacheProgram(data, vertex, fragment); if (!program) { goto fault; } @@ -1306,361 +741,352 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int data->glUseProgram(program->id); /* Set the current program */ - data->current_program = program; - - /* Activate an orthographic projection */ - if (GLES2_SetOrthographicProjection(renderer) < 0) { - goto fault; - } + data->drawstate.program = program; /* Clean up and return */ return 0; fault: if (vertex && vertex->references <= 0) { - GLES2_EvictShader(renderer, vertex); + GLES2_EvictShader(data, vertex); } if (fragment && fragment->references <= 0) { - GLES2_EvictShader(renderer, fragment); + GLES2_EvictShader(data, fragment); } - data->current_program = NULL; + data->drawstate.program = NULL; return -1; } static int -GLES2_SetOrthographicProjection(SDL_Renderer *renderer) +GLES2_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat projection[4][4]; + return 0; /* nothing to do in this backend. */ +} - if (!renderer->viewport.w || !renderer->viewport.h) { - return 0; - } +static int +GLES2_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) +{ + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - /* Prepare an orthographic projection */ - projection[0][0] = 2.0f / renderer->viewport.w; - projection[0][1] = 0.0f; - projection[0][2] = 0.0f; - projection[0][3] = 0.0f; - projection[1][0] = 0.0f; - if (renderer->target) { - projection[1][1] = 2.0f / renderer->viewport.h; - } else { - projection[1][1] = -2.0f / renderer->viewport.h; - } - projection[1][2] = 0.0f; - projection[1][3] = 0.0f; - projection[2][0] = 0.0f; - projection[2][1] = 0.0f; - projection[2][2] = 0.0f; - projection[2][3] = 0.0f; - projection[3][0] = -1.0f; - if (renderer->target) { - projection[3][1] = -1.0f; - } else { - projection[3][1] = 1.0f; + if (!verts) { + return -1; } - projection[3][2] = 0.0f; - projection[3][3] = 1.0f; - /* Set the projection matrix */ - if (SDL_memcmp(data->current_program->projection, projection, sizeof (projection)) != 0) { - const GLuint locProjection = data->current_program->uniform_locations[GLES2_UNIFORM_PROJECTION]; - data->glUniformMatrix4fv(locProjection, 1, GL_FALSE, (GLfloat *)projection); - SDL_memcpy(data->current_program->projection, projection, sizeof (projection)); + cmd->data.draw.count = count; + for (i = 0; i < count; i++) { + *(verts++) = 0.5f + points[i].x; + *(verts++) = 0.5f + points[i].y; } return 0; } -/************************************************************************************************* - * Rendering functions * - *************************************************************************************************/ - -static const float inv255f = 1.0f / 255.0f; - -static int GLES2_RenderClear(SDL_Renderer *renderer); -static int GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count); -static int GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count); -static int GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count); -static int GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, - const SDL_FRect *dstrect); -static int GLES2_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); -static int GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch); -static void GLES2_RenderPresent(SDL_Renderer *renderer); - -static SDL_bool -CompareColors(Uint8 r1, Uint8 g1, Uint8 b1, Uint8 a1, - Uint8 r2, Uint8 g2, Uint8 b2, Uint8 a2) -{ - Uint32 Pixel1, Pixel2; - RGBA8888_FROM_RGBA(Pixel1, r1, g1, b1, a1); - RGBA8888_FROM_RGBA(Pixel2, r2, g2, b2, a2); - return (Pixel1 == Pixel2); -} - static int -GLES2_RenderClear(SDL_Renderer * renderer) +GLES2_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - Uint8 r, g, b, a; - - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - GLES2_ActivateRenderer(renderer); - - if (!CompareColors(data->clear_r, data->clear_g, data->clear_b, data->clear_a, - renderer->r, renderer->g, renderer->b, renderer->a)) { - - /* Select the color to clear with */ - g = renderer->g; - a = renderer->a; - - if (renderer->target && - (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || - renderer->target->format == SDL_PIXELFORMAT_RGB888)) { - r = renderer->b; - b = renderer->r; - } else { - r = renderer->r; - b = renderer->b; - } - - data->glClearColor((GLfloat) r * inv255f, - (GLfloat) g * inv255f, - (GLfloat) b * inv255f, - (GLfloat) a * inv255f); - data->clear_r = renderer->r; - data->clear_g = renderer->g; - data->clear_b = renderer->b; - data->clear_a = renderer->a; - } + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 8 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - if (renderer->clipping_enabled) { - data->glDisable(GL_SCISSOR_TEST); + if (!verts) { + return -1; } - data->glClear(GL_COLOR_BUFFER_BIT); + cmd->data.draw.count = count; - if (renderer->clipping_enabled) { - data->glEnable(GL_SCISSOR_TEST); + for (i = 0; i < count; i++) { + const SDL_FRect *rect = &rects[i]; + const GLfloat minx = rect->x; + const GLfloat maxx = rect->x + rect->w; + const GLfloat miny = rect->y; + const GLfloat maxy = rect->y + rect->h; + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = maxy; } return 0; } -static void -GLES2_SetBlendMode(GLES2_DriverContext *data, SDL_BlendMode blendMode) -{ - if (blendMode != data->current.blendMode) { - if (blendMode == SDL_BLENDMODE_NONE) { - data->glDisable(GL_BLEND); - } else { - data->glEnable(GL_BLEND); - data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode)), - GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode))); - data->glBlendEquationSeparate(GetBlendEquation(SDL_GetBlendModeColorOperation(blendMode)), - GetBlendEquation(SDL_GetBlendModeAlphaOperation(blendMode))); - } - data->current.blendMode = blendMode; - } -} - -static void -GLES2_SetTexCoords(GLES2_DriverContext * data, SDL_bool enabled) -{ - if (enabled != data->current.tex_coords) { - if (enabled) { - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); - } else { - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); - } - data->current.tex_coords = enabled; - } -} - static int -GLES2_SetDrawingState(SDL_Renderer * renderer) +GLES2_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_ProgramCacheEntry *program; - Uint8 r, g, b, a; - - GLES2_ActivateRenderer(renderer); - - GLES2_SetBlendMode(data, renderer->blendMode); - - GLES2_SetTexCoords(data, SDL_FALSE); + GLfloat minx, miny, maxx, maxy; + GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first); - /* Activate an appropriate shader and set the projection matrix */ - if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID, 0, 0) < 0) { + if (!verts) { return -1; } - /* Select the color to draw with */ - g = renderer->g; - a = renderer->a; - - if (renderer->target && - (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || - renderer->target->format == SDL_PIXELFORMAT_RGB888)) { - r = renderer->b; - b = renderer->r; - } else { - r = renderer->r; - b = renderer->b; - } - - program = data->current_program; - if (!CompareColors(program->color_r, program->color_g, program->color_b, program->color_a, r, g, b, a)) { - /* Select the color to draw with */ - data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_COLOR], r * inv255f, g * inv255f, b * inv255f, a * inv255f); - program->color_r = r; - program->color_g = g; - program->color_b = b; - program->color_a = a; - } + cmd->data.draw.count = 1; + + minx = dstrect->x; + miny = dstrect->y; + maxx = dstrect->x + dstrect->w; + maxy = dstrect->y + dstrect->h; + + minu = (GLfloat) srcrect->x / texture->w; + maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w; + minv = (GLfloat) srcrect->y / texture->h; + maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; + + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = maxy; + + *(verts++) = minu; + *(verts++) = minv; + *(verts++) = maxu; + *(verts++) = minv; + *(verts++) = minu; + *(verts++) = maxv; + *(verts++) = maxu; + *(verts++) = maxv; return 0; } static int -GLES2_UpdateVertexBuffer(SDL_Renderer *renderer, GLES2_Attribute attr, - const void *vertexData, size_t dataSizeInBytes) +GLES2_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcquad, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - -#if !SDL_GLES2_USE_VBOS - data->glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, vertexData); -#else - if (!data->vertex_buffers[attr]) { - data->glGenBuffers(1, &data->vertex_buffers[attr]); + /* render expects cos value - 1 (see GLES2_VertexSrc_Default_) */ + const float radian_angle = (float)(M_PI * (360.0 - angle) / 180.0); + const GLfloat s = (GLfloat) SDL_sin(radian_angle); + const GLfloat c = (GLfloat) SDL_cos(radian_angle) - 1.0f; + const GLfloat centerx = center->x + dstrect->x; + const GLfloat centery = center->y + dstrect->y; + GLfloat minx, miny, maxx, maxy; + GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 32 * sizeof (GLfloat), 0, &cmd->data.draw.first); + + if (!verts) { + return -1; } - data->glBindBuffer(GL_ARRAY_BUFFER, data->vertex_buffers[attr]); - - if (data->vertex_buffer_size[attr] < dataSizeInBytes) { - data->glBufferData(GL_ARRAY_BUFFER, dataSizeInBytes, vertexData, GL_STREAM_DRAW); - data->vertex_buffer_size[attr] = dataSizeInBytes; + if (flip & SDL_FLIP_HORIZONTAL) { + minx = dstrect->x + dstrect->w; + maxx = dstrect->x; } else { - data->glBufferSubData(GL_ARRAY_BUFFER, 0, dataSizeInBytes, vertexData); + minx = dstrect->x; + maxx = dstrect->x + dstrect->w; } - data->glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, 0); -#endif + if (flip & SDL_FLIP_VERTICAL) { + miny = dstrect->y + dstrect->h; + maxy = dstrect->y; + } else { + miny = dstrect->y; + maxy = dstrect->y + dstrect->h; + } + + minu = ((GLfloat) srcquad->x) / ((GLfloat) texture->w); + maxu = ((GLfloat) (srcquad->x + srcquad->w)) / ((GLfloat) texture->w); + minv = ((GLfloat) srcquad->y) / ((GLfloat) texture->h); + maxv = ((GLfloat) (srcquad->y + srcquad->h)) / ((GLfloat) texture->h); + + + cmd->data.draw.count = 1; + + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = maxy; + + *(verts++) = minu; + *(verts++) = minv; + *(verts++) = maxu; + *(verts++) = minv; + *(verts++) = minu; + *(verts++) = maxv; + *(verts++) = maxu; + *(verts++) = maxv; + + *(verts++) = s; + *(verts++) = c; + *(verts++) = s; + *(verts++) = c; + *(verts++) = s; + *(verts++) = c; + *(verts++) = s; + *(verts++) = c; + + *(verts++) = centerx; + *(verts++) = centery; + *(verts++) = centerx; + *(verts++) = centery; + *(verts++) = centerx; + *(verts++) = centery; + *(verts++) = centerx; + *(verts++) = centery; return 0; } static int -GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count) +SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_ImageSource imgsrc) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat *vertices; - int idx; + const SDL_bool was_copy_ex = data->drawstate.is_copy_ex; + const SDL_bool is_copy_ex = (cmd->command == SDL_RENDERCMD_COPY_EX); + SDL_Texture *texture = cmd->data.draw.texture; + const SDL_BlendMode blend = cmd->data.draw.blend; + GLES2_ProgramCacheEntry *program; - if (GLES2_SetDrawingState(renderer) < 0) { - return -1; + SDL_assert((texture != NULL) == (imgsrc != GLES2_IMAGESOURCE_SOLID)); + + if (data->drawstate.viewport_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + data->glViewport(viewport->x, + data->drawstate.target ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h), + viewport->w, viewport->h); + if (viewport->w && viewport->h) { + data->drawstate.projection[0][0] = 2.0f / viewport->w; + data->drawstate.projection[1][1] = (data->drawstate.target ? 2.0f : -2.0f) / viewport->h; + data->drawstate.projection[3][1] = data->drawstate.target ? -1.0f : 1.0f; + } + data->drawstate.viewport_dirty = SDL_FALSE; } - /* Emit the specified vertices as points */ - vertices = SDL_stack_alloc(GLfloat, count * 2); - for (idx = 0; idx < count; ++idx) { - GLfloat x = points[idx].x + 0.5f; - GLfloat y = points[idx].y + 0.5f; + if (data->drawstate.cliprect_enabled_dirty) { + if (!data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } else { + data->glEnable(GL_SCISSOR_TEST); + } + data->drawstate.cliprect_enabled_dirty = SDL_FALSE; + } + + if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) { + const SDL_Rect *viewport = &data->drawstate.viewport; + const SDL_Rect *rect = &data->drawstate.cliprect; + data->glScissor(viewport->x + rect->x, + data->drawstate.target ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h, + rect->w, rect->h); + data->drawstate.cliprect_dirty = SDL_FALSE; + } + + if (texture != data->drawstate.texture) { + if ((texture != NULL) != data->drawstate.texturing) { + if (texture == NULL) { + data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_TEXCOORD); + data->drawstate.texturing = SDL_FALSE; + } else { + data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_TEXCOORD); + data->drawstate.texturing = SDL_TRUE; + } + } + + if (texture) { + GLES2_TextureData *tdata = (GLES2_TextureData *) texture->driverdata; + if (tdata->yuv) { + data->glActiveTexture(GL_TEXTURE2); + data->glBindTexture(tdata->texture_type, tdata->texture_v); + + data->glActiveTexture(GL_TEXTURE1); + data->glBindTexture(tdata->texture_type, tdata->texture_u); + + data->glActiveTexture(GL_TEXTURE0); + } else if (tdata->nv12) { + data->glActiveTexture(GL_TEXTURE1); + data->glBindTexture(tdata->texture_type, tdata->texture_u); - vertices[idx * 2] = x; - vertices[(idx * 2) + 1] = y; + data->glActiveTexture(GL_TEXTURE0); + } + data->glBindTexture(tdata->texture_type, tdata->texture); + } + + data->drawstate.texture = texture; } - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/ - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2 * sizeof(GLfloat)); - data->glDrawArrays(GL_POINTS, 0, count); - SDL_stack_free(vertices); - return 0; -} -static int -GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat *vertices; - int idx; + if (texture) { + data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) (cmd->data.draw.first + (sizeof (GLfloat) * 8))); + } - if (GLES2_SetDrawingState(renderer) < 0) { + if (GLES2_SelectProgram(data, imgsrc, texture ? texture->w : 0, texture ? texture->h : 0) < 0) { return -1; } - /* Emit a line strip including the specified vertices */ - vertices = SDL_stack_alloc(GLfloat, count * 2); - for (idx = 0; idx < count; ++idx) { - GLfloat x = points[idx].x + 0.5f; - GLfloat y = points[idx].y + 0.5f; + program = data->drawstate.program; - vertices[idx * 2] = x; - vertices[(idx * 2) + 1] = y; + if (program->uniform_locations[GLES2_UNIFORM_PROJECTION] != -1) { + if (SDL_memcmp(program->projection, data->drawstate.projection, sizeof (data->drawstate.projection)) != 0) { + data->glUniformMatrix4fv(program->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)data->drawstate.projection); + SDL_memcpy(program->projection, data->drawstate.projection, sizeof (data->drawstate.projection)); + } } - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/ - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2 * sizeof(GLfloat)); - data->glDrawArrays(GL_LINE_STRIP, 0, count); - /* We need to close the endpoint of the line */ - if (count == 2 || - points[0].x != points[count-1].x || points[0].y != points[count-1].y) { - data->glDrawArrays(GL_POINTS, count-1, 1); + if (program->uniform_locations[GLES2_UNIFORM_COLOR] != -1) { + if (data->drawstate.color != program->color) { + const Uint8 r = (data->drawstate.color >> 16) & 0xFF; + const Uint8 g = (data->drawstate.color >> 8) & 0xFF; + const Uint8 b = (data->drawstate.color >> 0) & 0xFF; + const Uint8 a = (data->drawstate.color >> 24) & 0xFF; + data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_COLOR], r * inv255f, g * inv255f, b * inv255f, a * inv255f); + program->color = data->drawstate.color; + } } - SDL_stack_free(vertices); - return GL_CheckError("", renderer); -} - -static int -GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat vertices[8]; - int idx; - - if (GLES2_SetDrawingState(renderer) < 0) { - return -1; + if (blend != data->drawstate.blend) { + if (blend == SDL_BLENDMODE_NONE) { + data->glDisable(GL_BLEND); + } else { + data->glEnable(GL_BLEND); + data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)), + GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)), + GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); + data->glBlendEquationSeparate(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)), + GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend))); + } + data->drawstate.blend = blend; } - /* Emit a line loop for each rectangle */ - for (idx = 0; idx < count; ++idx) { - const SDL_FRect *rect = &rects[idx]; + /* all drawing commands use this */ + data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) cmd->data.draw.first); - GLfloat xMin = rect->x; - GLfloat xMax = (rect->x + rect->w); - GLfloat yMin = rect->y; - GLfloat yMax = (rect->y + rect->h); + if (is_copy_ex != was_copy_ex) { + if (is_copy_ex) { + data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_ANGLE); + data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_CENTER); + } else { + data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_ANGLE); + data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_CENTER); + } + data->drawstate.is_copy_ex = is_copy_ex; + } - vertices[0] = xMin; - vertices[1] = yMin; - vertices[2] = xMax; - vertices[3] = yMin; - vertices[4] = xMin; - vertices[5] = yMax; - vertices[6] = xMax; - vertices[7] = yMax; - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/ - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat)); - data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + if (is_copy_ex) { + data->glVertexAttribPointer(GLES2_ATTRIBUTE_ANGLE, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) (cmd->data.draw.first + (sizeof (GLfloat) * 16))); + data->glVertexAttribPointer(GLES2_ATTRIBUTE_CENTER, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) (cmd->data.draw.first + (sizeof (GLfloat) * 24))); } - return GL_CheckError("", renderer); + + return 0; } static int -GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) +SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - GLES2_ProgramCacheEntry *program; - Uint8 r, g, b, a; + SDL_Texture *texture = cmd->data.draw.texture; - /* Activate an appropriate shader and set the projection matrix */ + /* Pick an appropriate shader */ if (renderer->target) { /* Check if we need to do color mapping between the source and render target textures */ if (renderer->target->format != texture->format) { @@ -1764,178 +1190,602 @@ GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) } } - if (GLES2_SelectProgram(renderer, sourceType, texture->w, texture->h) < 0) { + return SetDrawState(data, cmd, sourceType); +} + +static int +GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; + const SDL_bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || renderer->target->format == SDL_PIXELFORMAT_RGB888)); + const int vboidx = data->current_vertex_buffer; + const GLuint vbo = data->vertex_buffers[vboidx]; + size_t i; + + if (GLES2_ActivateRenderer(renderer) < 0) { return -1; } - /* Select the target texture */ - if (tdata->yuv) { - data->glActiveTexture(GL_TEXTURE2); - data->glBindTexture(tdata->texture_type, tdata->texture_v); + data->drawstate.target = renderer->target; + if (!data->drawstate.target) { + SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh); + } - data->glActiveTexture(GL_TEXTURE1); - data->glBindTexture(tdata->texture_type, tdata->texture_u); + /* upload the new VBO data for this set of commands. */ + data->glBindBuffer(GL_ARRAY_BUFFER, vbo); + if (data->vertex_buffer_size[vboidx] < vertsize) { + data->glBufferData(GL_ARRAY_BUFFER, vertsize, vertices, GL_STREAM_DRAW); + data->vertex_buffer_size[vboidx] = vertsize; + } else { + data->glBufferSubData(GL_ARRAY_BUFFER, 0, vertsize, vertices); + } - data->glActiveTexture(GL_TEXTURE0); + /* cycle through a few VBOs so the GL has some time with the data before we replace it. */ + data->current_vertex_buffer++; + if (data->current_vertex_buffer >= SDL_arraysize(data->vertex_buffers)) { + data->current_vertex_buffer = 0; } - if (tdata->nv12) { - data->glActiveTexture(GL_TEXTURE1); - data->glBindTexture(tdata->texture_type, tdata->texture_u); - data->glActiveTexture(GL_TEXTURE0); + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + const Uint8 r = colorswap ? cmd->data.color.b : cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = colorswap ? cmd->data.color.r : cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + data->drawstate.color = ((a << 24) | (r << 16) | (g << 8) | b); + break; + } + + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_Rect *viewport = &data->drawstate.viewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); + data->drawstate.viewport_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + data->drawstate.cliprect_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_CLEAR: { + const Uint8 r = colorswap ? cmd->data.color.b : cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = colorswap ? cmd->data.color.r : cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + if (color != data->drawstate.clear_color) { + const GLfloat fr = ((GLfloat) r) * inv255f; + const GLfloat fg = ((GLfloat) g) * inv255f; + const GLfloat fb = ((GLfloat) b) * inv255f; + const GLfloat fa = ((GLfloat) a) * inv255f; + data->glClearColor(fr, fg, fb, fa); + data->drawstate.clear_color = color; + } + + if (data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + + data->glClear(GL_COLOR_BUFFER_BIT); + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: { + if (SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID) == 0) { + data->glDrawArrays(GL_POINTS, 0, cmd->data.draw.count); + } + break; + } + + case SDL_RENDERCMD_DRAW_LINES: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + const size_t count = cmd->data.draw.count; + if (SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID) == 0) { + if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) { + /* GL_LINE_LOOP takes care of the final segment */ + data->glDrawArrays(GL_LINE_LOOP, 0, count - 1); + } else { + data->glDrawArrays(GL_LINE_STRIP, 0, count); + /* We need to close the endpoint of the line */ + data->glDrawArrays(GL_POINTS, count - 1, 1); + } + } + break; + } + + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + size_t offset = 0; + if (SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID) == 0) { + for (i = 0; i < count; ++i, offset += 4) { + data->glDrawArrays(GL_TRIANGLE_STRIP, offset, 4); + } + } + break; + } + + case SDL_RENDERCMD_COPY: + case SDL_RENDERCMD_COPY_EX: { + if (SetCopyState(renderer, cmd) == 0) { + data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } + + return GL_CheckError("", renderer); +} + +static void +GLES2_DestroyRenderer(SDL_Renderer *renderer) +{ + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; + + /* Deallocate everything */ + if (data) { + GLES2_ActivateRenderer(renderer); + + { + GLES2_ShaderCacheEntry *entry; + GLES2_ShaderCacheEntry *next; + entry = data->shader_cache.head; + while (entry) { + data->glDeleteShader(entry->id); + next = entry->next; + SDL_free(entry); + entry = next; + } + } + { + GLES2_ProgramCacheEntry *entry; + GLES2_ProgramCacheEntry *next; + entry = data->program_cache.head; + while (entry) { + data->glDeleteProgram(entry->id); + next = entry->next; + SDL_free(entry); + entry = next; + } + } + + if (data->context) { + while (data->framebuffers) { + GLES2_FBOList *nextnode = data->framebuffers->next; + data->glDeleteFramebuffers(1, &data->framebuffers->FBO); + GL_CheckError("", renderer); + SDL_free(data->framebuffers); + data->framebuffers = nextnode; + } + + data->glDeleteBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers); + GL_CheckError("", renderer); + + SDL_GL_DeleteContext(data->context); + } + + SDL_free(data->shader_formats); + SDL_free(data); + } + SDL_free(renderer); +} + +static int +GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + GLES2_RenderData *renderdata = (GLES2_RenderData *)renderer->driverdata; + GLES2_TextureData *data; + GLenum format; + GLenum type; + GLenum scaleMode; + + GLES2_ActivateRenderer(renderer); + + /* Determine the corresponding GLES texture format params */ + switch (texture->format) + { + case SDL_PIXELFORMAT_ARGB8888: + case SDL_PIXELFORMAT_ABGR8888: + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_BGR888: + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + format = GL_LUMINANCE; + type = GL_UNSIGNED_BYTE; + break; +#ifdef GL_TEXTURE_EXTERNAL_OES + case SDL_PIXELFORMAT_EXTERNAL_OES: + format = GL_NONE; + type = GL_NONE; + break; +#endif + default: + return SDL_SetError("Texture format not supported"); + } + + if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES && + texture->access != SDL_TEXTUREACCESS_STATIC) { + return SDL_SetError("Unsupported texture access for SDL_PIXELFORMAT_EXTERNAL_OES"); + } + + /* Allocate a texture struct */ + data = (GLES2_TextureData *)SDL_calloc(1, sizeof(GLES2_TextureData)); + if (!data) { + return SDL_OutOfMemory(); + } + data->texture = 0; +#ifdef GL_TEXTURE_EXTERNAL_OES + data->texture_type = (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; +#else + data->texture_type = GL_TEXTURE_2D; +#endif + data->pixel_format = format; + data->pixel_type = type; + data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12)); + data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21)); + data->texture_u = 0; + data->texture_v = 0; + scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR; + + /* Allocate a blob for image renderdata */ + if (texture->access == SDL_TEXTUREACCESS_STREAMING) { + size_t size; + data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); + size = texture->h * data->pitch; + if (data->yuv) { + /* Need to add size for the U and V planes */ + size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); + } else if (data->nv12) { + /* Need to add size for the U/V plane */ + size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); + } + data->pixel_data = SDL_calloc(1, size); + if (!data->pixel_data) { + SDL_free(data); + return SDL_OutOfMemory(); + } + } + + /* Allocate the texture */ + GL_CheckError("", renderer); + + if (data->yuv) { + renderdata->glGenTextures(1, &data->texture_v); + if (GL_CheckError("glGenTexures()", renderer) < 0) { + return -1; + } + renderdata->glActiveTexture(GL_TEXTURE2); + renderdata->glBindTexture(data->texture_type, data->texture_v); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL); + + renderdata->glGenTextures(1, &data->texture_u); + if (GL_CheckError("glGenTexures()", renderer) < 0) { + return -1; + } + renderdata->glActiveTexture(GL_TEXTURE1); + renderdata->glBindTexture(data->texture_type, data->texture_u); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL); + if (GL_CheckError("glTexImage2D()", renderer) < 0) { + return -1; + } + } else if (data->nv12) { + renderdata->glGenTextures(1, &data->texture_u); + if (GL_CheckError("glGenTexures()", renderer) < 0) { + return -1; + } + renderdata->glActiveTexture(GL_TEXTURE1); + renderdata->glBindTexture(data->texture_type, data->texture_u); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + if (GL_CheckError("glTexImage2D()", renderer) < 0) { + return -1; + } } - data->glBindTexture(tdata->texture_type, tdata->texture); - /* Configure color modulation */ - g = texture->g; - a = texture->a; + renderdata->glGenTextures(1, &data->texture); + if (GL_CheckError("glGenTexures()", renderer) < 0) { + return -1; + } + texture->driverdata = data; + renderdata->glActiveTexture(GL_TEXTURE0); + renderdata->glBindTexture(data->texture_type, data->texture); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (texture->format != SDL_PIXELFORMAT_EXTERNAL_OES) { + renderdata->glTexImage2D(data->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL); + if (GL_CheckError("glTexImage2D()", renderer) < 0) { + return -1; + } + } - if (renderer->target && - (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || - renderer->target->format == SDL_PIXELFORMAT_RGB888)) { - r = texture->b; - b = texture->r; + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + data->fbo = GLES2_GetFBO(renderer->driverdata, texture->w, texture->h); } else { - r = texture->r; - b = texture->b; + data->fbo = NULL; } - program = data->current_program; + return GL_CheckError("", renderer); +} + +static int +GLES2_TexSubImage2D(GLES2_RenderData *data, GLenum target, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, GLint pitch, GLint bpp) +{ + Uint8 *blob = NULL; + Uint8 *src; + int src_pitch; + int y; - if (!CompareColors(program->modulation_r, program->modulation_g, program->modulation_b, program->modulation_a, r, g, b, a)) { - data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_MODULATION], r * inv255f, g * inv255f, b * inv255f, a * inv255f); - program->modulation_r = r; - program->modulation_g = g; - program->modulation_b = b; - program->modulation_a = a; + if ((width == 0) || (height == 0) || (bpp == 0)) { + return 0; /* nothing to do */ } - /* Configure texture blending */ - GLES2_SetBlendMode(data, texture->blendMode); + /* Reformat the texture data into a tightly packed array */ + src_pitch = width * bpp; + src = (Uint8 *)pixels; + if (pitch != src_pitch) { + blob = (Uint8 *)SDL_malloc(src_pitch * height); + if (!blob) { + return SDL_OutOfMemory(); + } + src = blob; + for (y = 0; y < height; ++y) + { + SDL_memcpy(src, pixels, src_pitch); + src += src_pitch; + pixels = (Uint8 *)pixels + pitch; + } + src = blob; + } - GLES2_SetTexCoords(data, SDL_TRUE); + data->glTexSubImage2D(target, 0, xoffset, yoffset, width, height, format, type, src); + if (blob) { + SDL_free(blob); + } return 0; } static int -GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, - const SDL_FRect *dstrect) +GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, + const void *pixels, int pitch) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat vertices[8]; - GLfloat texCoords[8]; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; GLES2_ActivateRenderer(renderer); - if (GLES2_SetupCopy(renderer, texture) < 0) { - return -1; + /* Bail out if we're supposed to update an empty rectangle */ + if (rect->w <= 0 || rect->h <= 0) { + return 0; } - /* Emit the textured quad */ - vertices[0] = dstrect->x; - vertices[1] = dstrect->y; - vertices[2] = (dstrect->x + dstrect->w); - vertices[3] = dstrect->y; - vertices[4] = dstrect->x; - vertices[5] = (dstrect->y + dstrect->h); - vertices[6] = (dstrect->x + dstrect->w); - vertices[7] = (dstrect->y + dstrect->h); - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/ - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat)); - texCoords[0] = srcrect->x / (GLfloat)texture->w; - texCoords[1] = srcrect->y / (GLfloat)texture->h; - texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - texCoords[3] = srcrect->y / (GLfloat)texture->h; - texCoords[4] = srcrect->x / (GLfloat)texture->w; - texCoords[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - texCoords[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);*/ - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_TEXCOORD, texCoords, 8 * sizeof(GLfloat)); - data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + /* Create a texture subimage with the supplied data */ + data->glBindTexture(tdata->texture_type, tdata->texture); + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x, + rect->y, + rect->w, + rect->h, + tdata->pixel_format, + tdata->pixel_type, + pixels, pitch, SDL_BYTESPERPIXEL(texture->format)); + + if (tdata->yuv) { + /* Skip to the correct offset into the next texture */ + pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); + if (texture->format == SDL_PIXELFORMAT_YV12) { + data->glBindTexture(tdata->texture_type, tdata->texture_v); + } else { + data->glBindTexture(tdata->texture_type, tdata->texture_u); + } + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + tdata->pixel_format, + tdata->pixel_type, + pixels, (pitch + 1) / 2, 1); + - return GL_CheckError("", renderer); + /* Skip to the correct offset into the next texture */ + pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1)/2)); + if (texture->format == SDL_PIXELFORMAT_YV12) { + data->glBindTexture(tdata->texture_type, tdata->texture_u); + } else { + data->glBindTexture(tdata->texture_type, tdata->texture_v); + } + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + tdata->pixel_format, + tdata->pixel_type, + pixels, (pitch + 1) / 2, 1); + } else if (tdata->nv12) { + /* Skip to the correct offset into the next texture */ + pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); + data->glBindTexture(tdata->texture_type, tdata->texture_u); + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + pixels, 2 * ((pitch + 1) / 2), 2); + } + + return GL_CheckError("glTexSubImage2D()", renderer); } static int -GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, - const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) +GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *Uplane, int Upitch, + const Uint8 *Vplane, int Vpitch) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat vertices[8]; - GLfloat texCoords[8]; - GLfloat translate[8]; - GLfloat fAngle[8]; - GLfloat tmp; - float radian_angle; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; GLES2_ActivateRenderer(renderer); - if (GLES2_SetupCopy(renderer, texture) < 0) { - return -1; + /* Bail out if we're supposed to update an empty rectangle */ + if (rect->w <= 0 || rect->h <= 0) { + return 0; } - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_CENTER); - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE); + data->glBindTexture(tdata->texture_type, tdata->texture_v); + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + tdata->pixel_format, + tdata->pixel_type, + Vplane, Vpitch, 1); - radian_angle = (float)(M_PI * (360.0 - angle) / 180.0); - fAngle[0] = fAngle[2] = fAngle[4] = fAngle[6] = (GLfloat)SDL_sin(radian_angle); - /* render expects cos value - 1 (see GLES2_VertexSrc_Default_) */ - fAngle[1] = fAngle[3] = fAngle[5] = fAngle[7] = (GLfloat)SDL_cos(radian_angle) - 1.0f; - /* Calculate the center of rotation */ - translate[0] = translate[2] = translate[4] = translate[6] = (center->x + dstrect->x); - translate[1] = translate[3] = translate[5] = translate[7] = (center->y + dstrect->y); - - /* Emit the textured quad */ - vertices[0] = dstrect->x; - vertices[1] = dstrect->y; - vertices[2] = (dstrect->x + dstrect->w); - vertices[3] = dstrect->y; - vertices[4] = dstrect->x; - vertices[5] = (dstrect->y + dstrect->h); - vertices[6] = (dstrect->x + dstrect->w); - vertices[7] = (dstrect->y + dstrect->h); - if (flip & SDL_FLIP_HORIZONTAL) { - tmp = vertices[0]; - vertices[0] = vertices[4] = vertices[2]; - vertices[2] = vertices[6] = tmp; + data->glBindTexture(tdata->texture_type, tdata->texture_u); + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + tdata->pixel_format, + tdata->pixel_type, + Uplane, Upitch, 1); + + data->glBindTexture(tdata->texture_type, tdata->texture); + GLES2_TexSubImage2D(data, tdata->texture_type, + rect->x, + rect->y, + rect->w, + rect->h, + tdata->pixel_format, + tdata->pixel_type, + Yplane, Ypitch, 1); + + return GL_CheckError("glTexSubImage2D()", renderer); +} + +static int +GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, + void **pixels, int *pitch) +{ + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; + + /* Retrieve the buffer/pitch for the specified region */ + *pixels = (Uint8 *)tdata->pixel_data + + (tdata->pitch * rect->y) + + (rect->x * SDL_BYTESPERPIXEL(texture->format)); + *pitch = tdata->pitch; + + return 0; +} + +static void +GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; + SDL_Rect rect; + + /* We do whole texture updates, at least for now */ + rect.x = 0; + rect.y = 0; + rect.w = texture->w; + rect.h = texture->h; + GLES2_UpdateTexture(renderer, texture, &rect, tdata->pixel_data, tdata->pitch); +} + +static int +GLES2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) +{ + GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; + GLES2_TextureData *texturedata = NULL; + GLenum status; + + if (texture == NULL) { + data->glBindFramebuffer(GL_FRAMEBUFFER, data->window_framebuffer); + } else { + texturedata = (GLES2_TextureData *) texture->driverdata; + 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 SDL_SetError("glFramebufferTexture2D() failed"); + } } - if (flip & SDL_FLIP_VERTICAL) { - tmp = vertices[1]; - vertices[1] = vertices[3] = vertices[5]; - vertices[5] = vertices[7] = tmp; - } - - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_ANGLE, 1, GL_FLOAT, GL_FALSE, 0, &fAngle); - data->glVertexAttribPointer(GLES2_ATTRIBUTE_CENTER, 2, GL_FLOAT, GL_FALSE, 0, translate); - data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/ - - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_ANGLE, fAngle, 8 * sizeof(GLfloat)); - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_CENTER, translate, 8 * sizeof(GLfloat)); - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat)); - - texCoords[0] = srcrect->x / (GLfloat)texture->w; - texCoords[1] = srcrect->y / (GLfloat)texture->h; - texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - texCoords[3] = srcrect->y / (GLfloat)texture->h; - texCoords[4] = srcrect->x / (GLfloat)texture->w; - texCoords[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - texCoords[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);*/ - GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_TEXCOORD, texCoords, 8 * sizeof(GLfloat)); - data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_CENTER); - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE); + return 0; +} - return GL_CheckError("", renderer); +static void +GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; + + GLES2_ActivateRenderer(renderer); + + /* Destroy the texture */ + if (tdata) { + data->glDeleteTextures(1, &tdata->texture); + if (tdata->texture_v) { + data->glDeleteTextures(1, &tdata->texture_v); + } + if (tdata->texture_u) { + data->glDeleteTextures(1, &tdata->texture_u); + } + SDL_free(tdata->pixel_data); + SDL_free(tdata); + texture->driverdata = NULL; + } } static int GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, void * pixels, int pitch) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888; size_t buflen; void *temp_pixels; @@ -1944,8 +1794,6 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, int w, h, length, rows; int status; - GLES2_ActivateRenderer(renderer); - temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format); buflen = (size_t) (rect->h * temp_pitch); if (buflen == 0) { @@ -1967,10 +1815,11 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, /* Flip the rows to be top-down if necessary */ if (!renderer->target) { + SDL_bool isstack; length = rect->w * SDL_BYTESPERPIXEL(temp_format); src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch; dst = (Uint8*)temp_pixels; - tmp = SDL_stack_alloc(Uint8, length); + tmp = SDL_small_alloc(Uint8, length, &isstack); rows = rect->h / 2; while (rows--) { SDL_memcpy(tmp, dst, length); @@ -1979,7 +1828,7 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, dst += temp_pitch; src -= temp_pitch; } - SDL_stack_free(tmp); + SDL_small_free(tmp, isstack); } status = SDL_ConvertPixels(rect->w, rect->h, @@ -1993,8 +1842,6 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, static void GLES2_RenderPresent(SDL_Renderer *renderer) { - GLES2_ActivateRenderer(renderer); - /* Tell the video driver to swap buffers */ SDL_GL_SwapWindow(renderer->window); } @@ -2008,7 +1855,7 @@ static int GLES2_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture); static int GLES2_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; GLES2_TextureData *texturedata = (GLES2_TextureData *)texture->driverdata; GLES2_ActivateRenderer(renderer); @@ -2026,7 +1873,7 @@ static int GLES2_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, flo static int GLES2_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; GLES2_TextureData *texturedata = (GLES2_TextureData *)texture->driverdata; GLES2_ActivateRenderer(renderer); @@ -2044,40 +1891,12 @@ static int GLES2_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture) #define GL_NVIDIA_PLATFORM_BINARY_NV 0x890B #endif -static void -GLES2_ResetState(SDL_Renderer *renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; - - if (SDL_CurrentContext == data->context) { - GLES2_UpdateViewport(renderer); - } else { - GLES2_ActivateRenderer(renderer); - } - - data->current.blendMode = SDL_BLENDMODE_INVALID; - data->current.tex_coords = SDL_FALSE; - - data->glActiveTexture(GL_TEXTURE0); - data->glPixelStorei(GL_PACK_ALIGNMENT, 1); - data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - data->glClearColor((GLfloat) data->clear_r * inv255f, - (GLfloat) data->clear_g * inv255f, - (GLfloat) data->clear_b * inv255f, - (GLfloat) data->clear_a * inv255f); - - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION); - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); - - GL_CheckError("", renderer); -} static SDL_Renderer * GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) { SDL_Renderer *renderer; - GLES2_DriverContext *data; + GLES2_RenderData *data; GLint nFormats; #ifndef ZUNE_HD GLboolean hasCompiler; @@ -2099,6 +1918,7 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) } window_flags = SDL_GetWindowFlags(window); + /* OpenGL ES 3.0 is a superset of OpenGL ES 2.0 */ if (!(window_flags & SDL_WINDOW_OPENGL) || profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major < RENDERER_CONTEXT_MAJOR) { @@ -2120,7 +1940,7 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) goto error; } - data = (GLES2_DriverContext *)SDL_calloc(1, sizeof(GLES2_DriverContext)); + data = (GLES2_RenderData *)SDL_calloc(1, sizeof(GLES2_RenderData)); if (!data) { SDL_free(renderer); SDL_OutOfMemory(); @@ -2209,6 +2029,9 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) } #endif /* ZUNE_HD */ + /* we keep a few of these and cycle through them, so data can live for a few frames. */ + data->glGenBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers); + data->framebuffers = NULL; data->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &window_framebuffer); data->window_framebuffer = (GLuint)window_framebuffer; @@ -2223,14 +2046,14 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->LockTexture = GLES2_LockTexture; renderer->UnlockTexture = GLES2_UnlockTexture; renderer->SetRenderTarget = GLES2_SetRenderTarget; - renderer->UpdateViewport = GLES2_UpdateViewport; - renderer->UpdateClipRect = GLES2_UpdateClipRect; - renderer->RenderClear = GLES2_RenderClear; - renderer->RenderDrawPoints = GLES2_RenderDrawPoints; - renderer->RenderDrawLines = GLES2_RenderDrawLines; - renderer->RenderFillRects = GLES2_RenderFillRects; - renderer->RenderCopy = GLES2_RenderCopy; - renderer->RenderCopyEx = GLES2_RenderCopyEx; + renderer->QueueSetViewport = GLES2_QueueSetViewport; + renderer->QueueSetDrawColor = GLES2_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = GLES2_QueueDrawPoints; + renderer->QueueDrawLines = GLES2_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = GLES2_QueueFillRects; + renderer->QueueCopy = GLES2_QueueCopy; + renderer->QueueCopyEx = GLES2_QueueCopyEx; + renderer->RunCommandQueue = GLES2_RunCommandQueue; renderer->RenderReadPixels = GLES2_RenderReadPixels; renderer->RenderPresent = GLES2_RenderPresent; renderer->DestroyTexture = GLES2_DestroyTexture; @@ -2246,7 +2069,20 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_EXTERNAL_OES; #endif - GLES2_ResetState(renderer); + data->glActiveTexture(GL_TEXTURE0); + data->glPixelStorei(GL_PACK_ALIGNMENT, 1); + data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION); + data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); + + data->drawstate.blend = SDL_BLENDMODE_INVALID; + data->drawstate.color = 0xFFFFFFFF; + data->drawstate.clear_color = 0xFFFFFFFF; + data->drawstate.projection[3][0] = -1.0f; + data->drawstate.projection[3][3] = 1.0f; + + GL_CheckError("", renderer); return renderer; @@ -2261,6 +2097,23 @@ error: return NULL; } +SDL_RenderDriver GLES2_RenderDriver = { + GLES2_CreateRenderer, + { + "opengles2", + (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), + 4, + { + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_ABGR8888, + SDL_PIXELFORMAT_RGB888, + SDL_PIXELFORMAT_BGR888 + }, + 0, + 0 + } +}; + #endif /* SDL_VIDEO_RENDER_OGL_ES2 && !SDL_RENDER_DISABLED */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/opengles2/SDL_shaders_gles2.c b/src/render/opengles2/SDL_shaders_gles2.c index f428a49456..7c3e6c17cf 100644 --- a/src/render/opengles2/SDL_shaders_gles2.c +++ b/src/render/opengles2/SDL_shaders_gles2.c @@ -69,13 +69,13 @@ static const Uint8 GLES2_FragmentSrc_SolidSrc_[] = " \ static const Uint8 GLES2_FragmentSrc_TextureABGRSrc_[] = " \ precision mediump float; \ uniform sampler2D u_texture; \ - uniform vec4 u_modulation; \ + uniform vec4 u_color; \ varying vec2 v_texCoord; \ \ void main() \ { \ gl_FragColor = texture2D(u_texture, v_texCoord); \ - gl_FragColor *= u_modulation; \ + gl_FragColor *= u_color; \ } \ "; @@ -83,7 +83,7 @@ static const Uint8 GLES2_FragmentSrc_TextureABGRSrc_[] = " \ static const Uint8 GLES2_FragmentSrc_TextureARGBSrc_[] = " \ precision mediump float; \ uniform sampler2D u_texture; \ - uniform vec4 u_modulation; \ + uniform vec4 u_color; \ varying vec2 v_texCoord; \ \ void main() \ @@ -92,7 +92,7 @@ static const Uint8 GLES2_FragmentSrc_TextureARGBSrc_[] = " \ gl_FragColor = abgr; \ gl_FragColor.r = abgr.b; \ gl_FragColor.b = abgr.r; \ - gl_FragColor *= u_modulation; \ + gl_FragColor *= u_color; \ } \ "; @@ -100,7 +100,7 @@ static const Uint8 GLES2_FragmentSrc_TextureARGBSrc_[] = " \ static const Uint8 GLES2_FragmentSrc_TextureRGBSrc_[] = " \ precision mediump float; \ uniform sampler2D u_texture; \ - uniform vec4 u_modulation; \ + uniform vec4 u_color; \ varying vec2 v_texCoord; \ \ void main() \ @@ -110,7 +110,7 @@ static const Uint8 GLES2_FragmentSrc_TextureRGBSrc_[] = " \ gl_FragColor.r = abgr.b; \ gl_FragColor.b = abgr.r; \ gl_FragColor.a = 1.0; \ - gl_FragColor *= u_modulation; \ + gl_FragColor *= u_color; \ } \ "; @@ -118,7 +118,7 @@ static const Uint8 GLES2_FragmentSrc_TextureRGBSrc_[] = " \ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ precision mediump float; \ uniform sampler2D u_texture; \ - uniform vec4 u_modulation; \ + uniform vec4 u_color; \ varying vec2 v_texCoord; \ \ void main() \ @@ -126,7 +126,7 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ vec4 abgr = texture2D(u_texture, v_texCoord); \ gl_FragColor = abgr; \ gl_FragColor.a = 1.0; \ - gl_FragColor *= u_modulation; \ + gl_FragColor *= u_color; \ } \ "; @@ -163,7 +163,7 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ "uniform sampler2D u_texture;\n" \ "uniform sampler2D u_texture_u;\n" \ "uniform sampler2D u_texture_v;\n" \ -"uniform vec4 u_modulation;\n" \ +"uniform vec4 u_color;\n" \ "varying vec2 v_texCoord;\n" \ "\n" \ @@ -185,7 +185,7 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ "\n" \ " // That was easy. :) \n" \ " gl_FragColor = vec4(rgb, 1);\n" \ -" gl_FragColor *= u_modulation;\n" \ +" gl_FragColor *= u_color;\n" \ "}" \ #define NV12_SHADER_BODY \ @@ -205,7 +205,7 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ "\n" \ " // That was easy. :) \n" \ " gl_FragColor = vec4(rgb, 1);\n" \ -" gl_FragColor *= u_modulation;\n" \ +" gl_FragColor *= u_color;\n" \ "}" \ #define NV21_SHADER_BODY \ @@ -225,7 +225,7 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ "\n" \ " // That was easy. :) \n" \ " gl_FragColor = vec4(rgb, 1);\n" \ -" gl_FragColor *= u_modulation;\n" \ +" gl_FragColor *= u_color;\n" \ "}" \ /* YUV to ABGR conversion */ @@ -284,13 +284,13 @@ static const Uint8 GLES2_FragmentSrc_TextureExternalOESSrc_[] = " \ #extension GL_OES_EGL_image_external : require\n\ precision mediump float; \ uniform samplerExternalOES u_texture; \ - uniform vec4 u_modulation; \ + uniform vec4 u_color; \ varying vec2 v_texCoord; \ \ void main() \ { \ gl_FragColor = texture2D(u_texture, v_texCoord); \ - gl_FragColor *= u_modulation; \ + gl_FragColor *= u_color; \ } \ "; diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index babc2526a0..eaf2ac7054 100644 --- a/src/render/psp/SDL_render_psp.c +++ b/src/render/psp/SDL_render_psp.c @@ -23,6 +23,7 @@ #if SDL_VIDEO_RENDER_PSP #include "SDL_hints.h" +#include "SDL_assert.h" #include "../SDL_sysrender.h" #include <pspkernel.h> @@ -42,74 +43,6 @@ /* PSP renderer implementation, based on the PGE */ - -extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); - - -static SDL_Renderer *PSP_CreateRenderer(SDL_Window * window, Uint32 flags); -static void PSP_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static int PSP_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int PSP_SetTextureColorMod(SDL_Renderer * renderer, - SDL_Texture * texture); -static int PSP_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *pixels, - int pitch); -static int PSP_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void PSP_UnlockTexture(SDL_Renderer * renderer, - SDL_Texture * texture); -static int PSP_SetRenderTarget(SDL_Renderer * renderer, - SDL_Texture * texture); -static int PSP_UpdateViewport(SDL_Renderer * renderer); -static int PSP_RenderClear(SDL_Renderer * renderer); -static int PSP_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int PSP_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int PSP_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int PSP_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, - const SDL_FRect * dstrect); -static int PSP_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch); -static int PSP_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip); -static void PSP_RenderPresent(SDL_Renderer * renderer); -static void PSP_DestroyTexture(SDL_Renderer * renderer, - SDL_Texture * texture); -static void PSP_DestroyRenderer(SDL_Renderer * renderer); - -/* -SDL_RenderDriver PSP_RenderDriver = { - PSP_CreateRenderer, - { - "PSP", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), - 1, - {SDL_PIXELFORMAT_ABGR8888}, - 0, - 0} -}; -*/ -SDL_RenderDriver PSP_RenderDriver = { - .CreateRenderer = PSP_CreateRenderer, - .info = { - .name = "PSP", - .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, - .num_texture_formats = 4, - .texture_formats = { [0] = SDL_PIXELFORMAT_BGR565, - [1] = SDL_PIXELFORMAT_ABGR1555, - [2] = SDL_PIXELFORMAT_ABGR4444, - [3] = SDL_PIXELFORMAT_ABGR8888, - }, - .max_texture_width = 512, - .max_texture_height = 512, - } -}; - #define PSP_SCREEN_WIDTH 480 #define PSP_SCREEN_HEIGHT 272 @@ -169,6 +102,42 @@ typedef struct } VertTV; +#define PI 3.14159265358979f + +#define radToDeg(x) ((x)*180.f/PI) +#define degToRad(x) ((x)*PI/180.f) + +float MathAbs(float x) +{ + float result; + + __asm__ volatile ( + "mtv %1, S000\n" + "vabs.s S000, S000\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x)); + + return result; +} + +void MathSincos(float r, float *s, float *c) +{ + __asm__ volatile ( + "mtv %2, S002\n" + "vcst.s S003, VFPU_2_PI\n" + "vmul.s S002, S002, S003\n" + "vrot.p C000, S002, [s, c]\n" + "mfv %0, S000\n" + "mfv %1, S001\n" + : "=r"(*s), "=r"(*c): "r"(r)); +} + +void Swap(float *a, float *b) +{ + float n=*a; + *a = *b; + *b = n; +} /* Return next power of 2 */ static int @@ -326,123 +295,9 @@ int TextureUnswizzle(PSP_TextureData *psp_texture) return 1; } -SDL_Renderer * -PSP_CreateRenderer(SDL_Window * window, Uint32 flags) -{ - - SDL_Renderer *renderer; - PSP_RenderData *data; - int pixelformat; - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - return NULL; - } - - data = (PSP_RenderData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - PSP_DestroyRenderer(renderer); - SDL_OutOfMemory(); - return NULL; - } - - - renderer->WindowEvent = PSP_WindowEvent; - renderer->CreateTexture = PSP_CreateTexture; - renderer->SetTextureColorMod = PSP_SetTextureColorMod; - renderer->UpdateTexture = PSP_UpdateTexture; - renderer->LockTexture = PSP_LockTexture; - renderer->UnlockTexture = PSP_UnlockTexture; - renderer->SetRenderTarget = PSP_SetRenderTarget; - renderer->UpdateViewport = PSP_UpdateViewport; - renderer->RenderClear = PSP_RenderClear; - renderer->RenderDrawPoints = PSP_RenderDrawPoints; - renderer->RenderDrawLines = PSP_RenderDrawLines; - renderer->RenderFillRects = PSP_RenderFillRects; - renderer->RenderCopy = PSP_RenderCopy; - renderer->RenderReadPixels = PSP_RenderReadPixels; - renderer->RenderCopyEx = PSP_RenderCopyEx; - renderer->RenderPresent = PSP_RenderPresent; - renderer->DestroyTexture = PSP_DestroyTexture; - renderer->DestroyRenderer = PSP_DestroyRenderer; - renderer->info = PSP_RenderDriver.info; - renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); - renderer->driverdata = data; - renderer->window = window; - - if (data->initialized != SDL_FALSE) - return 0; - data->initialized = SDL_TRUE; - - if (flags & SDL_RENDERER_PRESENTVSYNC) { - data->vsync = SDL_TRUE; - } else { - data->vsync = SDL_FALSE; - } - - pixelformat=PixelFormatToPSPFMT(SDL_GetWindowPixelFormat(window)); - switch(pixelformat) - { - case GU_PSM_4444: - case GU_PSM_5650: - case GU_PSM_5551: - data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<1); - data->backbuffer = (unsigned int *)(0); - data->bpp = 2; - data->psm = pixelformat; - break; - default: - data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<2); - data->backbuffer = (unsigned int *)(0); - data->bpp = 4; - data->psm = GU_PSM_8888; - break; - } - - sceGuInit(); - /* setup GU */ - sceGuStart(GU_DIRECT, DisplayList); - sceGuDrawBuffer(data->psm, data->frontbuffer, PSP_FRAME_BUFFER_WIDTH); - sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, data->backbuffer, PSP_FRAME_BUFFER_WIDTH); - - - sceGuOffset(2048 - (PSP_SCREEN_WIDTH>>1), 2048 - (PSP_SCREEN_HEIGHT>>1)); - sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); - - data->frontbuffer = vabsptr(data->frontbuffer); - data->backbuffer = vabsptr(data->backbuffer); - - /* Scissoring */ - sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); - sceGuEnable(GU_SCISSOR_TEST); - - /* Backface culling */ - sceGuFrontFace(GU_CCW); - sceGuEnable(GU_CULL_FACE); - - /* Texturing */ - sceGuEnable(GU_TEXTURE_2D); - sceGuShadeModel(GU_SMOOTH); - sceGuTexWrap(GU_REPEAT, GU_REPEAT); - - /* Blending */ - sceGuEnable(GU_BLEND); - sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); - - sceGuTexFilter(GU_LINEAR,GU_LINEAR); - - sceGuFinish(); - sceGuSync(0,0); - sceDisplayWaitVblankStartCB(); - sceGuDisplay(GU_TRUE); - - return renderer; -} - static void PSP_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) { - } @@ -576,383 +431,429 @@ PSP_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) static int PSP_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) { - return 0; } static int -PSP_UpdateViewport(SDL_Renderer * renderer) +PSP_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - - return 0; + return 0; /* nothing to do in this backend. */ } - -static void -PSP_SetBlendMode(SDL_Renderer * renderer, int blendMode) -{ - PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; - if (blendMode != data-> currentBlendMode) { - switch (blendMode) { - case SDL_BLENDMODE_NONE: - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); - sceGuDisable(GU_BLEND); - break; - case SDL_BLENDMODE_BLEND: - sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); - sceGuEnable(GU_BLEND); - sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); - break; - case SDL_BLENDMODE_ADD: - sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); - sceGuEnable(GU_BLEND); - sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0x00FFFFFF ); - break; - case SDL_BLENDMODE_MOD: - sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); - sceGuEnable(GU_BLEND); - sceGuBlendFunc( GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0); - break; - } - data->currentBlendMode = blendMode; - } -} - - - static int -PSP_RenderClear(SDL_Renderer * renderer) +PSP_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - /* start list */ - StartDrawing(renderer); - int color = renderer->a << 24 | renderer->b << 16 | renderer->g << 8 | renderer->r; - sceGuClearColor(color); - sceGuClearDepth(0); - sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT|GU_FAST_CLEAR_BIT); + VertV *verts = (VertV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertV), 4, &cmd->data.draw.first); + size_t i; - return 0; -} + if (!verts) { + return -1; + } -static int -PSP_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) -{ - int color = renderer->a << 24 | renderer->b << 16 | renderer->g << 8 | renderer->r; - int i; - StartDrawing(renderer); - VertV* vertices = (VertV*)sceGuGetMemory(count*sizeof(VertV)); + cmd->data.draw.count = count; - for (i = 0; i < count; ++i) { - vertices[i].x = points[i].x; - vertices[i].y = points[i].y; - vertices[i].z = 0.0f; + for (i = 0; i < count; i++, verts++, points++) { + verts->x = points->x; + verts->y = points->y; + verts->z = 0.0f; } - sceGuDisable(GU_TEXTURE_2D); - sceGuColor(color); - sceGuShadeModel(GU_FLAT); - sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, vertices); - sceGuShadeModel(GU_SMOOTH); - sceGuEnable(GU_TEXTURE_2D); return 0; } static int -PSP_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) +PSP_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - int color = renderer->a << 24 | renderer->b << 16 | renderer->g << 8 | renderer->r; - int i; - StartDrawing(renderer); - VertV* vertices = (VertV*)sceGuGetMemory(count*sizeof(VertV)); + VertV *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (VertV), 4, &cmd->data.draw.first); + size_t i; - for (i = 0; i < count; ++i) { - vertices[i].x = points[i].x; - vertices[i].y = points[i].y; - vertices[i].z = 0.0f; + if (!verts) { + return -1; } - sceGuDisable(GU_TEXTURE_2D); - sceGuColor(color); - sceGuShadeModel(GU_FLAT); - sceGuDrawArray(GU_LINE_STRIP, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, vertices); - sceGuShadeModel(GU_SMOOTH); - sceGuEnable(GU_TEXTURE_2D); - - return 0; -} - -static int -PSP_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, - int count) -{ - int color = renderer->a << 24 | renderer->b << 16 | renderer->g << 8 | renderer->r; - int i; - StartDrawing(renderer); - - for (i = 0; i < count; ++i) { + cmd->data.draw.count = count; + for (i = 0; i < count; i++, rects++) { const SDL_FRect *rect = &rects[i]; - VertV* vertices = (VertV*)sceGuGetMemory((sizeof(VertV)<<1)); - vertices[0].x = rect->x; - vertices[0].y = rect->y; - vertices[0].z = 0.0f; - - vertices[1].x = rect->x + rect->w; - vertices[1].y = rect->y + rect->h; - vertices[1].z = 0.0f; - - sceGuDisable(GU_TEXTURE_2D); - sceGuColor(color); - sceGuShadeModel(GU_FLAT); - sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2, 0, vertices); - sceGuShadeModel(GU_SMOOTH); - sceGuEnable(GU_TEXTURE_2D); + verts->x = rect->x; + verts->y = rect->y; + verts->z = 0.0f; + verts++; + + verts->x = rect->x + rect->w; + verts->y = rect->y + rect->h; + verts->z = 0.0f; + verts++; } return 0; } - -#define PI 3.14159265358979f - -#define radToDeg(x) ((x)*180.f/PI) -#define degToRad(x) ((x)*PI/180.f) - -float MathAbs(float x) -{ - float result; - - __asm__ volatile ( - "mtv %1, S000\n" - "vabs.s S000, S000\n" - "mfv %0, S000\n" - : "=r"(result) : "r"(x)); - - return result; -} - -void MathSincos(float r, float *s, float *c) -{ - __asm__ volatile ( - "mtv %2, S002\n" - "vcst.s S003, VFPU_2_PI\n" - "vmul.s S002, S002, S003\n" - "vrot.p C000, S002, [s, c]\n" - "mfv %0, S000\n" - "mfv %1, S001\n" - : "=r"(*s), "=r"(*c): "r"(r)); -} - -void Swap(float *a, float *b) -{ - float n=*a; - *a = *b; - *b = n; -} - static int -PSP_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) +PSP_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - float x, y, width, height; - float u0, v0, u1, v1; - unsigned char alpha; - - x = dstrect->x; - y = dstrect->y; - width = dstrect->w; - height = dstrect->h; - - u0 = srcrect->x; - v0 = srcrect->y; - u1 = srcrect->x + srcrect->w; - v1 = srcrect->y + srcrect->h; - - alpha = texture->a; + VertTV *verts; + const float x = dstrect->x; + const float y = dstrect->y; + const float width = dstrect->w; + const float height = dstrect->h; - StartDrawing(renderer); - TextureActivate(texture); - PSP_SetBlendMode(renderer, renderer->blendMode); - - if(alpha != 255) - { - sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); - sceGuColor(GU_RGBA(255, 255, 255, alpha)); - }else{ - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); - sceGuColor(0xFFFFFFFF); - } + const float u0 = srcrect->x; + const float v0 = srcrect->y; + const float u1 = srcrect->x + srcrect->w; + const float v1 = srcrect->y + srcrect->h; if((MathAbs(u1) - MathAbs(u0)) < 64.0f) { - VertTV* vertices = (VertTV*)sceGuGetMemory((sizeof(VertTV))<<1); - - vertices[0].u = u0; - vertices[0].v = v0; - vertices[0].x = x; - vertices[0].y = y; - vertices[0].z = 0; - - vertices[1].u = u1; - vertices[1].v = v1; - vertices[1].x = x + width; - vertices[1].y = y + height; - vertices[1].z = 0; + verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (VertTV), 4, &cmd->data.draw.first); + if (!verts) { + return -1; + } - sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2, 0, vertices); + cmd->data.draw.count = 1; + + verts->u = u0; + verts->v = v0; + verts->x = x; + verts->y = y; + verts->z = 0; + verts++; + + verts->u = u1; + verts->v = v1; + verts->x = x + width; + verts->y = y + height; + verts->z = 0; + verts++; } else { float start, end; float curU = u0; float curX = x; - float endX = x + width; - float slice = 64.0f; + const float endX = x + width; + const float slice = 64.0f; + const size_t count = SDL_ceilf(width / slice); + size_t i; float ustep = (u1 - u0)/width * slice; if(ustep < 0.0f) ustep = -ustep; - for(start = 0, end = width; start < end; start += slice) + cmd->data.draw.count = count; + + verts = (VertTV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertTV), 4, &cmd->data.draw.first); + if (!verts) { + return -1; + } + + + for(i = 0, start = 0, end = width; i < count; i++, start += slice) { - VertTV* vertices = (VertTV*)sceGuGetMemory((sizeof(VertTV))<<1); + const float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice; + const float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep; - float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice; - float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep; + SDL_assert(start < end); - vertices[0].u = curU; - vertices[0].v = v0; - vertices[0].x = curX; - vertices[0].y = y; - vertices[0].z = 0; + verts->u = curU; + verts->v = v0; + verts->x = curX; + verts->y = y; + verts->z = 0; curU += sourceWidth; curX += polyWidth; - vertices[1].u = curU; - vertices[1].v = v1; - vertices[1].x = curX; - vertices[1].y = (y + height); - vertices[1].z = 0; - - sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2, 0, vertices); + verts->u = curU; + verts->v = v1; + verts->x = curX; + verts->y = (y + height); + verts->z = 0; } } - if(alpha != 255) - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); return 0; } static int -PSP_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 pixel_format, void * pixels, int pitch) - +PSP_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - return SDL_Unsupported(); -} + VertTV *verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 4 * sizeof (VertTV), 4, &cmd->data.draw.first); + const float centerx = center->x; + const float centery = center->y; + const float x = dstrect->x + centerx; + const float y = dstrect->y + centery; + const float width = dstrect->w - centerx; + const float height = dstrect->h - centery; + float s, c; + + float u0 = srcrect->x; + float v0 = srcrect->y; + float u1 = srcrect->x + srcrect->w; + float v1 = srcrect->y + srcrect->h; + + + if (!verts) { + return -1; + } + cmd->data.draw.count = 1; -static int -PSP_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) -{ - float x, y, width, height; - float u0, v0, u1, v1; - unsigned char alpha; - float centerx, centery; + MathSincos(degToRad(angle), &s, &c); - x = dstrect->x; - y = dstrect->y; - width = dstrect->w; - height = dstrect->h; + const float cw = c * width; + const float sw = s * width; + const float ch = c * height; + const float sh = s * height; - u0 = srcrect->x; - v0 = srcrect->y; - u1 = srcrect->x + srcrect->w; - v1 = srcrect->y + srcrect->h; + if (flip & SDL_FLIP_VERTICAL) { + Swap(&v0, &v1); + } - centerx = center->x; - centery = center->y; + if (flip & SDL_FLIP_HORIZONTAL) { + Swap(&u0, &u1); + } - alpha = texture->a; + verts->u = u0; + verts->v = v0; + verts->x = x - cw + sh; + verts->y = y - sw - ch; + verts->z = 0; + verts++; + + verts->u = u0; + verts->v = v1; + verts->x = x - cw - sh; + verts->y = y - sw + ch; + verts->z = 0; + verts++; + + verts->u = u1; + verts->v = v1; + verts->x = x + cw - sh; + verts->y = y + sw + ch; + verts->z = 0; + verts++; + + verts->u = u1; + verts->v = v0; + verts->x = x + cw + sh; + verts->y = y + sw - ch; + verts->z = 0; + verts++; - StartDrawing(renderer); - TextureActivate(texture); - PSP_SetBlendMode(renderer, renderer->blendMode); + return 0; +} - if(alpha != 255) - { - sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); - sceGuColor(GU_RGBA(255, 255, 255, alpha)); - }else{ - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); - sceGuColor(0xFFFFFFFF); +static void +PSP_SetBlendMode(SDL_Renderer * renderer, int blendMode) +{ + PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; + if (blendMode != data-> currentBlendMode) { + switch (blendMode) { + case SDL_BLENDMODE_NONE: + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuDisable(GU_BLEND); + break; + case SDL_BLENDMODE_BLEND: + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuEnable(GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); + break; + case SDL_BLENDMODE_ADD: + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuEnable(GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0x00FFFFFF ); + break; + case SDL_BLENDMODE_MOD: + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuEnable(GU_BLEND); + sceGuBlendFunc( GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0); + break; + } + data->currentBlendMode = blendMode; } +} -/* x += width * 0.5f; */ -/* y += height * 0.5f; */ - x += centerx; - y += centery; +static int +PSP_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; + size_t i; - float c, s; + StartDrawing(renderer); - MathSincos(degToRad(angle), &s, &c); + /* note that before the renderer interface change, this would do extrememly small + batches with sceGuGetMemory()--a few vertices at a time--and it's not clear that + this won't fail if you try to push 100,000 draw calls in a single batch. + I don't know what the limits on PSP hardware are. It might be useful to have + rendering backends report a reasonable maximum, so the higher level can flush + if we appear to be exceeding that. */ + Uint8 *gpumem = (Uint8 *) sceGuGetMemory(vertsize); + if (!gpumem) { + return SDL_SetError("Couldn't obtain a %d-byte vertex buffer!", (int) vertsize); + } + SDL_memcpy(gpumem, vertices, vertsize); -/* width *= 0.5f; */ -/* height *= 0.5f; */ - width -= centerx; - height -= centery; + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + break; /* !!! FIXME: we could cache drawstate like color */ + } + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_Rect *viewport = &data->drawstate.viewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); + data->drawstate.viewport_dirty = SDL_TRUE; + } + break; + } - float cw = c*width; - float sw = s*width; - float ch = c*height; - float sh = s*height; + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + } + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + data->drawstate.cliprect_dirty = SDL_TRUE; + } + break; + } - VertTV* vertices = (VertTV*)sceGuGetMemory(sizeof(VertTV)<<2); + case SDL_RENDERCMD_CLEAR: { + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); + /* !!! FIXME: we could cache drawstate like clear color */ + sceGuClearColor(color); + sceGuClearDepth(0); + sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT|GU_FAST_CLEAR_BIT); + break; + } - vertices[0].u = u0; - vertices[0].v = v0; - vertices[0].x = x - cw + sh; - vertices[0].y = y - sw - ch; - vertices[0].z = 0; + case SDL_RENDERCMD_DRAW_POINTS: { + const size_t count = cmd->data.draw.count; + const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); + /* !!! FIXME: we could cache draw state like color, texturing, etc */ + sceGuColor(color); + sceGuDisable(GU_TEXTURE_2D); + sceGuShadeModel(GU_FLAT); + sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); + sceGuShadeModel(GU_SMOOTH); + sceGuEnable(GU_TEXTURE_2D); + break; + } - vertices[1].u = u0; - vertices[1].v = v1; - vertices[1].x = x - cw - sh; - vertices[1].y = y - sw + ch; - vertices[1].z = 0; + case SDL_RENDERCMD_DRAW_LINES: { + const size_t count = cmd->data.draw.count; + const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); + /* !!! FIXME: we could cache draw state like color, texturing, etc */ + sceGuColor(color); + sceGuDisable(GU_TEXTURE_2D); + sceGuShadeModel(GU_FLAT); + sceGuDrawArray(GU_LINE_STRIP, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); + sceGuShadeModel(GU_SMOOTH); + sceGuEnable(GU_TEXTURE_2D); + break; + } - vertices[2].u = u1; - vertices[2].v = v1; - vertices[2].x = x + cw - sh; - vertices[2].y = y + sw + ch; - vertices[2].z = 0; + case SDL_RENDERCMD_FILL_RECTS: { + const size_t count = cmd->data.draw.count; + const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); + /* !!! FIXME: we could cache draw state like color, texturing, etc */ + sceGuColor(color); + sceGuDisable(GU_TEXTURE_2D); + sceGuShadeModel(GU_FLAT); + sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); + sceGuShadeModel(GU_SMOOTH); + sceGuEnable(GU_TEXTURE_2D); + break; + } - vertices[3].u = u1; - vertices[3].v = v0; - vertices[3].x = x + cw + sh; - vertices[3].y = y + sw - ch; - vertices[3].z = 0; + case SDL_RENDERCMD_COPY: { + const size_t count = cmd->data.draw.count; + const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); + const Uint8 alpha = cmd->data.draw.a; + TextureActivate(cmd->data.draw.texture); + PSP_SetBlendMode(renderer, cmd->data.draw.blend); + + if(alpha != 255) { /* !!! FIXME: is this right? */ + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuColor(GU_RGBA(255, 255, 255, alpha)); + } else { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuColor(0xFFFFFFFF); + } + + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); + + if(alpha != 255) { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } + break; + } - if (flip & SDL_FLIP_VERTICAL) { - Swap(&vertices[0].v, &vertices[2].v); - Swap(&vertices[1].v, &vertices[3].v); - } - if (flip & SDL_FLIP_HORIZONTAL) { - Swap(&vertices[0].u, &vertices[2].u); - Swap(&vertices[1].u, &vertices[3].u); - } + case SDL_RENDERCMD_COPY_EX: { + const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); + const Uint8 alpha = cmd->data.draw.a; + TextureActivate(cmd->data.draw.texture); + PSP_SetBlendMode(renderer, cmd->data.draw.blend); + + if(alpha != 255) { /* !!! FIXME: is this right? */ + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuColor(GU_RGBA(255, 255, 255, alpha)); + } else { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuColor(0xFFFFFFFF); + } + + sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, verts); + + if(alpha != 255) { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } - sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, vertices); + cmd = cmd->next; + } - if(alpha != 255) - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); return 0; } +static int +PSP_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, + Uint32 pixel_format, void * pixels, int pitch) +{ + return SDL_Unsupported(); +} + static void PSP_RenderPresent(SDL_Renderer * renderer) { @@ -1010,6 +911,136 @@ PSP_DestroyRenderer(SDL_Renderer * renderer) SDL_free(renderer); } +SDL_Renderer * +PSP_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + + SDL_Renderer *renderer; + PSP_RenderData *data; + int pixelformat; + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + data = (PSP_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + PSP_DestroyRenderer(renderer); + SDL_OutOfMemory(); + return NULL; + } + + + renderer->WindowEvent = PSP_WindowEvent; + renderer->CreateTexture = PSP_CreateTexture; + renderer->SetTextureColorMod = PSP_SetTextureColorMod; + renderer->UpdateTexture = PSP_UpdateTexture; + renderer->LockTexture = PSP_LockTexture; + renderer->UnlockTexture = PSP_UnlockTexture; + renderer->SetRenderTarget = PSP_SetRenderTarget; + renderer->QueueSetViewport = PSP_QueueSetViewport; + renderer->QueueSetDrawColor = PSP_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = PSP_QueueDrawPoints; + renderer->QueueDrawLines = PSP_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = PSP_QueueFillRects; + renderer->QueueCopy = PSP_QueueCopy; + renderer->QueueCopyEx = PSP_QueueCopyEx; + renderer->RunCommandQueue = PSP_RunCommandQueue; + renderer->RenderReadPixels = PSP_RenderReadPixels; + renderer->RenderPresent = PSP_RenderPresent; + renderer->DestroyTexture = PSP_DestroyTexture; + renderer->DestroyRenderer = PSP_DestroyRenderer; + renderer->info = PSP_RenderDriver.info; + renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + renderer->driverdata = data; + renderer->window = window; + + if (data->initialized != SDL_FALSE) + return 0; + data->initialized = SDL_TRUE; + + if (flags & SDL_RENDERER_PRESENTVSYNC) { + data->vsync = SDL_TRUE; + } else { + data->vsync = SDL_FALSE; + } + + pixelformat=PixelFormatToPSPFMT(SDL_GetWindowPixelFormat(window)); + switch(pixelformat) + { + case GU_PSM_4444: + case GU_PSM_5650: + case GU_PSM_5551: + data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<1); + data->backbuffer = (unsigned int *)(0); + data->bpp = 2; + data->psm = pixelformat; + break; + default: + data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<2); + data->backbuffer = (unsigned int *)(0); + data->bpp = 4; + data->psm = GU_PSM_8888; + break; + } + + sceGuInit(); + /* setup GU */ + sceGuStart(GU_DIRECT, DisplayList); + sceGuDrawBuffer(data->psm, data->frontbuffer, PSP_FRAME_BUFFER_WIDTH); + sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, data->backbuffer, PSP_FRAME_BUFFER_WIDTH); + + + sceGuOffset(2048 - (PSP_SCREEN_WIDTH>>1), 2048 - (PSP_SCREEN_HEIGHT>>1)); + sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); + + data->frontbuffer = vabsptr(data->frontbuffer); + data->backbuffer = vabsptr(data->backbuffer); + + /* Scissoring */ + sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); + sceGuEnable(GU_SCISSOR_TEST); + + /* Backface culling */ + sceGuFrontFace(GU_CCW); + sceGuEnable(GU_CULL_FACE); + + /* Texturing */ + sceGuEnable(GU_TEXTURE_2D); + sceGuShadeModel(GU_SMOOTH); + sceGuTexWrap(GU_REPEAT, GU_REPEAT); + + /* Blending */ + sceGuEnable(GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + + sceGuTexFilter(GU_LINEAR,GU_LINEAR); + + sceGuFinish(); + sceGuSync(0,0); + sceDisplayWaitVblankStartCB(); + sceGuDisplay(GU_TRUE); + + return renderer; +} + +SDL_RenderDriver PSP_RenderDriver = { + .CreateRenderer = PSP_CreateRenderer, + .info = { + .name = "PSP", + .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, + .num_texture_formats = 4, + .texture_formats = { [0] = SDL_PIXELFORMAT_BGR565, + [1] = SDL_PIXELFORMAT_ABGR1555, + [2] = SDL_PIXELFORMAT_ABGR4444, + [3] = SDL_PIXELFORMAT_ABGR8888, + }, + .max_texture_width = 512, + .max_texture_height = 512, + } +}; + #endif /* SDL_VIDEO_RENDER_PSP */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 709dfe8e48..4748873e88 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -25,6 +25,7 @@ #include "../SDL_sysrender.h" #include "SDL_render_sw_c.h" #include "SDL_hints.h" +#include "SDL_assert.h" #include "SDL_draw.h" #include "SDL_blendfillrect.h" @@ -36,65 +37,6 @@ /* SDL surface based renderer implementation */ -static SDL_Renderer *SW_CreateRenderer(SDL_Window * window, Uint32 flags); -static void SW_WindowEvent(SDL_Renderer * renderer, - const SDL_WindowEvent *event); -static int SW_GetOutputSize(SDL_Renderer * renderer, int *w, int *h); -static int SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int SW_SetTextureColorMod(SDL_Renderer * renderer, - SDL_Texture * texture); -static int SW_SetTextureAlphaMod(SDL_Renderer * renderer, - SDL_Texture * texture); -static int SW_SetTextureBlendMode(SDL_Renderer * renderer, - SDL_Texture * texture); -static int SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, const void *pixels, - int pitch); -static int SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * rect, void **pixels, int *pitch); -static void SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static int SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture); -static int SW_UpdateViewport(SDL_Renderer * renderer); -static int SW_UpdateClipRect(SDL_Renderer * renderer); -static int SW_RenderClear(SDL_Renderer * renderer); -static int SW_RenderDrawPoints(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int SW_RenderDrawLines(SDL_Renderer * renderer, - const SDL_FPoint * points, int count); -static int SW_RenderFillRects(SDL_Renderer * renderer, - const SDL_FRect * rects, int count); -static int SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); -static int SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, - const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip); -static int SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, - Uint32 format, void * pixels, int pitch); -static void SW_RenderPresent(SDL_Renderer * renderer); -static void SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); -static void SW_DestroyRenderer(SDL_Renderer * renderer); - - -SDL_RenderDriver SW_RenderDriver = { - SW_CreateRenderer, - { - "software", - SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE, - 8, - { - SDL_PIXELFORMAT_ARGB8888, - SDL_PIXELFORMAT_ABGR8888, - SDL_PIXELFORMAT_RGBA8888, - SDL_PIXELFORMAT_BGRA8888, - SDL_PIXELFORMAT_RGB888, - SDL_PIXELFORMAT_BGR888, - SDL_PIXELFORMAT_RGB565, - SDL_PIXELFORMAT_RGB555 - }, - 0, - 0} -}; - typedef struct { SDL_Surface *surface; @@ -114,82 +56,11 @@ SW_ActivateRenderer(SDL_Renderer * renderer) SDL_Surface *surface = SDL_GetWindowSurface(renderer->window); if (surface) { data->surface = data->window = surface; - - SW_UpdateViewport(renderer); - SW_UpdateClipRect(renderer); } } return data->surface; } -SDL_Renderer * -SW_CreateRendererForSurface(SDL_Surface * surface) -{ - SDL_Renderer *renderer; - SW_RenderData *data; - - if (!surface) { - SDL_SetError("Can't create renderer for NULL surface"); - return NULL; - } - - renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); - if (!renderer) { - SDL_OutOfMemory(); - return NULL; - } - - data = (SW_RenderData *) SDL_calloc(1, sizeof(*data)); - if (!data) { - SW_DestroyRenderer(renderer); - SDL_OutOfMemory(); - return NULL; - } - data->surface = surface; - data->window = surface; - - renderer->WindowEvent = SW_WindowEvent; - renderer->GetOutputSize = SW_GetOutputSize; - renderer->CreateTexture = SW_CreateTexture; - renderer->SetTextureColorMod = SW_SetTextureColorMod; - renderer->SetTextureAlphaMod = SW_SetTextureAlphaMod; - renderer->SetTextureBlendMode = SW_SetTextureBlendMode; - renderer->UpdateTexture = SW_UpdateTexture; - renderer->LockTexture = SW_LockTexture; - renderer->UnlockTexture = SW_UnlockTexture; - renderer->SetRenderTarget = SW_SetRenderTarget; - renderer->UpdateViewport = SW_UpdateViewport; - renderer->UpdateClipRect = SW_UpdateClipRect; - renderer->RenderClear = SW_RenderClear; - renderer->RenderDrawPoints = SW_RenderDrawPoints; - renderer->RenderDrawLines = SW_RenderDrawLines; - renderer->RenderFillRects = SW_RenderFillRects; - renderer->RenderCopy = SW_RenderCopy; - renderer->RenderCopyEx = SW_RenderCopyEx; - renderer->RenderReadPixels = SW_RenderReadPixels; - renderer->RenderPresent = SW_RenderPresent; - renderer->DestroyTexture = SW_DestroyTexture; - renderer->DestroyRenderer = SW_DestroyRenderer; - renderer->info = SW_RenderDriver.info; - renderer->driverdata = data; - - SW_ActivateRenderer(renderer); - - return renderer; -} - -SDL_Renderer * -SW_CreateRenderer(SDL_Window * window, Uint32 flags) -{ - SDL_Surface *surface; - - surface = SDL_GetWindowSurface(window); - if (!surface) { - return NULL; - } - return SW_CreateRendererForSurface(surface); -} - static void SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) { @@ -253,46 +124,6 @@ SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) } static int -SW_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture) -{ - SDL_Surface *surface = (SDL_Surface *) texture->driverdata; - /* If the color mod is ever enabled (non-white), permanently disable RLE (which doesn't support - * color mod) to avoid potentially frequent RLE encoding/decoding. - */ - if ((texture->r & texture->g & texture->b) != 255) { - SDL_SetSurfaceRLE(surface, 0); - } - return SDL_SetSurfaceColorMod(surface, texture->r, texture->g, - texture->b); -} - -static int -SW_SetTextureAlphaMod(SDL_Renderer * renderer, SDL_Texture * texture) -{ - SDL_Surface *surface = (SDL_Surface *) texture->driverdata; - /* If the texture ever has multiple alpha values (surface alpha plus alpha channel), permanently - * disable RLE (which doesn't support this) to avoid potentially frequent RLE encoding/decoding. - */ - if (texture->a != 255 && surface->format->Amask) { - SDL_SetSurfaceRLE(surface, 0); - } - return SDL_SetSurfaceAlphaMod(surface, texture->a); -} - -static int -SW_SetTextureBlendMode(SDL_Renderer * renderer, SDL_Texture * texture) -{ - SDL_Surface *surface = (SDL_Surface *) texture->driverdata; - /* If add or mod blending are ever enabled, permanently disable RLE (which doesn't support - * them) to avoid potentially frequent RLE encoding/decoding. - */ - if ((texture->blendMode == SDL_BLENDMODE_ADD || texture->blendMode == SDL_BLENDMODE_MOD)) { - SDL_SetSurfaceRLE(surface, 0); - } - return SDL_SetSurfaceBlendMode(surface, texture->blendMode); -} - -static int SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch) { @@ -350,251 +181,149 @@ SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) } static int -SW_UpdateViewport(SDL_Renderer * renderer) +SW_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - SW_RenderData *data = (SW_RenderData *) renderer->driverdata; - SDL_Surface *surface = data->surface; - - if (!surface) { - /* We'll update the viewport after we recreate the surface */ - return 0; - } - - SDL_SetClipRect(data->surface, &renderer->viewport); - return 0; -} - -static int -SW_UpdateClipRect(SDL_Renderer * renderer) -{ - SW_RenderData *data = (SW_RenderData *) renderer->driverdata; - SDL_Surface *surface = data->surface; - if (surface) { - if (renderer->clipping_enabled) { - SDL_Rect clip_rect; - clip_rect = renderer->clip_rect; - clip_rect.x += renderer->viewport.x; - clip_rect.y += renderer->viewport.y; - SDL_IntersectRect(&renderer->viewport, &clip_rect, &clip_rect); - SDL_SetClipRect(surface, &clip_rect); - } else { - SDL_SetClipRect(surface, &renderer->viewport); - } - } - return 0; + return 0; /* nothing to do in this backend. */ } static int -SW_RenderClear(SDL_Renderer * renderer) +SW_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - SDL_Surface *surface = SW_ActivateRenderer(renderer); - Uint32 color; - SDL_Rect clip_rect; + SDL_Point *verts = (SDL_Point *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Point), 0, &cmd->data.draw.first); + size_t i; - if (!surface) { + if (!verts) { return -1; } - color = SDL_MapRGBA(surface->format, - renderer->r, renderer->g, renderer->b, renderer->a); - - /* By definition the clear ignores the clip rect */ - clip_rect = surface->clip_rect; - SDL_SetClipRect(surface, NULL); - SDL_FillRect(surface, NULL, color); - SDL_SetClipRect(surface, &clip_rect); - return 0; -} - -static int -SW_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) -{ - SDL_Surface *surface = SW_ActivateRenderer(renderer); - SDL_Point *final_points; - int i, status; - - if (!surface) { - return -1; - } + cmd->data.draw.count = count; - final_points = SDL_stack_alloc(SDL_Point, count); - if (!final_points) { - return SDL_OutOfMemory(); - } if (renderer->viewport.x || renderer->viewport.y) { - int x = renderer->viewport.x; - int y = renderer->viewport.y; - - for (i = 0; i < count; ++i) { - final_points[i].x = (int)(x + points[i].x); - final_points[i].y = (int)(y + points[i].y); + const int x = renderer->viewport.x; + const int y = renderer->viewport.y; + for (i = 0; i < count; i++, verts++, points++) { + verts->x = (int)(x + points->x); + verts->y = (int)(y + points->y); } } else { - for (i = 0; i < count; ++i) { - final_points[i].x = (int)points[i].x; - final_points[i].y = (int)points[i].y; + for (i = 0; i < count; i++, verts++, points++) { + verts->x = (int)points->x; + verts->y = (int)points->y; } } - /* Draw the points! */ - if (renderer->blendMode == SDL_BLENDMODE_NONE) { - Uint32 color = SDL_MapRGBA(surface->format, - renderer->r, renderer->g, renderer->b, - renderer->a); - - status = SDL_DrawPoints(surface, final_points, count, color); - } else { - status = SDL_BlendPoints(surface, final_points, count, - renderer->blendMode, - renderer->r, renderer->g, renderer->b, - renderer->a); - } - SDL_stack_free(final_points); - - return status; + return 0; } static int -SW_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) +SW_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - SDL_Surface *surface = SW_ActivateRenderer(renderer); - SDL_Point *final_points; - int i, status; + SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Rect), 0, &cmd->data.draw.first); + size_t i; - if (!surface) { + if (!verts) { return -1; } - final_points = SDL_stack_alloc(SDL_Point, count); - if (!final_points) { - return SDL_OutOfMemory(); - } - if (renderer->viewport.x || renderer->viewport.y) { - int x = renderer->viewport.x; - int y = renderer->viewport.y; + cmd->data.draw.count = count; - for (i = 0; i < count; ++i) { - final_points[i].x = (int)(x + points[i].x); - final_points[i].y = (int)(y + points[i].y); + if (renderer->viewport.x || renderer->viewport.y) { + const int x = renderer->viewport.x; + const int y = renderer->viewport.y; + + for (i = 0; i < count; i++, verts++, rects++) { + verts->x = (int)(x + rects->x); + verts->y = (int)(y + rects->y); + verts->w = SDL_max((int)rects->w, 1); + verts->h = SDL_max((int)rects->h, 1); } } else { - for (i = 0; i < count; ++i) { - final_points[i].x = (int)points[i].x; - final_points[i].y = (int)points[i].y; + for (i = 0; i < count; i++, verts++, rects++) { + verts->x = (int)rects->x; + verts->y = (int)rects->y; + verts->w = SDL_max((int)rects->w, 1); + verts->h = SDL_max((int)rects->h, 1); } } - /* Draw the lines! */ - if (renderer->blendMode == SDL_BLENDMODE_NONE) { - Uint32 color = SDL_MapRGBA(surface->format, - renderer->r, renderer->g, renderer->b, - renderer->a); - - status = SDL_DrawLines(surface, final_points, count, color); - } else { - status = SDL_BlendLines(surface, final_points, count, - renderer->blendMode, - renderer->r, renderer->g, renderer->b, - renderer->a); - } - SDL_stack_free(final_points); - - return status; + return 0; } static int -SW_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count) +SW_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - SDL_Surface *surface = SW_ActivateRenderer(renderer); - SDL_Rect *final_rects; - int i, status; + SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (SDL_Rect), 0, &cmd->data.draw.first); - if (!surface) { + if (!verts) { return -1; } - final_rects = SDL_stack_alloc(SDL_Rect, count); - if (!final_rects) { - return SDL_OutOfMemory(); - } - if (renderer->viewport.x || renderer->viewport.y) { - int x = renderer->viewport.x; - int y = renderer->viewport.y; - - for (i = 0; i < count; ++i) { - final_rects[i].x = (int)(x + rects[i].x); - final_rects[i].y = (int)(y + rects[i].y); - final_rects[i].w = SDL_max((int)rects[i].w, 1); - final_rects[i].h = SDL_max((int)rects[i].h, 1); - } - } else { - for (i = 0; i < count; ++i) { - final_rects[i].x = (int)rects[i].x; - final_rects[i].y = (int)rects[i].y; - final_rects[i].w = SDL_max((int)rects[i].w, 1); - final_rects[i].h = SDL_max((int)rects[i].h, 1); - } - } + cmd->data.draw.count = 1; - if (renderer->blendMode == SDL_BLENDMODE_NONE) { - Uint32 color = SDL_MapRGBA(surface->format, - renderer->r, renderer->g, renderer->b, - renderer->a); - status = SDL_FillRects(surface, final_rects, count, color); + SDL_memcpy(verts, srcrect, sizeof (SDL_Rect)); + verts++; + + if (renderer->viewport.x || renderer->viewport.y) { + verts->x = (int)(renderer->viewport.x + dstrect->x); + verts->y = (int)(renderer->viewport.y + dstrect->y); } else { - status = SDL_BlendFillRects(surface, final_rects, count, - renderer->blendMode, - renderer->r, renderer->g, renderer->b, - renderer->a); + verts->x = (int)dstrect->x; + verts->y = (int)dstrect->y; } - SDL_stack_free(final_rects); + verts->w = (int)dstrect->w; + verts->h = (int)dstrect->h; - return status; + return 0; } +typedef struct CopyExData +{ + SDL_Rect srcrect; + SDL_Rect dstrect; + double angle; + SDL_FPoint center; + SDL_RendererFlip flip; +} CopyExData; + static int -SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) +SW_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect, + const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - SDL_Surface *surface = SW_ActivateRenderer(renderer); - SDL_Surface *src = (SDL_Surface *) texture->driverdata; - SDL_Rect final_rect; + CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first); - if (!surface) { + if (!verts) { return -1; } + cmd->data.draw.count = 1; + + SDL_memcpy(&verts->srcrect, srcrect, sizeof (SDL_Rect)); + if (renderer->viewport.x || renderer->viewport.y) { - final_rect.x = (int)(renderer->viewport.x + dstrect->x); - final_rect.y = (int)(renderer->viewport.y + dstrect->y); + verts->dstrect.x = (int)(renderer->viewport.x + dstrect->x); + verts->dstrect.y = (int)(renderer->viewport.y + dstrect->y); } else { - final_rect.x = (int)dstrect->x; - final_rect.y = (int)dstrect->y; + verts->dstrect.x = (int)dstrect->x; + verts->dstrect.y = (int)dstrect->y; } - final_rect.w = (int)dstrect->w; - final_rect.h = (int)dstrect->h; + verts->dstrect.w = (int)dstrect->w; + verts->dstrect.h = (int)dstrect->h; + verts->angle = angle; + SDL_memcpy(&verts->center, center, sizeof (SDL_FPoint)); + verts->flip = flip; - if ( srcrect->w == final_rect.w && srcrect->h == final_rect.h ) { - return SDL_BlitSurface(src, srcrect, surface, &final_rect); - } else { - /* If scaling is ever done, permanently disable RLE (which doesn't support scaling) - * to avoid potentially frequent RLE encoding/decoding. - */ - SDL_SetSurfaceRLE(surface, 0); - return SDL_BlitScaled(src, srcrect, surface, &final_rect); - } + return 0; } static int -SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect, +SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_Rect * final_rect, const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip) { - SDL_Surface *surface = SW_ActivateRenderer(renderer); SDL_Surface *src = (SDL_Surface *) texture->driverdata; - SDL_Rect final_rect, tmp_rect; + SDL_Rect tmp_rect; SDL_Surface *src_clone, *src_rotated, *src_scaled; SDL_Surface *mask = NULL, *mask_rotated = NULL; int retval = 0, dstwidth, dstheight, abscenterx, abscentery; @@ -609,19 +338,10 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - if (renderer->viewport.x || renderer->viewport.y) { - final_rect.x = (int)(renderer->viewport.x + dstrect->x); - final_rect.y = (int)(renderer->viewport.y + dstrect->y); - } else { - final_rect.x = (int)dstrect->x; - final_rect.y = (int)dstrect->y; - } - final_rect.w = (int)dstrect->w; - final_rect.h = (int)dstrect->h; - - tmp_rect = final_rect; tmp_rect.x = 0; tmp_rect.y = 0; + tmp_rect.w = final_rect->w; + tmp_rect.h = final_rect->h; /* It is possible to encounter an RLE encoded surface here and locking it is * necessary because this code is going to access the pixel buffer directly. @@ -653,7 +373,7 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, } /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */ - if (!(srcrect->w == final_rect.w && srcrect->h == final_rect.h && srcrect->x == 0 && srcrect->y == 0)) { + if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) { blitRequired = SDL_TRUE; } @@ -678,7 +398,7 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, * to clear the pixels in the destination surface. The other steps are explained below. */ if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) { - mask = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32, + mask = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); if (mask == NULL) { retval = -1; @@ -692,7 +412,7 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, */ if (!retval && (blitRequired || applyModulation)) { SDL_Rect scale_rect = tmp_rect; - src_scaled = SDL_CreateRGBSurface(0, final_rect.w, final_rect.h, 32, + src_scaled = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); if (src_scaled == NULL) { retval = -1; @@ -723,32 +443,32 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, } if (!retval) { /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */ - abscenterx = final_rect.x + (int)center->x; - abscentery = final_rect.y + (int)center->y; + abscenterx = final_rect->x + (int)center->x; + abscentery = final_rect->y + (int)center->y; /* Compensate the angle inversion to match the behaviour of the other backends */ sangle = -sangle; /* Top Left */ - px = final_rect.x - abscenterx; - py = final_rect.y - abscentery; + px = final_rect->x - abscenterx; + py = final_rect->y - abscentery; p1x = px * cangle - py * sangle + abscenterx; p1y = px * sangle + py * cangle + abscentery; /* Top Right */ - px = final_rect.x + final_rect.w - abscenterx; - py = final_rect.y - abscentery; + px = final_rect->x + final_rect->w - abscenterx; + py = final_rect->y - abscentery; p2x = px * cangle - py * sangle + abscenterx; p2y = px * sangle + py * cangle + abscentery; /* Bottom Left */ - px = final_rect.x - abscenterx; - py = final_rect.y + final_rect.h - abscentery; + px = final_rect->x - abscenterx; + py = final_rect->y + final_rect->h - abscentery; p3x = px * cangle - py * sangle + abscenterx; p3y = px * sangle + py * cangle + abscentery; /* Bottom Right */ - px = final_rect.x + final_rect.w - abscenterx; - py = final_rect.y + final_rect.h - abscentery; + px = final_rect->x + final_rect->w - abscenterx; + py = final_rect->y + final_rect->h - abscentery; p4x = px * cangle - py * sangle + abscenterx; p4y = px * sangle + py * cangle + abscentery; @@ -824,6 +544,170 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, return retval; } +static void +PrepTextureForCopy(const SDL_RenderCommand *cmd) +{ + const Uint8 r = cmd->data.draw.r; + const Uint8 g = cmd->data.draw.g; + const Uint8 b = cmd->data.draw.b; + const Uint8 a = cmd->data.draw.a; + const SDL_BlendMode blend = cmd->data.draw.blend; + SDL_Texture *texture = cmd->data.draw.texture; + SDL_Surface *surface = (SDL_Surface *) texture->driverdata; + + if (texture->modMode & SDL_TEXTUREMODULATE_COLOR) { + SDL_SetSurfaceRLE(surface, 0); + SDL_SetSurfaceColorMod(surface, r, g, b); + } + + if ((texture->modMode & SDL_TEXTUREMODULATE_ALPHA) && surface->format->Amask) { + SDL_SetSurfaceRLE(surface, 0); + SDL_SetSurfaceAlphaMod(surface, a); + } + + if ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD)) { + SDL_SetSurfaceRLE(surface, 0); + } + SDL_SetSurfaceBlendMode(surface, blend); +} + +static int +SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + SW_RenderData *data = (SW_RenderData *) renderer->driverdata; + SDL_Surface *surface = SW_ActivateRenderer(renderer); + const SDL_Rect *viewport = NULL; + const SDL_Rect *cliprect = NULL; + + if (!surface) { + return -1; + } + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + break; /* Not used in this backend. */ + } + + case SDL_RENDERCMD_SETVIEWPORT: { + viewport = &cmd->data.viewport.rect; + SDL_SetClipRect(data->surface, viewport); + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + SDL_assert(viewport != NULL); + cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL; + if (cliprect) { + SDL_Rect clip_rect = { cliprect->x + viewport->x, cliprect->y + viewport->y, cliprect->w, cliprect->h }; + SDL_IntersectRect(viewport, &clip_rect, &clip_rect); + SDL_SetClipRect(surface, &clip_rect); + } else { + SDL_SetClipRect(surface, viewport); + } + break; + } + + case SDL_RENDERCMD_CLEAR: { + const Uint8 r = cmd->data.color.r; + const Uint8 g = cmd->data.color.g; + const Uint8 b = cmd->data.color.b; + const Uint8 a = cmd->data.color.a; + const SDL_Rect clip_rect = surface->clip_rect; + /* By definition the clear ignores the clip rect */ + SDL_SetClipRect(surface, NULL); + SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, r, g, b, a)); + SDL_SetClipRect(surface, &clip_rect); + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: { + const Uint8 r = cmd->data.draw.r; + const Uint8 g = cmd->data.draw.g; + const Uint8 b = cmd->data.draw.b; + const Uint8 a = cmd->data.draw.a; + const size_t count = cmd->data.draw.count; + const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first); + const SDL_BlendMode blend = cmd->data.draw.blend; + if (blend == SDL_BLENDMODE_NONE) { + SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a)); + } else { + SDL_BlendPoints(surface, verts, count, blend, r, g, b, a); + } + break; + } + + case SDL_RENDERCMD_DRAW_LINES: { + const Uint8 r = cmd->data.draw.r; + const Uint8 g = cmd->data.draw.g; + const Uint8 b = cmd->data.draw.b; + const Uint8 a = cmd->data.draw.a; + const size_t count = cmd->data.draw.count; + const SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first); + const SDL_BlendMode blend = cmd->data.draw.blend; + if (blend == SDL_BLENDMODE_NONE) { + SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a)); + } else { + SDL_BlendLines(surface, verts, count, blend, r, g, b, a); + } + break; + } + + case SDL_RENDERCMD_FILL_RECTS: { + const Uint8 r = cmd->data.draw.r; + const Uint8 g = cmd->data.draw.g; + const Uint8 b = cmd->data.draw.b; + const Uint8 a = cmd->data.draw.a; + const size_t count = cmd->data.draw.count; + const SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first); + const SDL_BlendMode blend = cmd->data.draw.blend; + if (blend == SDL_BLENDMODE_NONE) { + SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a)); + } else { + SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a); + } + break; + } + + case SDL_RENDERCMD_COPY: { + SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first); + const SDL_Rect *srcrect = verts; + SDL_Rect *dstrect = verts + 1; + SDL_Texture *texture = cmd->data.draw.texture; + SDL_Surface *src = (SDL_Surface *) texture->driverdata; + + PrepTextureForCopy(cmd); + + if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) { + SDL_BlitSurface(src, srcrect, surface, dstrect); + } else { + /* If scaling is ever done, permanently disable RLE (which doesn't support scaling) + * to avoid potentially frequent RLE encoding/decoding. + */ + SDL_SetSurfaceRLE(surface, 0); + SDL_BlitScaled(src, srcrect, surface, dstrect); + } + break; + } + + case SDL_RENDERCMD_COPY_EX: { + const CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first); + PrepTextureForCopy(cmd); + SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, ©data->srcrect, + ©data->dstrect, copydata->angle, ©data->center, copydata->flip); + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } + + return 0; +} + static int SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch) @@ -882,6 +766,91 @@ SW_DestroyRenderer(SDL_Renderer * renderer) SDL_free(renderer); } +SDL_Renderer * +SW_CreateRendererForSurface(SDL_Surface * surface) +{ + SDL_Renderer *renderer; + SW_RenderData *data; + + if (!surface) { + SDL_SetError("Can't create renderer for NULL surface"); + return NULL; + } + + renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + SDL_OutOfMemory(); + return NULL; + } + + data = (SW_RenderData *) SDL_calloc(1, sizeof(*data)); + if (!data) { + SW_DestroyRenderer(renderer); + SDL_OutOfMemory(); + return NULL; + } + data->surface = surface; + data->window = surface; + + renderer->WindowEvent = SW_WindowEvent; + renderer->GetOutputSize = SW_GetOutputSize; + renderer->CreateTexture = SW_CreateTexture; + renderer->UpdateTexture = SW_UpdateTexture; + renderer->LockTexture = SW_LockTexture; + renderer->UnlockTexture = SW_UnlockTexture; + renderer->SetRenderTarget = SW_SetRenderTarget; + renderer->QueueSetViewport = SW_QueueSetViewport; + renderer->QueueSetDrawColor = SW_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueDrawPoints = SW_QueueDrawPoints; + renderer->QueueDrawLines = SW_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueFillRects = SW_QueueFillRects; + renderer->QueueCopy = SW_QueueCopy; + renderer->QueueCopyEx = SW_QueueCopyEx; + renderer->RunCommandQueue = SW_RunCommandQueue; + renderer->RenderReadPixels = SW_RenderReadPixels; + renderer->RenderPresent = SW_RenderPresent; + renderer->DestroyTexture = SW_DestroyTexture; + renderer->DestroyRenderer = SW_DestroyRenderer; + renderer->info = SW_RenderDriver.info; + renderer->driverdata = data; + + SW_ActivateRenderer(renderer); + + return renderer; +} + +SDL_Renderer * +SW_CreateRenderer(SDL_Window * window, Uint32 flags) +{ + SDL_Surface *surface; + + surface = SDL_GetWindowSurface(window); + if (!surface) { + return NULL; + } + return SW_CreateRendererForSurface(surface); +} + +SDL_RenderDriver SW_RenderDriver = { + SW_CreateRenderer, + { + "software", + SDL_RENDERER_SOFTWARE | SDL_RENDERER_TARGETTEXTURE, + 8, + { + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_ABGR8888, + SDL_PIXELFORMAT_RGBA8888, + SDL_PIXELFORMAT_BGRA8888, + SDL_PIXELFORMAT_RGB888, + SDL_PIXELFORMAT_BGR888, + SDL_PIXELFORMAT_RGB565, + SDL_PIXELFORMAT_RGB555 + }, + 0, + 0} +}; + #endif /* !SDL_RENDER_DISABLED */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 97ccd945dc..b614b795b1 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -187,6 +187,7 @@ Cocoa_InitModes(_THIS) CGDisplayErr result; CGDirectDisplayID *displays; CGDisplayCount numDisplays; + SDL_bool isstack; int pass, i; result = CGGetOnlineDisplayList(0, NULL, &numDisplays); @@ -194,11 +195,11 @@ Cocoa_InitModes(_THIS) CG_SetError("CGGetOnlineDisplayList()", result); return; } - displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays); + displays = SDL_small_alloc(CGDirectDisplayID, numDisplays, &isstack); result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays); if (result != kCGErrorSuccess) { CG_SetError("CGGetOnlineDisplayList()", result); - SDL_stack_free(displays); + SDL_small_free(displays, isstack); return; } @@ -260,7 +261,7 @@ Cocoa_InitModes(_THIS) SDL_free(display.name); } } - SDL_stack_free(displays); + SDL_small_free(displays, isstack); }} int diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index b6155e76d9..0759719d38 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -47,6 +47,7 @@ #include "xdg-shell-client-protocol.h" #include "xdg-shell-unstable-v6-client-protocol.h" +#include "org-kde-kwin-server-decoration-manager-client-protocol.h" #define WAYLANDVID_DRIVER_NAME "wayland" @@ -182,6 +183,7 @@ Wayland_CreateDevice(int devindex) device->SetWindowFullscreen = Wayland_SetWindowFullscreen; device->MaximizeWindow = Wayland_MaximizeWindow; device->RestoreWindow = Wayland_RestoreWindow; + device->SetWindowBordered = Wayland_SetWindowBordered; device->SetWindowSize = Wayland_SetWindowSize; device->SetWindowTitle = Wayland_SetWindowTitle; device->DestroyWindow = Wayland_DestroyWindow; @@ -345,6 +347,8 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id, { SDL_VideoData *d = data; + /*printf("WAYLAND INTERFACE: %s\n", interface);*/ + if (strcmp(interface, "wl_compositor") == 0) { d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1); } else if (strcmp(interface, "wl_output") == 0) { @@ -368,6 +372,8 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id, Wayland_display_add_pointer_constraints(d, id); } else if (strcmp(interface, "wl_data_device_manager") == 0) { d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, 3); + } else if (strcmp(interface, "org_kde_kwin_server_decoration_manager") == 0) { + d->kwin_server_decoration_manager = wl_registry_bind(d->registry, id, &org_kde_kwin_server_decoration_manager_interface, 1); #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH } else if (strcmp(interface, "qt_touch_extension") == 0) { diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index c16c0bdd49..4d6658fcae 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -61,6 +61,7 @@ typedef struct { struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; struct wl_data_device_manager *data_device_manager; + struct org_kde_kwin_server_decoration_manager *kwin_server_decoration_manager; EGLDisplay edpy; EGLContext context; diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index aa7299181d..828d69c3a4 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -35,6 +35,7 @@ #include "xdg-shell-client-protocol.h" #include "xdg-shell-unstable-v6-client-protocol.h" +#include "org-kde-kwin-server-decoration-manager-client-protocol.h" /* On modern desktops, we probably will use the xdg-shell protocol instead of wl_shell, but wl_shell might be useful on older Wayland installs that @@ -452,6 +453,17 @@ Wayland_RestoreWindow(_THIS, SDL_Window * window) } void +Wayland_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) +{ + SDL_WindowData *wind = window->driverdata; + const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata; + if ((viddata->kwin_server_decoration_manager) && (wind->kwin_server_decoration)) { + const enum org_kde_kwin_server_decoration_mode mode = bordered ? ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER : ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE; + org_kde_kwin_server_decoration_request_mode(wind->kwin_server_decoration, mode); + } +} + +void Wayland_MaximizeWindow(_THIS, SDL_Window * window) { SDL_WindowData *wind = window->driverdata; @@ -562,6 +574,15 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window) } #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ + if (c->kwin_server_decoration_manager) { + data->kwin_server_decoration = org_kde_kwin_server_decoration_manager_create(c->kwin_server_decoration_manager, data->surface); + if (data->kwin_server_decoration) { + const SDL_bool bordered = (window->flags & SDL_WINDOW_BORDERLESS) == 0; + const enum org_kde_kwin_server_decoration_mode mode = bordered ? ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER : ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE; + org_kde_kwin_server_decoration_request_mode(data->kwin_server_decoration, mode); + } + } + region = wl_compositor_create_region(c->compositor); wl_region_add(region, 0, 0, window->w, window->h); wl_surface_set_opaque_region(data->surface, region); @@ -636,6 +657,10 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window) SDL_EGL_DestroySurface(_this, wind->egl_surface); WAYLAND_wl_egl_window_destroy(wind->egl_window); + if (wind->kwin_server_decoration) { + org_kde_kwin_server_decoration_release(wind->kwin_server_decoration); + } + if (data->shell.xdg) { if (wind->shell_surface.xdg.roleobj.toplevel) { xdg_toplevel_destroy(wind->shell_surface.xdg.roleobj.toplevel); diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 69b9889233..38a204e9a6 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -62,6 +62,7 @@ typedef struct { struct SDL_WaylandInput *keyboard_device; EGLSurface egl_surface; struct zwp_locked_pointer_v1 *locked_pointer; + struct org_kde_kwin_server_decoration *kwin_server_decoration; #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH struct qt_extended_surface *extended_surface; @@ -74,6 +75,7 @@ extern void Wayland_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_bool fullscreen); extern void Wayland_MaximizeWindow(_THIS, SDL_Window * window); extern void Wayland_RestoreWindow(_THIS, SDL_Window * window); +extern void Wayland_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered); extern int Wayland_CreateWindow(_THIS, SDL_Window *window); extern void Wayland_SetWindowSize(_THIS, SDL_Window * window); extern void Wayland_SetWindowTitle(_THIS, SDL_Window * window); diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index e961cf568e..59c11bf225 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -908,7 +908,8 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_TOUCH: if (data->videodata->GetTouchInputInfo && data->videodata->CloseTouchInputHandle) { UINT i, num_inputs = LOWORD(wParam); - PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs); + SDL_bool isstack; + PTOUCHINPUT inputs = SDL_small_alloc(TOUCHINPUT, num_inputs, &isstack); if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) { RECT rect; float x, y; @@ -916,7 +917,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) if (!GetClientRect(hwnd, &rect) || (rect.right == rect.left && rect.bottom == rect.top)) { if (inputs) { - SDL_stack_free(inputs); + SDL_small_free(inputs, isstack); } break; } @@ -950,7 +951,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } } - SDL_stack_free(inputs); + SDL_small_free(inputs, isstack); data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam); return 0; @@ -961,17 +962,19 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { UINT i; HDROP drop = (HDROP) wParam; + SDL_bool isstack; UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0); for (i = 0; i < count; ++i) { + SDL_bool isstack; UINT size = DragQueryFile(drop, i, NULL, 0) + 1; - LPTSTR buffer = SDL_stack_alloc(TCHAR, size); + LPTSTR buffer = SDL_small_alloc(TCHAR, size, &isstack); if (buffer) { if (DragQueryFile(drop, i, buffer, size)) { char *file = WIN_StringToUTF8(buffer); SDL_SendDropFile(data->window, file); SDL_free(file); } - SDL_stack_free(buffer); + SDL_small_free(buffer, isstack); } } SDL_SendDropComplete(data->window); diff --git a/src/video/windows/SDL_windowsframebuffer.c b/src/video/windows/SDL_windowsframebuffer.c index e07d9c4313..f884f7fa0d 100644 --- a/src/video/windows/SDL_windowsframebuffer.c +++ b/src/video/windows/SDL_windowsframebuffer.c @@ -27,6 +27,7 @@ int WIN_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch) { SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_bool isstack; size_t size; LPBITMAPINFO info; HBITMAP hbm; @@ -41,7 +42,7 @@ int WIN_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, voi /* Find out the format of the screen */ size = sizeof(BITMAPINFOHEADER) + 256 * sizeof (RGBQUAD); - info = (LPBITMAPINFO)SDL_stack_alloc(Uint8, size); + info = (LPBITMAPINFO)SDL_small_alloc(Uint8, size, &isstack); if (!info) { return SDL_OutOfMemory(); } @@ -85,7 +86,7 @@ int WIN_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, voi data->mdc = CreateCompatibleDC(data->hdc); data->hbm = CreateDIBSection(data->hdc, info, DIB_RGB_COLORS, pixels, NULL, 0); - SDL_stack_free(info); + SDL_small_free(info, isstack); if (!data->hbm) { return WIN_SetError("Unable to create DIB"); diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index eff31605b3..3dc7e79750 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -97,6 +97,7 @@ WIN_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) LPVOID pixels; LPVOID maskbits; size_t maskbitslen; + SDL_bool isstack; ICONINFO ii; SDL_zero(bmh); @@ -112,7 +113,7 @@ WIN_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) bmh.bV4BlueMask = 0x000000FF; maskbitslen = ((surface->w + (pad - (surface->w % pad))) / 8) * surface->h; - maskbits = SDL_stack_alloc(Uint8,maskbitslen); + maskbits = SDL_small_alloc(Uint8,maskbitslen); if (maskbits == NULL) { SDL_OutOfMemory(); return NULL; @@ -129,7 +130,7 @@ WIN_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmh, DIB_RGB_COLORS, &pixels, NULL, 0); ii.hbmMask = CreateBitmap(surface->w, surface->h, 1, 1, maskbits); ReleaseDC(NULL, hdc); - SDL_stack_free(maskbits); + SDL_small_free(maskbits, isstack); SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888); SDL_assert(surface->pitch == surface->w * 4); diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 45463c4c7f..d56b8dbfe0 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -380,10 +380,11 @@ WIN_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) HWND hwnd = (HWND) data; LPTSTR title; int titleLen; + SDL_bool isstack; /* Query the title from the existing window */ titleLen = GetWindowTextLength(hwnd); - title = SDL_stack_alloc(TCHAR, titleLen + 1); + title = SDL_small_alloc(TCHAR, titleLen + 1, &isstack); if (title) { titleLen = GetWindowText(hwnd, title, titleLen); } else { @@ -393,7 +394,7 @@ WIN_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) window->title = WIN_StringToUTF8(title); } if (title) { - SDL_stack_free(title); + SDL_small_free(title, isstack); } if (SetupWindowData(_this, window, hwnd, GetParent(hwnd), SDL_FALSE) < 0) { @@ -443,14 +444,15 @@ WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) BYTE *icon_bmp; int icon_len, mask_len, y; SDL_RWops *dst; + SDL_bool isstack; /* Create temporary buffer for ICONIMAGE structure */ mask_len = (icon->h * (icon->w + 7)/8); icon_len = 40 + icon->h * icon->w * sizeof(Uint32) + mask_len; - icon_bmp = SDL_stack_alloc(BYTE, icon_len); + icon_bmp = SDL_small_alloc(BYTE, icon_len, &isstack); dst = SDL_RWFromMem(icon_bmp, icon_len); if (!dst) { - SDL_stack_free(icon_bmp); + SDL_small_free(icon_bmp, isstack); return; } @@ -481,7 +483,7 @@ WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); SDL_RWclose(dst); - SDL_stack_free(icon_bmp); + SDL_small_free(icon_bmp, isstack); /* Set the icon for the window */ SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon); diff --git a/test/testsprite2.c b/test/testsprite2.c index b0348f85d7..f471ff3199 100644 --- a/test/testsprite2.c +++ b/test/testsprite2.c @@ -37,6 +37,8 @@ static SDL_Rect *positions; static SDL_Rect *velocities; static int sprite_w, sprite_h; static SDL_BlendMode blendMode = SDL_BLENDMODE_BLEND; +static Uint32 next_fps_check, frames; +static const Uint32 fps_check_delay = 5000; /* Number of iterations to move sprites - used for visual tests. */ /* -1: infinite random moves (default); >=0: enables N deterministic moves */ @@ -244,6 +246,7 @@ MoveSprites(SDL_Renderer * renderer, SDL_Texture * sprite) void loop() { + Uint32 now; int i; SDL_Event event; @@ -261,13 +264,24 @@ loop() emscripten_cancel_main_loop(); } #endif + + frames++; + now = SDL_GetTicks(); + if (SDL_TICKS_PASSED(now, next_fps_check)) { + /* Print out some timing information */ + const Uint32 then = next_fps_check - fps_check_delay; + const double fps = ((double) frames * 1000) / (now - then); + SDL_Log("%2.2f frames per second\n", fps); + next_fps_check = now + fps_check_delay; + frames = 0; + } + } int main(int argc, char *argv[]) { int i; - Uint32 then, now, frames; Uint64 seed; const char *icon = "icon.bmp"; @@ -384,24 +398,17 @@ main(int argc, char *argv[]) /* Main render loop */ frames = 0; - then = SDL_GetTicks(); + next_fps_check = SDL_GetTicks() + fps_check_delay; done = 0; #ifdef __EMSCRIPTEN__ emscripten_set_main_loop(loop, 0, 1); #else while (!done) { - ++frames; loop(); } #endif - /* Print out some timing information */ - now = SDL_GetTicks(); - if (now > then) { - double fps = ((double) frames * 1000) / (now - then); - SDL_Log("%2.2f frames per second\n", fps); - } quit(0); return 0; } diff --git a/wayland-protocols/org-kde-kwin-server-decoration-manager.xml b/wayland-protocols/org-kde-kwin-server-decoration-manager.xml new file mode 100644 index 0000000000..8bc106c7c4 --- /dev/null +++ b/wayland-protocols/org-kde-kwin-server-decoration-manager.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="server_decoration"> + <copyright><![CDATA[ + Copyright (C) 2015 Martin Gräßlin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + ]]></copyright> + <interface name="org_kde_kwin_server_decoration_manager" version="1"> + <description summary="Server side window decoration manager"> + This interface allows to coordinate whether the server should create + a server-side window decoration around a wl_surface representing a + shell surface (wl_shell_surface or similar). By announcing support + for this interface the server indicates that it supports server + side decorations. + </description> + <request name="create"> + <description summary="Create a server-side decoration object for a given surface"> + When a client creates a server-side decoration object it indicates + that it supports the protocol. The client is supposed to tell the + server whether it wants server-side decorations or will provide + client-side decorations. + + If the client does not create a server-side decoration object for + a surface the server interprets this as lack of support for this + protocol and considers it as client-side decorated. Nevertheless a + client-side decorated surface should use this protocol to indicate + to the server that it does not want a server-side deco. + </description> + <arg name="id" type="new_id" interface="org_kde_kwin_server_decoration"/> + <arg name="surface" type="object" interface="wl_surface"/> + </request> + <enum name="mode"> + <description summary="Possible values to use in request_mode and the event mode."/> + <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/> + <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/> + <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/> + </enum> + <event name="default_mode"> + <description summary="The default mode used on the server"> + This event is emitted directly after binding the interface. It contains + the default mode for the decoration. When a new server decoration object + is created this new object will be in the default mode until the first + request_mode is requested. + + The server may change the default mode at any time. + </description> + <arg name="mode" type="uint" summary="The default decoration mode applied to newly created server decorations."/> + </event> + </interface> + <interface name="org_kde_kwin_server_decoration" version="1"> + <request name="release" type="destructor"> + <description summary="release the server decoration object"/> + </request> + <enum name="mode"> + <description summary="Possible values to use in request_mode and the event mode."/> + <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/> + <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/> + <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/> + </enum> + <request name="request_mode"> + <description summary="The decoration mode the surface wants to use."/> + <arg name="mode" type="uint" summary="The mode this surface wants to use."/> + </request> + <event name="mode"> + <description summary="The new decoration mode applied by the server"> + This event is emitted directly after the decoration is created and + represents the base decoration policy by the server. E.g. a server + which wants all surfaces to be client-side decorated will send Client, + a server which wants server-side decoration will send Server. + + The client can request a different mode through the decoration request. + The server will acknowledge this by another event with the same mode. So + even if a server prefers server-side decoration it's possible to force a + client-side decoration. + + The server may emit this event at any time. In this case the client can + again request a different mode. It's the responsibility of the server to + prevent a feedback loop. + </description> + <arg name="mode" type="uint" summary="The decoration mode applied to the surface by the server."/> + </event> + </interface> +</protocol> |