From 961a84f88e899c7da1004765fdcd2391799c02d1 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 25 Sep 2018 10:45:37 -0400 Subject: [PATCH 01/43] Created SDL-ryan-batching-renderer branch for the WIP renderer rewrite. --HG-- branch : SDL-ryan-batching-renderer From cc9ee08b653abf4acd536a79ba7410d53980d402 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 8 Sep 2018 18:26:11 -0400 Subject: [PATCH 02/43] gles2: Major renderer optimization. Work in progress! This moves all the rendering to a command list that is flushed to the GL as necessary, making most common activities upload a single vertex buffer per frame and dramatically reducing state changes. In pathological cases, like Emscripten running on iOS's Safari, performance can go from a dozen draw calls killing your performance to 1000 draw calls running smoothly. This is work in progress, and not ready to ship. Among other things, it has a hardcoded array that isn't checked for overflow. But the basic idea is sound! --HG-- branch : SDL-ryan-batching-renderer extra : source : eae4631409c03d3b7ab2e87526193d86ce996ba8 --- src/render/opengles2/SDL_gles2funcs.h | 1 + src/render/opengles2/SDL_render_gles2.c | 920 ++++++++++++----------- src/render/opengles2/SDL_shaders_gles2.c | 28 +- 3 files changed, 490 insertions(+), 459 deletions(-) diff --git a/src/render/opengles2/SDL_gles2funcs.h b/src/render/opengles2/SDL_gles2funcs.h index b6a143618..68096aaaf 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 fe51b9aaf..d8dc16268 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 */ @@ -101,6 +88,7 @@ typedef struct GLES2_TextureData GLenum texture_v; GLenum texture_u; GLES2_FBOList *fbo; + Uint32 last_cmd_generation; /* last command queue generation this texture was in. */ } GLES2_TextureData; typedef struct GLES2_ShaderCacheEntry @@ -152,7 +140,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 +147,7 @@ typedef enum typedef enum { + GLES2_IMAGESOURCE_INVALID, GLES2_IMAGESOURCE_SOLID, GLES2_IMAGESOURCE_TEXTURE_ABGR, GLES2_IMAGESOURCE_TEXTURE_ARGB, @@ -171,17 +159,51 @@ typedef enum GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES } GLES2_ImageSource; +typedef enum +{ + GLES2_RENDERCMD_VIEWPORT, + GLES2_RENDERCMD_CLIPRECT, + GLES2_RENDERCMD_CLEAR, + GLES2_RENDERCMD_ATTR, + GLES2_RENDERCMD_DRAW +} GLES2_RenderCommandType; + +typedef struct GLES2_RenderCommand +{ + GLES2_RenderCommandType cmd; + union { + SDL_Rect viewport; + struct { + SDL_bool enabled; + SDL_Rect rect; + } cliprect; + struct { + Uint8 r, g, b, a; + } clear; + struct { + GLES2_Attribute attr; + GLsizei offset; + GLsizei count; + } attr; + struct { + GLenum mode; + GLint first; + GLsizei count; + Uint8 attrs; + Uint8 r, g, b, a; + SDL_BlendMode blend; + GLES2_ImageSource imgsrc; + SDL_Texture *texture; + } draw; + } data; +} GLES2_RenderCommand; + typedef struct GLES2_DriverContext { 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 @@ -195,14 +217,30 @@ typedef struct GLES2_DriverContext 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 + int current_vertex_buffer; + GLES2_RenderCommand render_commands[1024 * 10]; + int current_render_command; + int current_vertex_data; + Uint32 command_generation; + GLfloat vertex_data[1024 * 1024 * 5]; } GLES2_DriverContext; #define GLES2_MAX_CACHED_PROGRAMS 8 +static const float inv255f = 1.0f / 255.0f; + +static SDL_bool +CompareColors(const Uint8 r1, const Uint8 g1, const Uint8 b1, const Uint8 a1, + const Uint8 r2, const Uint8 g2, const Uint8 b2, const Uint8 a2) +{ + Uint32 Pixel1, Pixel2; + RGBA8888_FROM_RGBA(Pixel1, r1, g1, b1, a1); + RGBA8888_FROM_RGBA(Pixel2, r2, g2, b2, a2); + return (Pixel1 == Pixel2); +} + SDL_FORCE_INLINE const char* GL_TranslateError (GLenum error) @@ -274,7 +312,6 @@ 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; @@ -434,57 +471,284 @@ GLES2_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) return SDL_TRUE; } +static int GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int h); + +static int +GLES2_FlushCommands(SDL_Renderer *renderer) +{ + GLES2_DriverContext *data = (GLES2_DriverContext *)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]; + const GLsizeiptr dataSizeInBytes = data->current_vertex_data * sizeof (float); + const int totalcmds = data->current_render_command; + Uint8 enabled_attrs = (1 << GLES2_ATTRIBUTE_POSITION); + SDL_Rect viewport; + SDL_Texture *bound_texture = NULL; + SDL_BlendMode blend = SDL_BLENDMODE_INVALID; + Uint8 clear_r, clear_g, clear_b, clear_a; + int drawablew = 0, drawableh = 0; + GLfloat projection[4][4]; + SDL_bool cliprect_enabled = SDL_FALSE; + SDL_Rect cliprect; + int i; + + GLES2_ActivateRenderer(renderer); + + if (totalcmds == 0) { /* nothing to do! */ + SDL_assert(data->current_vertex_data == 0); + return 0; + } + + /* 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; + } + data->current_vertex_data = 0; /* start next VBO at start. */ + data->current_render_command = 0; + + SDL_zero(projection); + projection[3][0] = -1.0f; + projection[3][1] = renderer->target ? -1.0f : 1.0f; + projection[3][3] = 1.0f; + + if (!renderer->target) { + SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + } + + /* upload the new VBO data for this set of commands. */ + data->glBindBuffer(GL_ARRAY_BUFFER, vbo); + if (data->vertex_buffer_size[vboidx] < dataSizeInBytes) { + data->glBufferData(GL_ARRAY_BUFFER, dataSizeInBytes, data->vertex_data, GL_STREAM_DRAW); + data->vertex_buffer_size[vboidx] = dataSizeInBytes; + } else { + data->glBufferSubData(GL_ARRAY_BUFFER, 0, dataSizeInBytes, data->vertex_data); + } + + data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION); + data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); + data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE); + data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_CENTER); + + clear_r = renderer->r; + clear_g = renderer->g; + clear_b = renderer->b; + clear_a = renderer->a; + data->glClearColor(clear_r * inv255f, clear_g * inv255f, clear_b * inv255f, clear_a * inv255f); + + SDL_memcpy(&viewport, &renderer->viewport, sizeof (viewport)); + data->glViewport(viewport.x, + (renderer->target) ? viewport.y : (drawableh - viewport.y - viewport.h), + viewport.w, viewport.h); + + SDL_memcpy(&cliprect, &renderer->clip_rect, sizeof (cliprect)); + cliprect_enabled = renderer->clipping_enabled; + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); + } + if (renderer->target) { + data->glScissor(viewport.x + cliprect.x, viewport.y + cliprect.y, cliprect.w, cliprect.h); + } else { + data->glScissor(viewport.x + cliprect.x, drawableh - viewport.y - cliprect.y - cliprect.h, cliprect.w, cliprect.h); + } + + for (i = 0; i < totalcmds; i++) { + const GLES2_RenderCommand *cmd = &data->render_commands[i]; + switch (cmd->cmd) { + case GLES2_RENDERCMD_VIEWPORT: + if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&viewport, &cmd->data.viewport, sizeof (SDL_Rect)); + data->glViewport(viewport.x, + (renderer->target) ? viewport.y : (drawableh - viewport.y - viewport.h), + viewport.w, viewport.h); + } + break; + + case GLES2_RENDERCMD_CLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + const SDL_bool changed = (SDL_memcmp(&cliprect, rect, sizeof (SDL_Rect)) != 0); + if (cliprect_enabled != cmd->data.cliprect.enabled) { + cliprect_enabled = cmd->data.cliprect.enabled; + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); + } + } + + if (cliprect_enabled && changed) { + SDL_memcpy(&cliprect, rect, sizeof (SDL_Rect)); + if (renderer->target) { + data->glScissor(viewport.x + rect->x, viewport.y + rect->y, rect->w, rect->h); + } else { + data->glScissor(viewport.x + rect->x, drawableh - viewport.y - rect->y - rect->h, rect->w, rect->h); + } + } + break; + } + + case GLES2_RENDERCMD_CLEAR: + if (!CompareColors(clear_r, clear_g, clear_b, clear_a, cmd->data.clear.r, cmd->data.clear.g, cmd->data.clear.b, cmd->data.clear.a)) { + GLfloat r, g, b, a; + + clear_r = cmd->data.clear.r; + clear_g = cmd->data.clear.g; + clear_b = cmd->data.clear.b; + clear_a = cmd->data.clear.a; + + r = ((GLfloat) (colorswap ? clear_b : clear_r)) * inv255f; + g = ((GLfloat) clear_g) * inv255f; + b = ((GLfloat) (colorswap ? clear_r : clear_b)) * inv255f; + a = ((GLfloat) clear_a) * inv255f; + + data->glClearColor(r, g, b, a); + } + + if (cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } + + data->glClear(GL_COLOR_BUFFER_BIT); + + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } + break; + + case GLES2_RENDERCMD_ATTR: + data->glVertexAttribPointer(cmd->data.attr.attr, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) cmd->data.attr.offset); + break; + + case GLES2_RENDERCMD_DRAW: { + const SDL_bool iscopy = (cmd->data.draw.imgsrc != GLES2_IMAGESOURCE_SOLID); + if (!viewport.w || !viewport.h) { + break; /* nothing to draw to. */ + } + + if (iscopy && (bound_texture != cmd->data.draw.texture)) { + GLES2_TextureData *tdata = (GLES2_TextureData *)cmd->data.draw.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); + } + if (tdata->nv12) { + data->glActiveTexture(GL_TEXTURE1); + data->glBindTexture(tdata->texture_type, tdata->texture_u); + + data->glActiveTexture(GL_TEXTURE0); + } + data->glBindTexture(tdata->texture_type, tdata->texture); + bound_texture = cmd->data.draw.texture; + } + + if (GLES2_SelectProgram(renderer, cmd->data.draw.imgsrc, iscopy ? bound_texture->w : 0, iscopy ? bound_texture->h : 0) == 0) { + GLES2_ProgramCacheEntry *program = data->current_program; + + if (enabled_attrs != cmd->data.draw.attrs) { + const Uint8 xored = enabled_attrs ^ cmd->data.draw.attrs; + int attr; + for (attr = 0; attr < GLES2_ATTRIBUTE_CENTER; attr++) { + if ((xored & (1 << attr)) != 0) { /* if changed */ + if (cmd->data.draw.attrs & (1 << attr)) { + data->glEnableVertexAttribArray((GLenum) attr); + } else { + data->glDisableVertexAttribArray((GLenum) attr); + } + } + } + enabled_attrs = cmd->data.draw.attrs; + } + + if (blend != cmd->data.draw.blend) { + const SDL_BlendMode bm = cmd->data.draw.blend; + if (bm == SDL_BLENDMODE_NONE) { + data->glDisable(GL_BLEND); + } else { + data->glEnable(GL_BLEND); + data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(bm)), + GetBlendFunc(SDL_GetBlendModeDstColorFactor(bm)), + GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(bm)), + GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(bm))); + data->glBlendEquationSeparate(GetBlendEquation(SDL_GetBlendModeColorOperation(bm)), + GetBlendEquation(SDL_GetBlendModeAlphaOperation(bm))); + } + blend = bm; + } + + if (program->uniform_locations[GLES2_UNIFORM_PROJECTION] != -1) { + projection[0][0] = 2.0f / viewport.w; + projection[1][1] = (renderer->target ? 2.0f : -2.0f) / viewport.h; + if (SDL_memcmp(program->projection, projection, sizeof (projection)) != 0) { + data->glUniformMatrix4fv(program->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)projection); + SDL_memcpy(program->projection, projection, sizeof (projection)); + } + } + + if (program->uniform_locations[GLES2_UNIFORM_COLOR] != -1) { + const Uint8 r = colorswap ? cmd->data.draw.b : cmd->data.draw.r; + const Uint8 g = cmd->data.draw.g; + const Uint8 b = colorswap ? cmd->data.draw.r : cmd->data.draw.b; + const Uint8 a = cmd->data.draw.a; + if (!CompareColors(program->color_r, program->color_g, program->color_b, program->color_a, r, g, b, a)) { + 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; + } + } + + data->glDrawArrays(cmd->data.draw.mode, cmd->data.draw.first, cmd->data.draw.count); + } + break; + } + + default: SDL_assert(!"Unknown rendering command"); break; + } + } + + data->command_generation++; + + return GL_CheckError("", renderer); +} + +static void +GLES2_FlushCommandsIfTextureNeeded(SDL_Renderer *renderer, SDL_Texture *texture) +{ + GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; + if (tdata->last_cmd_generation == data->command_generation) { + /* the current command queue depends on this texture, flush the queue now before it changes */ + GLES2_FlushCommands(renderer); + } +} + 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); + GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + cmd->cmd = GLES2_RENDERCMD_VIEWPORT; + SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (SDL_Rect)); + return 0; } 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); - } + GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + cmd->cmd = GLES2_RENDERCMD_CLIPRECT; + cmd->data.cliprect.enabled = renderer->clipping_enabled; + SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (SDL_Rect)); return 0; } @@ -527,8 +791,13 @@ GLES2_DestroyRenderer(SDL_Renderer *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); } @@ -757,6 +1026,8 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect GLES2_ActivateRenderer(renderer); + GLES2_FlushCommandsIfTextureNeeded(renderer, texture); + /* Bail out if we're supposed to update an empty rectangle */ if (rect->w <= 0 || rect->h <= 0) { return 0; @@ -837,6 +1108,8 @@ GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, GLES2_ActivateRenderer(renderer); + GLES2_FlushCommandsIfTextureNeeded(renderer, texture); + /* Bail out if we're supposed to update an empty rectangle */ if (rect->w <= 0 || rect->h <= 0) { return 0; @@ -911,6 +1184,8 @@ GLES2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) GLES2_TextureData *texturedata = NULL; GLenum status; + GLES2_FlushCommands(renderer); /* time to send everything to the GPU! */ + if (texture == NULL) { data->glBindFramebuffer(GL_FRAMEBUFFER, data->window_framebuffer); } else { @@ -935,6 +1210,8 @@ GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) GLES2_ActivateRenderer(renderer); + GLES2_FlushCommandsIfTextureNeeded(renderer, texture); + /* Destroy the texture */ if (tdata) { data->glDeleteTextures(1, &tdata->texture); @@ -959,7 +1236,6 @@ static void GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *en 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, @@ -1029,8 +1305,6 @@ 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"); @@ -1038,12 +1312,21 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, entry->color_r = entry->color_g = entry->color_b = entry->color_a = 255; 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], 1.0f, 1.0f, 1.0f, 1.0f); + } /* Cache the linked program */ if (data->program_cache.head) { @@ -1308,11 +1591,6 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int /* Set the current program */ data->current_program = program; - /* Activate an orthographic projection */ - if (GLES2_SetOrthographicProjection(renderer) < 0) { - goto fault; - } - /* Clean up and return */ return 0; fault: @@ -1326,58 +1604,11 @@ fault: return -1; } -static int -GLES2_SetOrthographicProjection(SDL_Renderer *renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat projection[4][4]; - - if (!renderer->viewport.w || !renderer->viewport.h) { - return 0; - } - - /* 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; - } - 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)); - } - - 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); @@ -1391,193 +1622,67 @@ 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) { - 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; - } - - if (renderer->clipping_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } - - data->glClear(GL_COLOR_BUFFER_BIT); - - if (renderer->clipping_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } - + GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + cmd->cmd = GLES2_RENDERCMD_CLEAR; + cmd->data.clear.r = renderer->r; + cmd->data.clear.g = renderer->g; + cmd->data.clear.b = renderer->b; + cmd->data.clear.a = renderer->a; 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_AddVertices(SDL_Renderer *renderer, const GLES2_Attribute attr, const float *vertexData, size_t dataSizeInElements) { 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); - - /* Activate an appropriate shader and set the projection matrix */ - if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID, 0, 0) < 0) { - 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; - } - - return 0; + GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + GLfloat *vdata = &data->vertex_data[data->current_vertex_data]; + SDL_memcpy(vdata, vertexData, dataSizeInElements * sizeof (GLfloat)); + cmd->cmd = GLES2_RENDERCMD_ATTR; + cmd->data.attr.attr = attr; + cmd->data.attr.offset = data->current_vertex_data * sizeof (GLfloat); + cmd->data.attr.count = dataSizeInElements; + data->current_vertex_data += dataSizeInElements; } -static int -GLES2_UpdateVertexBuffer(SDL_Renderer *renderer, GLES2_Attribute attr, - const void *vertexData, size_t dataSizeInBytes) +static GLES2_RenderCommand * +GLES2_InitSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLint first, const GLsizei count) { 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]); - } - - 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; - } else { - data->glBufferSubData(GL_ARRAY_BUFFER, 0, dataSizeInBytes, vertexData); - } - - data->glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, 0); -#endif - - return 0; + GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + cmd->cmd = GLES2_RENDERCMD_DRAW; + cmd->data.draw.mode = mode; + cmd->data.draw.first = first; + cmd->data.draw.count = count; + cmd->data.draw.attrs = (1 << GLES2_ATTRIBUTE_POSITION); + 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.imgsrc = GLES2_IMAGESOURCE_SOLID; + cmd->data.draw.texture = NULL; + return cmd; } static int GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat *vertices; + GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */ int idx; - if (GLES2_SetDrawingState(renderer) < 0) { - return -1; - } - /* 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; + vertices[idx * 2] = points[idx].x + 0.5f; + vertices[(idx * 2) + 1] = points[idx].y + 0.5f; } - /*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); + + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); + GLES2_InitSolidDrawCommand(renderer, GL_POINTS, 0, count); SDL_stack_free(vertices); return 0; } @@ -1585,48 +1690,36 @@ GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int cou static int GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLfloat *vertices; + GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */ int idx; - if (GLES2_SetDrawingState(renderer) < 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; - - vertices[idx * 2] = x; - vertices[(idx * 2) + 1] = y; + vertices[idx * 2] = points[idx].x + 0.5f; + vertices[(idx * 2) + 1] = points[idx].y + 0.5f; } - /*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); + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); + GLES2_InitSolidDrawCommand(renderer, GL_LINE_STRIP, 0, count); + +#if 0 /* !!! FIXME: ugh */ /* 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); + GLES2_DrawVertices(GL_POINTS, count-1, 1); } - SDL_stack_free(vertices); +#endif - return GL_CheckError("", renderer); + SDL_stack_free(vertices); + return 0; } 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; - } - /* Emit a line loop for each rectangle */ for (idx = 0; idx < count; ++idx) { const SDL_FRect *rect = &rects[idx]; @@ -1644,23 +1737,23 @@ GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) 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); + + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); + GLES2_InitSolidDrawCommand(renderer, GL_TRIANGLE_STRIP, 0, 4); } - return GL_CheckError("", renderer); + + return 0; } -static int -GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; - GLES2_ProgramCacheEntry *program; - Uint8 r, g, b, a; - /* Activate an appropriate shader and set the projection matrix */ +static GLES2_RenderCommand * +GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uint8 attrs) +{ + GLES2_RenderCommand *cmd = NULL; + GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; + + /* 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) { @@ -1727,7 +1820,8 @@ GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES; break; default: - return SDL_SetError("Unsupported texture format"); + SDL_SetError("Unsupported texture format"); + return NULL; } } else { sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; /* Texture formats match, use the non color mapping shader (even if the formats are not ABGR) */ @@ -1760,76 +1854,35 @@ GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES; break; default: - return SDL_SetError("Unsupported texture format"); + SDL_SetError("Unsupported texture format"); + return NULL; } } - if (GLES2_SelectProgram(renderer, sourceType, texture->w, texture->h) < 0) { - return -1; - } + ((GLES2_TextureData *)texture->driverdata)->last_cmd_generation = data->command_generation; - /* Select the target texture */ - if (tdata->yuv) { - data->glActiveTexture(GL_TEXTURE2); - data->glBindTexture(tdata->texture_type, tdata->texture_v); + cmd = &data->render_commands[data->current_render_command++]; + cmd->cmd = GLES2_RENDERCMD_DRAW; + cmd->data.draw.mode = GL_TRIANGLE_STRIP; + cmd->data.draw.first = 0; + cmd->data.draw.count = 4; + cmd->data.draw.attrs = attrs | (1 << GLES2_ATTRIBUTE_POSITION) | (1 << GLES2_ATTRIBUTE_TEXCOORD); + 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.imgsrc = sourceType; + cmd->data.draw.texture = texture; - data->glActiveTexture(GL_TEXTURE1); - data->glBindTexture(tdata->texture_type, tdata->texture_u); - - data->glActiveTexture(GL_TEXTURE0); - } - if (tdata->nv12) { - data->glActiveTexture(GL_TEXTURE1); - data->glBindTexture(tdata->texture_type, tdata->texture_u); - - data->glActiveTexture(GL_TEXTURE0); - } - data->glBindTexture(tdata->texture_type, tdata->texture); - - /* Configure color modulation */ - g = texture->g; - a = texture->a; - - if (renderer->target && - (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || - renderer->target->format == SDL_PIXELFORMAT_RGB888)) { - r = texture->b; - b = texture->r; - } else { - r = texture->r; - b = texture->b; - } - - program = data->current_program; - - 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; - } - - /* Configure texture blending */ - GLES2_SetBlendMode(data, texture->blendMode); - - GLES2_SetTexCoords(data, SDL_TRUE); - return 0; + return cmd; } static int GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_FRect *dstrect) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; GLfloat vertices[8]; - GLfloat texCoords[8]; - - GLES2_ActivateRenderer(renderer); - - if (GLES2_SetupCopy(renderer, texture) < 0) { - return -1; - } /* Emit the textured quad */ vertices[0] = dstrect->x; @@ -1840,51 +1893,38 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s 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); + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - return GL_CheckError("", renderer); + vertices[0] = srcrect->x / (GLfloat)texture->w; + vertices[1] = srcrect->y / (GLfloat)texture->h; + vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[3] = srcrect->y / (GLfloat)texture->h; + vertices[4] = srcrect->x / (GLfloat)texture->w; + vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); + + GLES2_InitCopyDrawCommand(renderer, texture, 0); + return 0; } 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_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + const float radian_angle = (float)(M_PI * (360.0 - angle) / 180.0); GLfloat vertices[8]; - GLfloat texCoords[8]; - GLfloat translate[8]; - GLfloat fAngle[8]; - GLfloat tmp; - float radian_angle; - GLES2_ActivateRenderer(renderer); - - if (GLES2_SetupCopy(renderer, texture) < 0) { - return -1; - } - - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_CENTER); - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE); - - radian_angle = (float)(M_PI * (360.0 - angle) / 180.0); - fAngle[0] = fAngle[2] = fAngle[4] = fAngle[6] = (GLfloat)SDL_sin(radian_angle); + vertices[0] = vertices[2] = vertices[4] = vertices[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; + vertices[1] = vertices[3] = vertices[5] = vertices[7] = (GLfloat)SDL_cos(radian_angle) - 1.0f; + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_ANGLE, vertices, 8); + /* 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); + vertices[0] = vertices[2] = vertices[4] = vertices[6] = (center->x + dstrect->x); + vertices[1] = vertices[3] = vertices[5] = vertices[7] = (center->y + dstrect->y); + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_CENTER, vertices, 8); /* Emit the textured quad */ vertices[0] = dstrect->x; @@ -1896,39 +1936,29 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect vertices[6] = (dstrect->x + dstrect->w); vertices[7] = (dstrect->y + dstrect->h); if (flip & SDL_FLIP_HORIZONTAL) { - tmp = vertices[0]; + const GLfloat tmp = vertices[0]; vertices[0] = vertices[4] = vertices[2]; vertices[2] = vertices[6] = tmp; } if (flip & SDL_FLIP_VERTICAL) { - tmp = vertices[1]; + const GLfloat tmp = vertices[1]; vertices[1] = vertices[3] = vertices[5]; vertices[5] = vertices[7] = tmp; } + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - /*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);*/ + vertices[0] = srcrect->x / (GLfloat)texture->w; + vertices[1] = srcrect->y / (GLfloat)texture->h; + vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[3] = srcrect->y / (GLfloat)texture->h; + vertices[4] = srcrect->x / (GLfloat)texture->w; + vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); - 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 GL_CheckError("", renderer); + GLES2_InitCopyDrawCommand(renderer, texture, (1 << GLES2_ATTRIBUTE_CENTER) | (1 << GLES2_ATTRIBUTE_ANGLE)); + return 0; } static int @@ -1944,7 +1974,7 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, int w, h, length, rows; int status; - GLES2_ActivateRenderer(renderer); + GLES2_FlushCommands(renderer); /* we need to render before we read the results. */ temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format); buflen = (size_t) (rect->h * temp_pitch); @@ -1993,7 +2023,7 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, static void GLES2_RenderPresent(SDL_Renderer *renderer) { - GLES2_ActivateRenderer(renderer); + GLES2_FlushCommands(renderer); /* time to send it to the GPU! */ /* Tell the video driver to swap buffers */ SDL_GL_SwapWindow(renderer->window); @@ -2055,9 +2085,6 @@ GLES2_ResetState(SDL_Renderer *renderer) 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); @@ -2204,6 +2231,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; diff --git a/src/render/opengles2/SDL_shaders_gles2.c b/src/render/opengles2/SDL_shaders_gles2.c index f428a4945..7c3e6c17c 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; \ } \ "; From 8353a58143de80e3ff63615f0d6be72658633e4d Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 9 Sep 2018 15:09:38 -0400 Subject: [PATCH 03/43] gles2: Make render command queue dynamic. It now uses a growable linked list that keeps a pool of allocated items for reuse, and reallocs the vertex array as necessary. Testsprite2 can scale to 20,000 (or more!) draws now without drama. --HG-- branch : SDL-ryan-batching-renderer extra : source : a4d56fdc03aa55ae4e04dc1bf7091b5dddc95fae --- src/render/opengles2/SDL_render_gles2.c | 297 +++++++++++++++++------- 1 file changed, 210 insertions(+), 87 deletions(-) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index d8dc16268..282f4baa8 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -196,6 +196,7 @@ typedef struct GLES2_RenderCommand SDL_Texture *texture; } draw; } data; + struct GLES2_RenderCommand *next; } GLES2_RenderCommand; typedef struct GLES2_DriverContext @@ -220,17 +221,49 @@ typedef struct GLES2_DriverContext GLuint vertex_buffers[4]; GLsizeiptr vertex_buffer_size[4]; int current_vertex_buffer; - GLES2_RenderCommand render_commands[1024 * 10]; - int current_render_command; + GLES2_RenderCommand *render_commands; + GLES2_RenderCommand *render_commands_tail; + GLES2_RenderCommand *render_commands_pool; int current_vertex_data; Uint32 command_generation; - GLfloat vertex_data[1024 * 1024 * 5]; + GLfloat *vertex_data; + GLsizeiptr vertex_data_allocation; } GLES2_DriverContext; #define GLES2_MAX_CACHED_PROGRAMS 8 static const float inv255f = 1.0f / 255.0f; +static GLES2_RenderCommand * +GLES2_AllocateRenderCommand(SDL_Renderer *renderer) +{ + GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; + GLES2_RenderCommand *retval = NULL; + + /* !!! FIXME: are there threading limitations in SDL's render API? */ + retval = data->render_commands_pool; + if (retval != NULL) { + data->render_commands_pool = retval->next; + retval->next = NULL; + } else { + retval = SDL_calloc(1, sizeof (*retval)); + if (!retval) { + SDL_OutOfMemory(); + return NULL; + } + } + + SDL_assert((data->render_commands == NULL) == (data->render_commands_tail == NULL)); + if (data->render_commands_tail != NULL) { + data->render_commands_tail->next = retval; + } else { + data->render_commands = retval; + } + data->render_commands_tail = retval; + + return retval; +} + static SDL_bool CompareColors(const Uint8 r1, const Uint8 g1, const Uint8 b1, const Uint8 a1, const Uint8 r2, const Uint8 g2, const Uint8 b2, const Uint8 a2) @@ -481,8 +514,9 @@ GLES2_FlushCommands(SDL_Renderer *renderer) const int vboidx = data->current_vertex_buffer; const GLuint vbo = data->vertex_buffers[vboidx]; const GLsizeiptr dataSizeInBytes = data->current_vertex_data * sizeof (float); - const int totalcmds = data->current_render_command; Uint8 enabled_attrs = (1 << GLES2_ATTRIBUTE_POSITION); + GLES2_RenderCommand *cmd; + GLES2_RenderCommand *next; SDL_Rect viewport; SDL_Texture *bound_texture = NULL; SDL_BlendMode blend = SDL_BLENDMODE_INVALID; @@ -491,11 +525,10 @@ GLES2_FlushCommands(SDL_Renderer *renderer) GLfloat projection[4][4]; SDL_bool cliprect_enabled = SDL_FALSE; SDL_Rect cliprect; - int i; GLES2_ActivateRenderer(renderer); - if (totalcmds == 0) { /* nothing to do! */ + if (data->render_commands == NULL) { /* nothing to do! */ SDL_assert(data->current_vertex_data == 0); return 0; } @@ -506,7 +539,9 @@ GLES2_FlushCommands(SDL_Renderer *renderer) data->current_vertex_buffer = 0; } data->current_vertex_data = 0; /* start next VBO at start. */ - data->current_render_command = 0; + cmd = data->render_commands; + data->render_commands = NULL; + data->render_commands_tail = NULL; SDL_zero(projection); projection[3][0] = -1.0f; @@ -555,8 +590,7 @@ GLES2_FlushCommands(SDL_Renderer *renderer) data->glScissor(viewport.x + cliprect.x, drawableh - viewport.y - cliprect.y - cliprect.h, cliprect.w, cliprect.h); } - for (i = 0; i < totalcmds; i++) { - const GLES2_RenderCommand *cmd = &data->render_commands[i]; + while (cmd != NULL) { switch (cmd->cmd) { case GLES2_RENDERCMD_VIEWPORT: if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) { @@ -713,6 +747,12 @@ GLES2_FlushCommands(SDL_Renderer *renderer) default: SDL_assert(!"Unknown rendering command"); break; } + + /* put this command in the pool for reuse, move on to next one. */ + next = cmd->next; + cmd->next = data->render_commands_pool; + data->render_commands_pool = cmd; + cmd = next; } data->command_generation++; @@ -734,8 +774,10 @@ GLES2_FlushCommandsIfTextureNeeded(SDL_Renderer *renderer, SDL_Texture *texture) static int GLES2_UpdateViewport(SDL_Renderer * renderer) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } cmd->cmd = GLES2_RENDERCMD_VIEWPORT; SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (SDL_Rect)); return 0; @@ -744,8 +786,10 @@ GLES2_UpdateViewport(SDL_Renderer * renderer) static int GLES2_UpdateClipRect(SDL_Renderer * renderer) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } cmd->cmd = GLES2_RENDERCMD_CLIPRECT; cmd->data.cliprect.enabled = renderer->clipping_enabled; SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (SDL_Rect)); @@ -759,8 +803,25 @@ GLES2_DestroyRenderer(SDL_Renderer *renderer) /* Deallocate everything */ if (data) { + GLES2_RenderCommand *cmd; + GLES2_ActivateRenderer(renderer); + if (data->render_commands_tail != NULL) { + data->render_commands_tail->next = data->render_commands_pool; + } else { + data->render_commands = data->render_commands_pool; + } + + cmd = data->render_commands; + while (cmd != NULL) { + GLES2_RenderCommand *next = cmd->next; + SDL_free(cmd); + cmd = next; + } + + SDL_free(data->vertex_data); + { GLES2_ShaderCacheEntry *entry; GLES2_ShaderCacheEntry *next; @@ -1625,8 +1686,10 @@ static void GLES2_RenderPresent(SDL_Renderer *renderer); static int GLES2_RenderClear(SDL_Renderer * renderer) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } cmd->cmd = GLES2_RENDERCMD_CLEAR; cmd->data.clear.r = renderer->r; cmd->data.clear.g = renderer->g; @@ -1635,25 +1698,48 @@ GLES2_RenderClear(SDL_Renderer * renderer) return 0; } -static void +static int GLES2_AddVertices(SDL_Renderer *renderer, const GLES2_Attribute attr, const float *vertexData, size_t dataSizeInElements) { GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; - GLfloat *vdata = &data->vertex_data[data->current_vertex_data]; + const GLsizeiptr needed = data->current_vertex_data + dataSizeInElements; + GLES2_RenderCommand *cmd; + GLfloat *vdata; + + if (needed > data->vertex_data_allocation) { + const GLsizeiptr newsize = data->vertex_data_allocation * 2; +printf("realloc'ing %p to %d\n", data->vertex_data, (int) newsize); + void *ptr = SDL_realloc(data->vertex_data, newsize * sizeof (GLfloat)); + if (ptr == NULL) { + SDL_OutOfMemory(); + return -1; + } + data->vertex_data = (GLfloat *) ptr; + data->vertex_data_allocation = newsize; + } + + cmd = GLES2_AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } + + vdata = &data->vertex_data[data->current_vertex_data]; SDL_memcpy(vdata, vertexData, dataSizeInElements * sizeof (GLfloat)); cmd->cmd = GLES2_RENDERCMD_ATTR; cmd->data.attr.attr = attr; cmd->data.attr.offset = data->current_vertex_data * sizeof (GLfloat); cmd->data.attr.count = dataSizeInElements; data->current_vertex_data += dataSizeInElements; + return 0; } -static GLES2_RenderCommand * -GLES2_InitSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLint first, const GLsizei count) +static int +GLES2_AddSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLint first, const GLsizei count) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++]; + GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } cmd->cmd = GLES2_RENDERCMD_DRAW; cmd->data.draw.mode = mode; cmd->data.draw.first = first; @@ -1666,7 +1752,7 @@ GLES2_InitSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLin cmd->data.draw.blend = renderer->blendMode; cmd->data.draw.imgsrc = GLES2_IMAGESOURCE_SOLID; cmd->data.draw.texture = NULL; - return cmd; + return 0; } static int @@ -1674,6 +1760,7 @@ GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int cou { GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */ int idx; + int rc; /* Emit the specified vertices as points */ for (idx = 0; idx < count; ++idx) { @@ -1681,10 +1768,14 @@ GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int cou vertices[(idx * 2) + 1] = points[idx].y + 0.5f; } - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); - GLES2_InitSolidDrawCommand(renderer, GL_POINTS, 0, count); + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); SDL_stack_free(vertices); - return 0; + + if (rc == 0) { + rc = GLES2_AddSolidDrawCommand(renderer, GL_POINTS, 0, count); + } + + return rc; } static int @@ -1692,6 +1783,7 @@ GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int coun { GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */ int idx; + int rc; /* Emit a line strip including the specified vertices */ for (idx = 0; idx < count; ++idx) { @@ -1699,8 +1791,10 @@ GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int coun vertices[(idx * 2) + 1] = points[idx].y + 0.5f; } - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); - GLES2_InitSolidDrawCommand(renderer, GL_LINE_STRIP, 0, count); + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); + if (rc == 0) { + rc = GLES2_AddSolidDrawCommand(renderer, GL_LINE_STRIP, 0, count); + } #if 0 /* !!! FIXME: ugh */ /* We need to close the endpoint of the line */ @@ -1711,7 +1805,7 @@ GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int coun #endif SDL_stack_free(vertices); - return 0; + return rc; } static int @@ -1719,9 +1813,10 @@ GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) { GLfloat vertices[8]; int idx; + int rc = 0; /* Emit a line loop for each rectangle */ - for (idx = 0; idx < count; ++idx) { + for (idx = 0; (rc == 0) && (idx < count); ++idx) { const SDL_FRect *rect = &rects[idx]; GLfloat xMin = rect->x; @@ -1738,16 +1833,18 @@ GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) vertices[6] = xMax; vertices[7] = yMax; - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - GLES2_InitSolidDrawCommand(renderer, GL_TRIANGLE_STRIP, 0, 4); + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); + if (rc == 0) { + rc = GLES2_AddSolidDrawCommand(renderer, GL_TRIANGLE_STRIP, 0, 4); + } } return 0; } -static GLES2_RenderCommand * -GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uint8 attrs) +static int +GLES2_AddCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uint8 attrs) { GLES2_RenderCommand *cmd = NULL; GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; @@ -1820,8 +1917,7 @@ GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Ui sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES; break; default: - SDL_SetError("Unsupported texture format"); - return NULL; + return SDL_SetError("Unsupported texture format"); } } else { sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; /* Texture formats match, use the non color mapping shader (even if the formats are not ABGR) */ @@ -1854,14 +1950,16 @@ GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Ui sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES; break; default: - SDL_SetError("Unsupported texture format"); - return NULL; + return SDL_SetError("Unsupported texture format"); } } ((GLES2_TextureData *)texture->driverdata)->last_cmd_generation = data->command_generation; - cmd = &data->render_commands[data->current_render_command++]; + cmd = GLES2_AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } cmd->cmd = GLES2_RENDERCMD_DRAW; cmd->data.draw.mode = GL_TRIANGLE_STRIP; cmd->data.draw.first = 0; @@ -1875,7 +1973,7 @@ GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Ui cmd->data.draw.imgsrc = sourceType; cmd->data.draw.texture = texture; - return cmd; + return 0; } static int @@ -1883,6 +1981,7 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s const SDL_FRect *dstrect) { GLfloat vertices[8]; + int rc; /* Emit the textured quad */ vertices[0] = dstrect->x; @@ -1893,20 +1992,25 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s vertices[5] = (dstrect->y + dstrect->h); vertices[6] = (dstrect->x + dstrect->w); vertices[7] = (dstrect->y + dstrect->h); - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - vertices[0] = srcrect->x / (GLfloat)texture->w; - vertices[1] = srcrect->y / (GLfloat)texture->h; - vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[3] = srcrect->y / (GLfloat)texture->h; - vertices[4] = srcrect->x / (GLfloat)texture->w; - vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); + if (rc == 0) { + vertices[0] = srcrect->x / (GLfloat)texture->w; + vertices[1] = srcrect->y / (GLfloat)texture->h; + vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[3] = srcrect->y / (GLfloat)texture->h; + vertices[4] = srcrect->x / (GLfloat)texture->w; + vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); - GLES2_InitCopyDrawCommand(renderer, texture, 0); - return 0; + if (rc == 0) { + rc = GLES2_AddCopyDrawCommand(renderer, texture, 0); + } + } + + return rc; } static int @@ -1915,50 +2019,60 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect { const float radian_angle = (float)(M_PI * (360.0 - angle) / 180.0); GLfloat vertices[8]; + int rc; vertices[0] = vertices[2] = vertices[4] = vertices[6] = (GLfloat)SDL_sin(radian_angle); /* render expects cos value - 1 (see GLES2_VertexSrc_Default_) */ vertices[1] = vertices[3] = vertices[5] = vertices[7] = (GLfloat)SDL_cos(radian_angle) - 1.0f; - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_ANGLE, vertices, 8); + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_ANGLE, vertices, 8); /* Calculate the center of rotation */ - vertices[0] = vertices[2] = vertices[4] = vertices[6] = (center->x + dstrect->x); - vertices[1] = vertices[3] = vertices[5] = vertices[7] = (center->y + dstrect->y); - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_CENTER, vertices, 8); + if (rc == 0) { + vertices[0] = vertices[2] = vertices[4] = vertices[6] = (center->x + dstrect->x); + vertices[1] = vertices[3] = vertices[5] = vertices[7] = (center->y + dstrect->y); + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_CENTER, vertices, 8); + + if (rc == 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); + if (flip & SDL_FLIP_HORIZONTAL) { + const GLfloat tmp = vertices[0]; + vertices[0] = vertices[4] = vertices[2]; + vertices[2] = vertices[6] = tmp; + } + if (flip & SDL_FLIP_VERTICAL) { + const GLfloat tmp = vertices[1]; + vertices[1] = vertices[3] = vertices[5]; + vertices[5] = vertices[7] = tmp; + } + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - /* 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) { - const GLfloat tmp = vertices[0]; - vertices[0] = vertices[4] = vertices[2]; - vertices[2] = vertices[6] = tmp; + if (rc == 0) { + vertices[0] = srcrect->x / (GLfloat)texture->w; + vertices[1] = srcrect->y / (GLfloat)texture->h; + vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[3] = srcrect->y / (GLfloat)texture->h; + vertices[4] = srcrect->x / (GLfloat)texture->w; + vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; + vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; + rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); + + if (rc == 0) { + GLES2_AddCopyDrawCommand(renderer, texture, (1 << GLES2_ATTRIBUTE_CENTER) | (1 << GLES2_ATTRIBUTE_ANGLE)); + } + } + } } - if (flip & SDL_FLIP_VERTICAL) { - const GLfloat tmp = vertices[1]; - vertices[1] = vertices[3] = vertices[5]; - vertices[5] = vertices[7] = tmp; - } - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - vertices[0] = srcrect->x / (GLfloat)texture->w; - vertices[1] = srcrect->y / (GLfloat)texture->h; - vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[3] = srcrect->y / (GLfloat)texture->h; - vertices[4] = srcrect->x / (GLfloat)texture->w; - vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); - - GLES2_InitCopyDrawCommand(renderer, texture, (1 << GLES2_ATTRIBUTE_CENTER) | (1 << GLES2_ATTRIBUTE_ANGLE)); - return 0; + return rc; } static int @@ -2158,6 +2272,15 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->driverdata = data; renderer->window = window; + data->vertex_data_allocation = 512; + data->vertex_data = (GLfloat *) SDL_malloc(data->vertex_data_allocation * sizeof (GLfloat)); + if (data->vertex_data == NULL) { + GLES2_DestroyRenderer(renderer); + SDL_OutOfMemory(); + goto error; + } +printf("malloc'd %p\n", data->vertex_data); + /* Create an OpenGL ES 2.0 context */ data->context = SDL_GL_CreateContext(window); if (!data->context) { From b06aace2968817f21ee4b833621800475c57a577 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 20 Sep 2018 15:41:57 -0400 Subject: [PATCH 04/43] testsprite2: report average FPS in blocks of five seconds. This makes the reporting more accurate, vs startup inefficiencies and other scheduling burps. --HG-- branch : SDL-ryan-batching-renderer extra : source : 6c2ad028778a96a0e34436ab7de121ecae4da0d8 --- test/testsprite2.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/test/testsprite2.c b/test/testsprite2.c index b0348f85d..f471ff319 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; } From e7e52b63c0e725f1d724f2898e49833411d78470 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 20 Sep 2018 15:46:02 -0400 Subject: [PATCH 05/43] render: Move to a batching system for rendering (work in progress). --HG-- branch : SDL-ryan-batching-renderer extra : source : 6160933718a85a6c45cf63723b404c49579b44f1 --- include/SDL_hints.h | 25 +++ src/render/SDL_render.c | 414 ++++++++++++++++++++++++++++++++----- src/render/SDL_sysrender.h | 86 ++++++-- 3 files changed, 455 insertions(+), 70 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 07a911338..b3c0c154f 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1029,6 +1029,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 */ diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index b1bcc46c1..3d28360fc 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -105,6 +105,258 @@ static const SDL_RenderDriver *render_drivers[] = { static char renderer_magic; static char texture_magic; + +static int +FlushRenderCommands(SDL_Renderer *renderer) +{ + 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; + } + + retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used); + + /* 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++; + 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); +} + +void * +SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset) +{ + const size_t needed = renderer->vertex_data_used + numbytes; + void *retval; + + while (needed > renderer->vertex_data_allocation) { + const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 128; + 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; + } + + retval = ((Uint8 *) renderer->vertex_data) + renderer->vertex_data_used; + if (offset) { + *offset = renderer->vertex_data_used; + } + + renderer->vertex_data_used += 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 +QueueCmdUpdateViewport(SDL_Renderer *renderer) +{ + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } + + cmd->command = SDL_RENDERCMD_SETVIEWPORT; + SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (cmd->data.viewport)); + return FlushRenderCommandsIfNotBatching(renderer); +} + +static int +QueueCmdUpdateClipRect(SDL_Renderer *renderer) +{ + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } + + 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)); + return FlushRenderCommandsIfNotBatching(renderer); +} + +static int +QueueCmdClear(SDL_Renderer *renderer) +{ + SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); + if (cmd == NULL) { + return -1; + } + + cmd->command = SDL_RENDERCMD_CLEAR; + 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 FlushRenderCommandsIfNotBatching(renderer); +} + +static SDL_RenderCommand * +PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype) +{ + SDL_RenderCommand *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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +static SDL_RenderCommand * +PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype) +{ + SDL_RenderCommand *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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + +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); + SDL_assert(renderer->QueueCopyEx != NULL); /* should have caught at higher level. */ + int retval = -1; + 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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} + + static int UpdateLogicalSize(SDL_Renderer *renderer); int @@ -183,7 +435,7 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) renderer->viewport.y = 0; renderer->viewport.w = w; renderer->viewport.h = h; - renderer->UpdateViewport(renderer); + QueueCmdUpdateViewport(renderer); } } @@ -300,12 +552,25 @@ 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->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 +600,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 +634,18 @@ 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 (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 +654,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 +698,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; @@ -758,11 +1042,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 @@ -799,11 +1080,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 @@ -831,11 +1109,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 @@ -940,7 +1215,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); @@ -967,7 +1241,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); } } @@ -1077,6 +1354,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(); @@ -1107,7 +1387,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); @@ -1125,11 +1404,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); } } @@ -1179,8 +1465,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) { @@ -1191,7 +1475,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); } } @@ -1216,6 +1500,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); @@ -1271,10 +1557,10 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) SDL_UnlockMutex(renderer->target_mutex); - if (renderer->UpdateViewport(renderer) < 0) { + if (QueueCmdUpdateViewport(renderer) < 0) { return -1; } - if (renderer->UpdateClipRect(renderer) < 0) { + if (QueueCmdUpdateClipRect(renderer) < 0) { return -1; } @@ -1465,7 +1751,7 @@ SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect) return -1; } } - return renderer->UpdateViewport(renderer); + return QueueCmdUpdateViewport(renderer); } void @@ -1496,7 +1782,7 @@ SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect) renderer->clipping_enabled = SDL_FALSE; SDL_zero(renderer->clip_rect); } - return renderer->UpdateClipRect(renderer); + return QueueCmdUpdateClipRect(renderer); } void @@ -1601,12 +1887,7 @@ int SDL_RenderClear(SDL_Renderer * renderer) { CHECK_RENDERER_MAGIC(renderer, -1); - - /* Don't draw while we're hidden */ - if (renderer->hidden) { - return 0; - } - return renderer->RenderClear(renderer); + return QueueCmdClear(renderer); } int @@ -1625,7 +1906,7 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, { SDL_FRect *frects; int i; - int status; + int status = -1; frects = SDL_stack_alloc(SDL_FRect, count); if (!frects) { @@ -1638,7 +1919,7 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, frects[i].h = renderer->scale.y; } - status = renderer->RenderFillRects(renderer, frects, count); + status = QueueCmdFillRects(renderer, frects, count); SDL_stack_free(frects); @@ -1680,7 +1961,7 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, fpoints[i].y = points[i].y * renderer->scale.y; } - status = renderer->RenderDrawPoints(renderer, fpoints, count); + status = QueueCmdDrawPoints(renderer, fpoints, count); SDL_stack_free(fpoints); @@ -1706,20 +1987,18 @@ RenderDrawLinesWithRects(SDL_Renderer * renderer, SDL_FRect *frect; SDL_FRect *frects; SDL_FPoint fpoints[2]; - int i, nrects; - int status; + int i, nrects = 0; + int status = 0; frects = SDL_stack_alloc(SDL_FRect, count-1); 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; @@ -1727,8 +2006,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; @@ -1741,11 +2020,11 @@ 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); + status += QueueCmdDrawLines(renderer, fpoints, 2); } } - status += renderer->RenderFillRects(renderer, frects, nrects); + status += QueueCmdFillRects(renderer, frects, nrects); SDL_stack_free(frects); @@ -1790,7 +2069,7 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, fpoints[i].y = points[i].y * renderer->scale.y; } - status = renderer->RenderDrawLines(renderer, fpoints, count); + status = QueueCmdDrawLines(renderer, fpoints, count); SDL_stack_free(fpoints); @@ -1904,7 +2183,7 @@ SDL_RenderFillRects(SDL_Renderer * renderer, frects[i].h = rects[i].h * renderer->scale.y; } - status = renderer->RenderFillRects(renderer, frects, count); + status = QueueCmdFillRects(renderer, frects, count); SDL_stack_free(frects); @@ -1960,7 +2239,9 @@ SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, frect.w = real_dstrect.w * renderer->scale.x; frect.h = real_dstrect.h * renderer->scale.y; - return renderer->RenderCopy(renderer, texture, &real_srcrect, &frect); + texture->last_command_generation = renderer->render_command_generation; + + return QueueCmdCopy(renderer, texture, &real_srcrect, &frect); } @@ -1985,7 +2266,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"); } @@ -2032,7 +2313,9 @@ SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, fcenter.x = real_center.x * renderer->scale.x; fcenter.y = 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; + + return QueueCmdCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip); } int @@ -2047,6 +2330,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); } @@ -2077,7 +2362,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; } @@ -2093,7 +2380,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; @@ -2122,10 +2411,31 @@ SDL_DestroyTexture(SDL_Texture * texture) void SDL_DestroyRenderer(SDL_Renderer * renderer) { + SDL_RenderCommand *cmd; + 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); + /* Free existing textures for this renderer */ while (renderer->textures) { SDL_Texture *tex = renderer->textures; (void) tex; @@ -2157,6 +2467,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(); @@ -2172,6 +2483,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); } @@ -2184,6 +2496,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; @@ -2195,6 +2508,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 940bebcc1..32b0aab49 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -75,12 +75,51 @@ 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_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 { + SDL_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 { + Uint8 r, g, b, a; + } color; + } data; + struct SDL_RenderCommand *next; +} SDL_RenderCommand; + + /* Define the SDL renderer structure */ struct SDL_Renderer { @@ -90,12 +129,18 @@ 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 (*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,16 @@ struct SDL_Renderer Uint8 r, g, b, a; /**< Color for drawing operations values */ SDL_BlendMode blendMode; /**< The drawing blend mode */ + SDL_bool batching; + SDL_RenderCommand *render_commands; + SDL_RenderCommand *render_commands_tail; + SDL_RenderCommand *render_commands_pool; + Uint32 render_command_generation; + + void *vertex_data; + size_t vertex_data_used; + size_t vertex_data_allocation; + void *driverdata; }; @@ -209,6 +250,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, size_t *offset); + #endif /* SDL_sysrender_h_ */ /* vi: set ts=4 sw=4 expandtab: */ From 2316f6a784f1d2b54e6509cead6279e511687b49 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 20 Sep 2018 16:36:54 -0400 Subject: [PATCH 06/43] render: first shot at reworking opengl backend for new batching system. --HG-- branch : SDL-ryan-batching-renderer extra : source : 5208dec7e274c9036cdb7ea205682f82f3ac2f7c --- src/render/opengl/SDL_render_gl.c | 1026 +++++++++++++++------------- src/render/opengl/SDL_shaders_gl.h | 1 + 2 files changed, 547 insertions(+), 480 deletions(-) diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index f3e8326ac..8cfd49bd2 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -51,9 +51,8 @@ extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); static const float inv255f = 1.0f / 255.0f; +/* !!! FIXME: delete these predeclarations and just move the functions before their use. */ 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); @@ -69,20 +68,17 @@ 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, +static int GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, 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_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, + const SDL_FRect * rects, int count); +static int GL_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, + const SDL_Rect *srcrect, const SDL_FRect *dstrect); +static int GL_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); +static int GL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); 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); @@ -122,14 +118,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; @@ -157,7 +149,6 @@ typedef struct typedef struct { GLuint texture; - GLenum type; GLfloat texw; GLfloat texh; GLenum format; @@ -284,21 +275,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 +291,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) { @@ -425,7 +383,6 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) goto error; } - renderer->WindowEvent = GL_WindowEvent; renderer->GetOutputSize = GL_GetOutputSize; renderer->SupportsBlendMode = GL_SupportsBlendMode; renderer->CreateTexture = GL_CreateTexture; @@ -434,14 +391,12 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) 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->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; @@ -501,11 +456,13 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) 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); @@ -562,7 +519,10 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) data->framebuffers = NULL; /* Set up parameters for rendering */ - GL_ResetState(renderer); + 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); */ return renderer; @@ -577,17 +537,6 @@ error: 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) { @@ -707,6 +656,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; @@ -770,19 +720,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; @@ -792,17 +739,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__ @@ -816,10 +763,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 @@ -829,17 +776,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; } @@ -850,33 +797,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 || @@ -884,20 +831,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); @@ -908,6 +855,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); @@ -915,11 +863,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) { @@ -928,22 +876,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); } @@ -953,12 +901,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); } @@ -971,30 +919,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); } @@ -1048,7 +997,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) { @@ -1057,337 +1006,65 @@ 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_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), &cmd->data.draw.first); + size_t i; - if (SDL_CurrentContext != data->context) { - /* We'll update the viewport after we rebind the context */ - return 0; + if (!verts) { + return -1; } - 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); -} - -static int -GL_UpdateClipRect(SDL_Renderer * renderer) -{ - 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; - - 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); + 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; } static int -GL_RenderDrawPoints(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; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), &cmd->data.draw.first); + size_t 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); + if (!verts) { + return -1; } - data->glEnd(); - return 0; -} - -static int -GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - int count) -{ - GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - int i; - - GL_SetDrawingState(renderer); - - 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(); - } - 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); - } - 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); + *(verts++) = rect->x; + *(verts++) = rect->y; + *(verts++) = rect->x + rect->w; + *(verts++) = rect->y + rect->h; } - 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), &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; @@ -1402,36 +1079,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), &cmd->data.draw.first); - GL_ActivateRenderer(renderer); - - if (GL_SetupCopy(renderer, texture) < 0) { + if (!verts) { return -1; } @@ -1465,24 +1136,415 @@ 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); + 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; +} - 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(); +static void +SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_Shader shader, + Uint32 *current_color, SDL_BlendMode *current_blend, GL_Shader *current_shader, + SDL_bool *current_texturing) +{ + 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 Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + const SDL_BlendMode blend = cmd->data.draw.blend; - data->glDisable(texturedata->type); + if (color != *current_color) { + data->glColor4f((GLfloat) r * inv255f, (GLfloat) g * inv255f, + (GLfloat) b * inv255f, (GLfloat) a * inv255f); + *current_color = color; + } + + if (blend != *current_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))); + } + *current_blend = blend; + } + + if (data->shaders && (shader != *current_shader)) { + GL_SelectShader(data->shaders, shader); + *current_shader = shader; + } + + if ((cmd->data.draw.texture != NULL) != *current_texturing) { + if (cmd->data.draw.texture == NULL) { + data->glDisable(data->textype); + *current_texturing = SDL_FALSE; + } else { + data->glEnable(data->textype); + *current_texturing = SDL_FALSE; + } + } +} + +static void +SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd, + Uint32 *current_color, SDL_BlendMode *current_blend, GL_Shader *current_shader, + SDL_bool *current_texturing, SDL_Texture **current_texture) +{ + 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, current_color, current_blend, current_shader, current_texturing); + + if (texture != *current_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); + + *current_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; + SDL_bool clipping_enabled = renderer->clipping_enabled; + SDL_Rect viewport; + SDL_Texture *bound_texture = NULL; + SDL_BlendMode blend = SDL_BLENDMODE_INVALID; + GL_Shader shader = SHADER_INVALID; + int drawablew = 0, drawableh = 0; + SDL_bool cliprect_enabled = SDL_FALSE; + SDL_Rect cliprect; + const SDL_bool istarget = renderer->target != NULL; + Uint32 clear_color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b); + Uint32 draw_color = clear_color; + SDL_bool texturing = SDL_FALSE; + size_t i; + + /* !!! FIXME: if we could guarantee the app isn't messing with the GL, too, we wouldn't + !!! FIXME: have to default a bunch of this state at the start. */ + + if (GL_ActivateRenderer(renderer) < 0) { + return -1; + } + + if (!istarget) { + SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + } + + data->glClearColor((GLfloat) renderer->r * inv255f, + (GLfloat) renderer->g * inv255f, + (GLfloat) renderer->b * inv255f, + (GLfloat) renderer->a * inv255f); + + data->glColor4f((GLfloat) renderer->r * inv255f, + (GLfloat) renderer->g * inv255f, + (GLfloat) renderer->b * inv255f, + (GLfloat) renderer->a * inv255f); + + SDL_memcpy(&viewport, &renderer->viewport, sizeof (viewport)); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport.x, + istarget ? viewport.y : (drawableh - viewport.y - viewport.h), + viewport.w, viewport.h); + if (viewport.w && viewport.h) { + data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w, + (GLdouble) istarget ? 0 : renderer->viewport.h, + (GLdouble) istarget ? renderer->viewport.h : 0, + 0.0, 1.0); + } + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + + SDL_memcpy(&cliprect, &renderer->clip_rect, sizeof (cliprect)); + cliprect_enabled = renderer->clipping_enabled; + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); + } + + data->glDisable(data->textype); + + data->glScissor(viewport.x + cliprect.x, + istarget ? viewport.y + cliprect.y : drawableh - viewport.y - cliprect.y - cliprect.h, + cliprect.w, cliprect.h); + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETVIEWPORT: { + if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) { + SDL_memcpy(&viewport, &cmd->data.viewport, sizeof (SDL_Rect)); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport.x, + istarget ? viewport.y : (drawableh - viewport.y - viewport.h), + viewport.w, viewport.h); + if (viewport.w && viewport.h) { + data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w, + (GLdouble) istarget ? 0 : renderer->viewport.h, + (GLdouble) istarget ? renderer->viewport.h : 0, + 0.0, 1.0); + } + data->glMatrixMode(GL_MODELVIEW); + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + const SDL_bool changed = (SDL_memcmp(&cliprect, rect, sizeof (SDL_Rect)) != 0); + if (cliprect_enabled != cmd->data.cliprect.enabled) { + cliprect_enabled = cmd->data.cliprect.enabled; + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); + } + } + + if (cliprect_enabled && changed) { + SDL_memcpy(&cliprect, rect, sizeof (SDL_Rect)); + data->glScissor(viewport.x + rect->x, + istarget ? viewport.y + rect->y : drawableh - viewport.y - rect->y - rect->h, + rect->w, rect->h); + } + 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 != clear_color) { + data->glClearColor((GLfloat) r * inv255f, (GLfloat) g * inv255f, + (GLfloat) b * inv255f, (GLfloat) a * inv255f); + clear_color = color; + } + + if (clipping_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } + + data->glClear(GL_COLOR_BUFFER_BIT); + + if (clipping_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } + 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, &draw_color, &blend, &shader, &texturing); + 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); + size_t count = cmd->data.draw.count; + SetDrawState(data, cmd, SHADER_SOLID, &draw_color, &blend, &shader, &texturing); + if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) { + --count; /* GL_LINE_LOOP takes care of the final segment */ + data->glBegin(GL_LINE_LOOP); + for (i = 0; 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, &draw_color, &blend, &shader, &texturing); + 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, &draw_color, &blend, &shader, &texturing, &bound_texture); + 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, &draw_color, &blend, &shader, &texturing, &bound_texture); + + /* 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); } @@ -1631,19 +1693,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; @@ -1656,19 +1720,21 @@ 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; } diff --git a/src/render/opengl/SDL_shaders_gl.h b/src/render/opengl/SDL_shaders_gl.h index 9805c599c..095abb8e2 100644 --- a/src/render/opengl/SDL_shaders_gl.h +++ b/src/render/opengl/SDL_shaders_gl.h @@ -23,6 +23,7 @@ /* OpenGL shader implementation */ typedef enum { + SHADER_INVALID = -1, SHADER_NONE, SHADER_SOLID, SHADER_RGB, From c5dd072e0124172835725bb1c4cf93e92be4b0e3 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 20 Sep 2018 16:40:04 -0400 Subject: [PATCH 07/43] render: First shot at moving metal backend over to new batching system. --HG-- branch : SDL-ryan-batching-renderer extra : source : 5e5ee5e583af6e120a84df5b2b48118bbadd5bb4 --- src/render/metal/SDL_render_metal.m | 684 ++++++++++++++++------------ 1 file changed, 395 insertions(+), 289 deletions(-) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 10c585b20..f3225c86a 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -64,20 +64,16 @@ 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_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, + int count); +static int METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, + int count); +static int METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect); +static int 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); +static int METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); 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); @@ -113,6 +109,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); @@ -588,6 +585,9 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags) float clearverts[6] = {0.0f, 0.0f, 0.0f, 2.0f, 2.0f, 0.0f}; id mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared]; + #if !__has_feature(objc_arc) + [mtlbufconstantstaging autorelease]; + #endif mtlbufconstantstaging.label = @"SDL constant staging data"; id mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate]; @@ -621,14 +621,12 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags) 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->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; @@ -698,7 +696,7 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags) }} 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 +723,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; @@ -742,10 +740,6 @@ 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); } } @@ -1024,126 +1018,6 @@ METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return 0; }} -static int -METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h) -{ @autoreleasepool { - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - float projection[4][4]; - - if (!w || !h) { - return 0; - } - - /* 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); - } - 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]; - } - } - return 0; -}} - -static int -METAL_RenderClear(SDL_Renderer * renderer) -{ @autoreleasepool { - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - - /* 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 }; - - 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; - - // 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); - } - - return 0; -}} // normalize a value from 0.0f to len into 0.0f to 1.0f. static inline float @@ -1153,145 +1027,113 @@ normtex(const float _val, const float len) } static int -DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count, - const MTLPrimitiveType primtype) -{ @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - +METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) +{ 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]; - - [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]; - + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first); + if (!verts) { + return -1; + } + cmd->data.draw.count = count; + SDL_memcpy(verts, points, vertlen); 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_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip); -} + // !!! FIXME: use an index buffer + const size_t vertlen = (sizeof (float) * 8) * count; + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first); + if (!verts) { + return -1; + } -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 }; - - [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]; + cmd->data.draw.count = count; for (int i = 0; i < count; i++, rects++) { - if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue; + 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; + } + } - 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 - }; - - [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0]; - [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + if (cmd->data.draw.count == 0) { + cmd->command = SDL_RENDERCMD_NO_OP; // nothing to do, just skip this one later. } return 0; -}} - -static void -METAL_SetupRenderCopy(METAL_RenderData *data, SDL_Texture *texture, METAL_TextureData *texturedata) -{ - 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; - } - - [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]; - - [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]; - } } static int -METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) -{ @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; +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, &cmd->data.draw.first); + if (!verts) { + return -1; + } - METAL_SetupRenderCopy(data, texture, texturedata); + cmd->data.draw.count = 1; - 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 - }; + *(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; - 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 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]; + *(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_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) -{ @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; +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) +{ 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]; + const float rads = (float)(M_PI * (float) angle / 180.0f); + const float c = cosf(rads), s = sinf(rads); float minu, maxu, minv, maxv; + // !!! FIXME: use an index buffer + const size_t vertlen = (sizeof (float) * 32); + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first); + if (!verts) { + return -1; + } - METAL_SetupRenderCopy(data, texture, texturedata); + cmd->data.draw.count = 1; - minu = normtex(srcrect->x, texw); - maxu = normtex(srcrect->x + srcrect->w, texw); - minv = normtex(srcrect->y, texh); - maxv = normtex(srcrect->y + srcrect->h, texh); + minu = normtex(srcquad->x, texw); + maxu = normtex(srcquad->x + srcquad->w, texw); + minv = normtex(srcquad->y, texh); + maxv = normtex(srcquad->y + srcquad->h, texh); if (flip & SDL_FLIP_HORIZONTAL) { float tmp = maxu; @@ -1304,42 +1146,300 @@ METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, minv = tmp; } - const float uv[] = { - minu, maxv, - minu, minv, - maxu, maxv, - maxu, minv - }; + // 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; - 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 - }; + // texcoords + *(verts++) = minu; + *(verts++) = maxv; + *(verts++) = minu; + *(verts++) = minv; + *(verts++) = maxu; + *(verts++) = maxv; + *(verts++) = maxu; + *(verts++) = minv; - { - float rads = (float)(M_PI * (float) angle / 180.0f); - float c = cosf(rads), s = sinf(rads); - SDL_memset(transform, 0, sizeof(transform)); + // 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; - transform[10] = transform[15] = 1.0f; + // translation + verts[12] = dstrect->x + center->x; + verts[13] = dstrect->y + center->y; - /* Rotation */ - transform[0] = c; - transform[1] = s; - transform[4] = -s; - transform[5] = c; + return 0; +} - /* Translation */ - transform[12] = dstrect->x + center->x; - transform[13] = dstrect->y + center->y; + +typedef struct +{ + id pipeline; + size_t constants_offset; + SDL_Texture *texture; + Uint32 color; + SDL_bool color_dirty; + SDL_bool cliprect_dirty; + SDL_bool cliprect_enabled; + SDL_bool viewport_dirty; + SDL_Rect viewport; + SDL_Rect cliprect; +} METAL_DrawStateCache; + +static void +SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_MetalFragmentFunction shader, + const size_t constants_offset, id mtlbufvertex, METAL_DrawStateCache *statecache) +{ + METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; + 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 Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); + const SDL_BlendMode blend = cmd->data.draw.blend; + id 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]; + + float projection[4][4]; /* Prepare an orthographic projection */ + SDL_memset(projection, '\0', sizeof (projection)); + + if (statecache->viewport.w && statecache->viewport.h) { + projection[0][0] = 2.0f / statecache->viewport.w; + projection[1][1] = -2.0f / statecache->viewport.h; + projection[3][0] = -1.0f; + projection[3][1] = 1.0f; + projection[3][3] = 1.0f; + } + + // !!! FIXME: This should be in a buffer... + [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2]; + statecache->viewport_dirty = SDL_FALSE; } - [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]; + 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; + } + + if (statecache->color_dirty || (color != statecache->color)) { + const float colorf[4] = { ((float)r) / 255.0f, ((float)g) / 255.0f, ((float)b) / 255.0f, ((float)a) / 255.0f }; + [data.mtlcmdencoder setFragmentBytes:colorf length:sizeof(colorf) atIndex:0]; + statecache->color_dirty = SDL_FALSE; + statecache->color = color; + } + + newpipeline = ChoosePipelineState(data, data.activepipelines, shader, blend); + if (newpipeline != statecache->pipeline) { + [data.mtlcmdencoder setRenderPipelineState:newpipeline]; + statecache->pipeline = newpipeline; + } + + 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:cmd->data.draw.first atIndex:0]; // position +} + +static void +SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const size_t constants_offset, + id 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; + + SetDrawState(renderer, cmd, texturedata.fragmentFunction, constants_offset, mtlbufvertex, statecache); + + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(8*sizeof (float)) atIndex:1]; // texcoords + + 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]; + } + + [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_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ @autoreleasepool { + METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; + METAL_DrawStateCache statecache; + + statecache.pipeline = nil; + statecache.constants_offset = CONSTANTS_OFFSET_INVALID; + statecache.texture = NULL; + statecache.color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b); + statecache.color_dirty = SDL_TRUE; + statecache.cliprect_dirty = SDL_TRUE; + statecache.viewport_dirty = SDL_TRUE; // TRUE so we set ortho matrix + statecache.cliprect_enabled = renderer->clipping_enabled; + SDL_memcpy(&statecache.viewport, &renderer->viewport, sizeof (statecache.viewport)); + SDL_memcpy(&statecache.cliprect, &renderer->clip_rect, sizeof (statecache.cliprect)); + + // !!! FIXME: have a ring of pre-made MTLBuffers we cycle through? How expensive is creation? + id 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. + id mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModePrivate]; + #if !__has_feature(objc_arc) + [mtlbufvertex autorelease]; + #endif + mtlbufvertex.label = @"SDL vertex data"; + id cmdbuffer = [data.mtlcmdqueue commandBuffer]; + id blitcmd = [cmdbuffer blitCommandEncoder]; + [blitcmd copyFromBuffer:mtlbufvertexstaging sourceOffset:0 toBuffer:mtlbufvertex destinationOffset:0 size:vertsize]; + [blitcmd endEncoding]; + [cmdbuffer commit]; + + // 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; + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETVIEWPORT: { + if (SDL_memcmp(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport)) != 0) { + SDL_memcpy(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport)); + statecache.viewport_dirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + if ((statecache.cliprect_enabled != cmd->data.cliprect.enabled) || + (SDL_memcmp(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)) != 0)) { + SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)); + statecache.cliprect_enabled = cmd->data.cliprect.enabled; + statecache.cliprect_dirty = SDL_TRUE; + } + break; + } + + 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]; + data.mtlcmdencoder = nil; + data.mtlcmdbuffer = nil; + } + + 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); + + // 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; + + // get new command encoder, set up with an initial clear operation. + METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear, &color); + break; + } + + 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; + } + + 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; + } + + case SDL_RENDERCMD_COPY: { + SetCopyState(renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache); + [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + break; + } + + case SDL_RENDERCMD_COPY_EX: { + SetCopyState(renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache); + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(16*sizeof (float)) atIndex:3]; // transform + [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + cmd = cmd->next; + } return 0; }} @@ -1348,10 +1448,16 @@ static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, void * pixels, int pitch) { @autoreleasepool { - METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad); - - // !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; + METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL); + + // 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 = data.mtlpassdesc.colorAttachments[0].texture; MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h); @@ -1422,7 +1528,7 @@ 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; }} From b288df490b1db85a95fdb0c1b4d5082a58aa2e96 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 23 Sep 2018 23:20:40 -0400 Subject: [PATCH 08/43] render: A bunch of high-level improvements. - high-level filters out duplicate render commands from the queue so backends don't have to. - Setting draw color is now a render command, so backends can put color information into the vertex buffer to upload with everything else instead of setting it with slower dynamic data later. - backends can request that they always batch, even for legacy programs, since the lowlevel API can deal with it (Metal, and eventually Vulkan and such...) - high-level makes sure the queue has at least one setdrawcolor and setviewport command before any draw calls, so the backends don't ever have to manage cases where this hasn't been explicitly set yet. - backends allocating vertex buffer space can specify alignment, and the high-level will keep track of gaps in the buffer between the last used positions and the aligned data that can be used for later allocations (Metal and such need to specify some constant data on 256 byte boundaries, but we don't want to waste all that space we had to skip to meet alignment requirements). --HG-- branch : SDL-ryan-batching-renderer extra : source : a3d62fbb80e46d9d544fa52944c3bac5375b5489 --- src/render/SDL_render.c | 279 ++++++++++++++++++++++++++++++------- src/render/SDL_sysrender.h | 28 +++- 2 files changed, 254 insertions(+), 53 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 3d28360fc..7d0099488 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -109,7 +109,10 @@ static char texture_magic; 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! */ @@ -119,6 +122,14 @@ FlushRenderCommands(SDL_Renderer *renderer) 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; @@ -128,6 +139,9 @@ FlushRenderCommands(SDL_Renderer *renderer) } 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; } @@ -148,14 +162,77 @@ FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer) return renderer->batching ? 0 : FlushRenderCommands(renderer); } -void * -SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset) +static SDL_AllocVertGap * +AllocateVertexGap(SDL_Renderer *renderer) { - const size_t needed = renderer->vertex_data_used + numbytes; + 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 : 128; + 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) { @@ -166,12 +243,26 @@ SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t renderer->vertex_data_allocation = newsize; } - retval = ((Uint8 *) renderer->vertex_data) + renderer->vertex_data_used; + 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 = renderer->vertex_data_used; + *offset = aligned; } - renderer->vertex_data_used += numbytes; + 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; } @@ -205,30 +296,77 @@ AllocateRenderCommand(SDL_Renderer *renderer) } static int -QueueCmdUpdateViewport(SDL_Renderer *renderer) +QueueCmdSetViewport(SDL_Renderer *renderer) { - SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); - if (cmd == NULL) { - return -1; + 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; + } + } } - - cmd->command = SDL_RENDERCMD_SETVIEWPORT; - SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (cmd->data.viewport)); - return FlushRenderCommandsIfNotBatching(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } static int -QueueCmdUpdateClipRect(SDL_Renderer *renderer) +QueueCmdSetClipRect(SDL_Renderer *renderer) { - SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); - if (cmd == NULL) { - return -1; + 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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +} - 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)); - return FlushRenderCommandsIfNotBatching(renderer); +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 < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } static int @@ -240,6 +378,7 @@ QueueCmdClear(SDL_Renderer *renderer) } 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; @@ -247,20 +386,39 @@ QueueCmdClear(SDL_Renderer *renderer) return FlushRenderCommandsIfNotBatching(renderer); } +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) { - SDL_RenderCommand *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. */ + 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; } @@ -310,17 +468,20 @@ QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int cou static SDL_RenderCommand * PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype) { - SDL_RenderCommand *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; + 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; } @@ -435,7 +596,7 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) renderer->viewport.y = 0; renderer->viewport.w = w; renderer->viewport.h = h; - QueueCmdUpdateViewport(renderer); + QueueCmdSetViewport(renderer); } } @@ -557,6 +718,8 @@ 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); @@ -641,7 +804,9 @@ SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags) VerifyDrawQueueFunctions(renderer); /* let app/user override batching decisions. */ - if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) { + if (renderer->always_batch) { + batching = SDL_TRUE; + } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) { batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE); } @@ -1557,10 +1722,10 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) SDL_UnlockMutex(renderer->target_mutex); - if (QueueCmdUpdateViewport(renderer) < 0) { + if (QueueCmdSetViewport(renderer) < 0) { return -1; } - if (QueueCmdUpdateClipRect(renderer) < 0) { + if (QueueCmdSetClipRect(renderer) < 0) { return -1; } @@ -1751,7 +1916,7 @@ SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect) return -1; } } - return QueueCmdUpdateViewport(renderer); + return QueueCmdSetViewport(renderer); } void @@ -1782,7 +1947,7 @@ SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect) renderer->clipping_enabled = SDL_FALSE; SDL_zero(renderer->clip_rect); } - return QueueCmdUpdateClipRect(renderer); + return QueueCmdSetClipRect(renderer); } void @@ -2412,6 +2577,8 @@ void SDL_DestroyRenderer(SDL_Renderer * renderer) { SDL_RenderCommand *cmd; + SDL_AllocVertGap *gap; + SDL_AllocVertGap *nextgap; CHECK_RENDERER_MAGIC(renderer, ); @@ -2436,6 +2603,16 @@ SDL_DestroyRenderer(SDL_Renderer * renderer) 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; diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 32b0aab49..240ce2de1 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -88,6 +88,7 @@ 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, @@ -100,7 +101,10 @@ typedef struct SDL_RenderCommand { SDL_RenderCommandType command; union { - SDL_Rect viewport; + struct { + size_t first; + SDL_Rect rect; + } viewport; struct { SDL_bool enabled; SDL_Rect rect; @@ -113,12 +117,20 @@ typedef struct SDL_RenderCommand 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 @@ -129,6 +141,8 @@ 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 (*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, @@ -209,15 +223,25 @@ 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; }; @@ -253,7 +277,7 @@ 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, size_t *offset); +extern void *SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset); #endif /* SDL_sysrender_h_ */ From 6fdeb95d0c07a3f8c87103494dce12ca47af1f33 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 23 Sep 2018 23:22:56 -0400 Subject: [PATCH 09/43] render: Update Metal and GL backends to use new high-level features, etc. Now nothing is uploaded as dynamic data with Metal's setVertexBytes, etc; it's all in the one big vertex buffer, now. --HG-- branch : SDL-ryan-batching-renderer extra : source : 67011faccbdbdff56162a9eb25a2bc964c064a03 --- src/render/metal/SDL_render_metal.m | 144 ++++++++++++++++++---------- src/render/opengl/SDL_render_gl.c | 26 +++-- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index f3225c86a..493c7c5e1 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -64,6 +64,8 @@ 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_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd); +static int METAL_QueueSetDrawColor(SDL_Renderer * renderer, SDL_RenderCommand *cmd); static int METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count); static int METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, @@ -621,6 +623,8 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags) 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; @@ -637,6 +641,8 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags) 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; @@ -740,6 +746,11 @@ METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load, } data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat); + + // 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]; } } @@ -1026,11 +1037,51 @@ normtex(const float _val, const float len) return _val / len; } +static int +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; + } + + 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); + + return 0; +} + +static int +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_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, &cmd->data.draw.first); + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); if (!verts) { return -1; } @@ -1044,7 +1095,7 @@ METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_ { // !!! FIXME: use an index buffer const size_t vertlen = (sizeof (float) * 8) * count; - float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first); + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); if (!verts) { return -1; } @@ -1082,7 +1133,7 @@ METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * t 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, &cmd->data.draw.first); + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); if (!verts) { return -1; } @@ -1123,7 +1174,7 @@ METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * float minu, maxu, minv, maxv; // !!! FIXME: use an index buffer const size_t vertlen = (sizeof (float) * 32); - float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first); + float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); if (!verts) { return -1; } @@ -1185,16 +1236,21 @@ METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * typedef struct { + #if __has_feature(objc_arc) + __unsafe_unretained id pipeline; + #else id pipeline; + #endif size_t constants_offset; SDL_Texture *texture; - Uint32 color; - SDL_bool color_dirty; SDL_bool cliprect_dirty; SDL_bool cliprect_enabled; + SDL_Rect cliprect; SDL_bool viewport_dirty; SDL_Rect viewport; - SDL_Rect cliprect; + size_t projection_offset; + SDL_bool color_dirty; + size_t color_offset; } METAL_DrawStateCache; static void @@ -1202,12 +1258,8 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met const size_t constants_offset, id mtlbufvertex, METAL_DrawStateCache *statecache) { METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; - 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 Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); const SDL_BlendMode blend = cmd->data.draw.blend; + size_t first = cmd->data.draw.first; id newpipeline; METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL); @@ -1221,20 +1273,7 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met viewport.znear = 0.0; viewport.zfar = 1.0; [data.mtlcmdencoder setViewport:viewport]; - - float projection[4][4]; /* Prepare an orthographic projection */ - SDL_memset(projection, '\0', sizeof (projection)); - - if (statecache->viewport.w && statecache->viewport.h) { - projection[0][0] = 2.0f / statecache->viewport.w; - projection[1][1] = -2.0f / statecache->viewport.h; - projection[3][0] = -1.0f; - projection[3][1] = 1.0f; - projection[3][3] = 1.0f; - } - - // !!! FIXME: This should be in a buffer... - [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2]; + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:statecache->projection_offset atIndex:2]; // projection statecache->viewport_dirty = SDL_FALSE; } @@ -1258,11 +1297,9 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met statecache->cliprect_dirty = SDL_FALSE; } - if (statecache->color_dirty || (color != statecache->color)) { - const float colorf[4] = { ((float)r) / 255.0f, ((float)g) / 255.0f, ((float)b) / 255.0f, ((float)a) / 255.0f }; - [data.mtlcmdencoder setFragmentBytes:colorf length:sizeof(colorf) atIndex:0]; + if (statecache->color_dirty) { + [data.mtlcmdencoder setFragmentBuffer:mtlbufvertex offset:statecache->color_offset atIndex:0]; statecache->color_dirty = SDL_FALSE; - statecache->color = color; } newpipeline = ChoosePipelineState(data, data.activepipelines, shader, blend); @@ -1278,7 +1315,7 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met statecache->constants_offset = constants_offset; } - [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first atIndex:0]; // position + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:first atIndex:0]; // position } static void @@ -1320,13 +1357,11 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver statecache.pipeline = nil; statecache.constants_offset = CONSTANTS_OFFSET_INVALID; statecache.texture = NULL; - statecache.color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b); statecache.color_dirty = SDL_TRUE; statecache.cliprect_dirty = SDL_TRUE; - statecache.viewport_dirty = SDL_TRUE; // TRUE so we set ortho matrix - statecache.cliprect_enabled = renderer->clipping_enabled; - SDL_memcpy(&statecache.viewport, &renderer->viewport, sizeof (statecache.viewport)); - SDL_memcpy(&statecache.cliprect, &renderer->clip_rect, sizeof (statecache.cliprect)); + 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? id mtlbufvertexstaging = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared]; @@ -1357,20 +1392,22 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver while (cmd) { switch (cmd->command) { case SDL_RENDERCMD_SETVIEWPORT: { - if (SDL_memcmp(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport)) != 0) { - SDL_memcpy(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport)); - statecache.viewport_dirty = SDL_TRUE; - } + SDL_memcpy(&statecache.viewport, &cmd->data.viewport.rect, sizeof (statecache.viewport)); + statecache.projection_offset = cmd->data.viewport.first; + statecache.viewport_dirty = SDL_TRUE; break; } case SDL_RENDERCMD_SETCLIPRECT: { - if ((statecache.cliprect_enabled != cmd->data.cliprect.enabled) || - (SDL_memcmp(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)) != 0)) { - SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)); - statecache.cliprect_enabled = cmd->data.cliprect.enabled; - statecache.cliprect_dirty = SDL_TRUE; - } + SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)); + statecache.cliprect_enabled = cmd->data.cliprect.enabled; + statecache.cliprect_dirty = SDL_TRUE; + break; + } + + case SDL_RENDERCMD_SETDRAWCOLOR: { + statecache.color_offset = cmd->data.color.first; + statecache.color_dirty = SDL_TRUE; break; } @@ -1380,16 +1417,13 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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; } - 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); - // force all this state to be reconfigured on next command buffer. statecache.pipeline = nil; statecache.constants_offset = CONSTANTS_OFFSET_INVALID; @@ -1398,6 +1432,12 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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; diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 8cfd49bd2..af0856075 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -68,6 +68,7 @@ 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_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd); static int GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count); static int GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, @@ -391,6 +392,8 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) 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; @@ -1009,10 +1012,16 @@ GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) /* !!! 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_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) +{ + return 0; /* nothing to do in this backend. */ +} + static int GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), &cmd->data.draw.first); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first); size_t i; if (!verts) { @@ -1031,7 +1040,7 @@ GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FP static int GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), &cmd->data.draw.first); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), 0, &cmd->data.draw.first); size_t i; if (!verts) { @@ -1057,7 +1066,7 @@ GL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * text 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), &cmd->data.draw.first); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 8 * sizeof (GLfloat), 0, &cmd->data.draw.first); if (!verts) { return -1; @@ -1100,7 +1109,7 @@ GL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * te GLfloat minx, miny, maxx, maxy; GLfloat centerx, centery; GLfloat minu, maxu, minv, maxv; - GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 11 * sizeof (GLfloat), &cmd->data.draw.first); + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 11 * sizeof (GLfloat), 0, &cmd->data.draw.first); if (!verts) { return -1; @@ -1338,9 +1347,14 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic while (cmd) { switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + // !!! FIXME: use this. + break; + } + case SDL_RENDERCMD_SETVIEWPORT: { - if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) { - SDL_memcpy(&viewport, &cmd->data.viewport, sizeof (SDL_Rect)); + if (SDL_memcmp(&cmd->data.viewport.rect, &viewport, sizeof (viewport)) != 0) { + SDL_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport)); data->glMatrixMode(GL_PROJECTION); data->glLoadIdentity(); data->glViewport(viewport.x, From b8773215d92a7644bd117ebf6a5e166cf9c82896 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 24 Sep 2018 02:07:35 -0400 Subject: [PATCH 10/43] render: Add command queue debug logging. --HG-- branch : SDL-ryan-batching-renderer extra : source : 7b514b380529e2e8d77cd63eda3fb81cc96d42a9 --- src/render/SDL_render.c | 92 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 7d0099488..5ead38617 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -105,6 +105,96 @@ 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) @@ -120,6 +210,8 @@ FlushRenderCommands(SDL_Renderer *renderer) return 0; } + DebugLogRenderCommands(renderer->render_commands); + retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used); while (gap) { From 7b78f799ae719783dfbe31d735b3af4808a1a2db Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 24 Sep 2018 02:08:34 -0400 Subject: [PATCH 11/43] render: Make opengl backend take advantage of new high-level features. --HG-- branch : SDL-ryan-batching-renderer extra : source : f4665fc62a607285c0c2352dadeb3b290c29d097 --- src/render/opengl/SDL_render_gl.c | 137 +++++++++--------------------- 1 file changed, 38 insertions(+), 99 deletions(-) diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index af0856075..765f49c7d 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -1161,23 +1161,12 @@ GL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * te } static void -SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_Shader shader, - Uint32 *current_color, SDL_BlendMode *current_blend, GL_Shader *current_shader, - SDL_bool *current_texturing) +SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, + const GL_Shader shader, SDL_BlendMode *current_blend, + GL_Shader *current_shader, SDL_bool *current_texturing) { - 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 Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b); const SDL_BlendMode blend = cmd->data.draw.blend; - if (color != *current_color) { - data->glColor4f((GLfloat) r * inv255f, (GLfloat) g * inv255f, - (GLfloat) b * inv255f, (GLfloat) a * inv255f); - *current_color = color; - } - if (blend != *current_blend) { if (blend == SDL_BLENDMODE_NONE) { data->glDisable(GL_BLEND); @@ -1210,7 +1199,7 @@ SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_S static void SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd, - Uint32 *current_color, SDL_BlendMode *current_blend, GL_Shader *current_shader, + SDL_BlendMode *current_blend, GL_Shader *current_shader, SDL_bool *current_texturing, SDL_Texture **current_texture) { SDL_Texture *texture = cmd->data.draw.texture; @@ -1254,7 +1243,7 @@ SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd, } } - SetDrawState(data, cmd, shader, current_color, current_blend, current_shader, current_texturing); + SetDrawState(data, cmd, shader, current_blend, current_shader, current_texturing); if (texture != *current_texture) { const GLenum textype = data->textype; @@ -1281,23 +1270,16 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic { /* !!! FIXME: it'd be nice to use a vertex buffer instead of immediate mode... */ GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - SDL_bool clipping_enabled = renderer->clipping_enabled; SDL_Rect viewport; SDL_Texture *bound_texture = NULL; SDL_BlendMode blend = SDL_BLENDMODE_INVALID; GL_Shader shader = SHADER_INVALID; int drawablew = 0, drawableh = 0; SDL_bool cliprect_enabled = SDL_FALSE; - SDL_Rect cliprect; const SDL_bool istarget = renderer->target != NULL; - Uint32 clear_color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b); - Uint32 draw_color = clear_color; SDL_bool texturing = SDL_FALSE; size_t i; - /* !!! FIXME: if we could guarantee the app isn't messing with the GL, too, we wouldn't - !!! FIXME: have to default a bunch of this state at the start. */ - if (GL_ActivateRenderer(renderer) < 0) { return -1; } @@ -1306,85 +1288,47 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); } - data->glClearColor((GLfloat) renderer->r * inv255f, - (GLfloat) renderer->g * inv255f, - (GLfloat) renderer->b * inv255f, - (GLfloat) renderer->a * inv255f); - - data->glColor4f((GLfloat) renderer->r * inv255f, - (GLfloat) renderer->g * inv255f, - (GLfloat) renderer->b * inv255f, - (GLfloat) renderer->a * inv255f); - - SDL_memcpy(&viewport, &renderer->viewport, sizeof (viewport)); - data->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - data->glViewport(viewport.x, - istarget ? viewport.y : (drawableh - viewport.y - viewport.h), - viewport.w, viewport.h); - if (viewport.w && viewport.h) { - data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w, - (GLdouble) istarget ? 0 : renderer->viewport.h, - (GLdouble) istarget ? renderer->viewport.h : 0, - 0.0, 1.0); - } + data->glDisable(data->textype); data->glMatrixMode(GL_MODELVIEW); data->glLoadIdentity(); - SDL_memcpy(&cliprect, &renderer->clip_rect, sizeof (cliprect)); - cliprect_enabled = renderer->clipping_enabled; - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } else { - data->glDisable(GL_SCISSOR_TEST); - } - - data->glDisable(data->textype); - - data->glScissor(viewport.x + cliprect.x, - istarget ? viewport.y + cliprect.y : drawableh - viewport.y - cliprect.y - cliprect.h, - cliprect.w, cliprect.h); - while (cmd) { switch (cmd->command) { case SDL_RENDERCMD_SETDRAWCOLOR: { - // !!! FIXME: use this. + data->glColor4f((GLfloat) cmd->data.color.r * inv255f, + (GLfloat) cmd->data.color.g * inv255f, + (GLfloat) cmd->data.color.b * inv255f, + (GLfloat) cmd->data.color.a * inv255f); break; } case SDL_RENDERCMD_SETVIEWPORT: { - if (SDL_memcmp(&cmd->data.viewport.rect, &viewport, sizeof (viewport)) != 0) { - SDL_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport)); - data->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - data->glViewport(viewport.x, + SDL_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport)); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport.x, istarget ? viewport.y : (drawableh - viewport.y - viewport.h), viewport.w, viewport.h); - if (viewport.w && viewport.h) { - data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w, - (GLdouble) istarget ? 0 : renderer->viewport.h, - (GLdouble) istarget ? renderer->viewport.h : 0, - 0.0, 1.0); - } - data->glMatrixMode(GL_MODELVIEW); + if (viewport.w && viewport.h) { + data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w, + (GLdouble) istarget ? 0 : renderer->viewport.h, + (GLdouble) istarget ? renderer->viewport.h : 0, + 0.0, 1.0); } + data->glMatrixMode(GL_MODELVIEW); break; } case SDL_RENDERCMD_SETCLIPRECT: { const SDL_Rect *rect = &cmd->data.cliprect.rect; - const SDL_bool changed = (SDL_memcmp(&cliprect, rect, sizeof (SDL_Rect)) != 0); - if (cliprect_enabled != cmd->data.cliprect.enabled) { - cliprect_enabled = cmd->data.cliprect.enabled; - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } else { - data->glDisable(GL_SCISSOR_TEST); - } + cliprect_enabled = cmd->data.cliprect.enabled; + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); } - if (cliprect_enabled && changed) { - SDL_memcpy(&cliprect, rect, sizeof (SDL_Rect)); + if (cliprect_enabled) { data->glScissor(viewport.x + rect->x, istarget ? viewport.y + rect->y : drawableh - viewport.y - rect->y - rect->h, rect->w, rect->h); @@ -1393,24 +1337,19 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic } 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 != clear_color) { - data->glClearColor((GLfloat) r * inv255f, (GLfloat) g * inv255f, - (GLfloat) b * inv255f, (GLfloat) a * inv255f); - clear_color = color; - } + const GLfloat r = ((GLfloat) cmd->data.color.r) * inv255f; + const GLfloat g = ((GLfloat) cmd->data.color.g) * inv255f; + const GLfloat b = ((GLfloat) cmd->data.color.b) * inv255f; + const GLfloat a = ((GLfloat) cmd->data.color.a) * inv255f; + data->glClearColor(r, g, b, a); - if (clipping_enabled) { + if (cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); } data->glClear(GL_COLOR_BUFFER_BIT); - if (clipping_enabled) { + if (cliprect_enabled) { data->glEnable(GL_SCISSOR_TEST); } break; @@ -1419,7 +1358,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic 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, &draw_color, &blend, &shader, &texturing); + SetDrawState(data, cmd, SHADER_SOLID, &blend, &shader, &texturing); data->glBegin(GL_POINTS); for (i = 0; i < count; i++, verts += 2) { data->glVertex2f(verts[0], verts[1]); @@ -1431,7 +1370,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic case SDL_RENDERCMD_DRAW_LINES: { const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); size_t count = cmd->data.draw.count; - SetDrawState(data, cmd, SHADER_SOLID, &draw_color, &blend, &shader, &texturing); + SetDrawState(data, cmd, SHADER_SOLID, &blend, &shader, &texturing); if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) { --count; /* GL_LINE_LOOP takes care of the final segment */ data->glBegin(GL_LINE_LOOP); @@ -1489,7 +1428,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic 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, &draw_color, &blend, &shader, &texturing); + SetDrawState(data, cmd, SHADER_SOLID, &blend, &shader, &texturing); for (i = 0; i < count; ++i, verts += 4) { data->glRectf(verts[0], verts[1], verts[2], verts[3]); } @@ -1506,7 +1445,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic const GLfloat maxu = verts[5]; const GLfloat minv = verts[6]; const GLfloat maxv = verts[7]; - SetCopyState(data, cmd, &draw_color, &blend, &shader, &texturing, &bound_texture); + SetCopyState(data, cmd, &blend, &shader, &texturing, &bound_texture); data->glBegin(GL_TRIANGLE_STRIP); data->glTexCoord2f(minu, minv); data->glVertex2f(minx, miny); @@ -1533,7 +1472,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic const GLfloat translatex = verts[8]; const GLfloat translatey = verts[9]; const GLdouble angle = verts[10]; - SetCopyState(data, cmd, &draw_color, &blend, &shader, &texturing, &bound_texture); + SetCopyState(data, cmd, &blend, &shader, &texturing, &bound_texture); /* Translate to flip, rotate, translate to position */ data->glPushMatrix(); From dbf1408ad8ececac7428752d674f43c0a4ece775 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 24 Sep 2018 12:30:47 -0400 Subject: [PATCH 12/43] render: get rid of the predeclared functions in the GL and Metal renderers. (others to come as I continue to update render backends!) --HG-- branch : SDL-ryan-batching-renderer extra : source : 17cc45c0b2e4274617a5ed8ac06f819d3eb40717 --- src/render/metal/SDL_render_metal.m | 564 +++++++++++++--------------- src/render/opengl/SDL_render_gl.c | 456 +++++++++++----------- 2 files changed, 474 insertions(+), 546 deletions(-) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 493c7c5e1..49d21b89b 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -46,62 +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_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd); -static int METAL_QueueSetDrawColor(SDL_Renderer * renderer, SDL_RenderCommand *cmd); -static int METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, - int count); -static int METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, - int count); -static int METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect); -static int 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); -static int METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); -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 @@ -456,251 +400,6 @@ 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 = 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 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 = [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 mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; - data.mtlsamplernearest = mtlsamplernearest; - - samplerdesc.minFilter = MTLSamplerMinMagFilterLinear; - samplerdesc.magFilter = MTLSamplerMinMagFilterLinear; - id 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 mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared]; - #if !__has_feature(objc_arc) - [mtlbufconstantstaging autorelease]; - #endif - mtlbufconstantstaging.label = @"SDL constant staging data"; - - id 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 cmdbuffer = [data.mtlcmdqueue commandBuffer]; - id 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; -}} - static void METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load, MTLClearColor *clear_color) { @@ -1573,6 +1272,269 @@ METAL_GetMetalCommandEncoder(SDL_Renderer * renderer) 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 = 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 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 = [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 mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; + data.mtlsamplernearest = mtlsamplernearest; + + samplerdesc.minFilter = MTLSamplerMinMagFilterLinear; + samplerdesc.magFilter = MTLSamplerMinMagFilterLinear; + id 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 mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared]; + #if !__has_feature(objc_arc) + [mtlbufconstantstaging autorelease]; + #endif + mtlbufconstantstaging.label = @"SDL constant staging data"; + + id 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 cmdbuffer = [data.mtlcmdqueue commandBuffer]; + id 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 765f49c7d..ebde16c4c 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -51,54 +51,6 @@ extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); static const float inv255f = 1.0f / 255.0f; -/* !!! FIXME: delete these predeclarations and just move the functions before their use. */ -static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags); -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_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd); -static int GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, - const SDL_FPoint * points, int count); -static int GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, - const SDL_FRect * rects, int count); -static int GL_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, - const SDL_Rect *srcrect, const SDL_FRect *dstrect); -static int GL_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); -static int GL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); -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 @@ -343,203 +295,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) { - GL_DestroyRenderer(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) { - GL_DestroyRenderer(renderer); - goto error; - } - if (SDL_GL_MakeCurrent(window, data->context) < 0) { - GL_DestroyRenderer(renderer); - goto error; - } - - if (GL_LoadFunctions(data) < 0) { - GL_DestroyRenderer(renderer); - 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->glDisable(GL_DEPTH_TEST); - data->glDisable(GL_CULL_FACE); - /* This ended up causing video discrepancies between OpenGL and Direct3D */ - /* data->glEnable(GL_LINE_SMOOTH); */ - - 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 int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) { @@ -1692,6 +1447,217 @@ GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture) 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) { + GL_DestroyRenderer(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) { + GL_DestroyRenderer(renderer); + goto error; + } + if (SDL_GL_MakeCurrent(window, data->context) < 0) { + GL_DestroyRenderer(renderer); + goto error; + } + + if (GL_LoadFunctions(data) < 0) { + GL_DestroyRenderer(renderer); + 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->glDisable(GL_DEPTH_TEST); + data->glDisable(GL_CULL_FACE); + /* This ended up causing video discrepancies between OpenGL and Direct3D */ + /* data->glEnable(GL_LINE_SMOOTH); */ + + 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: */ From 0675f2fa39c62592cc38b93261617b3d1fbf4c44 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 25 Sep 2018 10:41:25 -0400 Subject: [PATCH 13/43] render: First shot at converting opengles renderer to new interfaces. --HG-- branch : SDL-ryan-batching-renderer extra : source : db6a2cf729c557a192ec767944bae3168dde3960 --- src/render/opengles/SDL_render_gles.c | 1031 +++++++++++-------------- 1 file changed, 468 insertions(+), 563 deletions(-) diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 4007dff58..a21bdffe3 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,9 @@ 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_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 @@ -215,8 +159,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,192 +183,20 @@ 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; -} - static void 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 +495,77 @@ GLES_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) return 0; } + static int -GLES_UpdateViewport(SDL_Renderer * renderer) +GLES_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - 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; + return 0; /* nothing to do in this backend. */ } static int -GLES_UpdateClipRect(SDL_Renderer * renderer) +GLES_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - if (SDL_CurrentContext != data->context) { - /* We'll update the clip rect after we rebind the context */ - return 0; + if (!verts) { + return -1; } - 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) -{ - 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); -} - -static int -GLES_RenderClear(SDL_Renderer * renderer) -{ - 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); - - if (renderer->clipping_enabled) { - data->glEnable(GL_SCISSOR_TEST); + 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; } static int -GLES_RenderDrawPoints(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; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 8 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - 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; + if (!verts) { + return -1; } - data->glVertexPointer(2, GL_FLOAT, 0, vertices); - data->glDrawArrays(GL_POINTS, 0, count); - SDL_stack_free(vertices); - return 0; -} + cmd->data.draw.count = count; -static int -GLES_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, - 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; - - vertices[idx * 2] = x; - vertices[(idx * 2) + 1] = y; - } - - 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); - - 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; + GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; GLfloat minu, maxu, minv, maxv; - GLfloat vertices[8]; - GLfloat texCoords[8]; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first); - GLES_ActivateRenderer(renderer); - - 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,86 +581,61 @@ 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; + *(verts++) = minx; + *(verts++) = miny; + *(verts++) = maxx; + *(verts++) = miny; + *(verts++) = minx; + *(verts++) = maxy; + *(verts++) = maxx; + *(verts++) = 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++) = 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; + GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; - GLfloat minu, maxu, minv, maxv; GLfloat centerx, centery; - GLfloat vertices[8]; - GLfloat texCoords[8]; + GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 19 * sizeof (GLfloat), 0, &cmd->data.draw.first); - - GLES_ActivateRenderer(renderer); - - 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); - 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; @@ -1118,32 +647,254 @@ GLES_RenderCopyEx(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; + cmd->data.draw.count = 1; - 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); + *(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(const GLES_RenderData *data, const SDL_RenderCommand *cmd, + SDL_BlendMode *current_blend, SDL_bool *current_texturing) +{ + const SDL_BlendMode blend = cmd->data.draw.blend; + + if (blend != *current_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))); + } + } + *current_blend = blend; + } + + if ((cmd->data.draw.texture != NULL) != *current_texturing) { + if (cmd->data.draw.texture == NULL) { + data->glDisable(GL_TEXTURE_2D); + data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); + *current_texturing = SDL_FALSE; + } else { + data->glEnable(GL_TEXTURE_2D); + data->glEnableClientState(GL_TEXTURE_COORD_ARRAY); + *current_texturing = SDL_FALSE; + } + } +} + +static void +SetCopyState(const GLES_RenderData *data, const SDL_RenderCommand *cmd, + SDL_BlendMode *current_blend, SDL_bool *current_texturing, + SDL_Texture **current_texture) +{ + SDL_Texture *texture = cmd->data.draw.texture; + SetDrawState(data, cmd, shader, current_blend, current_texturing); + + if (texture != *current_texture) { + data->glBindTexture(textype, texturedata->texture); + *current_texture = texture; + } +} + +static int +GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; + SDL_Rect viewport; + SDL_Texture *bound_texture = NULL; + SDL_BlendMode blend = SDL_BLENDMODE_INVALID; + GLES_Shader shader = SHADER_INVALID; + int drawablew = 0, drawableh = 0; + SDL_bool cliprect_enabled = SDL_FALSE; + const SDL_bool istarget = renderer->target != NULL; + SDL_bool texturing = SDL_FALSE; + size_t i; + + if (GLES_ActivateRenderer(renderer) < 0) { + return -1; + } + + if (!istarget) { + SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + } + + data->glDisable(GL_TEXTURE_2D); + data->glMatrixMode(GL_MODELVIEW); + data->glLoadIdentity(); + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + data->glColor4f((GLfloat) cmd->data.color.r * inv255f, + (GLfloat) cmd->data.color.g * inv255f, + (GLfloat) cmd->data.color.b * inv255f, + (GLfloat) cmd->data.color.a * inv255f); + break; + } + + case SDL_RENDERCMD_SETVIEWPORT: { + SDL_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport)); + data->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport.x, + istarget ? viewport.y : (drawableh - viewport.y - viewport.h), + viewport.w, viewport.h); + if (viewport.w && viewport.h) { + data->glOrthof((GLfloat) 0, (GLfloat) renderer->viewport.w, + (GLfloat) istarget ? 0 : renderer->viewport.h, + (GLfloat) istarget ? renderer->viewport.h : 0, + 0.0, 1.0); + } + data->glMatrixMode(GL_MODELVIEW); + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + cliprect_enabled = cmd->data.cliprect.enabled; + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } else { + data->glDisable(GL_SCISSOR_TEST); + } + + if (cliprect_enabled) { + data->glScissor(viewport.x + rect->x, + istarget ? viewport.y + rect->y : drawableh - viewport.y - rect->y - rect->h, + rect->w, rect->h); + } + break; + } + + case SDL_RENDERCMD_CLEAR: { + const GLfloat r = ((GLfloat) cmd->data.color.r) * inv255f; + const GLfloat g = ((GLfloat) cmd->data.color.g) * inv255f; + const GLfloat b = ((GLfloat) cmd->data.color.b) * inv255f; + const GLfloat a = ((GLfloat) cmd->data.color.a) * inv255f; + data->glClearColor(r, g, b, a); + + if (cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } + + data->glClear(GL_COLOR_BUFFER_BIT); + + if (cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } + 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, &blend, &texturing); + data->glVertexPointer(2, GL_FLOAT, 0, vertices); + data->glDrawArrays(GL_POINTS, 0, count); + break; + } + + case SDL_RENDERCMD_DRAW_LINES: { + const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); + size_t count = cmd->data.draw.count; + SetDrawState(data, cmd, &blend, &texturing); + data->glVertexPointer(2, GL_FLOAT, 0, verts); + 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); + } + 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, &blend, &texturing); + 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, &blend, &texturing, &bound_texture); + 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, &blend, &texturing, &bound_texture); + 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 GL_CheckError("", renderer); +} + static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, void * pixels, int pitch) @@ -1273,6 +1024,160 @@ 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); + + 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: */ From d96daf4a2889802b98637146ef0df0810f810cff Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 25 Sep 2018 16:17:10 -0400 Subject: [PATCH 14/43] render: opengles renderer actually works now. :) --HG-- branch : SDL-ryan-batching-renderer --- src/render/opengles/SDL_render_gles.c | 31 +++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index a21bdffe3..4fe1594c5 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -556,7 +556,7 @@ static int GLES_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; + GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; GLfloat minu, maxu, minv, maxv; GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first); @@ -607,7 +607,7 @@ GLES_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * const SDL_Rect * srcquad, const SDL_FRect * dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { - GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; + GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; GLfloat minx, miny, maxx, maxy; GLfloat centerx, centery; GLfloat minu, maxu, minv, maxv; @@ -638,13 +638,13 @@ GLES_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * 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; cmd->data.draw.count = 1; @@ -723,10 +723,11 @@ SetCopyState(const GLES_RenderData *data, const SDL_RenderCommand *cmd, SDL_Texture **current_texture) { SDL_Texture *texture = cmd->data.draw.texture; - SetDrawState(data, cmd, shader, current_blend, current_texturing); + SetDrawState(data, cmd, current_blend, current_texturing); if (texture != *current_texture) { - data->glBindTexture(textype, texturedata->texture); + GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; + data->glBindTexture(GL_TEXTURE_2D, texturedata->texture); *current_texture = texture; } } @@ -738,7 +739,6 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert SDL_Rect viewport; SDL_Texture *bound_texture = NULL; SDL_BlendMode blend = SDL_BLENDMODE_INVALID; - GLES_Shader shader = SHADER_INVALID; int drawablew = 0, drawableh = 0; SDL_bool cliprect_enabled = SDL_FALSE; const SDL_bool istarget = renderer->target != NULL; @@ -824,24 +824,23 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert const size_t count = cmd->data.draw.count; const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); SetDrawState(data, cmd, &blend, &texturing); - data->glVertexPointer(2, GL_FLOAT, 0, vertices); + 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); - size_t count = cmd->data.draw.count; + const size_t count = cmd->data.draw.count; SetDrawState(data, cmd, &blend, &texturing); data->glVertexPointer(2, GL_FLOAT, 0, verts); - if (count > 2 && points[0].x == points[count-1].x && points[0].y == points[count-1].y) { + if (count > 2 && (verts[0] == verts[(count-1)*2]) && (verts[1] == verts[(count*2)-1])) { /* GL_LINE_LOOP takes care of the final segment */ - --count; - data->glDrawArrays(GL_LINE_LOOP, 0, count); + 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); + data->glDrawArrays(GL_POINTS, count - 1, 1); } break; } @@ -892,7 +891,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert cmd = cmd->next; } - return GL_CheckError("", renderer); + return 0; } static int From 6292445c7a745994235bd4837615254c487253d1 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 25 Sep 2018 17:04:47 -0400 Subject: [PATCH 15/43] render: Move non-batching flushes to different place. This lets us batch up a few commands that are all related to the same API call. --HG-- branch : SDL-ryan-batching-renderer --- src/render/SDL_render.c | 78 ++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 5ead38617..17348b312 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -407,7 +407,7 @@ QueueCmdSetViewport(SDL_Renderer *renderer) } } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static int @@ -429,7 +429,7 @@ QueueCmdSetClipRect(SDL_Renderer *renderer) renderer->cliprect_queued = SDL_TRUE; } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static int @@ -458,7 +458,7 @@ QueueCmdSetDrawColor(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const } } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static int @@ -475,7 +475,7 @@ QueueCmdClear(SDL_Renderer *renderer) cmd->data.color.g = renderer->g; cmd->data.color.b = renderer->b; cmd->data.color.a = renderer->a; - return FlushRenderCommandsIfNotBatching(renderer); + return 0; } static int @@ -526,7 +526,7 @@ QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int cmd->command = SDL_RENDERCMD_NO_OP; } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static int @@ -540,7 +540,7 @@ QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int c cmd->command = SDL_RENDERCMD_NO_OP; } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static int @@ -554,7 +554,7 @@ QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int cou cmd->command = SDL_RENDERCMD_NO_OP; } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static SDL_RenderCommand * @@ -589,7 +589,7 @@ QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * src cmd->command = SDL_RENDERCMD_NO_OP; } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } static int @@ -606,7 +606,7 @@ QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture, cmd->command = SDL_RENDERCMD_NO_OP; } } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } @@ -689,6 +689,7 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) renderer->viewport.w = w; renderer->viewport.h = h; QueueCmdSetViewport(renderer); + FlushRenderCommandsIfNotBatching(renderer); } } @@ -1822,7 +1823,7 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) } /* All set! */ - return 0; + return FlushRenderCommandsIfNotBatching(renderer); } SDL_Texture * @@ -1994,6 +1995,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) { @@ -2008,7 +2010,8 @@ SDL_RenderSetViewport(SDL_Renderer * renderer, const SDL_Rect * rect) return -1; } } - return QueueCmdSetViewport(renderer); + retval = QueueCmdSetViewport(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } void @@ -2027,6 +2030,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) { @@ -2039,7 +2043,9 @@ SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect) renderer->clipping_enabled = SDL_FALSE; SDL_zero(renderer->clip_rect); } - return QueueCmdSetClipRect(renderer); + + retval = QueueCmdSetClipRect(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } void @@ -2143,18 +2149,22 @@ SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode) int SDL_RenderClear(SDL_Renderer * renderer) { + int retval; CHECK_RENDERER_MAGIC(renderer, -1); - return QueueCmdClear(renderer); + retval = QueueCmdClear(renderer); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y) { SDL_Point point; + int retval; point.x = x; point.y = y; - return SDL_RenderDrawPoints(renderer, &point, 1); + retval = SDL_RenderDrawPoints(renderer, &point, 1); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } static int @@ -2163,7 +2173,7 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, { SDL_FRect *frects; int i; - int status = -1; + int retval = -1; frects = SDL_stack_alloc(SDL_FRect, count); if (!frects) { @@ -2176,11 +2186,11 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, frects[i].h = renderer->scale.y; } - status = QueueCmdFillRects(renderer, frects, count); + retval = QueueCmdFillRects(renderer, frects, count); SDL_stack_free(frects); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -2189,7 +2199,7 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, { SDL_FPoint *fpoints; int i; - int status; + int retval; CHECK_RENDERER_MAGIC(renderer, -1); @@ -2218,11 +2228,11 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, fpoints[i].y = points[i].y * renderer->scale.y; } - status = QueueCmdDrawPoints(renderer, fpoints, count); + retval = QueueCmdDrawPoints(renderer, fpoints, count); SDL_stack_free(fpoints); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -2245,7 +2255,7 @@ RenderDrawLinesWithRects(SDL_Renderer * renderer, SDL_FRect *frects; SDL_FPoint fpoints[2]; int i, nrects = 0; - int status = 0; + int retval = 0; frects = SDL_stack_alloc(SDL_FRect, count-1); if (!frects) { @@ -2277,18 +2287,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 += QueueCmdDrawLines(renderer, fpoints, 2); + retval += QueueCmdDrawLines(renderer, fpoints, 2); } } - status += QueueCmdFillRects(renderer, frects, nrects); + retval += QueueCmdFillRects(renderer, frects, nrects); SDL_stack_free(frects); - if (status < 0) { - status = -1; + if (retval < 0) { + retval = -1; } - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -2297,7 +2307,7 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, { SDL_FPoint *fpoints; int i; - int status; + int retval; CHECK_RENDERER_MAGIC(renderer, -1); @@ -2326,11 +2336,11 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, fpoints[i].y = points[i].y * renderer->scale.y; } - status = QueueCmdDrawLines(renderer, fpoints, count); + retval = QueueCmdDrawLines(renderer, fpoints, count); SDL_stack_free(fpoints); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -2413,7 +2423,7 @@ SDL_RenderFillRects(SDL_Renderer * renderer, { SDL_FRect *frects; int i; - int status; + int retval; CHECK_RENDERER_MAGIC(renderer, -1); @@ -2440,11 +2450,11 @@ SDL_RenderFillRects(SDL_Renderer * renderer, frects[i].h = rects[i].h * renderer->scale.y; } - status = QueueCmdFillRects(renderer, frects, count); + retval = QueueCmdFillRects(renderer, frects, count); SDL_stack_free(frects); - return status; + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int @@ -2454,6 +2464,7 @@ SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, SDL_Rect real_srcrect = { 0, 0, 0, 0 }; SDL_Rect real_dstrect = { 0, 0, 0, 0 }; SDL_FRect frect; + int retval; CHECK_RENDERER_MAGIC(renderer, -1); CHECK_TEXTURE_MAGIC(texture, -1); @@ -2498,7 +2509,8 @@ SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, texture->last_command_generation = renderer->render_command_generation; - return QueueCmdCopy(renderer, texture, &real_srcrect, &frect); + retval = QueueCmdCopy(renderer, texture, &real_srcrect, &frect); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } From e523763256a2f591dd40455f4b6c107b500a2468 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 25 Sep 2018 19:20:31 -0400 Subject: [PATCH 16/43] render: OpenGL renderer now caches some state, to improve non-batching mode. (other minor bug fixes in here, too) --HG-- branch : SDL-ryan-batching-renderer extra : amend_source : 1232922bfab60e6b4f23f1c2a35275716ed273bb --- src/render/opengl/SDL_render_gl.c | 169 ++++++++++++++++++------------ 1 file changed, 101 insertions(+), 68 deletions(-) diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index ebde16c4c..529c67088 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -60,6 +60,19 @@ struct GL_FBOList GL_FBOList *next; }; +typedef struct +{ + SDL_Rect viewport; + SDL_Texture *texture; + SDL_BlendMode blend; + GL_Shader shader; + SDL_bool cliprect_enabled; + SDL_Rect cliprect; + SDL_bool texturing; + Uint32 color; + Uint32 clear_color; +} GL_DrawStateCache; + typedef struct { SDL_GLContext context; @@ -97,6 +110,7 @@ typedef struct /* Shader support */ GL_ShaderContext *shaders; + GL_DrawStateCache drawstate; } GL_RenderData; typedef struct @@ -916,13 +930,11 @@ GL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * te } static void -SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, - const GL_Shader shader, SDL_BlendMode *current_blend, - GL_Shader *current_shader, SDL_bool *current_texturing) +SetDrawState(GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_Shader shader) { const SDL_BlendMode blend = cmd->data.draw.blend; - if (blend != *current_blend) { + if (blend != data->drawstate.blend) { if (blend == SDL_BLENDMODE_NONE) { data->glDisable(GL_BLEND); } else { @@ -933,29 +945,27 @@ SetDrawState(const GL_RenderData *data, const SDL_RenderCommand *cmd, GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blend))); } - *current_blend = blend; + data->drawstate.blend = blend; } - if (data->shaders && (shader != *current_shader)) { + if (data->shaders && (shader != data->drawstate.shader)) { GL_SelectShader(data->shaders, shader); - *current_shader = shader; + data->drawstate.shader = shader; } - if ((cmd->data.draw.texture != NULL) != *current_texturing) { + if ((cmd->data.draw.texture != NULL) != data->drawstate.texturing) { if (cmd->data.draw.texture == NULL) { data->glDisable(data->textype); - *current_texturing = SDL_FALSE; + data->drawstate.texturing = SDL_FALSE; } else { data->glEnable(data->textype); - *current_texturing = SDL_FALSE; + data->drawstate.texturing = SDL_TRUE; } } } static void -SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd, - SDL_BlendMode *current_blend, GL_Shader *current_shader, - SDL_bool *current_texturing, SDL_Texture **current_texture) +SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd) { SDL_Texture *texture = cmd->data.draw.texture; const GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; @@ -998,9 +1008,9 @@ SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd, } } - SetDrawState(data, cmd, shader, current_blend, current_shader, current_texturing); + SetDrawState(data, cmd, shader); - if (texture != *current_texture) { + if (texture != data->drawstate.texture) { const GLenum textype = data->textype; if (texturedata->yuv) { data->glActiveTextureARB(GL_TEXTURE2_ARB); @@ -1016,7 +1026,7 @@ SetCopyState(const GL_RenderData *data, const SDL_RenderCommand *cmd, data->glActiveTextureARB(GL_TEXTURE0_ARB); data->glBindTexture(textype, texturedata->texture); - *current_texture = texture; + data->drawstate.texture = texture; } } @@ -1025,14 +1035,8 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic { /* !!! FIXME: it'd be nice to use a vertex buffer instead of immediate mode... */ GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - SDL_Rect viewport; - SDL_Texture *bound_texture = NULL; - SDL_BlendMode blend = SDL_BLENDMODE_INVALID; - GL_Shader shader = SHADER_INVALID; int drawablew = 0, drawableh = 0; - SDL_bool cliprect_enabled = SDL_FALSE; const SDL_bool istarget = renderer->target != NULL; - SDL_bool texturing = SDL_FALSE; size_t i; if (GL_ActivateRenderer(renderer) < 0) { @@ -1043,68 +1047,86 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); } - data->glDisable(data->textype); - data->glMatrixMode(GL_MODELVIEW); - data->glLoadIdentity(); - while (cmd) { switch (cmd->command) { case SDL_RENDERCMD_SETDRAWCOLOR: { - data->glColor4f((GLfloat) cmd->data.color.r * inv255f, - (GLfloat) cmd->data.color.g * inv255f, - (GLfloat) cmd->data.color.b * inv255f, - (GLfloat) cmd->data.color.a * inv255f); + 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_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport)); - data->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - data->glViewport(viewport.x, - istarget ? viewport.y : (drawableh - viewport.y - viewport.h), - viewport.w, viewport.h); - if (viewport.w && viewport.h) { - data->glOrtho((GLdouble) 0, (GLdouble) renderer->viewport.w, - (GLdouble) istarget ? 0 : renderer->viewport.h, - (GLdouble) istarget ? renderer->viewport.h : 0, - 0.0, 1.0); + 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->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport->x, + istarget ? viewport->y : (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->glMatrixMode(GL_MODELVIEW); break; } case SDL_RENDERCMD_SETCLIPRECT: { const SDL_Rect *rect = &cmd->data.cliprect.rect; - cliprect_enabled = cmd->data.cliprect.enabled; - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } else { - data->glDisable(GL_SCISSOR_TEST); - } - - if (cliprect_enabled) { - data->glScissor(viewport.x + rect->x, - istarget ? viewport.y + rect->y : drawableh - viewport.y - rect->y - rect->h, - rect->w, rect->h); + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + if (!data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } else { + const SDL_Rect *viewport = &data->drawstate.viewport; + data->glEnable(GL_SCISSOR_TEST); + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + data->glScissor(viewport->x + rect->x, + istarget ? viewport->y + rect->y : drawableh - viewport->y - rect->y - rect->h, + rect->w, rect->h); + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + } + } } break; } case SDL_RENDERCMD_CLEAR: { - const GLfloat r = ((GLfloat) cmd->data.color.r) * inv255f; - const GLfloat g = ((GLfloat) cmd->data.color.g) * inv255f; - const GLfloat b = ((GLfloat) cmd->data.color.b) * inv255f; - const GLfloat a = ((GLfloat) cmd->data.color.a) * inv255f; - data->glClearColor(r, g, b, a); + 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 (cliprect_enabled) { + if (data->drawstate.cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); } data->glClear(GL_COLOR_BUFFER_BIT); - if (cliprect_enabled) { + if (data->drawstate.cliprect_enabled) { data->glEnable(GL_SCISSOR_TEST); } break; @@ -1113,7 +1135,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic 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, &blend, &shader, &texturing); + SetDrawState(data, cmd, SHADER_SOLID); data->glBegin(GL_POINTS); for (i = 0; i < count; i++, verts += 2) { data->glVertex2f(verts[0], verts[1]); @@ -1124,12 +1146,12 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic case SDL_RENDERCMD_DRAW_LINES: { const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); - size_t count = cmd->data.draw.count; - SetDrawState(data, cmd, SHADER_SOLID, &blend, &shader, &texturing); + 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])) { - --count; /* GL_LINE_LOOP takes care of the final segment */ data->glBegin(GL_LINE_LOOP); - for (i = 0; i < count; ++i, verts += 2) { + /* 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(); @@ -1183,7 +1205,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic 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, &blend, &shader, &texturing); + SetDrawState(data, cmd, SHADER_SOLID); for (i = 0; i < count; ++i, verts += 4) { data->glRectf(verts[0], verts[1], verts[2], verts[3]); } @@ -1200,7 +1222,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic const GLfloat maxu = verts[5]; const GLfloat minv = verts[6]; const GLfloat maxv = verts[7]; - SetCopyState(data, cmd, &blend, &shader, &texturing, &bound_texture); + SetCopyState(data, cmd); data->glBegin(GL_TRIANGLE_STRIP); data->glTexCoord2f(minu, minv); data->glVertex2f(minx, miny); @@ -1227,7 +1249,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic const GLfloat translatex = verts[8]; const GLfloat translatey = verts[9]; const GLdouble angle = verts[10]; - SetCopyState(data, cmd, &blend, &shader, &texturing, &bound_texture); + SetCopyState(data, cmd); /* Translate to flip, rotate, translate to position */ data->glPushMatrix(); @@ -1627,11 +1649,22 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) 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: From 1da21e6740be0adc36b78009558b3f8a4fdc2c12 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 25 Sep 2018 21:35:09 -0400 Subject: [PATCH 17/43] render: Do state cache improvements for GLES1, too. --HG-- branch : SDL-ryan-batching-renderer --- src/render/opengles/SDL_render_gles.c | 143 +++++++++++++++----------- 1 file changed, 83 insertions(+), 60 deletions(-) diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 4fe1594c5..866c005a0 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -61,6 +61,18 @@ struct GLES_FBOList GLES_FBOList *next; }; +typedef struct +{ + SDL_Rect viewport; + SDL_Texture *texture; + SDL_BlendMode blend; + SDL_bool cliprect_enabled; + SDL_Rect cliprect; + SDL_bool texturing; + Uint32 color; + Uint32 clear_color; +} GLES_DrawStateCache; + typedef struct { SDL_GLContext context; @@ -77,6 +89,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 @@ -675,12 +689,11 @@ GLES_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * } static void -SetDrawState(const GLES_RenderData *data, const SDL_RenderCommand *cmd, - SDL_BlendMode *current_blend, SDL_bool *current_texturing) +SetDrawState(GLES_RenderData *data, const SDL_RenderCommand *cmd) { const SDL_BlendMode blend = cmd->data.draw.blend; - if (blend != *current_blend) { + if (blend != data->drawstate.blend) { if (blend == SDL_BLENDMODE_NONE) { data->glDisable(GL_BLEND); } else { @@ -701,34 +714,32 @@ SetDrawState(const GLES_RenderData *data, const SDL_RenderCommand *cmd, data->glBlendEquationOES(GetBlendEquation(SDL_GetBlendModeColorOperation(blend))); } } - *current_blend = blend; + data->drawstate.blend = blend; } - if ((cmd->data.draw.texture != NULL) != *current_texturing) { + 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); - *current_texturing = SDL_FALSE; + data->drawstate.texturing = SDL_FALSE; } else { data->glEnable(GL_TEXTURE_2D); data->glEnableClientState(GL_TEXTURE_COORD_ARRAY); - *current_texturing = SDL_FALSE; + data->drawstate.texturing = SDL_TRUE; } } } static void -SetCopyState(const GLES_RenderData *data, const SDL_RenderCommand *cmd, - SDL_BlendMode *current_blend, SDL_bool *current_texturing, - SDL_Texture **current_texture) +SetCopyState(const GLES_RenderData *data, const SDL_RenderCommand *cmd) { SDL_Texture *texture = cmd->data.draw.texture; - SetDrawState(data, cmd, current_blend, current_texturing); + SetDrawState(data, cmd); - if (texture != *current_texture) { + if (texture != data->drawstate.texture) { GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata; data->glBindTexture(GL_TEXTURE_2D, texturedata->texture); - *current_texture = texture; + data->drawstate.texture = texture; } } @@ -736,13 +747,8 @@ static int GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - SDL_Rect viewport; - SDL_Texture *bound_texture = NULL; - SDL_BlendMode blend = SDL_BLENDMODE_INVALID; int drawablew = 0, drawableh = 0; - SDL_bool cliprect_enabled = SDL_FALSE; const SDL_bool istarget = renderer->target != NULL; - SDL_bool texturing = SDL_FALSE; size_t i; if (GLES_ActivateRenderer(renderer) < 0) { @@ -753,60 +759,77 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); } - data->glDisable(GL_TEXTURE_2D); - data->glMatrixMode(GL_MODELVIEW); - data->glLoadIdentity(); - while (cmd) { switch (cmd->command) { case SDL_RENDERCMD_SETDRAWCOLOR: { - data->glColor4f((GLfloat) cmd->data.color.r * inv255f, - (GLfloat) cmd->data.color.g * inv255f, - (GLfloat) cmd->data.color.b * inv255f, - (GLfloat) cmd->data.color.a * inv255f); + 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_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport)); - data->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - data->glViewport(viewport.x, - istarget ? viewport.y : (drawableh - viewport.y - viewport.h), - viewport.w, viewport.h); - if (viewport.w && viewport.h) { - data->glOrthof((GLfloat) 0, (GLfloat) renderer->viewport.w, - (GLfloat) istarget ? 0 : renderer->viewport.h, - (GLfloat) istarget ? renderer->viewport.h : 0, - 0.0, 1.0); - } - data->glMatrixMode(GL_MODELVIEW); + 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->glMatrixMode(GL_PROJECTION); + data->glLoadIdentity(); + data->glViewport(viewport->x, + istarget ? viewport->y : (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); break; } case SDL_RENDERCMD_SETCLIPRECT: { const SDL_Rect *rect = &cmd->data.cliprect.rect; - cliprect_enabled = cmd->data.cliprect.enabled; - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } else { - data->glDisable(GL_SCISSOR_TEST); - } - - if (cliprect_enabled) { - data->glScissor(viewport.x + rect->x, - istarget ? viewport.y + rect->y : drawableh - viewport.y - rect->y - rect->h, - rect->w, rect->h); + if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { + data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; + if (!data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } else { + const SDL_Rect *viewport = &data->drawstate.viewport; + data->glEnable(GL_SCISSOR_TEST); + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + data->glScissor(viewport->x + rect->x, + istarget ? viewport->y + rect->y : drawableh - viewport->y - rect->y - rect->h, + rect->w, rect->h); + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + } + } } break; } case SDL_RENDERCMD_CLEAR: { - const GLfloat r = ((GLfloat) cmd->data.color.r) * inv255f; - const GLfloat g = ((GLfloat) cmd->data.color.g) * inv255f; - const GLfloat b = ((GLfloat) cmd->data.color.b) * inv255f; - const GLfloat a = ((GLfloat) cmd->data.color.a) * inv255f; - data->glClearColor(r, g, b, a); + 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 (cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); @@ -823,7 +846,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert 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, &blend, &texturing); + SetDrawState(data, cmd); data->glVertexPointer(2, GL_FLOAT, 0, verts); data->glDrawArrays(GL_POINTS, 0, count); break; @@ -832,7 +855,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert 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, &blend, &texturing); + 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 */ @@ -849,7 +872,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert 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, &blend, &texturing); + 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); @@ -859,7 +882,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert case SDL_RENDERCMD_COPY: { const GLfloat *verts = (GLfloat *) (((Uint8 *) vertices) + cmd->data.draw.first); - SetCopyState(data, cmd, &blend, &texturing, &bound_texture); + 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); @@ -871,7 +894,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert const GLfloat translatex = verts[16]; const GLfloat translatey = verts[17]; const GLfloat angle = verts[18]; - SetCopyState(data, cmd, &blend, &texturing, &bound_texture); + SetCopyState(data, cmd); data->glVertexPointer(2, GL_FLOAT, 0, verts); data->glTexCoordPointer(2, GL_FLOAT, 0, verts + 8); From 4741821e57853c246024b15790c42deffbaa052a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 26 Sep 2018 20:10:32 -0400 Subject: [PATCH 18/43] cocoa: Force an OpenGL context update when the window becomes key. Fixes missing rendering on macOS 10.14 ("Mojave"). Fixes Bugzilla #4272. (transplanted from 55489adbb75c8eb7c7719a3f9c85a15d06df2f27) --HG-- branch : SDL-ryan-batching-renderer extra : transplant_source : UH%9A%DB%B7%5C%8E%B7%C7q%9A%3F%9C%85%A1%5D%06%DF/%27 --- src/video/cocoa/SDL_cocoawindow.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index 0cf7ec335..1785ab1ee 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -632,6 +632,8 @@ SetWindowStyle(SDL_Window * window, NSUInteger style) const unsigned int newflags = [NSEvent modifierFlags] & NSEventModifierFlagCapsLock; _data->videodata->modifierFlags = (_data->videodata->modifierFlags & ~NSEventModifierFlagCapsLock) | newflags; SDL_ToggleModState(KMOD_CAPS, newflags != 0); + + ScheduleContextUpdates(_data); } - (void)windowDidResignKey:(NSNotification *)aNotification From 3fb340541ab6f12a9c33122f6cebae898984be16 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 28 Sep 2018 19:47:44 -0400 Subject: [PATCH 19/43] render: moved opengles2 over to new interface. --HG-- branch : SDL-ryan-batching-renderer --- src/render/SDL_render.c | 2 + src/render/opengles2/SDL_render_gles2.c | 2195 ++++++++++------------- 2 files changed, 936 insertions(+), 1261 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 17348b312..9a8c2f7f6 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -497,6 +497,7 @@ PrepQueueCmdDraw(SDL_Renderer *renderer, const Uint8 r, const Uint8 g, const Uin 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); @@ -560,6 +561,7 @@ QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int cou 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); diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 282f4baa8..052a27758 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -38,29 +38,6 @@ /* Used to re-create the window with OpenGL ES capability */ 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 * *************************************************************************************************/ @@ -88,7 +65,6 @@ typedef struct GLES2_TextureData GLenum texture_v; GLenum texture_u; GLES2_FBOList *fbo; - Uint32 last_cmd_generation; /* last command queue generation this texture was in. */ } GLES2_TextureData; typedef struct GLES2_ShaderCacheEntry @@ -97,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; @@ -114,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; @@ -159,47 +133,22 @@ typedef enum GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES } GLES2_ImageSource; -typedef enum +typedef struct { - GLES2_RENDERCMD_VIEWPORT, - GLES2_RENDERCMD_CLIPRECT, - GLES2_RENDERCMD_CLEAR, - GLES2_RENDERCMD_ATTR, - GLES2_RENDERCMD_DRAW -} GLES2_RenderCommandType; + SDL_Rect viewport; + SDL_Texture *texture; + SDL_BlendMode blend; + SDL_bool cliprect_enabled; + SDL_Rect cliprect; + SDL_bool texturing; + SDL_bool is_copy_ex; + Uint32 color; + Uint32 clear_color; + GLES2_ProgramCacheEntry *program; + GLfloat projection[4][4]; +} GLES2_DrawStateCache; -typedef struct GLES2_RenderCommand -{ - GLES2_RenderCommandType cmd; - union { - SDL_Rect viewport; - struct { - SDL_bool enabled; - SDL_Rect rect; - } cliprect; - struct { - Uint8 r, g, b, a; - } clear; - struct { - GLES2_Attribute attr; - GLsizei offset; - GLsizei count; - } attr; - struct { - GLenum mode; - GLint first; - GLsizei count; - Uint8 attrs; - Uint8 r, g, b, a; - SDL_BlendMode blend; - GLES2_ImageSource imgsrc; - SDL_Texture *texture; - } draw; - } data; - struct GLES2_RenderCommand *next; -} GLES2_RenderCommand; - -typedef struct GLES2_DriverContext +typedef struct GLES2_RenderData { SDL_GLContext *context; @@ -215,65 +164,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; - GLuint vertex_buffers[4]; - GLsizeiptr vertex_buffer_size[4]; + GLuint vertex_buffers[8]; + GLsizeiptr vertex_buffer_size[8]; int current_vertex_buffer; - GLES2_RenderCommand *render_commands; - GLES2_RenderCommand *render_commands_tail; - GLES2_RenderCommand *render_commands_pool; - int current_vertex_data; - Uint32 command_generation; - GLfloat *vertex_data; - GLsizeiptr vertex_data_allocation; -} GLES2_DriverContext; + GLES2_DrawStateCache drawstate; +} GLES2_RenderData; #define GLES2_MAX_CACHED_PROGRAMS 8 static const float inv255f = 1.0f / 255.0f; -static GLES2_RenderCommand * -GLES2_AllocateRenderCommand(SDL_Renderer *renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata; - GLES2_RenderCommand *retval = NULL; - - /* !!! FIXME: are there threading limitations in SDL's render API? */ - retval = data->render_commands_pool; - if (retval != NULL) { - data->render_commands_pool = retval->next; - retval->next = NULL; - } else { - retval = SDL_calloc(1, sizeof (*retval)); - if (!retval) { - SDL_OutOfMemory(); - return NULL; - } - } - - SDL_assert((data->render_commands == NULL) == (data->render_commands_tail == NULL)); - if (data->render_commands_tail != NULL) { - data->render_commands_tail->next = retval; - } else { - data->render_commands = retval; - } - data->render_commands_tail = retval; - - return retval; -} - -static SDL_bool -CompareColors(const Uint8 r1, const Uint8 g1, const Uint8 b1, const Uint8 a1, - const Uint8 r2, const Uint8 g2, const Uint8 b2, const Uint8 a2) -{ - Uint32 Pixel1, Pixel2; - RGBA8888_FROM_RGBA(Pixel1, r1, g1, b1, a1); - RGBA8888_FROM_RGBA(Pixel2, r2, g2, b2, a2); - return (Pixel1 == Pixel2); -} - SDL_FORCE_INLINE const char* GL_TranslateError (GLenum error) @@ -294,7 +196,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; @@ -307,7 +209,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) { @@ -340,16 +242,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 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__ @@ -377,7 +270,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)) ) { @@ -397,18 +290,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); @@ -419,14 +309,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! */ @@ -504,805 +387,31 @@ GLES2_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) return SDL_TRUE; } -static int GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int h); - -static int -GLES2_FlushCommands(SDL_Renderer *renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)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]; - const GLsizeiptr dataSizeInBytes = data->current_vertex_data * sizeof (float); - Uint8 enabled_attrs = (1 << GLES2_ATTRIBUTE_POSITION); - GLES2_RenderCommand *cmd; - GLES2_RenderCommand *next; - SDL_Rect viewport; - SDL_Texture *bound_texture = NULL; - SDL_BlendMode blend = SDL_BLENDMODE_INVALID; - Uint8 clear_r, clear_g, clear_b, clear_a; - int drawablew = 0, drawableh = 0; - GLfloat projection[4][4]; - SDL_bool cliprect_enabled = SDL_FALSE; - SDL_Rect cliprect; - - GLES2_ActivateRenderer(renderer); - - if (data->render_commands == NULL) { /* nothing to do! */ - SDL_assert(data->current_vertex_data == 0); - return 0; - } - - /* 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; - } - data->current_vertex_data = 0; /* start next VBO at start. */ - cmd = data->render_commands; - data->render_commands = NULL; - data->render_commands_tail = NULL; - - SDL_zero(projection); - projection[3][0] = -1.0f; - projection[3][1] = renderer->target ? -1.0f : 1.0f; - projection[3][3] = 1.0f; - - if (!renderer->target) { - SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); - } - - /* upload the new VBO data for this set of commands. */ - data->glBindBuffer(GL_ARRAY_BUFFER, vbo); - if (data->vertex_buffer_size[vboidx] < dataSizeInBytes) { - data->glBufferData(GL_ARRAY_BUFFER, dataSizeInBytes, data->vertex_data, GL_STREAM_DRAW); - data->vertex_buffer_size[vboidx] = dataSizeInBytes; - } else { - data->glBufferSubData(GL_ARRAY_BUFFER, 0, dataSizeInBytes, data->vertex_data); - } - - data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION); - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD); - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE); - data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_CENTER); - - clear_r = renderer->r; - clear_g = renderer->g; - clear_b = renderer->b; - clear_a = renderer->a; - data->glClearColor(clear_r * inv255f, clear_g * inv255f, clear_b * inv255f, clear_a * inv255f); - - SDL_memcpy(&viewport, &renderer->viewport, sizeof (viewport)); - data->glViewport(viewport.x, - (renderer->target) ? viewport.y : (drawableh - viewport.y - viewport.h), - viewport.w, viewport.h); - - SDL_memcpy(&cliprect, &renderer->clip_rect, sizeof (cliprect)); - cliprect_enabled = renderer->clipping_enabled; - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } else { - data->glDisable(GL_SCISSOR_TEST); - } - if (renderer->target) { - data->glScissor(viewport.x + cliprect.x, viewport.y + cliprect.y, cliprect.w, cliprect.h); - } else { - data->glScissor(viewport.x + cliprect.x, drawableh - viewport.y - cliprect.y - cliprect.h, cliprect.w, cliprect.h); - } - - while (cmd != NULL) { - switch (cmd->cmd) { - case GLES2_RENDERCMD_VIEWPORT: - if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) { - SDL_memcpy(&viewport, &cmd->data.viewport, sizeof (SDL_Rect)); - data->glViewport(viewport.x, - (renderer->target) ? viewport.y : (drawableh - viewport.y - viewport.h), - viewport.w, viewport.h); - } - break; - - case GLES2_RENDERCMD_CLIPRECT: { - const SDL_Rect *rect = &cmd->data.cliprect.rect; - const SDL_bool changed = (SDL_memcmp(&cliprect, rect, sizeof (SDL_Rect)) != 0); - if (cliprect_enabled != cmd->data.cliprect.enabled) { - cliprect_enabled = cmd->data.cliprect.enabled; - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } else { - data->glDisable(GL_SCISSOR_TEST); - } - } - - if (cliprect_enabled && changed) { - SDL_memcpy(&cliprect, rect, sizeof (SDL_Rect)); - if (renderer->target) { - data->glScissor(viewport.x + rect->x, viewport.y + rect->y, rect->w, rect->h); - } else { - data->glScissor(viewport.x + rect->x, drawableh - viewport.y - rect->y - rect->h, rect->w, rect->h); - } - } - break; - } - - case GLES2_RENDERCMD_CLEAR: - if (!CompareColors(clear_r, clear_g, clear_b, clear_a, cmd->data.clear.r, cmd->data.clear.g, cmd->data.clear.b, cmd->data.clear.a)) { - GLfloat r, g, b, a; - - clear_r = cmd->data.clear.r; - clear_g = cmd->data.clear.g; - clear_b = cmd->data.clear.b; - clear_a = cmd->data.clear.a; - - r = ((GLfloat) (colorswap ? clear_b : clear_r)) * inv255f; - g = ((GLfloat) clear_g) * inv255f; - b = ((GLfloat) (colorswap ? clear_r : clear_b)) * inv255f; - a = ((GLfloat) clear_a) * inv255f; - - data->glClearColor(r, g, b, a); - } - - if (cliprect_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } - - data->glClear(GL_COLOR_BUFFER_BIT); - - if (cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } - break; - - case GLES2_RENDERCMD_ATTR: - data->glVertexAttribPointer(cmd->data.attr.attr, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) cmd->data.attr.offset); - break; - - case GLES2_RENDERCMD_DRAW: { - const SDL_bool iscopy = (cmd->data.draw.imgsrc != GLES2_IMAGESOURCE_SOLID); - if (!viewport.w || !viewport.h) { - break; /* nothing to draw to. */ - } - - if (iscopy && (bound_texture != cmd->data.draw.texture)) { - GLES2_TextureData *tdata = (GLES2_TextureData *)cmd->data.draw.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); - } - if (tdata->nv12) { - data->glActiveTexture(GL_TEXTURE1); - data->glBindTexture(tdata->texture_type, tdata->texture_u); - - data->glActiveTexture(GL_TEXTURE0); - } - data->glBindTexture(tdata->texture_type, tdata->texture); - bound_texture = cmd->data.draw.texture; - } - - if (GLES2_SelectProgram(renderer, cmd->data.draw.imgsrc, iscopy ? bound_texture->w : 0, iscopy ? bound_texture->h : 0) == 0) { - GLES2_ProgramCacheEntry *program = data->current_program; - - if (enabled_attrs != cmd->data.draw.attrs) { - const Uint8 xored = enabled_attrs ^ cmd->data.draw.attrs; - int attr; - for (attr = 0; attr < GLES2_ATTRIBUTE_CENTER; attr++) { - if ((xored & (1 << attr)) != 0) { /* if changed */ - if (cmd->data.draw.attrs & (1 << attr)) { - data->glEnableVertexAttribArray((GLenum) attr); - } else { - data->glDisableVertexAttribArray((GLenum) attr); - } - } - } - enabled_attrs = cmd->data.draw.attrs; - } - - if (blend != cmd->data.draw.blend) { - const SDL_BlendMode bm = cmd->data.draw.blend; - if (bm == SDL_BLENDMODE_NONE) { - data->glDisable(GL_BLEND); - } else { - data->glEnable(GL_BLEND); - data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(bm)), - GetBlendFunc(SDL_GetBlendModeDstColorFactor(bm)), - GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(bm)), - GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(bm))); - data->glBlendEquationSeparate(GetBlendEquation(SDL_GetBlendModeColorOperation(bm)), - GetBlendEquation(SDL_GetBlendModeAlphaOperation(bm))); - } - blend = bm; - } - - if (program->uniform_locations[GLES2_UNIFORM_PROJECTION] != -1) { - projection[0][0] = 2.0f / viewport.w; - projection[1][1] = (renderer->target ? 2.0f : -2.0f) / viewport.h; - if (SDL_memcmp(program->projection, projection, sizeof (projection)) != 0) { - data->glUniformMatrix4fv(program->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)projection); - SDL_memcpy(program->projection, projection, sizeof (projection)); - } - } - - if (program->uniform_locations[GLES2_UNIFORM_COLOR] != -1) { - const Uint8 r = colorswap ? cmd->data.draw.b : cmd->data.draw.r; - const Uint8 g = cmd->data.draw.g; - const Uint8 b = colorswap ? cmd->data.draw.r : cmd->data.draw.b; - const Uint8 a = cmd->data.draw.a; - if (!CompareColors(program->color_r, program->color_g, program->color_b, program->color_a, r, g, b, a)) { - 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; - } - } - - data->glDrawArrays(cmd->data.draw.mode, cmd->data.draw.first, cmd->data.draw.count); - } - break; - } - - default: SDL_assert(!"Unknown rendering command"); break; - } - - /* put this command in the pool for reuse, move on to next one. */ - next = cmd->next; - cmd->next = data->render_commands_pool; - data->render_commands_pool = cmd; - cmd = next; - } - - data->command_generation++; - - return GL_CheckError("", renderer); -} static void -GLES2_FlushCommandsIfTextureNeeded(SDL_Renderer *renderer, SDL_Texture *texture) +GLES2_EvictShader(GLES2_RenderData *data, GLES2_ShaderCacheEntry *entry) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - if (tdata->last_cmd_generation == data->command_generation) { - /* the current command queue depends on this texture, flush the queue now before it changes */ - GLES2_FlushCommands(renderer); + /* 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_UpdateViewport(SDL_Renderer * renderer) -{ - GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); - if (cmd == NULL) { - return -1; - } - cmd->cmd = GLES2_RENDERCMD_VIEWPORT; - SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (SDL_Rect)); - return 0; -} - -static int -GLES2_UpdateClipRect(SDL_Renderer * renderer) -{ - GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); - if (cmd == NULL) { - return -1; - } - cmd->cmd = GLES2_RENDERCMD_CLIPRECT; - cmd->data.cliprect.enabled = renderer->clipping_enabled; - SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (SDL_Rect)); - return 0; -} - -static void -GLES2_DestroyRenderer(SDL_Renderer *renderer) -{ - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - - /* Deallocate everything */ - if (data) { - GLES2_RenderCommand *cmd; - - GLES2_ActivateRenderer(renderer); - - if (data->render_commands_tail != NULL) { - data->render_commands_tail->next = data->render_commands_pool; - } else { - data->render_commands = data->render_commands_pool; - } - - cmd = data->render_commands; - while (cmd != NULL) { - GLES2_RenderCommand *next = cmd->next; - SDL_free(cmd); - cmd = next; - } - - SDL_free(data->vertex_data); - - { - 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); -} - -/************************************************************************************************* - * 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); - - GLES2_FlushCommandsIfTextureNeeded(renderer, texture); - - /* 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); - } - - 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); - - GLES2_FlushCommandsIfTextureNeeded(renderer, texture); - - /* Bail out if we're supposed to update an empty rectangle */ - if (rect->w <= 0 || rect->h <= 0) { - return 0; - } - - 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; - - GLES2_FlushCommands(renderer); /* time to send everything to the GPU! */ - - 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"); - } - } - 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); - - GLES2_FlushCommandsIfTextureNeeded(renderer, texture); - - /* 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; - } -} - -/************************************************************************************************* - * 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 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; @@ -1369,8 +478,7 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, 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); if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V] != -1) { @@ -1386,7 +494,7 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, 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], 1.0f, 1.0f, 1.0f, 1.0f); + data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_COLOR], 0.0f, 0.0f, 0.0f, 0.0f); } /* Cache the linked program */ @@ -1407,11 +515,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; @@ -1423,9 +531,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; @@ -1519,32 +626,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; @@ -1624,24 +708,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; } @@ -1650,205 +734,314 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int data->glUseProgram(program->id); /* Set the current program */ - data->current_program = program; + 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; } - -/************************************************************************************************* - * Rendering functions * - *************************************************************************************************/ - -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 int -GLES2_RenderClear(SDL_Renderer * renderer) +GLES2_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) { - GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); - if (cmd == NULL) { - return -1; - } - cmd->cmd = GLES2_RENDERCMD_CLEAR; - cmd->data.clear.r = renderer->r; - cmd->data.clear.g = renderer->g; - cmd->data.clear.b = renderer->b; - cmd->data.clear.a = renderer->a; - return 0; + return 0; /* nothing to do in this backend. */ } static int -GLES2_AddVertices(SDL_Renderer *renderer, const GLES2_Attribute attr, const float *vertexData, size_t dataSizeInElements) +GLES2_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; - const GLsizeiptr needed = data->current_vertex_data + dataSizeInElements; - GLES2_RenderCommand *cmd; - GLfloat *vdata; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - if (needed > data->vertex_data_allocation) { - const GLsizeiptr newsize = data->vertex_data_allocation * 2; -printf("realloc'ing %p to %d\n", data->vertex_data, (int) newsize); - void *ptr = SDL_realloc(data->vertex_data, newsize * sizeof (GLfloat)); - if (ptr == NULL) { - SDL_OutOfMemory(); - return -1; - } - data->vertex_data = (GLfloat *) ptr; - data->vertex_data_allocation = newsize; - } - - cmd = GLES2_AllocateRenderCommand(renderer); - if (cmd == NULL) { + if (!verts) { return -1; } - vdata = &data->vertex_data[data->current_vertex_data]; - SDL_memcpy(vdata, vertexData, dataSizeInElements * sizeof (GLfloat)); - cmd->cmd = GLES2_RENDERCMD_ATTR; - cmd->data.attr.attr = attr; - cmd->data.attr.offset = data->current_vertex_data * sizeof (GLfloat); - cmd->data.attr.count = dataSizeInElements; - data->current_vertex_data += dataSizeInElements; - return 0; -} - -static int -GLES2_AddSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLint first, const GLsizei count) -{ - GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer); - if (cmd == NULL) { - return -1; - } - cmd->cmd = GLES2_RENDERCMD_DRAW; - cmd->data.draw.mode = mode; - cmd->data.draw.first = first; cmd->data.draw.count = count; - cmd->data.draw.attrs = (1 << GLES2_ATTRIBUTE_POSITION); - 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.imgsrc = GLES2_IMAGESOURCE_SOLID; - cmd->data.draw.texture = NULL; + for (i = 0; i < count; i++) { + *(verts++) = 0.5f + points[i].x; + *(verts++) = 0.5f + points[i].y; + } + return 0; } static int -GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count) +GLES2_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */ - int idx; - int rc; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 8 * sizeof (GLfloat), 0, &cmd->data.draw.first); + size_t i; - /* Emit the specified vertices as points */ - for (idx = 0; idx < count; ++idx) { - vertices[idx * 2] = points[idx].x + 0.5f; - vertices[(idx * 2) + 1] = points[idx].y + 0.5f; + if (!verts) { + return -1; } - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); - SDL_stack_free(vertices); + cmd->data.draw.count = count; - if (rc == 0) { - rc = GLES2_AddSolidDrawCommand(renderer, GL_POINTS, 0, count); + 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 rc; + return 0; } static int -GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count) +GLES2_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */ - int idx; - int rc; + GLfloat minx, miny, maxx, maxy; + GLfloat minu, maxu, minv, maxv; + GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 16 * sizeof (GLfloat), 0, &cmd->data.draw.first); - /* Emit a line strip including the specified vertices */ - for (idx = 0; idx < count; ++idx) { - vertices[idx * 2] = points[idx].x + 0.5f; - vertices[(idx * 2) + 1] = points[idx].y + 0.5f; + if (!verts) { + return -1; } - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2); - if (rc == 0) { - rc = GLES2_AddSolidDrawCommand(renderer, GL_LINE_STRIP, 0, count); - } + cmd->data.draw.count = 1; -#if 0 /* !!! FIXME: ugh */ - /* 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) { - GLES2_DrawVertices(GL_POINTS, count-1, 1); - } -#endif + minx = dstrect->x; + miny = dstrect->y; + maxx = dstrect->x + dstrect->w; + maxy = dstrect->y + dstrect->h; - SDL_stack_free(vertices); - return rc; + 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_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count) +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) { - GLfloat vertices[8]; - int idx; - int rc = 0; + /* 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); - /* Emit a line loop for each rectangle */ - for (idx = 0; (rc == 0) && (idx < count); ++idx) { - const SDL_FRect *rect = &rects[idx]; + if (!verts) { + return -1; + } - GLfloat xMin = rect->x; - GLfloat xMax = (rect->x + rect->w); - GLfloat yMin = rect->y; - GLfloat yMax = (rect->y + rect->h); + if (flip & SDL_FLIP_HORIZONTAL) { + minx = dstrect->x + dstrect->w; + maxx = dstrect->x; + } + else { + minx = dstrect->x; + maxx = dstrect->x + dstrect->w; + } - vertices[0] = xMin; - vertices[1] = yMin; - vertices[2] = xMax; - vertices[3] = yMin; - vertices[4] = xMin; - vertices[5] = yMax; - vertices[6] = xMax; - vertices[7] = yMax; + if (flip & SDL_FLIP_VERTICAL) { + miny = dstrect->y + dstrect->h; + maxy = dstrect->y; + } + else { + miny = dstrect->y; + maxy = dstrect->y + dstrect->h; + } - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - if (rc == 0) { - rc = GLES2_AddSolidDrawCommand(renderer, GL_TRIANGLE_STRIP, 0, 4); + minu = (GLfloat) (srcquad->x / texture->w); + maxu = (GLfloat) ((srcquad->x + srcquad->w) / texture->w); + minv = (GLfloat) (srcquad->y / texture->h); + maxv = (GLfloat) ((srcquad->y + srcquad->h) / 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 +SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_ImageSource imgsrc) +{ + 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; + GLES2_ProgramCacheEntry *program; + + SDL_assert((texture != NULL) == (imgsrc != GLES2_IMAGESOURCE_SOLID)); + + 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->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) cmd->data.draw.first + (sizeof (GLfloat) * 8)); + 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); + + data->glActiveTexture(GL_TEXTURE0); + } + data->glBindTexture(tdata->texture_type, tdata->texture); + } + + data->drawstate.texture = texture; + } + + if (GLES2_SelectProgram(data, imgsrc, texture ? texture->w : 0, texture ? texture->h : 0) < 0) { + return -1; + } + + program = data->drawstate.program; + + 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)); } } + 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; + } + } + + const SDL_BlendMode blend = cmd->data.draw.blend; + 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; + } + + /* all drawing commands use this */ + data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) cmd->data.draw.first); + + if (is_copy_ex != was_copy_ex) { + if (is_copy_ex) { + data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_ANGLE); + data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_CENTER); + 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))); + } else { + data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_ANGLE); + data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_CENTER); + } + data->drawstate.is_copy_ex = is_copy_ex; + } + return 0; } - static int -GLES2_AddCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uint8 attrs) +SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd) { - GLES2_RenderCommand *cmd = NULL; - GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; + GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; + SDL_Texture *texture = cmd->data.draw.texture; /* Pick an appropriate shader */ if (renderer->target) { @@ -1954,132 +1147,619 @@ GLES2_AddCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uin } } - ((GLES2_TextureData *)texture->driverdata)->last_cmd_generation = data->command_generation; + return SetDrawState(data, cmd, sourceType); +} - cmd = GLES2_AllocateRenderCommand(renderer); - if (cmd == NULL) { +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]; + int drawablew = 0, drawableh = 0; + const SDL_bool istarget = renderer->target != NULL; + size_t i; + + if (GLES2_ActivateRenderer(renderer) < 0) { return -1; } - cmd->cmd = GLES2_RENDERCMD_DRAW; - cmd->data.draw.mode = GL_TRIANGLE_STRIP; - cmd->data.draw.first = 0; - cmd->data.draw.count = 4; - cmd->data.draw.attrs = attrs | (1 << GLES2_ATTRIBUTE_POSITION) | (1 << GLES2_ATTRIBUTE_TEXCOORD); - 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.imgsrc = sourceType; - cmd->data.draw.texture = texture; + if (!istarget) { + SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + } + + /* 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); + } + + /* 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; + } + + 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->glViewport(viewport->x, + istarget ? viewport->y : (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] = (renderer->target ? 2.0f : -2.0f) / viewport->h; + data->drawstate.projection[3][1] = renderer->target ? -1.0f : 1.0f; + } + } + 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; + if (!data->drawstate.cliprect_enabled) { + data->glDisable(GL_SCISSOR_TEST); + } else { + const SDL_Rect *viewport = &data->drawstate.viewport; + data->glEnable(GL_SCISSOR_TEST); + if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { + data->glScissor(viewport->x + rect->x, + istarget ? viewport->y + rect->y : drawableh - viewport->y - rect->y - rect->h, + rect->w, rect->h); + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + } + } + } + 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) colorswap ? b : r) * inv255f; + const GLfloat fg = ((GLfloat) g) * inv255f; + const GLfloat fb = ((GLfloat) colorswap ? r : 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->glClear(GL_COLOR_BUFFER_BIT); + + if (data->drawstate.cliprect_enabled) { + data->glEnable(GL_SCISSOR_TEST); + } + 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; + } + } + + 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_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 ((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_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) { - GLfloat vertices[8]; - int rc; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - /* 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); - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); + GLES2_ActivateRenderer(renderer); - if (rc == 0) { - vertices[0] = srcrect->x / (GLfloat)texture->w; - vertices[1] = srcrect->y / (GLfloat)texture->h; - vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[3] = srcrect->y / (GLfloat)texture->h; - vertices[4] = srcrect->x / (GLfloat)texture->w; - vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); - - if (rc == 0) { - rc = GLES2_AddCopyDrawCommand(renderer, texture, 0); - } + /* Bail out if we're supposed to update an empty rectangle */ + if (rect->w <= 0 || rect->h <= 0) { + return 0; } - return rc; + /* 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); + } 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) { - const float radian_angle = (float)(M_PI * (360.0 - angle) / 180.0); - GLfloat vertices[8]; - int rc; + GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata; + GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata; - vertices[0] = vertices[2] = vertices[4] = vertices[6] = (GLfloat)SDL_sin(radian_angle); - /* render expects cos value - 1 (see GLES2_VertexSrc_Default_) */ - vertices[1] = vertices[3] = vertices[5] = vertices[7] = (GLfloat)SDL_cos(radian_angle) - 1.0f; - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_ANGLE, vertices, 8); + GLES2_ActivateRenderer(renderer); - /* Calculate the center of rotation */ - if (rc == 0) { - vertices[0] = vertices[2] = vertices[4] = vertices[6] = (center->x + dstrect->x); - vertices[1] = vertices[3] = vertices[5] = vertices[7] = (center->y + dstrect->y); - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_CENTER, vertices, 8); - - if (rc == 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); - if (flip & SDL_FLIP_HORIZONTAL) { - const GLfloat tmp = vertices[0]; - vertices[0] = vertices[4] = vertices[2]; - vertices[2] = vertices[6] = tmp; - } - if (flip & SDL_FLIP_VERTICAL) { - const GLfloat tmp = vertices[1]; - vertices[1] = vertices[3] = vertices[5]; - vertices[5] = vertices[7] = tmp; - } - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8); - - if (rc == 0) { - vertices[0] = srcrect->x / (GLfloat)texture->w; - vertices[1] = srcrect->y / (GLfloat)texture->h; - vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[3] = srcrect->y / (GLfloat)texture->h; - vertices[4] = srcrect->x / (GLfloat)texture->w; - vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w; - vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h; - rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8); - - if (rc == 0) { - GLES2_AddCopyDrawCommand(renderer, texture, (1 << GLES2_ATTRIBUTE_CENTER) | (1 << GLES2_ATTRIBUTE_ANGLE)); - } - } - } + /* Bail out if we're supposed to update an empty rectangle */ + if (rect->w <= 0 || rect->h <= 0) { + return 0; } - return rc; + 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_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"); + } + } + return 0; +} + +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; @@ -2088,8 +1768,6 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, int w, h, length, rows; int status; - GLES2_FlushCommands(renderer); /* we need to render before we read the results. */ - temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format); buflen = (size_t) (rect->h * temp_pitch); if (buflen == 0) { @@ -2137,8 +1815,6 @@ GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, static void GLES2_RenderPresent(SDL_Renderer *renderer) { - GLES2_FlushCommands(renderer); /* time to send it to the GPU! */ - /* Tell the video driver to swap buffers */ SDL_GL_SwapWindow(renderer->window); } @@ -2152,7 +1828,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); @@ -2170,7 +1846,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); @@ -2188,37 +1864,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->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; @@ -2240,6 +1891,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) { @@ -2261,7 +1913,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) { GLES2_DestroyRenderer(renderer); SDL_OutOfMemory(); @@ -2272,15 +1924,6 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->driverdata = data; renderer->window = window; - data->vertex_data_allocation = 512; - data->vertex_data = (GLfloat *) SDL_malloc(data->vertex_data_allocation * sizeof (GLfloat)); - if (data->vertex_data == NULL) { - GLES2_DestroyRenderer(renderer); - SDL_OutOfMemory(); - goto error; - } -printf("malloc'd %p\n", data->vertex_data); - /* Create an OpenGL ES 2.0 context */ data->context = SDL_GL_CreateContext(window); if (!data->context) { @@ -2371,14 +2014,14 @@ printf("malloc'd %p\n", data->vertex_data); 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; @@ -2394,7 +2037,20 @@ printf("malloc'd %p\n", data->vertex_data); 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; @@ -2409,6 +2065,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: */ From 454906e0b9bd03368f0108c2fdc560bc8a377f99 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 28 Sep 2018 19:48:14 -0400 Subject: [PATCH 20/43] opengles1: set some non-zero drawstate defaults. --HG-- branch : SDL-ryan-batching-renderer --- src/render/opengles/SDL_render_gles.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 866c005a0..52a3e9991 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -1176,6 +1176,10 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags) 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: From 8e76fc1c37a728ef0d2c3547ed331dc9a2bef1aa Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 29 Sep 2018 04:00:38 +0000 Subject: [PATCH 21/43] render: patched to compile on C89 compilers, other untested code fixes. --HG-- branch : SDL-ryan-batching-renderer --- src/render/SDL_render.c | 8 +------- src/render/opengles/SDL_render_gles.c | 9 +++++---- src/render/opengles2/SDL_render_gles2.c | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 9a8c2f7f6..5c7e36da0 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -600,8 +600,8 @@ QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) { SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX); - SDL_assert(renderer->QueueCopyEx != NULL); /* should have caught at higher level. */ 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) { @@ -1287,11 +1287,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 { @@ -1327,11 +1324,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 { diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 52a3e9991..64801db73 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -731,7 +731,7 @@ SetDrawState(GLES_RenderData *data, const SDL_RenderCommand *cmd) } static void -SetCopyState(const GLES_RenderData *data, const SDL_RenderCommand *cmd) +SetCopyState(GLES_RenderData *data, const SDL_RenderCommand *cmd) { SDL_Texture *texture = cmd->data.draw.texture; SetDrawState(data, cmd); @@ -793,6 +793,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert 0.0, 1.0); } data->glMatrixMode(GL_MODELVIEW); + } break; } @@ -831,13 +832,13 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert data->drawstate.clear_color = color; } - if (cliprect_enabled) { + if (data->drawstate.cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); } data->glClear(GL_COLOR_BUFFER_BIT); - if (cliprect_enabled) { + if (data->drawstate.cliprect_enabled) { data->glEnable(GL_SCISSOR_TEST); } break; @@ -919,7 +920,7 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert 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; diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 052a27758..c1be71023 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -939,6 +939,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I 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; SDL_assert((texture != NULL) == (imgsrc != GLES2_IMAGESOURCE_SOLID)); @@ -1001,7 +1002,6 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I } } - const SDL_BlendMode blend = cmd->data.draw.blend; if (blend != data->drawstate.blend) { if (blend == SDL_BLENDMODE_NONE) { data->glDisable(GL_BLEND); From 8a2013f1c2f68bfa954ceca1498687d3a3cad48c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 1 Oct 2018 01:23:02 -0400 Subject: [PATCH 22/43] render: first shot at moving Direct3D 9 backend to new interface. Untested! --HG-- branch : SDL-ryan-batching-renderer --- src/render/direct3d/SDL_render_d3d.c | 1458 +++++++++++++------------- 1 file changed, 732 insertions(+), 726 deletions(-) diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 69a9dff54..aaa468da4 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -41,61 +41,23 @@ #include "SDL_shaders_d3d.h" +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; + /* 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); - - -SDL_RenderDriver D3D_RenderDriver = { - D3D_CreateRenderer, - { - "direct3d", - (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE), - 1, - {SDL_PIXELFORMAT_ARGB8888}, - 0, - 0} -}; - typedef struct { void* d3dDLL; @@ -106,11 +68,17 @@ typedef struct SDL_bool updateSize; SDL_bool beginScene; SDL_bool enableSeparateAlphaBlend; + SDL_bool supportsStreamOffset; D3DTEXTUREFILTERTYPE scaleMode[8]; IDirect3DSurface9 *defaultRenderTarget; IDirect3DSurface9 *currentRenderTarget; void* d3dxDLL; LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS]; + IDirect3DVertexBuffer9 vertexBuffers[8]; + GLsizeiptr vertexBufferSize[8]; + int currentVertexBuffer; + SDL_bool reportedVboProblem; + D3D_DrawStateCache drawstate; } D3D_RenderData; typedef struct @@ -265,9 +233,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 +267,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); @@ -431,177 +387,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) { @@ -701,33 +486,6 @@ D3D_CreateStagingTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture) return 0; } -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) { @@ -1072,330 +830,256 @@ 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; - - 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; - - result = IDirect3DDevice9_SetScissorRect(data->device, &r); - if (result != D3D_OK) { - D3D_SetError("SetScissor()", result); - return -1; - } - } else { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); - } - return 0; -} - -static int -D3D_RenderClear(SDL_Renderer * renderer) -{ - D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - DWORD color; - HRESULT result; - int BackBufferWidth, BackBufferHeight; - - if (D3D_ActivateRenderer(renderer) < 0) { + if (!verts) { return -1; } - color = D3DCOLOR_ARGB(renderer->a, renderer->r, renderer->g, renderer->b); + SDL_memset(verts, '\0', vertslen); + cmd->data.draw.count = count; - if (renderer->target) { - BackBufferWidth = renderer->target->w; - BackBufferHeight = renderer->target->h; - } else { - BackBufferWidth = data->pparams.BackBufferWidth; - BackBufferHeight = data->pparams.BackBufferHeight; + for (i = 0; i < count; i++, verts++, points++) { + verts->x = points->x; + verts->y = points->y; + verts->color = color; } - if (renderer->clipping_enabled) { - IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); - } - - /* 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; - - /* 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); - - result = IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); - - /* 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); - } - - 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_RenderData *data = (D3D_RenderData *) renderer->driverdata; - DWORD color; - Vertex *vertices; - int i; - 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); - - 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_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - 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); + 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; } - D3D_SetBlendMode(data, renderer->blendMode); + SDL_memset(verts, '\0', vertslen); + cmd->data.draw.count = count; - 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); - - 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_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)); - } - - 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) -{ - 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) { + 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; - minx = rect->x; - miny = rect->y; - maxx = rect->x + rect->w; - maxy = rect->y + rect->h; + verts->x = minx; + verts->y = miny; + verts->color = color; + verts++; - 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; + verts->x = maxx; + verts->y = miny; + verts->color = color; + verts++; - 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; + verts->x = maxx; + verts->y = maxy; + verts->color = color; + verts++; - 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; + verts->x = minx; + verts->y = maxy; + verts->color = color; + verts++; + } - 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; + return 0; +} - result = - IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, - 2, vertices, sizeof(*vertices)); - if (FAILED(result)) { - return D3D_SetError("DrawPrimitiveUP()", result); +static int +D3D_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + 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 (!verts) { + return -1; + } + + cmd->data.draw.count = 1; + + minx = dstrect->x - 0.5f; + miny = dstrect->y - 0.5f; + maxx = dstrect->x + dstrect->w - 0.5f; + maxy = dstrect->y + dstrect->h - 0.5f; + + 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 = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b); + + 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++; + + return 0; +} + +static int +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) +{ + 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 (!verts) { + return -1; + } + + cmd->data.draw.count = 1; + + minx = -center->x; + maxx = dstrect->w - center->x; + miny = -center->y; + maxy = dstrect->h - center->y; + + 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; + } + + 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++; + + return 0; +} + +static int +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 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 +1095,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 +1127,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 +1141,330 @@ 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, const D3D_ImageSource imgsrc) { - 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 (D3D_ActivateRenderer(renderer) < 0) { - return -1; - } + 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; - minx = dstrect->x - 0.5f; - miny = dstrect->y - 0.5f; - maxx = dstrect->x + dstrect->w - 0.5f; - maxy = dstrect->y + dstrect->h - 0.5f; - - 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 = 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 (shader) { - result = IDirect3DDevice9_SetPixelShader(data->device, shader); - if (FAILED(result)) { - return D3D_SetError("SetShader()", result); + /* 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 && RenderSetupTextureState(renderer, texture, &shader) < 0) { + return -1; } - } - result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, - vertices, sizeof(*vertices)); - if (FAILED(result)) { - D3D_SetError("DrawPrimitiveUP()", result); - } - if (shader) { - IDirect3DDevice9_SetPixelShader(data->device, NULL); - } - return FAILED(result) ? -1 : 0; -} + if (shader != data->drawstate.shader) + if (FAILED(IDirect3DDevice9_SetPixelShader(data->device, shader))) { + return D3D_SetError("IDirect3DDevice9_SetPixelShader()", result); + } + data->drawstate.shader = shader; + } + + data->drawstate.texture = texture; + } + + 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(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))); + } + } + + data->drawstate.blend = blend; + } + + 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; + } + + 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 (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 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; + /* upload the new VBO data for this set of commands. */ + if (data->supportsStreamOffset) { + 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); + } - minx = -centerx; - maxx = dstrect->w - centerx; - miny = -centery; - maxy = dstrect->h - centery; + if (FAILED(IDirect3DVertexBuffer9_CreateVertexBuffer(vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) { + vbo = NULL; + } + data->vertexBuffers[vboidx] = vbo; + data->vertex_buffer_size[vboidx] = vbo ? vertsize : 0; + } - 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 (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. :( */ + } + } + } - 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; - } - - /* 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; + /* 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; + } } } - 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); + + if (!vbo && !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; } - modelMatrix = MatrixIdentity(); - IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&modelMatrix); + 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; + } - return FAILED(result) ? -1 : 0; + 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 = 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.backh; + + if (data->drawstate.clipping_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)) { + result = 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_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, 0, 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 Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.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_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, 0, count - 1); + if (close_endpoint) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, 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; + IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); + for (i = 0; i < count; ++i, offset += 4) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, 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; + IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); + for (i = 0; i < count; ++i, offset += 4) { + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, 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 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_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, 0, 2); + } else { + IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex)); + } + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } + + return 0; } + static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch) @@ -1782,6 +1601,193 @@ D3D_DestroyRenderer(SDL_Renderer * renderer) } SDL_free(renderer); } + +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; + } + + data->supportsStreamOffset = ((caps.Caps2 & D3DDEVCAPS2_STREAMOFFSET) == D3DDEVCAPS2_STREAMOFFSET); + + 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__ From d6753c690b771ffe80c3a0800c60f97efc8d415a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 1 Oct 2018 03:02:54 -0400 Subject: [PATCH 23/43] render: Patched to compile. --HG-- branch : SDL-ryan-batching-renderer --- src/render/direct3d/SDL_render_d3d.c | 162 ++++++++++++------------ src/render/opengles2/SDL_render_gles2.c | 2 +- 2 files changed, 84 insertions(+), 80 deletions(-) diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index aaa468da4..c93f55713 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" @@ -74,8 +76,8 @@ typedef struct IDirect3DSurface9 *currentRenderTarget; void* d3dxDLL; LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS]; - IDirect3DVertexBuffer9 vertexBuffers[8]; - GLsizeiptr vertexBufferSize[8]; + LPDIRECT3DVERTEXBUFFER9 vertexBuffers[8]; + size_t vertexBufferSize[8]; int currentVertexBuffer; SDL_bool reportedVboProblem; D3D_DrawStateCache drawstate; @@ -282,63 +284,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) @@ -933,8 +879,6 @@ D3D_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * tex minv = (float) srcrect->y / texture->h; maxv = (float) (srcrect->y + srcrect->h) / texture->h; - color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b); - verts->x = minx; verts->y = miny; verts->z = 0.0f; @@ -1141,7 +1085,7 @@ SetupTextureState(D3D_RenderData *data, SDL_Texture * texture, LPDIRECT3DPIXELSH } static int -SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd, const D3D_ImageSource imgsrc) +SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) { const SDL_bool was_copy_ex = data->drawstate.is_copy_ex; const SDL_bool is_copy_ex = (cmd->command == SDL_RENDERCMD_COPY_EX); @@ -1161,12 +1105,13 @@ SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd, const D3D_Image IDirect3DDevice9_SetTexture(data->device, 1, NULL); IDirect3DDevice9_SetTexture(data->device, 2, NULL); } - if (texture && RenderSetupTextureState(renderer, texture, &shader) < 0) { + if (texture && SetupTextureState(data, texture, &shader) < 0) { return -1; } - if (shader != data->drawstate.shader) - if (FAILED(IDirect3DDevice9_SetPixelShader(data->device, shader))) { + 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; @@ -1181,14 +1126,14 @@ SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd, const D3D_Image } else { IDirect3DDevice9_SetRenderState(data->device, D3DRS_ALPHABLENDENABLE, TRUE); IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLEND, - GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blendMode))); + GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend))); IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLEND, - GetBlendFunc(SDL_GetBlendModeDstColorFactor(blendMode))); + GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend))); if (data->enableSeparateAlphaBlend) { IDirect3DDevice9_SetRenderState(data->device, D3DRS_SRCBLENDALPHA, - GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blendMode))); + GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend))); IDirect3DDevice9_SetRenderState(data->device, D3DRS_DESTBLENDALPHA, - GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blendMode))); + GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); } } @@ -1263,16 +1208,16 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti IDirect3DVertexBuffer9_Release(vbo); } - if (FAILED(IDirect3DVertexBuffer9_CreateVertexBuffer(vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) { + if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, vertsize, usage, fvf, D3DPOOL_MANAGED, &vbo, NULL))) { vbo = NULL; } data->vertexBuffers[vboidx] = vbo; - data->vertex_buffer_size[vboidx] = vbo ? vertsize : 0; + data->vertexBufferSize[vboidx] = vbo ? vertsize : 0; } if (vbo) { void *ptr; - if (FAILED(IDirect3DVertexBuffer9_Lock(vbo, 0, vertsize, &ptr, D3DLOCK_DISCARD)) { + 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); @@ -1321,7 +1266,7 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti 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 = true; + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; } if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { @@ -1335,16 +1280,16 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti 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.backh; + const int backh = istarget ? renderer->target->h : data->pparams.BackBufferHeight; - if (data->drawstate.clipping_enabled) { + 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)) { - result = IDirect3DDevice9_Clear(data->device, 0, NULL, D3DCLEAR_TARGET, color, 0.0f, 0); + 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 }; @@ -1371,9 +1316,9 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti } case SDL_RENDERCMD_DRAW_LINES: { - const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); 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. */ @@ -1435,6 +1380,7 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti } 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; @@ -1602,6 +1548,64 @@ 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) { @@ -1708,7 +1712,7 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags) device_flags |= D3DCREATE_MULTITHREADED; } - data->supportsStreamOffset = ((caps.Caps2 & D3DDEVCAPS2_STREAMOFFSET) == D3DDEVCAPS2_STREAMOFFSET); + data->supportsStreamOffset = ((caps.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET) == D3DDEVCAPS2_STREAMOFFSET); result = IDirect3D9_CreateDevice(data->d3d, data->adapter, D3DDEVTYPE_HAL, diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index c1be71023..ff2bce2a0 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -951,7 +951,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I data->drawstate.texturing = SDL_FALSE; } else { data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_TEXCOORD); - data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) cmd->data.draw.first + (sizeof (GLfloat) * 8)); + data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) (cmd->data.draw.first + (sizeof (GLfloat) * 8))); data->drawstate.texturing = SDL_TRUE; } } From 7aab4dc8b91c3206f6104403461b0d0005880ffd Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 1 Oct 2018 11:32:08 -0400 Subject: [PATCH 24/43] render: Set the D3D9 stream source once and choose offsets during draw calls. This is _much_ faster than setting the offsets with SetStreamSource! --HG-- branch : SDL-ryan-batching-renderer --- src/render/direct3d/SDL_render_d3d.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index c93f55713..695b51e0f 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -1208,7 +1208,7 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti IDirect3DVertexBuffer9_Release(vbo); } - if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, vertsize, usage, fvf, D3DPOOL_MANAGED, &vbo, NULL))) { + if (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) { vbo = NULL; } data->vertexBuffers[vboidx] = vbo; @@ -1244,6 +1244,8 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti data->reportedVboProblem = SDL_TRUE; } + IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, 0, sizeof (Vertex)); + while (cmd) { switch (cmd->command) { case SDL_RENDERCMD_SETDRAWCOLOR: { @@ -1306,8 +1308,7 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti const size_t first = cmd->data.draw.first; SetDrawState(data, cmd); if (vbo) { - IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); - IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, 0, count); + 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)); @@ -1327,10 +1328,9 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti SetDrawState(data, cmd); if (vbo) { - IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); - IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, 0, count - 1); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_LINESTRIP, first / sizeof (Vertex), count - 1); if (close_endpoint) { - IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, count - 1, 1); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_POINTLIST, (first / sizeof (Vertex)) + (count - 1), 1); } } else { IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_LINESTRIP, count - 1, verts, sizeof (Vertex)); @@ -1347,9 +1347,8 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti SetDrawState(data, cmd); if (vbo) { size_t offset = 0; - IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); for (i = 0; i < count; ++i, offset += 4) { - IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, offset, 2); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (first / sizeof (Vertex)) + offset, 2); } } else { const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); @@ -1366,9 +1365,8 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti SetDrawState(data, cmd); if (vbo) { size_t offset = 0; - IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); for (i = 0; i < count; ++i, offset += 4) { - IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, offset, 2); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, (first / sizeof (Vertex)) + offset, 2); } } else { const Vertex *verts = (Vertex *) (((Uint8 *) vertices) + first); @@ -1392,8 +1390,7 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&d3dmatrix); if (vbo) { - IDirect3DDevice9_SetStreamSource(data->device, 0, vbo, (UINT) first, sizeof (Vertex)); - IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, 0, 2); + IDirect3DDevice9_DrawPrimitive(data->device, D3DPT_TRIANGLEFAN, first / sizeof (Vertex), 2); } else { IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, verts, sizeof (Vertex)); } From c60a01a0827c7a28228dcdb7d1354d37db3729d7 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 1 Oct 2018 13:41:15 -0400 Subject: [PATCH 25/43] render: D3D9 doesn't need to check for stream offset support anymore. We don't use offsets at all now. Too slow. --HG-- branch : SDL-ryan-batching-renderer extra : histedit_source : bf5eafc07800e64529ffc423abf3e605aa965264 --- src/render/direct3d/SDL_render_d3d.c | 61 ++++++++++++---------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 695b51e0f..9d000ccdd 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -70,7 +70,6 @@ typedef struct SDL_bool updateSize; SDL_bool beginScene; SDL_bool enableSeparateAlphaBlend; - SDL_bool supportsStreamOffset; D3DTEXTUREFILTERTYPE scaleMode[8]; IDirect3DSurface9 *defaultRenderTarget; IDirect3DSurface9 *currentRenderTarget; @@ -1199,44 +1198,40 @@ D3D_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *verti } /* upload the new VBO data for this set of commands. */ - if (data->supportsStreamOffset) { - 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 (FAILED(IDirect3DDevice9_CreateVertexBuffer(data->device, vertsize, usage, fvf, D3DPOOL_DEFAULT, &vbo, NULL))) { - vbo = NULL; - } - data->vertexBuffers[vboidx] = vbo; - data->vertexBufferSize[vboidx] = vbo ? vertsize : 0; + 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 (vbo) { - void *ptr; - if (FAILED(IDirect3DVertexBuffer9_Lock(vbo, 0, vertsize, &ptr, D3DLOCK_DISCARD))) { + 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; + } + + 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. :( */ - } else { - SDL_memcpy(ptr, vertices, vertsize); - if (FAILED(IDirect3DVertexBuffer9_Unlock(vbo))) { - vbo = NULL; /* oh well, we'll do immediate mode drawing. :( */ - } - } - } - - /* 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; } } } - if (!vbo && !data->reportedVboProblem) { + /* 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."); @@ -1709,8 +1704,6 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags) device_flags |= D3DCREATE_MULTITHREADED; } - data->supportsStreamOffset = ((caps.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET) == D3DDEVCAPS2_STREAMOFFSET); - result = IDirect3D9_CreateDevice(data->d3d, data->adapter, D3DDEVTYPE_HAL, pparams.hDeviceWindow, From e33a87b4e6b2212145ba0cb15a73c348f64c28bc Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 1 Oct 2018 22:53:45 -0400 Subject: [PATCH 26/43] render: Make the GL backends cache and defer more state changes. --HG-- branch : SDL-ryan-batching-renderer extra : histedit_source : 9989445300dd74771e6143b70ef194e88d123c18 --- src/render/opengl/SDL_render_gl.c | 84 +++++++++++------- src/render/opengles/SDL_render_gles.c | 113 +++++++++++++++--------- src/render/opengles2/SDL_render_gles2.c | 84 +++++++++++------- 3 files changed, 174 insertions(+), 107 deletions(-) diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 529c67088..ef8507ebf 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -62,11 +62,17 @@ 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; @@ -934,6 +940,42 @@ SetDrawState(GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_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); @@ -1035,18 +1077,18 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic { /* !!! FIXME: it'd be nice to use a vertex buffer instead of immediate mode... */ GL_RenderData *data = (GL_RenderData *) renderer->driverdata; - int drawablew = 0, drawableh = 0; - const SDL_bool istarget = renderer->target != NULL; size_t i; if (GL_ActivateRenderer(renderer) < 0) { return -1; } - if (!istarget) { - SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + 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: { @@ -1069,18 +1111,7 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic 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->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - data->glViewport(viewport->x, - istarget ? viewport->y : (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_TRUE; } break; } @@ -1089,18 +1120,11 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic 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; - if (!data->drawstate.cliprect_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } else { - const SDL_Rect *viewport = &data->drawstate.viewport; - data->glEnable(GL_SCISSOR_TEST); - if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { - data->glScissor(viewport->x + rect->x, - istarget ? viewport->y + rect->y : drawableh - viewport->y - rect->y - rect->h, - rect->w, rect->h); - SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); - } - } + 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; } @@ -1122,13 +1146,11 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic if (data->drawstate.cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; } data->glClear(GL_COLOR_BUFFER_BIT); - if (data->drawstate.cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } break; } diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 64801db73..6f662afa9 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -64,9 +64,15 @@ struct GLES_FBOList 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; @@ -692,6 +698,57 @@ 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) { @@ -747,52 +804,29 @@ static int GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata; - int drawablew = 0, drawableh = 0; - const SDL_bool istarget = renderer->target != NULL; size_t i; if (GLES_ActivateRenderer(renderer) < 0) { return -1; } - if (!istarget) { - SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + 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: { - 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; + 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->glMatrixMode(GL_PROJECTION); - data->glLoadIdentity(); - data->glViewport(viewport->x, - istarget ? viewport->y : (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_TRUE; } break; } @@ -801,18 +835,11 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert 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; - if (!data->drawstate.cliprect_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } else { - const SDL_Rect *viewport = &data->drawstate.viewport; - data->glEnable(GL_SCISSOR_TEST); - if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { - data->glScissor(viewport->x + rect->x, - istarget ? viewport->y + rect->y : drawableh - viewport->y - rect->y - rect->h, - rect->w, rect->h); - SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); - } - } + 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; } @@ -834,13 +861,11 @@ GLES_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vert if (data->drawstate.cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; } data->glClear(GL_COLOR_BUFFER_BIT); - if (data->drawstate.cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } break; } diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index ff2bce2a0..1acc25e7c 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -136,14 +136,20 @@ typedef enum 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; @@ -944,6 +950,37 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I 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; + } + + 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_TRUE; + } + + 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); + SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + } + if (texture != data->drawstate.texture) { if ((texture != NULL) != data->drawstate.texturing) { if (texture == NULL) { @@ -1157,16 +1194,15 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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]; - int drawablew = 0, drawableh = 0; - const SDL_bool istarget = renderer->target != NULL; size_t i; if (GLES2_ActivateRenderer(renderer) < 0) { return -1; } - if (!istarget) { - SDL_GL_GetDrawableSize(renderer->window, &drawablew, &drawableh); + data->drawstate.target = renderer->target; + if (!data->drawstate.target) { + SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh); } /* upload the new VBO data for this set of commands. */ @@ -1199,14 +1235,7 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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->glViewport(viewport->x, - istarget ? viewport->y : (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] = (renderer->target ? 2.0f : -2.0f) / viewport->h; - data->drawstate.projection[3][1] = renderer->target ? -1.0f : 1.0f; - } + data->drawstate.viewport_dirty = SDL_TRUE; } break; } @@ -1215,32 +1244,26 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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; - if (!data->drawstate.cliprect_enabled) { - data->glDisable(GL_SCISSOR_TEST); - } else { - const SDL_Rect *viewport = &data->drawstate.viewport; - data->glEnable(GL_SCISSOR_TEST); - if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { - data->glScissor(viewport->x + rect->x, - istarget ? viewport->y + rect->y : drawableh - viewport->y - rect->y - rect->h, - rect->w, rect->h); - SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); - } - } + 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 r = colorswap ? cmd->data.color.b : cmd->data.color.r; const Uint8 g = cmd->data.color.g; - const Uint8 b = cmd->data.color.b; + 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) colorswap ? b : r) * inv255f; + const GLfloat fr = ((GLfloat) r) * inv255f; const GLfloat fg = ((GLfloat) g) * inv255f; - const GLfloat fb = ((GLfloat) colorswap ? r : b) * inv255f; + const GLfloat fb = ((GLfloat) b) * inv255f; const GLfloat fa = ((GLfloat) a) * inv255f; data->glClearColor(fr, fg, fb, fa); data->drawstate.clear_color = color; @@ -1248,13 +1271,10 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver if (data->drawstate.cliprect_enabled) { data->glDisable(GL_SCISSOR_TEST); + data->drawstate.cliprect_enabled_dirty = SDL_TRUE; } data->glClear(GL_COLOR_BUFFER_BIT); - - if (data->drawstate.cliprect_enabled) { - data->glEnable(GL_SCISSOR_TEST); - } break; } From 048c707bc7ccda4a0c33acc001024a669fcc1314 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 3 Oct 2018 00:52:37 -0400 Subject: [PATCH 27/43] render: first (untested!) shot at converting D3D11 renderer to new interfaces. Probably doesn't even compile yet. --HG-- branch : SDL-ryan-batching-renderer extra : histedit_source : a386a4b001a3cc26ea67e75b607b43b8343ea955 --- src/render/direct3d11/SDL_render_d3d11.c | 1278 +++++++++++----------- 1 file changed, 633 insertions(+), 645 deletions(-) diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 153cee743..f381ff5b9 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 { @@ -145,6 +148,12 @@ 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; } D3D11_RenderData; @@ -175,75 +184,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,85 +216,6 @@ 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_ReleaseAll(SDL_Renderer * renderer) { @@ -1066,11 +927,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); @@ -1671,184 +1528,265 @@ 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; - - if (matrix) { - data->vertexShaderConstantsData.model = *matrix; - } else { - data->vertexShaderConstantsData.model = MatrixIdentity(); - } - - ID3D11DeviceContext_UpdateSubresource(data->d3dContext, - (ID3D11Resource *)data->vertexShaderConstants, - 0, - NULL, - &data->vertexShaderConstantsData, - 0, - 0 - ); + return 0; /* nothing to do in this backend. */ } static int -D3D11_UpdateViewport(SDL_Renderer * renderer) +D3D11_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) { - D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; - Float4X4 projection; - Float4X4 view; - SDL_FRect orientationAlignedViewport; - BOOL swapDimensions; - D3D11_VIEWPORT viewport; - const int rotation = D3D11_GetRotationForCurrentRenderTarget(renderer); + 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 (renderer->viewport.w == 0 || renderer->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. - */ - /* SDL_Log("%s, no viewport was set!\n", __FUNCTION__); */ - return 0; + if (!verts) { + return -1; } - /* Make sure the SDL viewport gets rotated to that of the physical display's rotation. - * Keep in mind here that the Y-axis will be been inverted (from Direct3D's - * default coordinate system) so rotations will be done in the opposite - * direction of the DXGI_MODE_ROTATION enumeration. - */ - switch (rotation) { - case DXGI_MODE_ROTATION_IDENTITY: - projection = MatrixIdentity(); - break; - case DXGI_MODE_ROTATION_ROTATE270: - projection = MatrixRotationZ(SDL_static_cast(float, M_PI * 0.5f)); - break; - case DXGI_MODE_ROTATION_ROTATE180: - projection = MatrixRotationZ(SDL_static_cast(float, M_PI)); - break; - case DXGI_MODE_ROTATION_ROTATE90: - projection = MatrixRotationZ(SDL_static_cast(float, -M_PI * 0.5f)); - break; - default: - return SDL_SetError("An unknown DisplayOrientation is being used"); + 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.u = 0.0f + verts->tex.v = 0.0f + verts->color.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = a; + verts++; } - /* 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; - 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 - * set here (as of this writing, on Dec 26, 2013). When done, store it - * for eventual transfer to the GPU. - */ - data->vertexShaderConstantsData.projectionAndView = MatrixMultiply( - 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, - * for Windows Phone devices. - */ - 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; - } 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; - } - /* 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); - return 0; } static int -D3D11_UpdateClipRect(SDL_Renderer * renderer) +D3D11_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata; + 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 (!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); + 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.u = 0.0f + verts->tex.v = 0.0f + verts->color.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = a; + verts++; + + verts->pos.x = rects[i].x; + verts->pos.y = rects[i].y + rects[i].h; + verts->pos.z = 0.0f + verts->tex.u = 0.0f + verts->tex.v = 0.0f + verts->color.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = a; + verts++; + + verts->pos.x = rects[i].x + rects[i].w; + verts->pos.y = rects[i].y; + verts->pos.z = 0.0f + verts->tex.u = 0.0f + verts->tex.v = 0.0f + verts->color.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.u = 0.0f + verts->tex.v = 0.0f + verts->color.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = a; + verts++; } 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; - if (data->currentOffscreenRenderTargetView) { - return data->currentOffscreenRenderTargetView; - } else { - return data->mainRenderTargetView; - } -} - static int -D3D11_RenderClear(SDL_Renderer * renderer) +D3D11_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) { - 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 - ); + 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); + 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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, count * 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 (flip & SDL_FLIP_HORIZONTAL) { + minu = (float) srcrect->x / texture->w; + maxu = (float) (srcrect->x + srcrect->w) / texture->w; + } else { + minu = (float) (srcrect->x + srcrect->w) / texture->w; + maxu = (float) srcrect->x / texture->w; + } + + } + 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.r = r; + verts->color.g = g; + verts->color.b = b; + verts->color.a = 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.u = 0.0f; + verts->tex.v = 0.0f; + verts->color.r = 0; + verts->color.g = 0; + verts->color.b = 0; + verts->color.a = 0; + verts++; + + return 0; +} + + +/* !!! FIXME: rotate through a few vertex buffers so the GPU has time to finish using them */ static int D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, const void * vertexData, size_t dataSizeInBytes) @@ -1916,12 +1854,114 @@ D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, return 0; } -static void -D3D11_RenderStartDrawOp(SDL_Renderer * renderer) +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 d3dviewport; + const int rotation = D3D11_GetRotationForCurrentRenderTarget(renderer); + + 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. + */ + /* SDL_Log("%s, no viewport was set!\n", __FUNCTION__); */ + return 0; + } + + /* Make sure the SDL viewport gets rotated to that of the physical display's rotation. + * Keep in mind here that the Y-axis will be been inverted (from Direct3D's + * default coordinate system) so rotations will be done in the opposite + * direction of the DXGI_MODE_ROTATION enumeration. + */ + switch (rotation) { + case DXGI_MODE_ROTATION_IDENTITY: + projection = MatrixIdentity(); + break; + case DXGI_MODE_ROTATION_ROTATE270: + projection = MatrixRotationZ(SDL_static_cast(float, M_PI * 0.5f)); + break; + case DXGI_MODE_ROTATION_ROTATE180: + projection = MatrixRotationZ(SDL_static_cast(float, M_PI)); + break; + case DXGI_MODE_ROTATION_ROTATE90: + projection = MatrixRotationZ(SDL_static_cast(float, -M_PI * 0.5f)); + break; + default: + return SDL_SetError("An unknown DisplayOrientation is being used"); + } + + /* Update the view matrix */ + 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[3][0] = -1.0f; + view.m[3][1] = 1.0f; + view.m[3][3] = 1.0f; + + /* Combine the projection + view matrix together now, as both only get + * set here (as of this writing, on Dec 26, 2013). When done, store it + * for eventual transfer to the GPU. + */ + data->vertexShaderConstantsData.projectionAndView = MatrixMultiply( + view, + projection); + + /* 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, + * for Windows Phone devices. + */ + swapDimensions = D3D11_IsDisplayRotated90Degrees(rotation); + if (swapDimensions) { + orientationAlignedViewport.x = (float) viewport->y; + orientationAlignedViewport.y = (float) viewport->x; + orientationAlignedViewport.w = (float) viewport->h; + orientationAlignedViewport.h = (float) viewport->w; + } else { + 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) */ + + 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); + + data->viewportDirty = SDL_FALSE; + + return 0; +} + +static int +D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11PixelShader * shader, + const int numShaderResources, ID3D11ShaderResourceView ** shaderResources, + ID3D11SamplerState * sampler, const Float4X4 *matrix) + { D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata; + static const Float4X4 identity = MatrixIdentity(); + const Float4X4 *newmatrix = matrix ? matrix : &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 +1971,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(data->d3dContext, 1, &scissorRect); + } + rendererData->cliprectDirty = SDL_FALSE; + } + + if (!rendererData->currentCliprectEnabled) { rasterizerState = rendererData->mainRasterizer; } else { rasterizerState = rendererData->clippedRasterizer; @@ -1940,13 +1998,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) { @@ -1967,17 +2019,7 @@ D3D11_RenderSetBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) 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 +2037,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(&data->vertexShaderConstantsData.model, newmatrix, sizeof (*newmatrix)) != 0) { + SDL_memcpy(&data->vertexShaderConstantsData.model, newmatrix, sizeof (*newmatrix)); + ID3D11DeviceContext_UpdateSubresource(data->d3dContext, + (ID3D11Resource *)data->vertexShaderConstants, 0, NULL, - NULL); - - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, SDL_arraysize(vertices)); + &data->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 +2094,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,194 +2118,162 @@ 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); + } + + return D3D11_SetDrawState(renderer, cmd, renderer, rendererData->pixelShaders[SHADER_RGB], + 1, &textureData->mainTextureResourceView, textureSampler, matrix); +} + +static void +D3D11_DrawPrimitives(SDL_Renderer * renderer, D3D11_PRIMITIVE_TOPOLOGY primitiveTopology, const UINT vertexStart, const UINT vertexCount) +{ + D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; + ID3D11DeviceContext_IASetPrimitiveTopology(rendererData->d3dContext, primitiveTopology); + ID3D11DeviceContext_Draw(rendererData->d3dContext, vertexCount, vertexStart); +} + +static int +D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + 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_UpdateVertexBuffer(renderer, vertices, vertsize) < 0) { + return -1; + } + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: { + break; /* this isn't currently used in this render backend. */ + } + + 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)); + data->drawstate.viewportDirty = SDL_TRUE; + } + break; + } + + 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; + } + + 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(data->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), colorRGBA); + break; + } + + 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; + } + + 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].x != verts[count - 1].x || verts[0].y != verts[count - 1].y) { + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, start + (count-1), 1); + } + break; + } + + 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; + } + + case SDL_RENDERCMD_COPY: { + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof (VertexPositionColor); + D3D11_SetCopyState(data, cmd, NULL); + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start, 4); + break; + } + + 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 Vertex *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(data, cmd, &matrix); + D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start, 4); + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } + + 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; + if (data->currentOffscreenRenderTargetView) { + return data->currentOffscreenRenderTargetView; } else { - D3D11_SetPixelShader( - renderer, - rendererData->pixelShaders[SHADER_RGB], - 1, - &textureData->mainTextureResourceView, - textureSampler); + return data->mainRenderTargetView; } - - return 0; -} - -static int -D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; - 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; - } - - if (D3D11_RenderSetupSampler(renderer, texture) < 0) { - return -1; - } - - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); - - return 0; -} - -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) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; - D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; - float minu, maxu, minv, maxv; - Float4 color; - Float4X4 modelMatrix; - float minx, maxx, miny, maxy; - 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 */ - } - - if (flip & SDL_FLIP_HORIZONTAL) { - float tmp = maxu; - maxu = minu; - minu = tmp; - } - if (flip & SDL_FLIP_VERTICAL) { - float tmp = maxv; - maxv = minv; - minv = tmp; - } - - modelMatrix = MatrixMultiply( - MatrixRotationZ((float)(M_PI * (float) angle / 180.0f)), - MatrixTranslation(dstrect->x + center->x, dstrect->y + center->y, 0) - ); - D3D11_SetModelMatrix(renderer, &modelMatrix); - - minx = -center->x; - maxx = dstrect->w - center->x; - miny = -center->y; - maxy = dstrect->h - center->y; - - 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; - } - - if (D3D11_RenderSetupSampler(renderer, texture) < 0) { - return -1; - } - - D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); - - D3D11_SetModelMatrix(renderer, NULL); - - return 0; } static int @@ -2554,6 +2440,108 @@ 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; + } + + 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: */ From c1de5c95553f676f9860e23dc3effd2a4eee985b Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 3 Oct 2018 18:23:53 -0400 Subject: [PATCH 28/43] render: D3D11 renderer patched to compile. --HG-- branch : SDL-ryan-batching-renderer --- src/render/direct3d11/SDL_render_d3d11.c | 304 ++++++++++++----------- 1 file changed, 154 insertions(+), 150 deletions(-) diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index f381ff5b9..e9baadc7f 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -154,6 +154,7 @@ typedef struct SDL_Rect currentViewport; int currentViewportRotation; SDL_bool viewportDirty; + Float4X4 identity; } D3D11_RenderData; @@ -216,6 +217,8 @@ SDLPixelFormatToDXGIFormat(Uint32 sdlFormat) } } +static void D3D11_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); + static void D3D11_ReleaseAll(SDL_Renderer * renderer) { @@ -338,7 +341,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; @@ -367,21 +370,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. */ @@ -825,6 +828,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 @@ -941,35 +983,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) { @@ -1551,13 +1564,13 @@ D3D11_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL 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.u = 0.0f - verts->tex.v = 0.0f - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + 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++; } @@ -1581,46 +1594,46 @@ D3D11_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_ 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.u = 0.0f - verts->tex.v = 0.0f - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + 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.u = 0.0f - verts->tex.v = 0.0f - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + 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.u = 0.0f - verts->tex.v = 0.0f - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + 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.u = 0.0f - verts->tex.v = 0.0f - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + 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++; } @@ -1631,7 +1644,7 @@ 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, count * 4 * sizeof (VertexPositionColor), 0, &cmd->data.draw.first); + 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); @@ -1650,10 +1663,10 @@ D3D11_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * t verts->pos.z = 0.0f; verts->tex.x = minu; verts->tex.y = minv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; verts->pos.x = dstrect->x; @@ -1661,10 +1674,10 @@ D3D11_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * t verts->pos.z = 0.0f; verts->tex.x = minu; verts->tex.y = maxv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; verts->pos.x = dstrect->x + dstrect->w; @@ -1672,10 +1685,10 @@ D3D11_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * t verts->pos.z = 0.0f; verts->tex.x = maxu; verts->tex.y = minv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; verts->pos.x = dstrect->x + dstrect->w; @@ -1683,10 +1696,10 @@ D3D11_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * t verts->pos.z = 0.0f; verts->tex.x = maxu; verts->tex.y = maxv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; return 0; @@ -1697,7 +1710,7 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_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, count * 5 * sizeof (VertexPositionColor), 0, &cmd->data.draw.first); + 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); @@ -1713,7 +1726,6 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * maxu = (float) srcrect->x / texture->w; } - } if (flip & SDL_FLIP_VERTICAL) { minv = (float) srcrect->y / texture->h; maxv = (float) (srcrect->y + srcrect->h) / texture->h; @@ -1732,10 +1744,10 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * verts->pos.z = 0.0f; verts->tex.x = minu; verts->tex.y = minv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; verts->pos.x = minx; @@ -1743,10 +1755,10 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * verts->pos.z = 0.0f; verts->tex.x = minu; verts->tex.y = maxv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; verts->pos.x = maxx; @@ -1754,10 +1766,10 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * verts->pos.z = 0.0f; verts->tex.x = maxu; verts->tex.y = minv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + verts->color.x = r; + verts->color.y = g; + verts->color.z = b; + verts->color.w = a; verts++; verts->pos.x = maxx; @@ -1765,21 +1777,21 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * verts->pos.z = 0.0f; verts->tex.x = maxu; verts->tex.y = maxv; - verts->color.r = r; - verts->color.g = g; - verts->color.b = b; - verts->color.a = a; + 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.u = 0.0f; - verts->tex.v = 0.0f; - verts->color.r = 0; - verts->color.g = 0; - verts->color.b = 0; - verts->color.a = 0; + 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; @@ -1947,6 +1959,18 @@ D3D11_UpdateViewport(SDL_Renderer * renderer) return 0; } +static ID3D11RenderTargetView * +D3D11_GetCurrentRenderTargetView(SDL_Renderer * renderer) +{ + D3D11_RenderData *data = (D3D11_RenderData *)renderer->driverdata; + if (data->currentOffscreenRenderTargetView) { + return data->currentOffscreenRenderTargetView; + } + else { + return data->mainRenderTargetView; + } +} + static int D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11PixelShader * shader, const int numShaderResources, ID3D11ShaderResourceView ** shaderResources, @@ -1954,8 +1978,7 @@ D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11 { D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata; - static const Float4X4 identity = MatrixIdentity(); - const Float4X4 *newmatrix = matrix ? matrix : &identity; + const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity; ID3D11RasterizerState *rasterizerState; ID3D11RenderTargetView *renderTargetView = D3D11_GetCurrentRenderTargetView(renderer); ID3D11ShaderResourceView *shaderResource; @@ -1984,7 +2007,7 @@ D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11 /* D3D11_GetViewportAlignedD3DRect will have set the SDL error */ return -1; } - ID3D11DeviceContext_RSSetScissorRects(data->d3dContext, 1, &scissorRect); + ID3D11DeviceContext_RSSetScissorRects(rendererData->d3dContext, 1, &scissorRect); } rendererData->cliprectDirty = SDL_FALSE; } @@ -2008,11 +2031,10 @@ D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11 } } 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) { @@ -2038,13 +2060,13 @@ D3D11_SetDrawState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, ID3D11 rendererData->currentSampler = sampler; } - if (SDL_memcmp(&data->vertexShaderConstantsData.model, newmatrix, sizeof (*newmatrix)) != 0) { - SDL_memcpy(&data->vertexShaderConstantsData.model, newmatrix, sizeof (*newmatrix)); - ID3D11DeviceContext_UpdateSubresource(data->d3dContext, - (ID3D11Resource *)data->vertexShaderConstants, + 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, - &data->vertexShaderConstantsData, + &rendererData->vertexShaderConstantsData, 0, 0 ); @@ -2123,16 +2145,16 @@ D3D11_SetCopyState(SDL_Renderer * renderer, const SDL_RenderCommand *cmd, const } - return D3D11_SetDrawState(renderer, cmd, renderer, rendererData->pixelShaders[SHADER_RGB], + 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 UINT vertexStart, const UINT vertexCount) +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, vertexCount, vertexStart); + ID3D11DeviceContext_Draw(rendererData->d3dContext, (UINT) vertexCount, (UINT) vertexStart); } static int @@ -2161,7 +2183,7 @@ D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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)); - data->drawstate.viewportDirty = SDL_TRUE; + rendererData->viewportDirty = SDL_TRUE; } break; } @@ -2186,7 +2208,7 @@ D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver (cmd->data.color.b / 255.0f), (cmd->data.color.a / 255.0f) }; - ID3D11DeviceContext_ClearRenderTargetView(data->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), colorRGBA); + ID3D11DeviceContext_ClearRenderTargetView(rendererData->d3dContext, D3D11_GetCurrentRenderTargetView(renderer), colorRGBA); break; } @@ -2206,7 +2228,7 @@ D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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].x != verts[count - 1].x || verts[0].y != verts[count - 1].y) { + 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; @@ -2227,7 +2249,7 @@ D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver case SDL_RENDERCMD_COPY: { const size_t first = cmd->data.draw.first; const size_t start = first / sizeof (VertexPositionColor); - D3D11_SetCopyState(data, cmd, NULL); + D3D11_SetCopyState(renderer, cmd, NULL); D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start, 4); break; } @@ -2236,12 +2258,12 @@ D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver const size_t first = cmd->data.draw.first; const size_t start = first / sizeof (VertexPositionColor); const VertexPositionColor *verts = (VertexPositionColor *) (((Uint8 *) vertices) + first); - const Vertex *transvert = verts + 4; + 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(data, cmd, &matrix); + D3D11_SetCopyState(renderer, cmd, &matrix); D3D11_DrawPrimitives(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, start, 4); break; } @@ -2256,26 +2278,6 @@ D3D11_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver 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; - if (data->currentOffscreenRenderTargetView) { - return data->currentOffscreenRenderTargetView; - } else { - return data->mainRenderTargetView; - } -} - static int D3D11_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 format, void * pixels, int pitch) @@ -2458,6 +2460,8 @@ D3D11_CreateRenderer(SDL_Window * window, Uint32 flags) return NULL; } + data->identity = MatrixIdentity(); + renderer->WindowEvent = D3D11_WindowEvent; renderer->SupportsBlendMode = D3D11_SupportsBlendMode; renderer->CreateTexture = D3D11_CreateTexture; From a0efc7ab099b4c96f4f98a5e595844b4e7d0c821 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 3 Oct 2018 19:05:20 -0400 Subject: [PATCH 29/43] render: D3D11 now cycles through 8 vertex buffers. This means it doesn't have to block while the current frame finishes using the vertex buffer; it just moves on to the next, probably-not-in-use buffer. --HG-- branch : SDL-ryan-batching-renderer --- src/render/direct3d11/SDL_render_d3d11.c | 43 +++++++++++++----------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index e9baadc7f..ea2fd3a5e 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -123,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; @@ -155,6 +156,7 @@ typedef struct int currentViewportRotation; SDL_bool viewportDirty; Float4X4 identity; + int currentVertexBuffer; } D3D11_RenderData; @@ -242,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]); @@ -1798,28 +1802,18 @@ D3D11_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * } -/* !!! FIXME: rotate through a few vertex buffers so the GPU has time to finish using them */ 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; + const int vbidx = rendererData->currentVertexBuffer; - if (rendererData->vertexBuffer) { - ID3D11Buffer_GetDesc(rendererData->vertexBuffer, &vertexBufferDesc); - } else { - SDL_zero(vertexBufferDesc); - } - - if (rendererData->vertexBuffer && vertexBufferDesc.ByteWidth >= dataSizeInBytes) { + if (rendererData->vertexBuffers[vbidx] && rendererData->vertexBufferSizes[vbidx] >= dataSizeInBytes) { D3D11_MAPPED_SUBRESOURCE mappedResource; result = ID3D11DeviceContext_Map(rendererData->d3dContext, - (ID3D11Resource *)rendererData->vertexBuffer, + (ID3D11Resource *)rendererData->vertexBuffers[vbidx], 0, D3D11_MAP_WRITE_DISCARD, 0, @@ -1830,10 +1824,16 @@ D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, return -1; } SDL_memcpy(mappedResource.pData, vertexData, dataSizeInBytes); - ID3D11DeviceContext_Unmap(rendererData->d3dContext, (ID3D11Resource *)rendererData->vertexBuffer, 0); + ID3D11DeviceContext_Unmap(rendererData->d3dContext, (ID3D11Resource *)rendererData->vertexBuffers[vbidx], 0); } else { - SAFE_RELEASE(rendererData->vertexBuffer); + 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; @@ -1847,7 +1847,7 @@ D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, &vertexBufferDesc, &vertexBufferData, - &rendererData->vertexBuffer + &rendererData->vertexBuffers[vbidx] ); if (FAILED(result)) { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateBuffer [vertex buffer]"), result); @@ -1857,12 +1857,17 @@ D3D11_UpdateVertexBuffer(SDL_Renderer *renderer, ID3D11DeviceContext_IASetVertexBuffers(rendererData->d3dContext, 0, 1, - &rendererData->vertexBuffer, + &rendererData->vertexBuffers[vbidx], &stride, &offset ); } + rendererData->currentVertexBuffer++; + if (rendererData->currentVertexBuffer >= SDL_arraysize(rendererData->vertexBuffers)) { + rendererData->currentVertexBuffer = 0; + } + return 0; } From 49670f5555fb7cd4bbe2bfe7be1d597cfed03ad1 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 3 Oct 2018 23:37:29 -0400 Subject: [PATCH 30/43] render: moved software renderer to new interface. --HG-- branch : SDL-ryan-batching-renderer --- src/render/software/SDL_render_sw.c | 791 +++++++++++++--------------- 1 file changed, 380 insertions(+), 411 deletions(-) diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 0c14cac5a..807391abe 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) { @@ -252,46 +123,6 @@ SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) return 0; } -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; + return 0; /* nothing to do in this backend. */ +} - if (!surface) { - /* We'll update the viewport after we recreate the surface */ - return 0; +static int +SW_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) +{ + SDL_Point *verts = (SDL_Point *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Point), 0, &cmd->data.draw.first); + size_t i; + + if (!verts) { + return -1; + } + + cmd->data.draw.count = count; + + 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++, points++) { + verts->x = (int)(x + points->x); + verts->y = (int)(y + points->y); + } + } else { + for (i = 0; i < count; i++, verts++, points++) { + verts->x = (int)points->x; + verts->y = (int)points->y; + } } - SDL_SetClipRect(data->surface, &renderer->viewport); return 0; } static int -SW_UpdateClipRect(SDL_Renderer * renderer) +SW_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { - 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); + SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Rect), 0, &cmd->data.draw.first); + size_t i; + + if (!verts) { + return -1; + } + + cmd->data.draw.count = count; + + 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++, 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); } } + return 0; } static int -SW_RenderClear(SDL_Renderer * renderer) +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); - Uint32 color; - SDL_Rect clip_rect; + SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (SDL_Rect), 0, &cmd->data.draw.first); - if (!surface) { + if (!verts) { return -1; } - color = SDL_MapRGBA(surface->format, - renderer->r, renderer->g, renderer->b, renderer->a); + cmd->data.draw.count = 1; + + 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 { + verts->x = (int)dstrect->x; + verts->y = (int)dstrect->y; + } + verts->w = (int)dstrect->w; + verts->h = (int)dstrect->h; + + return 0; +} + +typedef struct CopyExData +{ + SDL_Rect srcrect; + SDL_Rect dstrect; + double angle; + SDL_FPoint center; + SDL_RendererFlip flip; +} CopyExData; + +static int +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) +{ + CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first); + + if (!verts) { + return -1; + } + + cmd->data.draw.count = 1; + + SDL_memcpy(&verts->srcrect, srcrect, sizeof (SDL_Rect)); + + if (renderer->viewport.x || renderer->viewport.y) { + verts->dstrect.x = (int)(renderer->viewport.x + dstrect->x); + verts->dstrect.y = (int)(renderer->viewport.y + dstrect->y); + } else { + verts->dstrect.x = (int)dstrect->x; + verts->dstrect.y = (int)dstrect->y; + } + 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; - /* 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; - } - - 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); - } - } else { - for (i = 0; i < count; ++i) { - final_points[i].x = (int)points[i].x; - final_points[i].y = (int)points[i].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; -} - -static int -SW_RenderDrawLines(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; - } - - 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); - } - } else { - for (i = 0; i < count; ++i) { - final_points[i].x = (int)points[i].x; - final_points[i].y = (int)points[i].y; - } - } - - /* 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; -} - -static int -SW_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count) -{ - SDL_Surface *surface = SW_ActivateRenderer(renderer); - SDL_Rect *final_rects; - int i, status; - - if (!surface) { - 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); - } - } - - 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); - } else { - status = SDL_BlendFillRects(surface, final_rects, count, - renderer->blendMode, - renderer->r, renderer->g, renderer->b, - renderer->a); - } - SDL_stack_free(final_rects); - - return status; -} - -static int -SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) -{ - SDL_Surface *surface = SW_ActivateRenderer(renderer); - SDL_Surface *src = (SDL_Surface *) texture->driverdata; - SDL_Rect final_rect; - - if (!surface) { - 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; - - 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); - } -} - -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; } @@ -673,7 +393,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; @@ -687,7 +407,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; @@ -718,32 +438,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; @@ -819,6 +539,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) @@ -877,6 +761,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: */ From 5f11d18f0d3e3dccde5b58907ad348ab9eb8ba86 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Oct 2018 16:11:43 -0400 Subject: [PATCH 31/43] render: Move PSP backend to new interface. I have no idea if this works (or if it ever worked, having now examined this code), as I have no way to compile or test this. If it's broken, send patches. :) --HG-- branch : SDL-ryan-batching-renderer --- src/render/psp/SDL_render_psp.c | 997 ++++++++++++++++---------------- 1 file changed, 514 insertions(+), 483 deletions(-) diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index babc2526a..eaf2ac705 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 @@ -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,17 +431,221 @@ PSP_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) static int PSP_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) { + return 0; +} + +static int +PSP_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) +{ + return 0; /* nothing to do in this backend. */ +} + +static int +PSP_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) +{ + VertV *verts = (VertV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertV), 4, &cmd->data.draw.first); + size_t i; + + if (!verts) { + return -1; + } + + cmd->data.draw.count = count; + + for (i = 0; i < count; i++, verts++, points++) { + verts->x = points->x; + verts->y = points->y; + verts->z = 0.0f; + } return 0; } static int -PSP_UpdateViewport(SDL_Renderer * renderer) +PSP_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) { + VertV *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (VertV), 4, &cmd->data.draw.first); + size_t i; + + if (!verts) { + return -1; + } + + cmd->data.draw.count = count; + for (i = 0; i < count; i++, rects++) { + const SDL_FRect *rect = &rects[i]; + 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; } +static int +PSP_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, + const SDL_Rect * srcrect, const SDL_FRect * dstrect) +{ + VertTV *verts; + const float x = dstrect->x; + const float y = dstrect->y; + const float width = dstrect->w; + const float height = dstrect->h; + + 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) + { + verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (VertTV), 4, &cmd->data.draw.first); + if (!verts) { + return -1; + } + + 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; + 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; + + 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) + { + const float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice; + const float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep; + + SDL_assert(start < end); + + verts->u = curU; + verts->v = v0; + verts->x = curX; + verts->y = y; + verts->z = 0; + + curU += sourceWidth; + curX += polyWidth; + + verts->u = curU; + verts->v = v1; + verts->x = curX; + verts->y = (y + height); + verts->z = 0; + } + } + + return 0; +} + +static int +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) +{ + 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; + + MathSincos(degToRad(angle), &s, &c); + + const float cw = c * width; + const float sw = s * width; + const float ch = c * height; + const float sh = s * height; + + if (flip & SDL_FLIP_VERTICAL) { + Swap(&v0, &v1); + } + + if (flip & SDL_FLIP_HORIZONTAL) { + Swap(&u0, &u1); + } + + 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++; + + return 0; +} static void PSP_SetBlendMode(SDL_Renderer * renderer, int blendMode) @@ -618,341 +677,183 @@ PSP_SetBlendMode(SDL_Renderer * renderer, int blendMode) } } - - static int -PSP_RenderClear(SDL_Renderer * renderer) +PSP_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { - /* 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); + PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; + size_t i; - return 0; -} - -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)); - - for (i = 0; i < count; ++i) { - vertices[i].x = points[i].x; - vertices[i].y = points[i].y; - vertices[i].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) -{ - int color = renderer->a << 24 | renderer->b << 16 | renderer->g << 8 | renderer->r; - int i; - StartDrawing(renderer); - VertV* vertices = (VertV*)sceGuGetMemory(count*sizeof(VertV)); - - for (i = 0; i < count; ++i) { - vertices[i].x = points[i].x; - vertices[i].y = points[i].y; - vertices[i].z = 0.0f; - } - - 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) { - 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); + /* 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); - return 0; -} + 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; + } -#define PI 3.14159265358979f + 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; + } -#define radToDeg(x) ((x)*180.f/PI) -#define degToRad(x) ((x)*PI/180.f) + 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; + } -float MathAbs(float x) -{ - float result; + 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; + } - __asm__ volatile ( - "mtv %1, S000\n" - "vabs.s S000, S000\n" - "mfv %0, S000\n" - : "=r"(result) : "r"(x)); + 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; + } - return result; -} + 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; + } -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)); -} + 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); -void Swap(float *a, float *b) -{ - float n=*a; - *a = *b; - *b = n; -} + 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); + } -static int -PSP_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, - const SDL_Rect * srcrect, const SDL_FRect * dstrect) -{ - float x, y, width, height; - float u0, v0, u1, v1; - unsigned char alpha; + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); - x = dstrect->x; - y = dstrect->y; - width = dstrect->w; - height = dstrect->h; + if(alpha != 255) { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } + break; + } - u0 = srcrect->x; - v0 = srcrect->y; - u1 = srcrect->x + srcrect->w; - v1 = srcrect->y + srcrect->h; + 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); - alpha = texture->a; + 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); + } - StartDrawing(renderer); - TextureActivate(texture); - PSP_SetBlendMode(renderer, renderer->blendMode); + sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, verts); - 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); - } + if(alpha != 255) { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } + break; + } - 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; - - sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2, 0, vertices); - } - else - { - float start, end; - float curU = u0; - float curX = x; - float endX = x + width; - float slice = 64.0f; - float ustep = (u1 - u0)/width * slice; - - if(ustep < 0.0f) - ustep = -ustep; - - for(start = 0, end = width; start < end; start += slice) - { - VertTV* vertices = (VertTV*)sceGuGetMemory((sizeof(VertTV))<<1); - - float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice; - float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep; - - vertices[0].u = curU; - vertices[0].v = v0; - vertices[0].x = curX; - vertices[0].y = y; - vertices[0].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); + case SDL_RENDERCMD_NO_OP: + break; } + + 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 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; - - 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; - - centerx = center->x; - centery = center->y; - - alpha = texture->a; - - 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); - } - -/* x += width * 0.5f; */ -/* y += height * 0.5f; */ - x += centerx; - y += centery; - - float c, s; - - MathSincos(degToRad(angle), &s, &c); - -/* width *= 0.5f; */ -/* height *= 0.5f; */ - width -= centerx; - height -= centery; - - - float cw = c*width; - float sw = s*width; - float ch = c*height; - float sh = s*height; - - VertTV* vertices = (VertTV*)sceGuGetMemory(sizeof(VertTV)<<2); - - vertices[0].u = u0; - vertices[0].v = v0; - vertices[0].x = x - cw + sh; - vertices[0].y = y - sw - ch; - vertices[0].z = 0; - - vertices[1].u = u0; - vertices[1].v = v1; - vertices[1].x = x - cw - sh; - vertices[1].y = y - sw + ch; - vertices[1].z = 0; - - vertices[2].u = u1; - vertices[2].v = v1; - vertices[2].x = x + cw - sh; - vertices[2].y = y + sw + ch; - vertices[2].z = 0; - - vertices[3].u = u1; - vertices[3].v = v0; - vertices[3].x = x + cw + sh; - vertices[3].y = y + sw - ch; - vertices[3].z = 0; - - 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); - } - - sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, vertices); - - if(alpha != 255) - sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); - return 0; -} - 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: */ From 7d96cb5cef57ef891c94497152818d994787c72c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Oct 2018 16:34:44 -0400 Subject: [PATCH 32/43] render: Added SDL_RenderFlush(). --HG-- branch : SDL-ryan-batching-renderer --- include/SDL_render.h | 25 +++++++++++++++++++++++++ src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/render/SDL_render.c | 6 ++++++ 4 files changed, 33 insertions(+) diff --git a/include/SDL_render.h b/include/SDL_render.h index d33619297..850e908ec 100644 --- a/include/SDL_render.h +++ b/include/SDL_render.h @@ -876,6 +876,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/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index b1f029f37..000604168 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -696,3 +696,4 @@ #define SDL_SensorUpdate SDL_SensorUpdate_REAL #define SDL_IsTablet SDL_IsTablet_REAL #define SDL_GetDisplayOrientation SDL_GetDisplayOrientation_REAL +#define SDL_RenderFlush SDL_RenderFlush_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 6619f882a..4461d5519 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -738,3 +738,4 @@ SDL_DYNAPI_PROC(void,SDL_SensorClose,(SDL_Sensor *a),(a),) SDL_DYNAPI_PROC(void,SDL_SensorUpdate,(void),(),) SDL_DYNAPI_PROC(SDL_bool,SDL_IsTablet,(void),(),return) SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetDisplayOrientation,(int a),(a),return) +SDL_DYNAPI_PROC(int,SDL_RenderFlush,(SDL_Renderer *a),(a),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 5c7e36da0..5bbcd790b 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -254,6 +254,12 @@ 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) { From b7db24b1410662ec0e4ac65c3dae35f0bdac57f2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Oct 2018 20:21:23 -0400 Subject: [PATCH 33/43] metal: Don't try to create a zero-byte vertex buffer. (Which will cause a crash in Metal, or an assert in the validation layer.) --HG-- branch : SDL-ryan-batching-renderer --- src/render/metal/SDL_render_metal.m | 37 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 49d21b89b..affe0de25 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -1052,6 +1052,7 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver { @autoreleasepool { METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; METAL_DrawStateCache statecache; + id mtlbufvertex = nil; statecache.pipeline = nil; statecache.constants_offset = CONSTANTS_OFFSET_INVALID; @@ -1063,24 +1064,26 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver statecache.color_offset = 0; // !!! FIXME: have a ring of pre-made MTLBuffers we cycle through? How expensive is creation? - id 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); + if (vertsize > 0) { + id 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. - id mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModePrivate]; - #if !__has_feature(objc_arc) - [mtlbufvertex autorelease]; - #endif - mtlbufvertex.label = @"SDL vertex data"; - id cmdbuffer = [data.mtlcmdqueue commandBuffer]; - id blitcmd = [cmdbuffer blitCommandEncoder]; - [blitcmd copyFromBuffer:mtlbufvertexstaging sourceOffset:0 toBuffer:mtlbufvertex destinationOffset:0 size:vertsize]; - [blitcmd endEncoding]; - [cmdbuffer commit]; + // 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 cmdbuffer = [data.mtlcmdqueue commandBuffer]; + id blitcmd = [cmdbuffer blitCommandEncoder]; + [blitcmd copyFromBuffer:mtlbufvertexstaging sourceOffset:0 toBuffer:mtlbufvertex destinationOffset:0 size:vertsize]; + [blitcmd endEncoding]; + [cmdbuffer commit]; + } // If there's a command buffer here unexpectedly (app requested one?). Commit it so we can start fresh. [data.mtlcmdencoder endEncoding]; From ceefc68ccbda0f8693efc8d58b2bedcbfb7b3202 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Oct 2018 20:21:58 -0400 Subject: [PATCH 34/43] metal: CopyEx transform matrix must be aligned for constant buffer access. --HG-- branch : SDL-ryan-batching-renderer --- src/render/metal/SDL_render_metal.m | 40 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index affe0de25..4fe90dc71 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -871,14 +871,33 @@ METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * const float rads = (float)(M_PI * (float) angle / 180.0f); const float c = cosf(rads), s = sinf(rads); float minu, maxu, minv, maxv; - // !!! FIXME: use an index buffer const size_t vertlen = (sizeof (float) * 32); - float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); + float *verts; + + // 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; } - cmd->data.draw.count = 1; + // 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; + } minu = normtex(srcquad->x, texw); maxu = normtex(srcquad->x + srcquad->w, texw); @@ -916,19 +935,6 @@ METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * *(verts++) = maxu; *(verts++) = minv; - // 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; - return 0; } @@ -1172,7 +1178,7 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver case SDL_RENDERCMD_COPY_EX: { SetCopyState(renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache); - [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(16*sizeof (float)) atIndex:3]; // transform + [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.count atIndex:3]; // transform [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; break; } From 3b9a14d82350da011f02c3b6754728e40776adb2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Oct 2018 20:22:28 -0400 Subject: [PATCH 35/43] opengles2: Fixed incorrect cliprect state. --HG-- branch : SDL-ryan-batching-renderer --- src/render/opengles2/SDL_render_gles2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 1acc25e7c..929e3765a 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -969,7 +969,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I } else { data->glEnable(GL_SCISSOR_TEST); } - data->drawstate.cliprect_enabled_dirty = SDL_TRUE; + data->drawstate.cliprect_enabled_dirty = SDL_FALSE; } if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) { @@ -979,6 +979,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I data->drawstate.target ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h, rect->w, rect->h); SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); + data->drawstate.cliprect_dirty = SDL_FALSE; } if (texture != data->drawstate.texture) { From 41007ab39f1a598ac96754159d99166b98558794 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Oct 2018 21:10:42 -0400 Subject: [PATCH 36/43] opengles2: removed useless memcpy. --HG-- branch : SDL-ryan-batching-renderer --- src/render/opengles2/SDL_render_gles2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 929e3765a..77ba19418 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -978,7 +978,6 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I 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); - SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); data->drawstate.cliprect_dirty = SDL_FALSE; } From 1c7254cb5885d558ebdd9d89779c6d2f9185bf09 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 6 Oct 2018 17:08:04 -0400 Subject: [PATCH 37/43] opengles2: Fixed several incorrect things. --HG-- branch : SDL-ryan-batching-renderer --- src/render/opengles2/SDL_render_gles2.c | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 77ba19418..97d1d4461 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -878,8 +878,7 @@ GLES2_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * if (flip & SDL_FLIP_HORIZONTAL) { minx = dstrect->x + dstrect->w; maxx = dstrect->x; - } - else { + } else { minx = dstrect->x; maxx = dstrect->x + dstrect->w; } @@ -887,16 +886,16 @@ GLES2_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * if (flip & SDL_FLIP_VERTICAL) { miny = dstrect->y + dstrect->h; maxy = dstrect->y; - } - else { + } else { miny = dstrect->y; maxy = dstrect->y + dstrect->h; } - minu = (GLfloat) (srcquad->x / texture->w); - maxu = (GLfloat) ((srcquad->x + srcquad->w) / texture->w); - minv = (GLfloat) (srcquad->y / texture->h); - maxv = (GLfloat) ((srcquad->y + srcquad->h) / texture->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; @@ -988,7 +987,6 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I data->drawstate.texturing = SDL_FALSE; } else { data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_TEXCOORD); - data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) (cmd->data.draw.first + (sizeof (GLfloat) * 8))); data->drawstate.texturing = SDL_TRUE; } } @@ -1015,6 +1013,10 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I data->drawstate.texture = texture; } + if (texture) { + data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) (cmd->data.draw.first + (sizeof (GLfloat) * 8))); + } + if (GLES2_SelectProgram(data, imgsrc, texture ? texture->w : 0, texture ? texture->h : 0) < 0) { return -1; } @@ -1061,8 +1063,6 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I if (is_copy_ex) { data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_ANGLE); data->glEnableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_CENTER); - 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))); } else { data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_ANGLE); data->glDisableVertexAttribArray((GLenum) GLES2_ATTRIBUTE_CENTER); @@ -1070,6 +1070,11 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I data->drawstate.is_copy_ex = is_copy_ex; } + 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 0; } From 637cfa5d6b8b852d2f90ca647b51ca64c4a2b37d Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 22 Oct 2018 20:50:32 -0400 Subject: [PATCH 38/43] Small stack allocations fall back to malloc if they're unexpectedly large. --HG-- branch : SDL-ryan-batching-renderer --- src/SDL_assert.c | 1 + src/SDL_internal.h | 4 ++++ src/SDL_log.c | 8 ++++--- src/core/android/SDL_android.c | 5 +++-- src/file/SDL_rwops.c | 1 + src/joystick/SDL_gamecontroller.c | 5 +++-- src/joystick/SDL_joystick.c | 5 +++-- src/loadso/dlopen/SDL_sysloadso.c | 5 +++-- src/main/windows/SDL_windows_main.c | 10 +++++---- src/render/SDL_render.c | 25 +++++++++++++--------- src/render/opengl/SDL_render_gl.c | 5 +++-- src/render/opengl/SDL_shaders_gl.c | 5 +++-- src/render/opengles/SDL_render_gles.c | 5 +++-- src/render/opengles2/SDL_render_gles2.c | 10 +++++---- src/video/cocoa/SDL_cocoamodes.m | 7 +++--- src/video/windows/SDL_windowsevents.c | 13 ++++++----- src/video/windows/SDL_windowsframebuffer.c | 5 +++-- src/video/windows/SDL_windowsmouse.c | 5 +++-- src/video/windows/SDL_windowswindow.c | 12 ++++++----- 19 files changed, 84 insertions(+), 52 deletions(-) diff --git a/src/SDL_assert.c b/src/SDL_assert.c index 1ca083ad4..5a9556d4e 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 e0ba2a82c..f8215bf34 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 b1bf27d7a..b89f7ee3a 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 91786042d..81ccd4a96 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -466,10 +466,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 @@ -502,7 +503,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/file/SDL_rwops.c b/src/file/SDL_rwops.c index cf5d3aa0b..5b0969d56 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 fa647819c..41100a413 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -203,13 +203,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; } @@ -220,7 +221,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 72c5656d2..9d22122a4 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -752,13 +752,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; } @@ -769,7 +770,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 18b4b8436..ed89a211d 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/main/windows/SDL_windows_main.c b/src/main/windows/SDL_windows_main.c index 5e643a44b..212a6a3e2 100644 --- a/src/main/windows/SDL_windows_main.c +++ b/src/main/windows/SDL_windows_main.c @@ -131,6 +131,7 @@ main_utf8(int argc, char *argv[]) static int main_getcmdline() { + SDL_bool isstack; char **argv; int argc; char *cmdline; @@ -150,7 +151,7 @@ main_getcmdline() /* Parse it into argv and argc */ argc = ParseCommandLine(cmdline, NULL); - argv = SDL_stack_alloc(char *, argc + 1); + argv = SDL_small_alloc(char *, argc + 1, &isstack); if (argv == NULL) { return OutOfMemory(); } @@ -158,7 +159,7 @@ main_getcmdline() retval = main_utf8(argc, argv); - SDL_stack_free(argv); + SDL_small_free(argv, isstack); SDL_free(cmdline); return retval; @@ -177,8 +178,9 @@ console_ansi_main(int argc, char *argv[]) int console_wmain(int argc, wchar_t *wargv[], wchar_t *wenvp) { + SDL_bool isstack; int retval = 0; - char **argv = SDL_stack_alloc(char*, argc + 1); + char **argv = SDL_small_alloc(char*, argc + 1, &isstack); int i; for (i = 0; i < argc; ++i) { @@ -189,7 +191,7 @@ console_wmain(int argc, wchar_t *wargv[], wchar_t *wenvp) retval = main_utf8(argc, argv); /* !!! FIXME: we are leaking all the elements of argv we allocated. */ - SDL_stack_free(argv); + SDL_small_free(argv, isstack); return retval; } diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 5bbcd790b..b03524b4c 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -2176,8 +2176,9 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, SDL_FRect *frects; int i; int retval = -1; + SDL_bool isstack; - frects = SDL_stack_alloc(SDL_FRect, count); + frects = SDL_small_alloc(SDL_FRect, count, &isstack); if (!frects) { return SDL_OutOfMemory(); } @@ -2190,7 +2191,7 @@ RenderDrawPointsWithRects(SDL_Renderer * renderer, retval = QueueCmdFillRects(renderer, frects, count); - SDL_stack_free(frects); + SDL_small_free(frects, isstack); return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } @@ -2202,6 +2203,7 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, SDL_FPoint *fpoints; int i; int retval; + SDL_bool isstack; CHECK_RENDERER_MAGIC(renderer, -1); @@ -2221,7 +2223,7 @@ 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(); } @@ -2232,7 +2234,7 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, retval = QueueCmdDrawPoints(renderer, fpoints, count); - SDL_stack_free(fpoints); + SDL_small_free(fpoints, isstack); return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } @@ -2258,8 +2260,9 @@ RenderDrawLinesWithRects(SDL_Renderer * renderer, SDL_FPoint fpoints[2]; 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(); } @@ -2295,7 +2298,7 @@ RenderDrawLinesWithRects(SDL_Renderer * renderer, retval += QueueCmdFillRects(renderer, frects, nrects); - SDL_stack_free(frects); + SDL_small_free(frects, isstack); if (retval < 0) { retval = -1; @@ -2310,6 +2313,7 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, SDL_FPoint *fpoints; int i; int retval; + SDL_bool isstack; CHECK_RENDERER_MAGIC(renderer, -1); @@ -2329,7 +2333,7 @@ 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(); } @@ -2340,7 +2344,7 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, retval = QueueCmdDrawLines(renderer, fpoints, count); - SDL_stack_free(fpoints); + SDL_small_free(fpoints, isstack); return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } @@ -2426,6 +2430,7 @@ SDL_RenderFillRects(SDL_Renderer * renderer, SDL_FRect *frects; int i; int retval; + SDL_bool isstack; CHECK_RENDERER_MAGIC(renderer, -1); @@ -2441,7 +2446,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(); } @@ -2454,7 +2459,7 @@ SDL_RenderFillRects(SDL_Renderer * renderer, retval = QueueCmdFillRects(renderer, frects, count); - SDL_stack_free(frects); + SDL_small_free(frects, isstack); return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index ef8507ebf..7d0f08d71 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -1348,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); @@ -1360,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, diff --git a/src/render/opengl/SDL_shaders_gl.c b/src/render/opengl/SDL_shaders_gl.c index 251b54d13..650b24463 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/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 6f662afa9..a410152e7 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -972,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); @@ -984,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, diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 97d1d4461..c6c9f80f6 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -601,19 +601,20 @@ GLES2_CacheShader(GLES2_RenderData *data, 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"); } @@ -1814,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); @@ -1826,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, diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 97ccd945d..b614b795b 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/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 5773f3995..6505b89b7 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -901,7 +901,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; @@ -909,7 +910,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; } @@ -943,7 +944,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; @@ -954,17 +955,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 e07d9c431..f884f7fa0 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 1ddeae24b..a8abc48c3 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 ba5ef0583..e6ce7633b 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); From c025d6c10e1c231133280c6f0075fffb14643125 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 18 Oct 2018 11:59:48 -0400 Subject: [PATCH 39/43] cocoa: GL_GetDrawableSize only uses -[NSView convertRectToBacking] for highDPI. On Mojave, this will report large numbers for retina displays in fullscreen mode, which isn't how it works on previous versions. (transplanted from a02aa66a76d84f7cb4181125c926e978b2284a57) --HG-- branch : SDL-ryan-batching-renderer extra : transplant_source : %A0%2A%A6jv%D8O%7C%B4%18%11%25%C9%26%E9x%B2%28JW --- src/video/cocoa/SDL_cocoaopengl.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/video/cocoa/SDL_cocoaopengl.m b/src/video/cocoa/SDL_cocoaopengl.m index 5f18a2ee8..c7d0c60e5 100644 --- a/src/video/cocoa/SDL_cocoaopengl.m +++ b/src/video/cocoa/SDL_cocoaopengl.m @@ -347,10 +347,12 @@ Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) NSView *contentView = [windata->nswindow contentView]; NSRect viewport = [contentView bounds]; - /* This gives us the correct viewport for a Retina-enabled view, only - * supported on 10.7+. */ - if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { - viewport = [contentView convertRectToBacking:viewport]; + if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { + /* This gives us the correct viewport for a Retina-enabled view, only + * supported on 10.7+. */ + if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { + viewport = [contentView convertRectToBacking:viewport]; + } } if (w) { From dc792b46b978a90fe4808a13dc07ca862012fc16 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 18 Oct 2018 12:05:05 -0400 Subject: [PATCH 40/43] cocoa: Fix OpenGL rendering on macOS 10.14 ("Mojave"). Fixes Bugzilla #4272. (transplanted from 86dcfbbcacaf0c4a556501644af11b7f99b4352d) --HG-- branch : SDL-ryan-batching-renderer extra : transplant_source : %86%DC%FB%BC%AC%AF%0CJUe%01dJ%F1%1B%7F%99%B45- --- src/video/cocoa/SDL_cocoawindow.m | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index 1785ab1ee..cd61c539e 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -632,8 +632,6 @@ SetWindowStyle(SDL_Window * window, NSUInteger style) const unsigned int newflags = [NSEvent modifierFlags] & NSEventModifierFlagCapsLock; _data->videodata->modifierFlags = (_data->videodata->modifierFlags & ~NSEventModifierFlagCapsLock) | newflags; SDL_ToggleModState(KMOD_CAPS, newflags != 0); - - ScheduleContextUpdates(_data); } - (void)windowDidResignKey:(NSNotification *)aNotification @@ -1145,14 +1143,18 @@ SetWindowStyle(SDL_Window * window, NSUInteger style) - (BOOL)mouseDownCanMoveWindow; - (void)drawRect:(NSRect)dirtyRect; - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent; +- (BOOL)wantsUpdateLayer; +- (void)updateLayer; @end @implementation SDLView + - (void)setSDLWindow:(SDL_Window*)window { _sdlWindow = window; } +/* this is used on older macOS revisions. 10.8 and later use updateLayer. */ - (void)drawRect:(NSRect)dirtyRect { /* Force the graphics context to clear to black so we don't get a flash of @@ -1163,6 +1165,21 @@ SetWindowStyle(SDL_Window * window, NSUInteger style) SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); } +-(BOOL) wantsUpdateLayer +{ + return YES; +} + +-(void) updateLayer +{ + /* Force the graphics context to clear to black so we don't get a flash of + white until the app is ready to draw. In practice on modern macOS, this + only gets called for window creation and other extraordinary events. */ + self.layer.backgroundColor = NSColor.blackColor.CGColor; + ScheduleContextUpdates((SDL_WindowData *) _sdlWindow->driverdata); + SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); +} + - (void)rightMouseDown:(NSEvent *)theEvent { [[self nextResponder] rightMouseDown:theEvent]; @@ -1345,6 +1362,7 @@ Cocoa_CreateWindow(_THIS, SDL_Window * window) [contentView setWantsBestResolutionOpenGLSurface:YES]; } } + #if SDL_VIDEO_OPENGL_ES2 #if SDL_VIDEO_OPENGL_EGL if ((window->flags & SDL_WINDOW_OPENGL) && From 538f2f7649dc03c308dc68297f2556463329a351 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 23 Oct 2018 01:34:03 -0400 Subject: [PATCH 41/43] render: Add floating point versions of various draw APIs. --HG-- branch : SDL-ryan-batching-renderer --- include/SDL_rect.h | 30 +- include/SDL_render.h | 142 +++++++++ src/dynapi/SDL_dynapi_overrides.h | 10 + src/dynapi/SDL_dynapi_procs.h | 10 + src/render/SDL_render.c | 513 ++++++++++++++++++++++++++---- src/render/SDL_sysrender.h | 14 - 6 files changed, 643 insertions(+), 76 deletions(-) diff --git a/include/SDL_rect.h b/include/SDL_rect.h index 543bb6186..986764cd6 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 850e908ec..738b7392a 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. * diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 000604168..0bc6f44f0 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -697,3 +697,13 @@ #define SDL_IsTablet SDL_IsTablet_REAL #define SDL_GetDisplayOrientation SDL_GetDisplayOrientation_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 4461d5519..ce88ad9c0 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -739,3 +739,13 @@ SDL_DYNAPI_PROC(void,SDL_SensorUpdate,(void),(),) SDL_DYNAPI_PROC(SDL_bool,SDL_IsTablet,(void),(),return) SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetDisplayOrientation,(int 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/render/SDL_render.c b/src/render/SDL_render.c index b03524b4c..8d1ca56d8 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -2157,31 +2157,37 @@ SDL_RenderClear(SDL_Renderer * 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; - int retval; + const SDL_FPoint fpoint = { (float) x, (float) y }; + return SDL_RenderDrawPointsF(renderer, &fpoint, 1); +} - point.x = x; - point.y = y; - retval = SDL_RenderDrawPoints(renderer, &point, 1); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); +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 i; int retval = -1; SDL_bool isstack; + SDL_FRect *frects = SDL_small_alloc(SDL_FRect, count, &isstack); + int i; - frects = SDL_small_alloc(SDL_FRect, count, &isstack); 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; @@ -2239,21 +2245,148 @@ SDL_RenderDrawPoints(SDL_Renderer * renderer, 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(); + } + 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); +} + 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; @@ -2349,20 +2482,83 @@ SDL_RenderDrawLines(SDL_Renderer * renderer, 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(); + } + 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_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; @@ -2375,7 +2571,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 @@ -2406,21 +2602,76 @@ SDL_RenderDrawRects(SDL_Renderer * renderer, return 0; } +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 @@ -2464,13 +2715,124 @@ SDL_RenderFillRects(SDL_Renderer * renderer, 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); @@ -2495,11 +2857,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; @@ -2509,31 +2873,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; texture->last_command_generation = renderer->render_command_generation; - retval = QueueCmdCopy(renderer, texture, &real_srcrect, &frect); + 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); @@ -2565,9 +2954,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) { @@ -2577,21 +2969,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; texture->last_command_generation = renderer->render_command_generation; - return QueueCmdCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip); + retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip); + return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); } int diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 240ce2de1..87586f1bb 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 { From 25e38e5a3e9e59c46705d6df8162ee877e15d69c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 29 Oct 2018 10:14:59 -0400 Subject: [PATCH 42/43] wayland: ask KDE protocol extension to use server-side decorations if possible. --HG-- branch : SDL-ryan-batching-renderer --- src/video/wayland/SDL_waylandvideo.c | 6 ++ src/video/wayland/SDL_waylandvideo.h | 1 + src/video/wayland/SDL_waylandwindow.c | 25 +++++ src/video/wayland/SDL_waylandwindow.h | 2 + ...org-kde-kwin-server-decoration-manager.xml | 94 +++++++++++++++++++ 5 files changed, 128 insertions(+) create mode 100644 wayland-protocols/org-kde-kwin-server-decoration-manager.xml diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 06230277f..acea440d6 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 c16c0bdd4..4d6658fca 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 7055336d6..cbd85088c 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 @@ -459,6 +460,17 @@ Wayland_RestoreWindow(_THIS, SDL_Window * window) WAYLAND_wl_display_flush( ((SDL_VideoData*)_this->driverdata)->display ); } +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) { @@ -570,6 +582,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); @@ -644,6 +665,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 69b988923..38a204e9a 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/wayland-protocols/org-kde-kwin-server-decoration-manager.xml b/wayland-protocols/org-kde-kwin-server-decoration-manager.xml new file mode 100644 index 000000000..8bc106c7c --- /dev/null +++ b/wayland-protocols/org-kde-kwin-server-decoration-manager.xml @@ -0,0 +1,94 @@ + + + . + ]]> + + + 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. + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + 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. + + + + + From 7ac65007de8f6930d6b77cc437c82196fa501a48 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 Oct 2018 14:50:20 -0400 Subject: [PATCH 43/43] Closing SDL-ryan-batching-renderer branch. --HG-- branch : SDL-ryan-batching-renderer extra : close : 1