mirror of
https://github.com/libsdl-org/SDL.git
synced 2024-12-12 05:03:30 +08:00
render: Move to a batching system for rendering (work in progress).
This commit is contained in:
parent
e0cc19f2d8
commit
5fb67f9f55
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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: */
|
||||
|
Loading…
Reference in New Issue
Block a user