render: Move to a batching system for rendering (work in progress). SDL-ryan-batching-renderer
authorRyan C. Gordon <icculus@icculus.org>
Thu, 20 Sep 2018 15:46:02 -0400
branchSDL-ryan-batching-renderer
changeset 122110d8f33ad5fbb
parent 12210 9f9dbcce30b3
child 12212 e5a6ed2f98a3
render: Move to a batching system for rendering (work in progress).
include/SDL_hints.h
src/render/SDL_render.c
src/render/SDL_sysrender.h
     1.1 --- a/include/SDL_hints.h	Thu Sep 20 15:41:57 2018 -0400
     1.2 +++ b/include/SDL_hints.h	Thu Sep 20 15:46:02 2018 -0400
     1.3 @@ -1030,6 +1030,31 @@
     1.4  #define SDL_HINT_AUDIO_CATEGORY   "SDL_AUDIO_CATEGORY"
     1.5  
     1.6  /**
     1.7 + *  \brief  A variable controlling whether the 2D render API is compatible or efficient.
     1.8 + *
     1.9 + *  This variable can be set to the following values:
    1.10 + *
    1.11 + *    "0"     - Don't use batching to make rendering more efficient.
    1.12 + *    "1"     - Use batching, but might cause problems if app makes its own direct OpenGL calls.
    1.13 + *
    1.14 + *  Up to SDL 2.0.9, the render API would draw immediately when requested. Now
    1.15 + *  it batches up draw requests and sends them all to the GPU only when forced
    1.16 + *  to (during SDL_RenderPresent, when changing render targets, by updating a
    1.17 + *  texture that the batch needs, etc). This is significantly more efficient,
    1.18 + *  but it can cause problems for apps that expect to render on top of the
    1.19 + *  render API's output. As such, SDL will disable batching if a specific
    1.20 + *  render backend is requested (since this might indicate that the app is
    1.21 + *  planning to use the underlying graphics API directly). This hint can
    1.22 + *  be used to explicitly request batching in this instance. It is a contract
    1.23 + *  that you will either never use the underlying graphics API directly, or
    1.24 + *  if you do, you will call SDL_RenderFlush() before you do so any current
    1.25 + *  batch goes to the GPU before your work begins. Not following this contract
    1.26 + *  will result in undefined behavior.
    1.27 + */
    1.28 +#define SDL_HINT_RENDER_BATCHING  "SDL_RENDER_BATCHING"
    1.29 +
    1.30 +
    1.31 +/**
    1.32   *  \brief  An enumeration of hint priorities
    1.33   */
    1.34  typedef enum
     2.1 --- a/src/render/SDL_render.c	Thu Sep 20 15:41:57 2018 -0400
     2.2 +++ b/src/render/SDL_render.c	Thu Sep 20 15:46:02 2018 -0400
     2.3 @@ -105,6 +105,258 @@
     2.4  static char renderer_magic;
     2.5  static char texture_magic;
     2.6  
     2.7 +
     2.8 +static int
     2.9 +FlushRenderCommands(SDL_Renderer *renderer)
    2.10 +{
    2.11 +    int retval;
    2.12 +    SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
    2.13 +
    2.14 +    if (renderer->render_commands == NULL) {  /* nothing to do! */
    2.15 +        SDL_assert(renderer->vertex_data_used == 0);
    2.16 +        return 0;
    2.17 +    }
    2.18 +
    2.19 +    retval = renderer->RunCommandQueue(renderer, renderer->render_commands, renderer->vertex_data, renderer->vertex_data_used);
    2.20 +
    2.21 +    /* Move the whole render command queue to the unused pool so we can reuse them next time. */
    2.22 +    if (renderer->render_commands_tail != NULL) {
    2.23 +        renderer->render_commands_tail->next = renderer->render_commands_pool;
    2.24 +        renderer->render_commands_pool = renderer->render_commands;
    2.25 +        renderer->render_commands_tail = NULL;
    2.26 +        renderer->render_commands = NULL;
    2.27 +    }
    2.28 +    renderer->vertex_data_used = 0;
    2.29 +    renderer->render_command_generation++;
    2.30 +    return retval;
    2.31 +}
    2.32 +
    2.33 +static int
    2.34 +FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
    2.35 +{
    2.36 +    SDL_Renderer *renderer = texture->renderer;
    2.37 +    if (texture->last_command_generation == renderer->render_command_generation) {
    2.38 +        /* the current command queue depends on this texture, flush the queue now before it changes */
    2.39 +        return FlushRenderCommands(renderer);
    2.40 +    }
    2.41 +    return 0;
    2.42 +}
    2.43 +
    2.44 +static SDL_INLINE int
    2.45 +FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer)
    2.46 +{
    2.47 +    return renderer->batching ? 0 : FlushRenderCommands(renderer);
    2.48 +}
    2.49 +
    2.50 +void *
    2.51 +SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset)
    2.52 +{
    2.53 +    const size_t needed = renderer->vertex_data_used + numbytes;
    2.54 +    void *retval;
    2.55 +
    2.56 +    while (needed > renderer->vertex_data_allocation) {
    2.57 +        const size_t current_allocation = renderer->vertex_data ? renderer->vertex_data_allocation : 128;
    2.58 +        const size_t newsize = current_allocation * 2;
    2.59 +        void *ptr = SDL_realloc(renderer->vertex_data, newsize);
    2.60 +        if (ptr == NULL) {
    2.61 +            SDL_OutOfMemory();
    2.62 +            return NULL;
    2.63 +        }
    2.64 +        renderer->vertex_data = ptr;
    2.65 +        renderer->vertex_data_allocation = newsize;
    2.66 +    }
    2.67 +
    2.68 +    retval = ((Uint8 *) renderer->vertex_data) + renderer->vertex_data_used;
    2.69 +    if (offset) {
    2.70 +        *offset = renderer->vertex_data_used;
    2.71 +    }
    2.72 +
    2.73 +    renderer->vertex_data_used += numbytes;
    2.74 +    return retval;
    2.75 +}
    2.76 +
    2.77 +static SDL_RenderCommand *
    2.78 +AllocateRenderCommand(SDL_Renderer *renderer)
    2.79 +{
    2.80 +    SDL_RenderCommand *retval = NULL;
    2.81 +
    2.82 +    /* !!! FIXME: are there threading limitations in SDL's render API? If not, we need to mutex this. */
    2.83 +    retval = renderer->render_commands_pool;
    2.84 +    if (retval != NULL) {
    2.85 +        renderer->render_commands_pool = retval->next;
    2.86 +        retval->next = NULL;
    2.87 +    } else {
    2.88 +        retval = SDL_calloc(1, sizeof (*retval));
    2.89 +        if (!retval) {
    2.90 +            SDL_OutOfMemory();
    2.91 +            return NULL;
    2.92 +        }
    2.93 +    }
    2.94 +
    2.95 +    SDL_assert((renderer->render_commands == NULL) == (renderer->render_commands_tail == NULL));
    2.96 +    if (renderer->render_commands_tail != NULL) {
    2.97 +        renderer->render_commands_tail->next = retval;
    2.98 +    } else {
    2.99 +        renderer->render_commands = retval;
   2.100 +    }
   2.101 +    renderer->render_commands_tail = retval;
   2.102 +
   2.103 +    return retval;
   2.104 +}
   2.105 +
   2.106 +static int
   2.107 +QueueCmdUpdateViewport(SDL_Renderer *renderer)
   2.108 +{
   2.109 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   2.110 +    if (cmd == NULL) {
   2.111 +        return -1;
   2.112 +    }
   2.113 +
   2.114 +    cmd->command = SDL_RENDERCMD_SETVIEWPORT;
   2.115 +    SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (cmd->data.viewport));
   2.116 +    return FlushRenderCommandsIfNotBatching(renderer);
   2.117 +}
   2.118 +
   2.119 +static int
   2.120 +QueueCmdUpdateClipRect(SDL_Renderer *renderer)
   2.121 +{
   2.122 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   2.123 +    if (cmd == NULL) {
   2.124 +        return -1;
   2.125 +    }
   2.126 +
   2.127 +    cmd->command = SDL_RENDERCMD_SETCLIPRECT;
   2.128 +    cmd->data.cliprect.enabled = renderer->clipping_enabled;
   2.129 +    SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect));
   2.130 +    return FlushRenderCommandsIfNotBatching(renderer);
   2.131 +}
   2.132 +
   2.133 +static int
   2.134 +QueueCmdClear(SDL_Renderer *renderer)
   2.135 +{
   2.136 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   2.137 +    if (cmd == NULL) {
   2.138 +        return -1;
   2.139 +    }
   2.140 +
   2.141 +    cmd->command = SDL_RENDERCMD_CLEAR;
   2.142 +    cmd->data.color.r = renderer->r;
   2.143 +    cmd->data.color.g = renderer->g;
   2.144 +    cmd->data.color.b = renderer->b;
   2.145 +    cmd->data.color.a = renderer->a;
   2.146 +    return FlushRenderCommandsIfNotBatching(renderer);
   2.147 +}
   2.148 +
   2.149 +static SDL_RenderCommand *
   2.150 +PrepQueueCmdDrawSolid(SDL_Renderer *renderer, const SDL_RenderCommandType cmdtype)
   2.151 +{
   2.152 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   2.153 +    if (cmd != NULL) {
   2.154 +        cmd->command = cmdtype;
   2.155 +        cmd->data.draw.first = 0;  /* render backend will fill this in. */
   2.156 +        cmd->data.draw.count = 0;  /* render backend will fill this in. */
   2.157 +        cmd->data.draw.r = renderer->r;
   2.158 +        cmd->data.draw.g = renderer->g;
   2.159 +        cmd->data.draw.b = renderer->b;
   2.160 +        cmd->data.draw.a = renderer->a;
   2.161 +        cmd->data.draw.blend = renderer->blendMode;
   2.162 +        cmd->data.draw.texture = NULL;  /* no texture. */
   2.163 +    }
   2.164 +    return cmd;
   2.165 +}
   2.166 +
   2.167 +static int
   2.168 +QueueCmdDrawPoints(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
   2.169 +{
   2.170 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_POINTS);
   2.171 +    int retval = -1;
   2.172 +    if (cmd != NULL) {
   2.173 +        retval = renderer->QueueDrawPoints(renderer, cmd, points, count);
   2.174 +        if (retval < 0) {
   2.175 +            cmd->command = SDL_RENDERCMD_NO_OP;
   2.176 +        }
   2.177 +    }
   2.178 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2.179 +}
   2.180 +
   2.181 +static int
   2.182 +QueueCmdDrawLines(SDL_Renderer *renderer, const SDL_FPoint * points, const int count)
   2.183 +{
   2.184 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_DRAW_LINES);
   2.185 +    int retval = -1;
   2.186 +    if (cmd != NULL) {
   2.187 +        retval = renderer->QueueDrawLines(renderer, cmd, points, count);
   2.188 +        if (retval < 0) {
   2.189 +            cmd->command = SDL_RENDERCMD_NO_OP;
   2.190 +        }
   2.191 +    }
   2.192 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2.193 +}
   2.194 +
   2.195 +static int
   2.196 +QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect * rects, const int count)
   2.197 +{
   2.198 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawSolid(renderer, SDL_RENDERCMD_FILL_RECTS);
   2.199 +    int retval = -1;
   2.200 +    if (cmd != NULL) {
   2.201 +        retval = renderer->QueueFillRects(renderer, cmd, rects, count);
   2.202 +        if (retval < 0) {
   2.203 +            cmd->command = SDL_RENDERCMD_NO_OP;
   2.204 +        }
   2.205 +    }
   2.206 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2.207 +}
   2.208 +
   2.209 +static SDL_RenderCommand *
   2.210 +PrepQueueCmdDrawTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_RenderCommandType cmdtype)
   2.211 +{
   2.212 +    SDL_RenderCommand *cmd = AllocateRenderCommand(renderer);
   2.213 +    if (cmd != NULL) {
   2.214 +        cmd->command = cmdtype;
   2.215 +        cmd->data.draw.first = 0;  /* render backend will fill this in. */
   2.216 +        cmd->data.draw.count = 0;  /* render backend will fill this in. */
   2.217 +        cmd->data.draw.r = texture->r;
   2.218 +        cmd->data.draw.g = texture->g;
   2.219 +        cmd->data.draw.b = texture->b;
   2.220 +        cmd->data.draw.a = texture->a;
   2.221 +        cmd->data.draw.blend = texture->blendMode;
   2.222 +        cmd->data.draw.texture = texture;
   2.223 +    }
   2.224 +    return cmd;
   2.225 +}
   2.226 +
   2.227 +static int
   2.228 +QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   2.229 +{
   2.230 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY);
   2.231 +    int retval = -1;
   2.232 +    if (cmd != NULL) {
   2.233 +        retval = renderer->QueueCopy(renderer, cmd, texture, srcrect, dstrect);
   2.234 +        if (retval < 0) {
   2.235 +            cmd->command = SDL_RENDERCMD_NO_OP;
   2.236 +        }
   2.237 +    }
   2.238 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2.239 +}
   2.240 +
   2.241 +static int
   2.242 +QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
   2.243 +               const SDL_Rect * srcquad, const SDL_FRect * dstrect,
   2.244 +               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   2.245 +{
   2.246 +    SDL_RenderCommand *cmd = PrepQueueCmdDrawTexture(renderer, texture, SDL_RENDERCMD_COPY_EX);
   2.247 +    SDL_assert(renderer->QueueCopyEx != NULL);  /* should have caught at higher level. */
   2.248 +    int retval = -1;
   2.249 +    if (cmd != NULL) {
   2.250 +        retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
   2.251 +        if (retval < 0) {
   2.252 +            cmd->command = SDL_RENDERCMD_NO_OP;
   2.253 +        }
   2.254 +    }
   2.255 +    return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
   2.256 +}
   2.257 +
   2.258 +
   2.259  static int UpdateLogicalSize(SDL_Renderer *renderer);
   2.260  
   2.261  int
   2.262 @@ -183,7 +435,7 @@
   2.263                          renderer->viewport.y = 0;
   2.264                          renderer->viewport.w = w;
   2.265                          renderer->viewport.h = h;
   2.266 -                        renderer->UpdateViewport(renderer);
   2.267 +                        QueueCmdUpdateViewport(renderer);
   2.268                      }
   2.269                  }
   2.270  
   2.271 @@ -300,12 +552,25 @@
   2.272      return 0;
   2.273  }
   2.274  
   2.275 +static SDL_INLINE
   2.276 +void VerifyDrawQueueFunctions(const SDL_Renderer *renderer)
   2.277 +{
   2.278 +    /* all of these functions are required to be implemented, even as no-ops, so we don't
   2.279 +        have to check that they aren't NULL over and over. */
   2.280 +    SDL_assert(renderer->QueueDrawPoints != NULL);
   2.281 +    SDL_assert(renderer->QueueDrawLines != NULL);
   2.282 +    SDL_assert(renderer->QueueFillRects != NULL);
   2.283 +    SDL_assert(renderer->QueueCopy != NULL);
   2.284 +    SDL_assert(renderer->RunCommandQueue != NULL);
   2.285 +}
   2.286 +
   2.287  SDL_Renderer *
   2.288  SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
   2.289  {
   2.290  #if !SDL_RENDER_DISABLED
   2.291      SDL_Renderer *renderer = NULL;
   2.292      int n = SDL_GetNumRenderDrivers();
   2.293 +    SDL_bool batching = SDL_TRUE;
   2.294      const char *hint;
   2.295  
   2.296      if (!window) {
   2.297 @@ -335,6 +600,9 @@
   2.298                  if (SDL_strcasecmp(hint, driver->info.name) == 0) {
   2.299                      /* Create a new renderer instance */
   2.300                      renderer = driver->CreateRenderer(window, flags);
   2.301 +                    if (renderer) {
   2.302 +                        batching = SDL_FALSE;
   2.303 +                    }
   2.304                      break;
   2.305                  }
   2.306              }
   2.307 @@ -366,9 +634,18 @@
   2.308          }
   2.309          /* Create a new renderer instance */
   2.310          renderer = render_drivers[index]->CreateRenderer(window, flags);
   2.311 +        batching = SDL_FALSE;
   2.312      }
   2.313  
   2.314      if (renderer) {
   2.315 +        VerifyDrawQueueFunctions(renderer);
   2.316 +
   2.317 +        /* let app/user override batching decisions. */
   2.318 +        if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
   2.319 +            batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
   2.320 +        }
   2.321 +
   2.322 +        renderer->batching = batching;
   2.323          renderer->magic = &renderer_magic;
   2.324          renderer->window = window;
   2.325          renderer->target_mutex = SDL_CreateMutex();
   2.326 @@ -377,6 +654,9 @@
   2.327          renderer->dpi_scale.x = 1.0f;
   2.328          renderer->dpi_scale.y = 1.0f;
   2.329  
   2.330 +        /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
   2.331 +        renderer->render_command_generation = 1;
   2.332 +
   2.333          if (window && renderer->GetOutputSize) {
   2.334              int window_w, window_h;
   2.335              int output_w, output_h;
   2.336 @@ -418,11 +698,15 @@
   2.337      renderer = SW_CreateRendererForSurface(surface);
   2.338  
   2.339      if (renderer) {
   2.340 +        VerifyDrawQueueFunctions(renderer);
   2.341          renderer->magic = &renderer_magic;
   2.342          renderer->target_mutex = SDL_CreateMutex();
   2.343          renderer->scale.x = 1.0f;
   2.344          renderer->scale.y = 1.0f;
   2.345  
   2.346 +        /* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
   2.347 +        renderer->render_command_generation = 1;
   2.348 +
   2.349          SDL_RenderSetViewport(renderer, NULL);
   2.350      }
   2.351      return renderer;
   2.352 @@ -758,11 +1042,8 @@
   2.353      texture->b = b;
   2.354      if (texture->native) {
   2.355          return SDL_SetTextureColorMod(texture->native, r, g, b);
   2.356 -    } else if (renderer->SetTextureColorMod) {
   2.357 -        return renderer->SetTextureColorMod(renderer, texture);
   2.358 -    } else {
   2.359 -        return 0;
   2.360      }
   2.361 +    return 0;
   2.362  }
   2.363  
   2.364  int
   2.365 @@ -799,11 +1080,8 @@
   2.366      texture->a = alpha;
   2.367      if (texture->native) {
   2.368          return SDL_SetTextureAlphaMod(texture->native, alpha);
   2.369 -    } else if (renderer->SetTextureAlphaMod) {
   2.370 -        return renderer->SetTextureAlphaMod(renderer, texture);
   2.371 -    } else {
   2.372 -        return 0;
   2.373      }
   2.374 +    return 0;
   2.375  }
   2.376  
   2.377  int
   2.378 @@ -831,11 +1109,8 @@
   2.379      texture->blendMode = blendMode;
   2.380      if (texture->native) {
   2.381          return SDL_SetTextureBlendMode(texture->native, blendMode);
   2.382 -    } else if (renderer->SetTextureBlendMode) {
   2.383 -        return renderer->SetTextureBlendMode(renderer, texture);
   2.384 -    } else {
   2.385 -        return 0;
   2.386      }
   2.387 +    return 0;
   2.388  }
   2.389  
   2.390  int
   2.391 @@ -940,7 +1215,6 @@
   2.392  SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
   2.393                    const void *pixels, int pitch)
   2.394  {
   2.395 -    SDL_Renderer *renderer;
   2.396      SDL_Rect full_rect;
   2.397  
   2.398      CHECK_TEXTURE_MAGIC(texture, -1);
   2.399 @@ -967,7 +1241,10 @@
   2.400      } else if (texture->native) {
   2.401          return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
   2.402      } else {
   2.403 -        renderer = texture->renderer;
   2.404 +        SDL_Renderer *renderer = texture->renderer;
   2.405 +        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   2.406 +            return -1;
   2.407 +        }
   2.408          return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
   2.409      }
   2.410  }
   2.411 @@ -1077,6 +1354,9 @@
   2.412          renderer = texture->renderer;
   2.413          SDL_assert(renderer->UpdateTextureYUV);
   2.414          if (renderer->UpdateTextureYUV) {
   2.415 +            if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   2.416 +                return -1;
   2.417 +            }
   2.418              return renderer->UpdateTextureYUV(renderer, texture, rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
   2.419          } else {
   2.420              return SDL_Unsupported();
   2.421 @@ -1107,7 +1387,6 @@
   2.422  SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
   2.423                  void **pixels, int *pitch)
   2.424  {
   2.425 -    SDL_Renderer *renderer;
   2.426      SDL_Rect full_rect;
   2.427  
   2.428      CHECK_TEXTURE_MAGIC(texture, -1);
   2.429 @@ -1125,11 +1404,18 @@
   2.430      }
   2.431  
   2.432      if (texture->yuv) {
   2.433 +        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   2.434 +            return -1;
   2.435 +        }
   2.436          return SDL_LockTextureYUV(texture, rect, pixels, pitch);
   2.437      } else if (texture->native) {
   2.438 +        /* Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. */
   2.439          return SDL_LockTextureNative(texture, rect, pixels, pitch);
   2.440      } else {
   2.441 -        renderer = texture->renderer;
   2.442 +        SDL_Renderer *renderer = texture->renderer;
   2.443 +        if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
   2.444 +            return -1;
   2.445 +        }
   2.446          return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
   2.447      }
   2.448  }
   2.449 @@ -1179,8 +1465,6 @@
   2.450  void
   2.451  SDL_UnlockTexture(SDL_Texture * texture)
   2.452  {
   2.453 -    SDL_Renderer *renderer;
   2.454 -
   2.455      CHECK_TEXTURE_MAGIC(texture, );
   2.456  
   2.457      if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   2.458 @@ -1191,7 +1475,7 @@
   2.459      } else if (texture->native) {
   2.460          SDL_UnlockTextureNative(texture);
   2.461      } else {
   2.462 -        renderer = texture->renderer;
   2.463 +        SDL_Renderer *renderer = texture->renderer;
   2.464          renderer->UnlockTexture(renderer, texture);
   2.465      }
   2.466  }
   2.467 @@ -1216,6 +1500,8 @@
   2.468          return 0;
   2.469      }
   2.470  
   2.471 +    FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
   2.472 +
   2.473      /* texture == NULL is valid and means reset the target to the window */
   2.474      if (texture) {
   2.475          CHECK_TEXTURE_MAGIC(texture, -1);
   2.476 @@ -1271,10 +1557,10 @@
   2.477  
   2.478      SDL_UnlockMutex(renderer->target_mutex);
   2.479  
   2.480 -    if (renderer->UpdateViewport(renderer) < 0) {
   2.481 +    if (QueueCmdUpdateViewport(renderer) < 0) {
   2.482          return -1;
   2.483      }
   2.484 -    if (renderer->UpdateClipRect(renderer) < 0) {
   2.485 +    if (QueueCmdUpdateClipRect(renderer) < 0) {
   2.486          return -1;
   2.487      }
   2.488  
   2.489 @@ -1465,7 +1751,7 @@
   2.490              return -1;
   2.491          }
   2.492      }
   2.493 -    return renderer->UpdateViewport(renderer);
   2.494 +    return QueueCmdUpdateViewport(renderer);
   2.495  }
   2.496  
   2.497  void
   2.498 @@ -1496,7 +1782,7 @@
   2.499          renderer->clipping_enabled = SDL_FALSE;
   2.500          SDL_zero(renderer->clip_rect);
   2.501      }
   2.502 -    return renderer->UpdateClipRect(renderer);
   2.503 +    return QueueCmdUpdateClipRect(renderer);
   2.504  }
   2.505  
   2.506  void
   2.507 @@ -1601,12 +1887,7 @@
   2.508  SDL_RenderClear(SDL_Renderer * renderer)
   2.509  {
   2.510      CHECK_RENDERER_MAGIC(renderer, -1);
   2.511 -
   2.512 -    /* Don't draw while we're hidden */
   2.513 -    if (renderer->hidden) {
   2.514 -        return 0;
   2.515 -    }
   2.516 -    return renderer->RenderClear(renderer);
   2.517 +    return QueueCmdClear(renderer);
   2.518  }
   2.519  
   2.520  int
   2.521 @@ -1625,7 +1906,7 @@
   2.522  {
   2.523      SDL_FRect *frects;
   2.524      int i;
   2.525 -    int status;
   2.526 +    int status = -1;
   2.527  
   2.528      frects = SDL_stack_alloc(SDL_FRect, count);
   2.529      if (!frects) {
   2.530 @@ -1638,7 +1919,7 @@
   2.531          frects[i].h = renderer->scale.y;
   2.532      }
   2.533  
   2.534 -    status = renderer->RenderFillRects(renderer, frects, count);
   2.535 +    status = QueueCmdFillRects(renderer, frects, count);
   2.536  
   2.537      SDL_stack_free(frects);
   2.538  
   2.539 @@ -1680,7 +1961,7 @@
   2.540          fpoints[i].y = points[i].y * renderer->scale.y;
   2.541      }
   2.542  
   2.543 -    status = renderer->RenderDrawPoints(renderer, fpoints, count);
   2.544 +    status = QueueCmdDrawPoints(renderer, fpoints, count);
   2.545  
   2.546      SDL_stack_free(fpoints);
   2.547  
   2.548 @@ -1706,20 +1987,18 @@
   2.549      SDL_FRect *frect;
   2.550      SDL_FRect *frects;
   2.551      SDL_FPoint fpoints[2];
   2.552 -    int i, nrects;
   2.553 -    int status;
   2.554 +    int i, nrects = 0;
   2.555 +    int status = 0;
   2.556  
   2.557      frects = SDL_stack_alloc(SDL_FRect, count-1);
   2.558      if (!frects) {
   2.559          return SDL_OutOfMemory();
   2.560      }
   2.561  
   2.562 -    status = 0;
   2.563 -    nrects = 0;
   2.564      for (i = 0; i < count-1; ++i) {
   2.565          if (points[i].x == points[i+1].x) {
   2.566 -            int minY = SDL_min(points[i].y, points[i+1].y);
   2.567 -            int maxY = SDL_max(points[i].y, points[i+1].y);
   2.568 +            const int minY = SDL_min(points[i].y, points[i+1].y);
   2.569 +            const int maxY = SDL_max(points[i].y, points[i+1].y);
   2.570  
   2.571              frect = &frects[nrects++];
   2.572              frect->x = points[i].x * renderer->scale.x;
   2.573 @@ -1727,8 +2006,8 @@
   2.574              frect->w = renderer->scale.x;
   2.575              frect->h = (maxY - minY + 1) * renderer->scale.y;
   2.576          } else if (points[i].y == points[i+1].y) {
   2.577 -            int minX = SDL_min(points[i].x, points[i+1].x);
   2.578 -            int maxX = SDL_max(points[i].x, points[i+1].x);
   2.579 +            const int minX = SDL_min(points[i].x, points[i+1].x);
   2.580 +            const int maxX = SDL_max(points[i].x, points[i+1].x);
   2.581  
   2.582              frect = &frects[nrects++];
   2.583              frect->x = minX * renderer->scale.x;
   2.584 @@ -1741,11 +2020,11 @@
   2.585              fpoints[0].y = points[i].y * renderer->scale.y;
   2.586              fpoints[1].x = points[i+1].x * renderer->scale.x;
   2.587              fpoints[1].y = points[i+1].y * renderer->scale.y;
   2.588 -            status += renderer->RenderDrawLines(renderer, fpoints, 2);
   2.589 +            status += QueueCmdDrawLines(renderer, fpoints, 2);
   2.590          }
   2.591      }
   2.592  
   2.593 -    status += renderer->RenderFillRects(renderer, frects, nrects);
   2.594 +    status += QueueCmdFillRects(renderer, frects, nrects);
   2.595  
   2.596      SDL_stack_free(frects);
   2.597  
   2.598 @@ -1790,7 +2069,7 @@
   2.599          fpoints[i].y = points[i].y * renderer->scale.y;
   2.600      }
   2.601  
   2.602 -    status = renderer->RenderDrawLines(renderer, fpoints, count);
   2.603 +    status = QueueCmdDrawLines(renderer, fpoints, count);
   2.604  
   2.605      SDL_stack_free(fpoints);
   2.606  
   2.607 @@ -1904,7 +2183,7 @@
   2.608          frects[i].h = rects[i].h * renderer->scale.y;
   2.609      }
   2.610  
   2.611 -    status = renderer->RenderFillRects(renderer, frects, count);
   2.612 +    status = QueueCmdFillRects(renderer, frects, count);
   2.613  
   2.614      SDL_stack_free(frects);
   2.615  
   2.616 @@ -1960,7 +2239,9 @@
   2.617      frect.w = real_dstrect.w * renderer->scale.x;
   2.618      frect.h = real_dstrect.h * renderer->scale.y;
   2.619  
   2.620 -    return renderer->RenderCopy(renderer, texture, &real_srcrect, &frect);
   2.621 +    texture->last_command_generation = renderer->render_command_generation;
   2.622 +
   2.623 +    return QueueCmdCopy(renderer, texture, &real_srcrect, &frect);
   2.624  }
   2.625  
   2.626  
   2.627 @@ -1985,7 +2266,7 @@
   2.628      if (renderer != texture->renderer) {
   2.629          return SDL_SetError("Texture was not created with this renderer");
   2.630      }
   2.631 -    if (!renderer->RenderCopyEx) {
   2.632 +    if (!renderer->QueueCopyEx) {
   2.633          return SDL_SetError("Renderer does not support RenderCopyEx");
   2.634      }
   2.635  
   2.636 @@ -2032,7 +2313,9 @@
   2.637      fcenter.x = real_center.x * renderer->scale.x;
   2.638      fcenter.y = real_center.y * renderer->scale.y;
   2.639  
   2.640 -    return renderer->RenderCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip);
   2.641 +    texture->last_command_generation = renderer->render_command_generation;
   2.642 +
   2.643 +    return QueueCmdCopyEx(renderer, texture, &real_srcrect, &frect, angle, &fcenter, flip);
   2.644  }
   2.645  
   2.646  int
   2.647 @@ -2047,6 +2330,8 @@
   2.648          return SDL_Unsupported();
   2.649      }
   2.650  
   2.651 +    FlushRenderCommands(renderer);  /* we need to render before we read the results. */
   2.652 +
   2.653      if (!format) {
   2.654          format = SDL_GetWindowPixelFormat(renderer->window);
   2.655      }
   2.656 @@ -2077,7 +2362,9 @@
   2.657  {
   2.658      CHECK_RENDERER_MAGIC(renderer, );
   2.659  
   2.660 -    /* Don't draw while we're hidden */
   2.661 +    FlushRenderCommands(renderer);  /* time to send everything to the GPU! */
   2.662 +
   2.663 +    /* Don't present while we're hidden */
   2.664      if (renderer->hidden) {
   2.665          return;
   2.666      }
   2.667 @@ -2093,7 +2380,9 @@
   2.668  
   2.669      renderer = texture->renderer;
   2.670      if (texture == renderer->target) {
   2.671 -        SDL_SetRenderTarget(renderer, NULL);
   2.672 +        SDL_SetRenderTarget(renderer, NULL);  /* implies command queue flush */
   2.673 +    } else {
   2.674 +        FlushRenderCommandsIfTextureNeeded(texture);
   2.675      }
   2.676  
   2.677      texture->magic = NULL;
   2.678 @@ -2122,10 +2411,31 @@
   2.679  void
   2.680  SDL_DestroyRenderer(SDL_Renderer * renderer)
   2.681  {
   2.682 +    SDL_RenderCommand *cmd;
   2.683 +
   2.684      CHECK_RENDERER_MAGIC(renderer, );
   2.685  
   2.686      SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
   2.687  
   2.688 +    if (renderer->render_commands_tail != NULL) {
   2.689 +        renderer->render_commands_tail->next = renderer->render_commands_pool;
   2.690 +        cmd = renderer->render_commands;
   2.691 +    } else {
   2.692 +        cmd = renderer->render_commands_pool;
   2.693 +    }
   2.694 +
   2.695 +    renderer->render_commands_pool = NULL;
   2.696 +    renderer->render_commands_tail = NULL;
   2.697 +    renderer->render_commands = NULL;
   2.698 +
   2.699 +    while (cmd != NULL) {
   2.700 +        SDL_RenderCommand *next = cmd->next;
   2.701 +        SDL_free(cmd);
   2.702 +        cmd = next;
   2.703 +    }
   2.704 +
   2.705 +    SDL_free(renderer->vertex_data);
   2.706 +
   2.707      /* Free existing textures for this renderer */
   2.708      while (renderer->textures) {
   2.709          SDL_Texture *tex = renderer->textures; (void) tex;
   2.710 @@ -2157,6 +2467,7 @@
   2.711      if (texture->native) {
   2.712          return SDL_GL_BindTexture(texture->native, texw, texh);
   2.713      } else if (renderer && renderer->GL_BindTexture) {
   2.714 +        FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app is going to mess with it. */
   2.715          return renderer->GL_BindTexture(renderer, texture, texw, texh);
   2.716      } else {
   2.717          return SDL_Unsupported();
   2.718 @@ -2172,6 +2483,7 @@
   2.719      if (texture->native) {
   2.720          return SDL_GL_UnbindTexture(texture->native);
   2.721      } else if (renderer && renderer->GL_UnbindTexture) {
   2.722 +        FlushRenderCommandsIfTextureNeeded(texture);  /* in case the app messed with it. */
   2.723          return renderer->GL_UnbindTexture(renderer, texture);
   2.724      }
   2.725  
   2.726 @@ -2184,6 +2496,7 @@
   2.727      CHECK_RENDERER_MAGIC(renderer, NULL);
   2.728  
   2.729      if (renderer->GetMetalLayer) {
   2.730 +        FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
   2.731          return renderer->GetMetalLayer(renderer);
   2.732      }
   2.733      return NULL;
   2.734 @@ -2195,6 +2508,7 @@
   2.735      CHECK_RENDERER_MAGIC(renderer, NULL);
   2.736  
   2.737      if (renderer->GetMetalCommandEncoder) {
   2.738 +        FlushRenderCommands(renderer);  /* in case the app is going to mess with it. */
   2.739          return renderer->GetMetalCommandEncoder(renderer);
   2.740      }
   2.741      return NULL;
     3.1 --- a/src/render/SDL_sysrender.h	Thu Sep 20 15:41:57 2018 -0400
     3.2 +++ b/src/render/SDL_sysrender.h	Thu Sep 20 15:46:02 2018 -0400
     3.3 @@ -75,12 +75,51 @@
     3.4      int pitch;
     3.5      SDL_Rect locked_rect;
     3.6  
     3.7 +    Uint32 last_command_generation; /* last command queue generation this texture was in. */
     3.8 +
     3.9      void *driverdata;           /**< Driver specific texture representation */
    3.10  
    3.11      SDL_Texture *prev;
    3.12      SDL_Texture *next;
    3.13  };
    3.14  
    3.15 +typedef enum
    3.16 +{
    3.17 +    SDL_RENDERCMD_NO_OP,
    3.18 +    SDL_RENDERCMD_SETVIEWPORT,
    3.19 +    SDL_RENDERCMD_SETCLIPRECT,
    3.20 +    SDL_RENDERCMD_CLEAR,
    3.21 +    SDL_RENDERCMD_DRAW_POINTS,
    3.22 +    SDL_RENDERCMD_DRAW_LINES,
    3.23 +    SDL_RENDERCMD_FILL_RECTS,
    3.24 +    SDL_RENDERCMD_COPY,
    3.25 +    SDL_RENDERCMD_COPY_EX
    3.26 +} SDL_RenderCommandType;
    3.27 +
    3.28 +typedef struct SDL_RenderCommand
    3.29 +{
    3.30 +    SDL_RenderCommandType command;
    3.31 +    union {
    3.32 +        SDL_Rect viewport;
    3.33 +        struct {
    3.34 +            SDL_bool enabled;
    3.35 +            SDL_Rect rect;
    3.36 +        } cliprect;
    3.37 +        struct {
    3.38 +            size_t first;
    3.39 +            size_t count;
    3.40 +            Uint8 r, g, b, a;
    3.41 +            SDL_BlendMode blend;
    3.42 +            SDL_Texture *texture;
    3.43 +        } draw;
    3.44 +        struct {
    3.45 +            Uint8 r, g, b, a;
    3.46 +        } color;
    3.47 +    } data;
    3.48 +    struct SDL_RenderCommand *next;
    3.49 +} SDL_RenderCommand;
    3.50 +
    3.51 +
    3.52  /* Define the SDL renderer structure */
    3.53  struct SDL_Renderer
    3.54  {
    3.55 @@ -90,12 +129,18 @@
    3.56      int (*GetOutputSize) (SDL_Renderer * renderer, int *w, int *h);
    3.57      SDL_bool (*SupportsBlendMode)(SDL_Renderer * renderer, SDL_BlendMode blendMode);
    3.58      int (*CreateTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
    3.59 -    int (*SetTextureColorMod) (SDL_Renderer * renderer,
    3.60 -                               SDL_Texture * texture);
    3.61 -    int (*SetTextureAlphaMod) (SDL_Renderer * renderer,
    3.62 -                               SDL_Texture * texture);
    3.63 -    int (*SetTextureBlendMode) (SDL_Renderer * renderer,
    3.64 -                                SDL_Texture * texture);
    3.65 +    int (*QueueDrawPoints) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points,
    3.66 +                             int count);
    3.67 +    int (*QueueDrawLines) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points,
    3.68 +                            int count);
    3.69 +    int (*QueueFillRects) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects,
    3.70 +                            int count);
    3.71 +    int (*QueueCopy) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
    3.72 +                       const SDL_Rect * srcrect, const SDL_FRect * dstrect);
    3.73 +    int (*QueueCopyEx) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
    3.74 +                        const SDL_Rect * srcquad, const SDL_FRect * dstrect,
    3.75 +                        const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
    3.76 +    int (*RunCommandQueue) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize);
    3.77      int (*UpdateTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
    3.78                            const SDL_Rect * rect, const void *pixels,
    3.79                            int pitch);
    3.80 @@ -108,20 +153,6 @@
    3.81                          const SDL_Rect * rect, void **pixels, int *pitch);
    3.82      void (*UnlockTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
    3.83      int (*SetRenderTarget) (SDL_Renderer * renderer, SDL_Texture * texture);
    3.84 -    int (*UpdateViewport) (SDL_Renderer * renderer);
    3.85 -    int (*UpdateClipRect) (SDL_Renderer * renderer);
    3.86 -    int (*RenderClear) (SDL_Renderer * renderer);
    3.87 -    int (*RenderDrawPoints) (SDL_Renderer * renderer, const SDL_FPoint * points,
    3.88 -                             int count);
    3.89 -    int (*RenderDrawLines) (SDL_Renderer * renderer, const SDL_FPoint * points,
    3.90 -                            int count);
    3.91 -    int (*RenderFillRects) (SDL_Renderer * renderer, const SDL_FRect * rects,
    3.92 -                            int count);
    3.93 -    int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture,
    3.94 -                       const SDL_Rect * srcrect, const SDL_FRect * dstrect);
    3.95 -    int (*RenderCopyEx) (SDL_Renderer * renderer, SDL_Texture * texture,
    3.96 -                       const SDL_Rect * srcquad, const SDL_FRect * dstrect,
    3.97 -                       const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
    3.98      int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect,
    3.99                               Uint32 format, void * pixels, int pitch);
   3.100      void (*RenderPresent) (SDL_Renderer * renderer);
   3.101 @@ -178,6 +209,16 @@
   3.102      Uint8 r, g, b, a;                   /**< Color for drawing operations values */
   3.103      SDL_BlendMode blendMode;            /**< The drawing blend mode */
   3.104  
   3.105 +    SDL_bool batching;
   3.106 +    SDL_RenderCommand *render_commands;
   3.107 +    SDL_RenderCommand *render_commands_tail;
   3.108 +    SDL_RenderCommand *render_commands_pool;
   3.109 +    Uint32 render_command_generation;
   3.110 +
   3.111 +    void *vertex_data;
   3.112 +    size_t vertex_data_used;
   3.113 +    size_t vertex_data_allocation;
   3.114 +
   3.115      void *driverdata;
   3.116  };
   3.117  
   3.118 @@ -209,6 +250,11 @@
   3.119  extern SDL_BlendFactor SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode);
   3.120  extern SDL_BlendOperation SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode);
   3.121  
   3.122 +/* drivers call this during their Queue*() methods to make space in a array that are used
   3.123 +   for a vertex buffer during RunCommandQueue(). Pointers returned here are only valid until
   3.124 +   the next call, because it might be in an array that gets realloc()'d. */
   3.125 +extern void *SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset);
   3.126 +
   3.127  #endif /* SDL_sysrender_h_ */
   3.128  
   3.129  /* vi: set ts=4 sw=4 expandtab: */