src/render/SDL_render.c
branchSDL-ryan-batching-renderer
changeset 12211 0d8f33ad5fbb
parent 12021 3239c8ad2986
child 12214 d377f3ccade7
     1.1 --- a/src/render/SDL_render.c	Thu Sep 20 15:41:57 2018 -0400
     1.2 +++ b/src/render/SDL_render.c	Thu Sep 20 15:46:02 2018 -0400
     1.3 @@ -105,6 +105,258 @@
     1.4  static char renderer_magic;
     1.5  static char texture_magic;
     1.6  
     1.7 +
     1.8 +static int
     1.9 +FlushRenderCommands(SDL_Renderer *renderer)
    1.10 +{
    1.11 +    int retval;
    1.12 +    SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
    1.13 +
    1.14 +    if (renderer->render_commands == NULL) {  /* nothing to do! */
    1.15 +        SDL_assert(renderer->vertex_data_used == 0);
    1.16 +        return 0;
    1.17 +    }
    1.18 +
    1.19 +    retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
    1.20 +
    1.21 +    /* Move the whole render command queue to the unused pool so we can reuse them next time. */
    1.22 +    if (renderer->render_commands_tail != NULL) {
    1.23 +        renderer->render_commands_tail->next = renderer->render_commands_pool;
    1.24 +        renderer->render_commands_pool = renderer->render_commands;
    1.25 +        renderer->render_commands_tail = NULL;
    1.26 +        renderer->render_commands = NULL;
    1.27 +    }
    1.28 +    renderer->vertex_data_used = 0;
    1.29 +    renderer->render_command_generation++;
    1.30 +    return retval;
    1.31 +}
    1.32 +
    1.33 +static int
    1.34 +FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
    1.35 +{
    1.36 +    SDL_Renderer *renderer = texture->renderer;
    1.37 +    if (texture->last_command_generation == renderer->render_command_generation) {
    1.38 +        /* the current command queue depends on this texture, flush the queue now before it changes */
    1.39 +        return FlushRenderCommands(renderer);
    1.40 +    }
    1.41 +    return 0;
    1.42 +}
    1.43 +
    1.44 +static SDL_INLINE int
    1.45 +FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
    1.46 +{
    1.47 +    return renderer->batching ? 0 : FlushRenderCommands(renderer);
    1.48 +}
    1.49 +
    1.50 +void *
    1.51 +SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset)
    1.52 +{
    1.53 +    const size_t needed = renderer->vertex_data_used + numbytes;
    1.54 +    void *retval;
    1.55 +
    1.56 +    while (needed > renderer->vertex_data_allocation) {
    1.57 +        const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 128;
    1.58 +        const size_t newsize = current_allocation * 2;
    1.59 +        void *ptr = SDL_realloc(renderer->vertex_data, newsize);
    1.60 +        if (ptr == NULL) {
    1.61 +            SDL_OutOfMemory();
    1.62 +            return NULL;
    1.63 +        }
    1.64 +        renderer->vertex_data = ptr;
    1.65 +        renderer->vertex_data_allocation = newsize;
    1.66 +    }
    1.67 +
    1.68 +    retval = ((Uint8 *) renderer->vertex_data) + renderer->vertex_data_used;
    1.69 +    if (offset) {
    1.70 +        *offset = renderer->vertex_data_used;
    1.71 +    }
    1.72 +
    1.73 +    renderer->vertex_data_used += numbytes;
    1.74 +    return retval;
    1.75 +}
    1.76 +
    1.77 +static SDL_RenderCommand *
    1.78 +AllocateRenderCommand(SDL_Renderer *renderer)
    1.79 +{
    1.80 +    SDL_RenderCommand *retval = NULL;
    1.81 +
    1.82 +    /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */
    1.83 +    retval = renderer->render_commands_pool;
    1.84 +    if (retval != NULL) {
    1.85 +        renderer->render_commands_pool = retval->next;
    1.86 +        retval->next = NULL;
    1.87 +    } else {
    1.88 +        retval = SDL_calloc(1, sizeof (*retval));
    1.89 +        if (!retval) {
    1.90 +            SDL_OutOfMemory();
    1.91 +            return NULL;
    1.92 +        }
    1.93 +    }
    1.94 +
    1.95 +    SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
    1.96 +    if (renderer->render_commands_tail != NULL) {
    1.97 +        renderer->render_commands_tail->next = retval;
    1.98 +    } else {
    1.99 +        renderer->render_commands = retval;
   1.100 +    }
   1.101 +    renderer->render_commands_tail = retval;
   1.102 +
   1.103 +    return retval;
   1.104 +}
   1.105 +
   1.106 +static int
   1.107 +QueueCmdUpdateViewport(SDL_Renderer *renderer)
   1.108 +{
   1.109 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   1.110 +    if (cmd == NULL) {
   1.111 +        return -1;
   1.112 +    }
   1.113 +
   1.114 +    cmd->command = SDL_RENDERCMD_SETVIEWPORT;
   1.115 +    SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (cmd->data.viewport));
   1.116 +    return FlushRenderCommandsIfNotBatching(renderer);
   1.117 +}
   1.118 +
   1.119 +static int
   1.120 +QueueCmdUpdateClipRect(SDL_Renderer *renderer)
   1.121 +{
   1.122 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   1.123 +    if (cmd == NULL) {
   1.124 +        return -1;
   1.125 +    }
   1.126 +
   1.127 +    cmd->command = SDL_RENDERCMD_SETCLIPRECT;
   1.128 +    cmd->data.cliprect.enabled = renderer->clipping_enabled;
   1.129 +    SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect));
   1.130 +    return FlushRenderCommandsIfNotBatching(renderer);
   1.131 +}
   1.132 +
   1.133 +static int
   1.134 +QueueCmdClear(SDL_Renderer *renderer)
   1.135 +{
   1.136 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   1.137 +    if (cmd == NULL) {
   1.138 +        return -1;
   1.139 +    }
   1.140 +
   1.141 +    cmd->command = SDL_RENDERCMD_CLEAR;
   1.142 +    cmd->data.color.r = renderer->r;
   1.143 +    cmd->data.color.g = renderer->g;
   1.144 +    cmd->data.color.b = renderer->b;
   1.145 +    cmd->data.color.a = renderer->a;
   1.146 +    return FlushRenderCommandsIfNotBatching(renderer);
   1.147 +}
   1.148 +
   1.149 +static SDL_RenderCommand *
   1.150 +PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
   1.151 +{
   1.152 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   1.153 +    if (cmd != NULL) {
   1.154 +        cmd->command = cmdtype;
   1.155 +        cmd->data.draw.first = 0;  /* render backend will fill this in. */
   1.156 +        cmd->data.draw.count = 0;  /* render backend will fill this in. */
   1.157 +        cmd->data.draw.r = renderer->r;
   1.158 +        cmd->data.draw.g = renderer->g;
   1.159 +        cmd->data.draw.b = renderer->b;
   1.160 +        cmd->data.draw.a = renderer->a;
   1.161 +        cmd->data.draw.blend = renderer->blendMode;
   1.162 +        cmd->data.draw.texture = NULL;  /* no texture. */
   1.163 +    }
   1.164 +    return cmd;
   1.165 +}
   1.166 +
   1.167 +static int
   1.168 +QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
   1.169 +{
   1.170 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
   1.171 +    int retval = -1;
   1.172 +    if (cmd != NULL) {
   1.173 +        retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
   1.174 +        if (retval < 0) {
   1.175 +            cmd->command = SDL_RENDERCMD_NO_OP;
   1.176 +        }
   1.177 +    }
   1.178 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   1.179 +}
   1.180 +
   1.181 +static int
   1.182 +QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
   1.183 +{
   1.184 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
   1.185 +    int retval = -1;
   1.186 +    if (cmd != NULL) {
   1.187 +        retval = renderer->QueueDrawLines(renderer, cmd, points, count);
   1.188 +        if (retval < 0) {
   1.189 +            cmd->command = SDL_RENDERCMD_NO_OP;
   1.190 +        }
   1.191 +    }
   1.192 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   1.193 +}
   1.194 +
   1.195 +static int
   1.196 +QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
   1.197 +{
   1.198 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
   1.199 +    int retval = -1;
   1.200 +    if (cmd != NULL) {
   1.201 +        retval = renderer->QueueFillRects(renderer, cmd, rects, count);
   1.202 +        if (retval < 0) {
   1.203 +            cmd->command = SDL_RENDERCMD_NO_OP;
   1.204 +        }
   1.205 +    }
   1.206 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   1.207 +}
   1.208 +
   1.209 +static SDL_RenderCommand *
   1.210 +PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
   1.211 +{
   1.212 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   1.213 +    if (cmd != NULL) {
   1.214 +        cmd->command = cmdtype;
   1.215 +        cmd->data.draw.first = 0;  /* render backend will fill this in. */
   1.216 +        cmd->data.draw.count = 0;  /* render backend will fill this in. */
   1.217 +        cmd->data.draw.r = texture->r;
   1.218 +        cmd->data.draw.g = texture->g;
   1.219 +        cmd->data.draw.b = texture->b;
   1.220 +        cmd->data.draw.a = texture->a;
   1.221 +        cmd->data.draw.blend = texture->blendMode;
   1.222 +        cmd->data.draw.texture = texture;
   1.223 +    }
   1.224 +    return cmd;
   1.225 +}
   1.226 +
   1.227 +static int
   1.228 +QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   1.229 +{
   1.230 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
   1.231 +    int retval = -1;
   1.232 +    if (cmd != NULL) {
   1.233 +        retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
   1.234 +        if (retval < 0) {
   1.235 +            cmd->command = SDL_RENDERCMD_NO_OP;
   1.236 +        }
   1.237 +    }
   1.238 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   1.239 +}
   1.240 +
   1.241 +static int
   1.242 +QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
   1.243 +               const SDL_Rect * srcquad, const SDL_FRect * dstrect,
   1.244 +               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   1.245 +{
   1.246 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
   1.247 +    SDL_assert(renderer->QueueCopyEx != NULL);  /* should have caught at higher level. */
   1.248 +    int retval = -1;
   1.249 +    if (cmd != NULL) {
   1.250 +        retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
   1.251 +        if (retval < 0) {
   1.252 +            cmd->command = SDL_RENDERCMD_NO_OP;
   1.253 +        }
   1.254 +    }
   1.255 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   1.256 +}
   1.257 +
   1.258 +
   1.259  static int UpdateLogicalSize(SDL_Renderer *renderer);
   1.260  
   1.261  int
   1.262 @@ -183,7 +435,7 @@
   1.263                          renderer->viewport.y = 0;
   1.264                          renderer->viewport.w = w;
   1.265                          renderer->viewport.h = h;
   1.266 -                        renderer->UpdateViewport(renderer);
   1.267 +                        QueueCmdUpdateViewport(renderer);
   1.268                      }
   1.269                  }
   1.270  
   1.271 @@ -300,12 +552,25 @@
   1.272      return 0;
   1.273  }
   1.274  
   1.275 +static SDL_INLINE
   1.276 +void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
   1.277 +{
   1.278 +    /* all of these functions are required to be implemented, even as no-ops, so we don't
   1.279 +        have to check that they aren't NULL over and over. */
   1.280 +    SDL_assert(renderer->QueueDrawPoints != NULL);
   1.281 +    SDL_assert(renderer->QueueDrawLines != NULL);
   1.282 +    SDL_assert(renderer->QueueFillRects != NULL);
   1.283 +    SDL_assert(renderer->QueueCopy != NULL);
   1.284 +    SDL_assert(renderer->RunCommandQueue != NULL);
   1.285 +}
   1.286 +
   1.287  SDL_Renderer *
   1.288  SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
   1.289  {
   1.290  #if !SDL_RENDER_DISABLED
   1.291      SDL_Renderer *renderer = NULL;
   1.292      int n = SDL_GetNumRenderDrivers();
   1.293 +    SDL_bool batching = SDL_TRUE;
   1.294      const char *hint;
   1.295  
   1.296      if (!window) {
   1.297 @@ -335,6 +600,9 @@
   1.298                  if (SDL_strcasecmp(hint, driver->info.name) == 0) {
   1.299                      /* Create a new renderer instance */
   1.300                      renderer = driver->CreateRenderer(window, flags);
   1.301 +                    if (renderer) {
   1.302 +                        batching = SDL_FALSE;
   1.303 +                    }
   1.304                      break;
   1.305                  }
   1.306              }
   1.307 @@ -366,9 +634,18 @@
   1.308          }
   1.309          /* Create a new renderer instance */
   1.310          renderer = render_drivers[index]->CreateRenderer(window, flags);
   1.311 +        batching = SDL_FALSE;
   1.312      }
   1.313  
   1.314      if (renderer) {
   1.315 +        VerifyDrawQueueFunctions(renderer);
   1.316 +
   1.317 +        /* let app/user override batching decisions. */
   1.318 +        if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
   1.319 +            batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
   1.320 +        }
   1.321 +
   1.322 +        renderer->batching = batching;
   1.323          renderer->magic = &renderer_magic;
   1.324          renderer->window = window;
   1.325          renderer->target_mutex = SDL_CreateMutex();
   1.326 @@ -377,6 +654,9 @@
   1.327          renderer->dpi_scale.x = 1.0f;
   1.328          renderer->dpi_scale.y = 1.0f;
   1.329  
   1.330 +        /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
   1.331 +        renderer->render_command_generation = 1;
   1.332 +
   1.333          if (window && renderer->GetOutputSize) {
   1.334              int window_w, window_h;
   1.335              int output_w, output_h;
   1.336 @@ -418,11 +698,15 @@
   1.337      renderer = SW_CreateRendererForSurface(surface);
   1.338  
   1.339      if (renderer) {
   1.340 +        VerifyDrawQueueFunctions(renderer);
   1.341          renderer->magic = &renderer_magic;
   1.342          renderer->target_mutex = SDL_CreateMutex();
   1.343          renderer->scale.x = 1.0f;
   1.344          renderer->scale.y = 1.0f;
   1.345  
   1.346 +        /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
   1.347 +        renderer->render_command_generation = 1;
   1.348 +
   1.349          SDL_RenderSetViewport(renderer, NULL);
   1.350      }
   1.351      return renderer;
   1.352 @@ -758,11 +1042,8 @@
   1.353      texture->b = b;
   1.354      if (texture->native) {
   1.355          return SDL_SetTextureColorMod(texture->native, r, g, b);
   1.356 -    } else if (renderer->SetTextureColorMod) {
   1.357 -        return renderer->SetTextureColorMod(renderer, texture);
   1.358 -    } else {
   1.359 -        return 0;
   1.360      }
   1.361 +    return 0;
   1.362  }
   1.363  
   1.364  int
   1.365 @@ -799,11 +1080,8 @@
   1.366      texture->a = alpha;
   1.367      if (texture->native) {
   1.368          return SDL_SetTextureAlphaMod(texture->native, alpha);
   1.369 -    } else if (renderer->SetTextureAlphaMod) {
   1.370 -        return renderer->SetTextureAlphaMod(renderer, texture);
   1.371 -    } else {
   1.372 -        return 0;
   1.373      }
   1.374 +    return 0;
   1.375  }
   1.376  
   1.377  int
   1.378 @@ -831,11 +1109,8 @@
   1.379      texture->blendMode = blendMode;
   1.380      if (texture->native) {
   1.381          return SDL_SetTextureBlendMode(texture->native, blendMode);
   1.382 -    } else if (renderer->SetTextureBlendMode) {
   1.383 -        return renderer->SetTextureBlendMode(renderer, texture);
   1.384 -    } else {
   1.385 -        return 0;
   1.386      }
   1.387 +    return 0;
   1.388  }
   1.389  
   1.390  int
   1.391 @@ -940,7 +1215,6 @@
   1.392  SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
   1.393                    const void *pixels, int pitch)
   1.394  {
   1.395 -    SDL_Renderer *renderer;
   1.396      SDL_Rect full_rect;
   1.397  
   1.398      CHECK_TEXTURE_MAGIC(texture, -1);
   1.399 @@ -967,7 +1241,10 @@
   1.400      } else if (texture->native) {
   1.401          return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
   1.402      } else {
   1.403 -        renderer = texture->renderer;
   1.404 +        SDL_Renderer *renderer = texture->renderer;
   1.405 +        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1.406 +            return -1;
   1.407 +        }
   1.408          return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
   1.409      }
   1.410  }
   1.411 @@ -1077,6 +1354,9 @@
   1.412          renderer = texture->renderer;
   1.413          SDL_assert(renderer->UpdateTextureYUV);
   1.414          if (renderer->UpdateTextureYUV) {
   1.415 +            if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1.416 +                return -1;
   1.417 +            }
   1.418              return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
   1.419          } else {
   1.420              return SDL_Unsupported();
   1.421 @@ -1107,7 +1387,6 @@
   1.422  SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
   1.423                  void **pixels, int *pitch)
   1.424  {
   1.425 -    SDL_Renderer *renderer;
   1.426      SDL_Rect full_rect;
   1.427  
   1.428      CHECK_TEXTURE_MAGIC(texture, -1);
   1.429 @@ -1125,11 +1404,18 @@
   1.430      }
   1.431  
   1.432      if (texture->yuv) {
   1.433 +        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1.434 +            return -1;
   1.435 +        }
   1.436          return SDL_LockTextureYUV(texture, rect, pixels, pitch);
   1.437      } else if (texture->native) {
   1.438 +        /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
   1.439          return SDL_LockTextureNative(texture, rect, pixels, pitch);
   1.440      } else {
   1.441 -        renderer = texture->renderer;
   1.442 +        SDL_Renderer *renderer = texture->renderer;
   1.443 +        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   1.444 +            return -1;
   1.445 +        }
   1.446          return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
   1.447      }
   1.448  }
   1.449 @@ -1179,8 +1465,6 @@
   1.450  void
   1.451  SDL_UnlockTexture(SDL_Texture * texture)
   1.452  {
   1.453 -    SDL_Renderer *renderer;
   1.454 -
   1.455      CHECK_TEXTURE_MAGIC(texture, );
   1.456  
   1.457      if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   1.458 @@ -1191,7 +1475,7 @@
   1.459      } else if (texture->native) {
   1.460          SDL_UnlockTextureNative(texture);
   1.461      } else {
   1.462 -        renderer = texture->renderer;
   1.463 +        SDL_Renderer *renderer = texture->renderer;
   1.464          renderer->UnlockTexture(renderer, texture);
   1.465      }
   1.466  }
   1.467 @@ -1216,6 +1500,8 @@
   1.468          return 0;
   1.469      }
   1.470  
   1.471 +    FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
   1.472 +
   1.473      /* texture == NULL is valid and means reset the target to the window */
   1.474      if (texture) {
   1.475          CHECK_TEXTURE_MAGIC(texture, -1);
   1.476 @@ -1271,10 +1557,10 @@
   1.477  
   1.478      SDL_UnlockMutex(renderer->target_mutex);
   1.479  
   1.480 -    if (renderer->UpdateViewport(renderer) < 0) {
   1.481 +    if (QueueCmdUpdateViewport(renderer) < 0) {
   1.482          return -1;
   1.483      }
   1.484 -    if (renderer->UpdateClipRect(renderer) < 0) {
   1.485 +    if (QueueCmdUpdateClipRect(renderer) < 0) {
   1.486          return -1;
   1.487      }
   1.488  
   1.489 @@ -1465,7 +1751,7 @@
   1.490              return -1;
   1.491          }
   1.492      }
   1.493 -    return renderer->UpdateViewport(renderer);
   1.494 +    return QueueCmdUpdateViewport(renderer);
   1.495  }
   1.496  
   1.497  void
   1.498 @@ -1496,7 +1782,7 @@
   1.499          renderer->clipping_enabled = SDL_FALSE;
   1.500          SDL_zero(renderer->clip_rect);
   1.501      }
   1.502 -    return renderer->UpdateClipRect(renderer);
   1.503 +    return QueueCmdUpdateClipRect(renderer);
   1.504  }
   1.505  
   1.506  void
   1.507 @@ -1601,12 +1887,7 @@
   1.508  SDL_RenderClear(SDL_Renderer * renderer)
   1.509  {
   1.510      CHECK_RENDERER_MAGIC(renderer, -1);
   1.511 -
   1.512 -    /* Don't draw while we're hidden */
   1.513 -    if (renderer->hidden) {
   1.514 -        return 0;
   1.515 -    }
   1.516 -    return renderer->RenderClear(renderer);
   1.517 +    return QueueCmdClear(renderer);
   1.518  }
   1.519  
   1.520  int
   1.521 @@ -1625,7 +1906,7 @@
   1.522  {
   1.523      SDL_FRect *frects;
   1.524      int i;
   1.525 -    int status;
   1.526 +    int status = -1;
   1.527  
   1.528      frects = SDL_stack_alloc(SDL_FRect, count);
   1.529      if (!frects) {
   1.530 @@ -1638,7 +1919,7 @@
   1.531          frects[i].h = renderer->scale.y;
   1.532      }
   1.533  
   1.534 -    status = renderer->RenderFillRects(renderer, frects, count);
   1.535 +    status = QueueCmdFillRects(renderer, frects, count);
   1.536  
   1.537      SDL_stack_free(frects);
   1.538  
   1.539 @@ -1680,7 +1961,7 @@
   1.540          fpoints[i].y = points[i].y * renderer->scale.y;
   1.541      }
   1.542  
   1.543 -    status = renderer->RenderDrawPoints(renderer, fpoints, count);
   1.544 +    status = QueueCmdDrawPoints(renderer, fpoints, count);
   1.545  
   1.546      SDL_stack_free(fpoints);
   1.547  
   1.548 @@ -1706,20 +1987,18 @@
   1.549      SDL_FRect *frect;
   1.550      SDL_FRect *frects;
   1.551      SDL_FPoint fpoints[2];
   1.552 -    int i, nrects;
   1.553 -    int status;
   1.554 +    int i, nrects = 0;
   1.555 +    int status = 0;
   1.556  
   1.557      frects = SDL_stack_alloc(SDL_FRect, count-1);
   1.558      if (!frects) {
   1.559          return SDL_OutOfMemory();
   1.560      }
   1.561  
   1.562 -    status = 0;
   1.563 -    nrects = 0;
   1.564      for (i = 0; i < count-1; ++i) {
   1.565          if (points[i].x == points[i+1].x) {
   1.566 -            int minY = SDL_min(points[i].y, points[i+1].y);
   1.567 -            int maxY = SDL_max(points[i].y, points[i+1].y);
   1.568 +            const int minY = SDL_min(points[i].y, points[i+1].y);
   1.569 +            const int maxY = SDL_max(points[i].y, points[i+1].y);
   1.570  
   1.571              frect = &frects[nrects++];
   1.572              frect->x = points[i].x * renderer->scale.x;
   1.573 @@ -1727,8 +2006,8 @@
   1.574              frect->w = renderer->scale.x;
   1.575              frect->h = (maxY - minY + 1) * renderer->scale.y;
   1.576          } else if (points[i].y == points[i+1].y) {
   1.577 -            int minX = SDL_min(points[i].x, points[i+1].x);
   1.578 -            int maxX = SDL_max(points[i].x, points[i+1].x);
   1.579 +            const int minX = SDL_min(points[i].x, points[i+1].x);
   1.580 +            const int maxX = SDL_max(points[i].x, points[i+1].x);
   1.581  
   1.582              frect = &frects[nrects++];
   1.583              frect->x = minX * renderer->scale.x;
   1.584 @@ -1741,11 +2020,11 @@
   1.585              fpoints[0].y = points[i].y * renderer->scale.y;
   1.586              fpoints[1].x = points[i+1].x * renderer->scale.x;
   1.587              fpoints[1].y = points[i+1].y * renderer->scale.y;
   1.588 -            status += renderer->RenderDrawLines(renderer, fpoints, 2);
   1.589 +            status += QueueCmdDrawLines(renderer, fpoints, 2);
   1.590          }
   1.591      }
   1.592  
   1.593 -    status += renderer->RenderFillRects(renderer, frects, nrects);
   1.594 +    status += QueueCmdFillRects(renderer, frects, nrects);
   1.595  
   1.596      SDL_stack_free(frects);
   1.597  
   1.598 @@ -1790,7 +2069,7 @@
   1.599          fpoints[i].y = points[i].y * renderer->scale.y;
   1.600      }
   1.601  
   1.602 -    status = renderer->RenderDrawLines(renderer, fpoints, count);
   1.603 +    status = QueueCmdDrawLines(renderer, fpoints, count);
   1.604  
   1.605      SDL_stack_free(fpoints);
   1.606  
   1.607 @@ -1904,7 +2183,7 @@
   1.608          frects[i].h = rects[i].h * renderer->scale.y;
   1.609      }
   1.610  
   1.611 -    status = renderer->RenderFillRects(renderer, frects, count);
   1.612 +    status = QueueCmdFillRects(renderer, frects, count);
   1.613  
   1.614      SDL_stack_free(frects);
   1.615  
   1.616 @@ -1960,7 +2239,9 @@
   1.617      frect.w = real_dstrect.w * renderer->scale.x;
   1.618      frect.h = real_dstrect.h * renderer->scale.y;
   1.619  
   1.620 -    return renderer->RenderCopy(renderer, texture, &real_srcrect, &frect);
   1.621 +    texture->last_command_generation = renderer->render_command_generation;
   1.622 +
   1.623 +    return QueueCmdCopy(renderer, texture, &real_srcrect, &frect);
   1.624  }
   1.625  
   1.626  
   1.627 @@ -1985,7 +2266,7 @@
   1.628      if (renderer != texture->renderer) {
   1.629          return SDL_SetError("Texture was not created with this renderer");
   1.630      }
   1.631 -    if (!renderer->RenderCopyEx) {
   1.632 +    if (!renderer->QueueCopyEx) {
   1.633          return SDL_SetError("Renderer does not support RenderCopyEx");
   1.634      }
   1.635  
   1.636 @@ -2032,7 +2313,9 @@
   1.637      fcenter.x = real_center.x * renderer->scale.x;
   1.638      fcenter.y = real_center.y * renderer->scale.y;
   1.639  
   1.640 -    return renderer->RenderCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip);
   1.641 +    texture->last_command_generation = renderer->render_command_generation;
   1.642 +
   1.643 +    return QueueCmdCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip);
   1.644  }
   1.645  
   1.646  int
   1.647 @@ -2047,6 +2330,8 @@
   1.648          return SDL_Unsupported();
   1.649      }
   1.650  
   1.651 +    FlushRenderCommands(renderer);  /* we need to render before we read the results. */
   1.652 +
   1.653      if (!format) {
   1.654          format = SDL_GetWindowPixelFormat(renderer->window);
   1.655      }
   1.656 @@ -2077,7 +2362,9 @@
   1.657  {
   1.658      CHECK_RENDERER_MAGIC(renderer, );
   1.659  
   1.660 -    /* Don't draw while we're hidden */
   1.661 +    FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
   1.662 +
   1.663 +    /* Don't present while we're hidden */
   1.664      if (renderer->hidden) {
   1.665          return;
   1.666      }
   1.667 @@ -2093,7 +2380,9 @@
   1.668  
   1.669      renderer = texture->renderer;
   1.670      if (texture == renderer->target) {
   1.671 -        SDL_SetRenderTarget(renderer, NULL);
   1.672 +        SDL_SetRenderTarget(renderer, NULL);  /* implies command queue flush */
   1.673 +    } else {
   1.674 +        FlushRenderCommandsIfTextureNeeded(texture);
   1.675      }
   1.676  
   1.677      texture->magic = NULL;
   1.678 @@ -2122,10 +2411,31 @@
   1.679  void
   1.680  SDL_DestroyRenderer(SDL_Renderer * renderer)
   1.681  {
   1.682 +    SDL_RenderCommand *cmd;
   1.683 +
   1.684      CHECK_RENDERER_MAGIC(renderer, );
   1.685  
   1.686      SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
   1.687  
   1.688 +    if (renderer->render_commands_tail != NULL) {
   1.689 +        renderer->render_commands_tail->next = renderer->render_commands_pool;
   1.690 +        cmd = renderer->render_commands;
   1.691 +    } else {
   1.692 +        cmd = renderer->render_commands_pool;
   1.693 +    }
   1.694 +
   1.695 +    renderer->render_commands_pool = NULL;
   1.696 +    renderer->render_commands_tail = NULL;
   1.697 +    renderer->render_commands = NULL;
   1.698 +
   1.699 +    while (cmd != NULL) {
   1.700 +        SDL_RenderCommand *next = cmd->next;
   1.701 +        SDL_free(cmd);
   1.702 +        cmd = next;
   1.703 +    }
   1.704 +
   1.705 +    SDL_free(renderer->vertex_data);
   1.706 +
   1.707      /* Free existing textures for this renderer */
   1.708      while (renderer->textures) {
   1.709          SDL_Texture *tex = renderer->textures; (void) tex;
   1.710 @@ -2157,6 +2467,7 @@
   1.711      if (texture->native) {
   1.712          return SDL_GL_BindTexture(texture->native, texw, texh);
   1.713      } else if (renderer && renderer->GL_BindTexture) {
   1.714 +        FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app is going to mess with it. */
   1.715          return renderer->GL_BindTexture(renderer, texture, texw, texh);
   1.716      } else {
   1.717          return SDL_Unsupported();
   1.718 @@ -2172,6 +2483,7 @@
   1.719      if (texture->native) {
   1.720          return SDL_GL_UnbindTexture(texture->native);
   1.721      } else if (renderer && renderer->GL_UnbindTexture) {
   1.722 +        FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app messed with it. */
   1.723          return renderer->GL_UnbindTexture(renderer, texture);
   1.724      }
   1.725  
   1.726 @@ -2184,6 +2496,7 @@
   1.727      CHECK_RENDERER_MAGIC(renderer, NULL);
   1.728  
   1.729      if (renderer->GetMetalLayer) {
   1.730 +        FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
   1.731          return renderer->GetMetalLayer(renderer);
   1.732      }
   1.733      return NULL;
   1.734 @@ -2195,6 +2508,7 @@
   1.735      CHECK_RENDERER_MAGIC(renderer, NULL);
   1.736  
   1.737      if (renderer->GetMetalCommandEncoder) {
   1.738 +        FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
   1.739          return renderer->GetMetalCommandEncoder(renderer);
   1.740      }
   1.741      return NULL;