|
|
@@ -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); |
|
|
} |
|
|
|
|
|
static SDL_AllocVertGap * |
|
|
AllocateVertexGap(SDL_Renderer *renderer) |
|
|
{ |
|
|
SDL_AllocVertGap *retval = renderer->vertex_data_gaps_pool; |
|
|
if (retval) { |
|
|
renderer->vertex_data_gaps_pool = retval->next; |
|
|
retval->next = NULL; |
|
|
} else { |
|
|
retval = (SDL_AllocVertGap *) SDL_malloc(sizeof (SDL_AllocVertGap)); |
|
|
if (!retval) { |
|
|
SDL_OutOfMemory(); |
|
|
} |
|
|
} |
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
void * |
|
|
SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, size_t *offset) |
|
|
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; |
|
|
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) |
|
|
{ |
|
|
SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
|
|
if (cmd == NULL) { |
|
|
return -1; |
|
|
QueueCmdSetViewport(SDL_Renderer *renderer) |
|
|
{ |
|
|
int retval = 0; |
|
|
if (!renderer->viewport_queued || (SDL_memcmp(&renderer->viewport, &renderer->last_queued_viewport, sizeof (SDL_Rect)) != 0)) { |
|
|
SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
|
|
retval = -1; |
|
|
if (cmd != NULL) { |
|
|
cmd->command = SDL_RENDERCMD_SETVIEWPORT; |
|
|
cmd->data.viewport.first = 0; /* render backend will fill this in. */ |
|
|
SDL_memcpy(&cmd->data.viewport.rect, &renderer->viewport, sizeof (renderer->viewport)); |
|
|
retval = renderer->QueueSetViewport(renderer, cmd); |
|
|
if (retval < 0) { |
|
|
cmd->command = SDL_RENDERCMD_NO_OP; |
|
|
} else { |
|
|
SDL_memcpy(&renderer->last_queued_viewport, &renderer->viewport, sizeof (SDL_Rect)); |
|
|
renderer->viewport_queued = SDL_TRUE; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
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) |
|
|
{ |
|
|
SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
|
|
if (cmd == NULL) { |
|
|
return -1; |
|
|
QueueCmdSetClipRect(SDL_Renderer *renderer) |
|
|
{ |
|
|
int retval = 0; |
|
|
if ((!renderer->cliprect_queued) || |
|
|
(renderer->clipping_enabled != renderer->last_queued_cliprect_enabled) || |
|
|
(SDL_memcmp(&renderer->clip_rect, &renderer->last_queued_cliprect, sizeof (SDL_Rect)) != 0)) { |
|
|
SDL_RenderCommand *cmd = AllocateRenderCommand(renderer); |
|
|
if (cmd == NULL) { |
|
|
retval = -1; |
|
|
} else { |
|
|
cmd->command = SDL_RENDERCMD_SETCLIPRECT; |
|
|
cmd->data.cliprect.enabled = renderer->clipping_enabled; |
|
|
SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (cmd->data.cliprect.rect)); |
|
|
SDL_memcpy(&renderer->last_queued_cliprect, &renderer->clip_rect, sizeof (SDL_Rect)); |
|
|
renderer->last_queued_cliprect_enabled = renderer->clipping_enabled; |
|
|
renderer->cliprect_queued = SDL_TRUE; |
|
|
} |
|
|
} |
|
|
return retval < 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,27 +378,47 @@ 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; |
|
|
cmd->data.color.a = renderer->a; |
|
|
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; |
|
|
|