gles2: Make render command queue dynamic.

It now uses a growable linked list that keeps a pool of allocated items for
reuse, and reallocs the vertex array as necessary. Testsprite2 can scale to
20,000 (or more!) draws now without drama.
This commit is contained in:
Ryan C. Gordon 2018-09-09 15:09:38 -04:00
parent 0d3275297d
commit a2f7af9a47
1 changed files with 210 additions and 87 deletions

View File

@ -196,6 +196,7 @@ typedef struct GLES2_RenderCommand
SDL_Texture *texture;
} draw;
} data;
struct GLES2_RenderCommand *next;
} GLES2_RenderCommand;
typedef struct GLES2_DriverContext
@ -220,17 +221,49 @@ typedef struct GLES2_DriverContext
GLuint vertex_buffers[4];
GLsizeiptr vertex_buffer_size[4];
int current_vertex_buffer;
GLES2_RenderCommand render_commands[1024 * 10];
int current_render_command;
GLES2_RenderCommand *render_commands;
GLES2_RenderCommand *render_commands_tail;
GLES2_RenderCommand *render_commands_pool;
int current_vertex_data;
Uint32 command_generation;
GLfloat vertex_data[1024 * 1024 * 5];
GLfloat *vertex_data;
GLsizeiptr vertex_data_allocation;
} GLES2_DriverContext;
#define GLES2_MAX_CACHED_PROGRAMS 8
static const float inv255f = 1.0f / 255.0f;
static GLES2_RenderCommand *
GLES2_AllocateRenderCommand(SDL_Renderer *renderer)
{
GLES2_DriverContext *data = (GLES2_DriverContext *) renderer->driverdata;
GLES2_RenderCommand *retval = NULL;
/* !!! FIXME: are there threading limitations in SDL's render API? */
retval = data->render_commands_pool;
if (retval != NULL) {
data->render_commands_pool = retval->next;
retval->next = NULL;
} else {
retval = SDL_calloc(1, sizeof (*retval));
if (!retval) {
SDL_OutOfMemory();
return NULL;
}
}
SDL_assert((data->render_commands == NULL) == (data->render_commands_tail == NULL));
if (data->render_commands_tail != NULL) {
data->render_commands_tail->next = retval;
} else {
data->render_commands = retval;
}
data->render_commands_tail = retval;
return retval;
}
static SDL_bool
CompareColors(const Uint8 r1, const Uint8 g1, const Uint8 b1, const Uint8 a1,
const Uint8 r2, const Uint8 g2, const Uint8 b2, const Uint8 a2)
@ -481,8 +514,9 @@ GLES2_FlushCommands(SDL_Renderer *renderer)
const int vboidx = data->current_vertex_buffer;
const GLuint vbo = data->vertex_buffers[vboidx];
const GLsizeiptr dataSizeInBytes = data->current_vertex_data * sizeof (float);
const int totalcmds = data->current_render_command;
Uint8 enabled_attrs = (1 << GLES2_ATTRIBUTE_POSITION);
GLES2_RenderCommand *cmd;
GLES2_RenderCommand *next;
SDL_Rect viewport;
SDL_Texture *bound_texture = NULL;
SDL_BlendMode blend = SDL_BLENDMODE_INVALID;
@ -491,11 +525,10 @@ GLES2_FlushCommands(SDL_Renderer *renderer)
GLfloat projection[4][4];
SDL_bool cliprect_enabled = SDL_FALSE;
SDL_Rect cliprect;
int i;
GLES2_ActivateRenderer(renderer);
if (totalcmds == 0) { /* nothing to do! */
if (data->render_commands == NULL) { /* nothing to do! */
SDL_assert(data->current_vertex_data == 0);
return 0;
}
@ -506,7 +539,9 @@ GLES2_FlushCommands(SDL_Renderer *renderer)
data->current_vertex_buffer = 0;
}
data->current_vertex_data = 0; /* start next VBO at start. */
data->current_render_command = 0;
cmd = data->render_commands;
data->render_commands = NULL;
data->render_commands_tail = NULL;
SDL_zero(projection);
projection[3][0] = -1.0f;
@ -555,8 +590,7 @@ GLES2_FlushCommands(SDL_Renderer *renderer)
data->glScissor(viewport.x + cliprect.x, drawableh - viewport.y - cliprect.y - cliprect.h, cliprect.w, cliprect.h);
}
for (i = 0; i < totalcmds; i++) {
const GLES2_RenderCommand *cmd = &data->render_commands[i];
while (cmd != NULL) {
switch (cmd->cmd) {
case GLES2_RENDERCMD_VIEWPORT:
if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) {
@ -713,6 +747,12 @@ GLES2_FlushCommands(SDL_Renderer *renderer)
default: SDL_assert(!"Unknown rendering command"); break;
}
/* put this command in the pool for reuse, move on to next one. */
next = cmd->next;
cmd->next = data->render_commands_pool;
data->render_commands_pool = cmd;
cmd = next;
}
data->command_generation++;
@ -734,8 +774,10 @@ GLES2_FlushCommandsIfTextureNeeded(SDL_Renderer *renderer, SDL_Texture *texture)
static int
GLES2_UpdateViewport(SDL_Renderer * renderer)
{
GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++];
GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer);
if (cmd == NULL) {
return -1;
}
cmd->cmd = GLES2_RENDERCMD_VIEWPORT;
SDL_memcpy(&cmd->data.viewport, &renderer->viewport, sizeof (SDL_Rect));
return 0;
@ -744,8 +786,10 @@ GLES2_UpdateViewport(SDL_Renderer * renderer)
static int
GLES2_UpdateClipRect(SDL_Renderer * renderer)
{
GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++];
GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer);
if (cmd == NULL) {
return -1;
}
cmd->cmd = GLES2_RENDERCMD_CLIPRECT;
cmd->data.cliprect.enabled = renderer->clipping_enabled;
SDL_memcpy(&cmd->data.cliprect.rect, &renderer->clip_rect, sizeof (SDL_Rect));
@ -759,8 +803,25 @@ GLES2_DestroyRenderer(SDL_Renderer *renderer)
/* Deallocate everything */
if (data) {
GLES2_RenderCommand *cmd;
GLES2_ActivateRenderer(renderer);
if (data->render_commands_tail != NULL) {
data->render_commands_tail->next = data->render_commands_pool;
} else {
data->render_commands = data->render_commands_pool;
}
cmd = data->render_commands;
while (cmd != NULL) {
GLES2_RenderCommand *next = cmd->next;
SDL_free(cmd);
cmd = next;
}
SDL_free(data->vertex_data);
{
GLES2_ShaderCacheEntry *entry;
GLES2_ShaderCacheEntry *next;
@ -1625,8 +1686,10 @@ static void GLES2_RenderPresent(SDL_Renderer *renderer);
static int
GLES2_RenderClear(SDL_Renderer * renderer)
{
GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++];
GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer);
if (cmd == NULL) {
return -1;
}
cmd->cmd = GLES2_RENDERCMD_CLEAR;
cmd->data.clear.r = renderer->r;
cmd->data.clear.g = renderer->g;
@ -1635,25 +1698,48 @@ GLES2_RenderClear(SDL_Renderer * renderer)
return 0;
}
static void
static int
GLES2_AddVertices(SDL_Renderer *renderer, const GLES2_Attribute attr, const float *vertexData, size_t dataSizeInElements)
{
GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++];
GLfloat *vdata = &data->vertex_data[data->current_vertex_data];
const GLsizeiptr needed = data->current_vertex_data + dataSizeInElements;
GLES2_RenderCommand *cmd;
GLfloat *vdata;
if (needed > data->vertex_data_allocation) {
const GLsizeiptr newsize = data->vertex_data_allocation * 2;
printf("realloc'ing %p to %d\n", data->vertex_data, (int) newsize);
void *ptr = SDL_realloc(data->vertex_data, newsize * sizeof (GLfloat));
if (ptr == NULL) {
SDL_OutOfMemory();
return -1;
}
data->vertex_data = (GLfloat *) ptr;
data->vertex_data_allocation = newsize;
}
cmd = GLES2_AllocateRenderCommand(renderer);
if (cmd == NULL) {
return -1;
}
vdata = &data->vertex_data[data->current_vertex_data];
SDL_memcpy(vdata, vertexData, dataSizeInElements * sizeof (GLfloat));
cmd->cmd = GLES2_RENDERCMD_ATTR;
cmd->data.attr.attr = attr;
cmd->data.attr.offset = data->current_vertex_data * sizeof (GLfloat);
cmd->data.attr.count = dataSizeInElements;
data->current_vertex_data += dataSizeInElements;
return 0;
}
static GLES2_RenderCommand *
GLES2_InitSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLint first, const GLsizei count)
static int
GLES2_AddSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLint first, const GLsizei count)
{
GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
GLES2_RenderCommand *cmd = &data->render_commands[data->current_render_command++];
GLES2_RenderCommand *cmd = GLES2_AllocateRenderCommand(renderer);
if (cmd == NULL) {
return -1;
}
cmd->cmd = GLES2_RENDERCMD_DRAW;
cmd->data.draw.mode = mode;
cmd->data.draw.first = first;
@ -1666,7 +1752,7 @@ GLES2_InitSolidDrawCommand(SDL_Renderer *renderer, const GLenum mode, const GLin
cmd->data.draw.blend = renderer->blendMode;
cmd->data.draw.imgsrc = GLES2_IMAGESOURCE_SOLID;
cmd->data.draw.texture = NULL;
return cmd;
return 0;
}
static int
@ -1674,6 +1760,7 @@ GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int cou
{
GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */
int idx;
int rc;
/* Emit the specified vertices as points */
for (idx = 0; idx < count; ++idx) {
@ -1681,10 +1768,14 @@ GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int cou
vertices[(idx * 2) + 1] = points[idx].y + 0.5f;
}
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2);
GLES2_InitSolidDrawCommand(renderer, GL_POINTS, 0, count);
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2);
SDL_stack_free(vertices);
return 0;
if (rc == 0) {
rc = GLES2_AddSolidDrawCommand(renderer, GL_POINTS, 0, count);
}
return rc;
}
static int
@ -1692,6 +1783,7 @@ GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int coun
{
GLfloat *vertices = SDL_stack_alloc(GLfloat, count * 2); /* !!! FIXME: We could do this without a stack_alloc... */
int idx;
int rc;
/* Emit a line strip including the specified vertices */
for (idx = 0; idx < count; ++idx) {
@ -1699,8 +1791,10 @@ GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int coun
vertices[(idx * 2) + 1] = points[idx].y + 0.5f;
}
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2);
GLES2_InitSolidDrawCommand(renderer, GL_LINE_STRIP, 0, count);
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2);
if (rc == 0) {
rc = GLES2_AddSolidDrawCommand(renderer, GL_LINE_STRIP, 0, count);
}
#if 0 /* !!! FIXME: ugh */
/* We need to close the endpoint of the line */
@ -1711,7 +1805,7 @@ GLES2_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *points, int coun
#endif
SDL_stack_free(vertices);
return 0;
return rc;
}
static int
@ -1719,9 +1813,10 @@ GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count)
{
GLfloat vertices[8];
int idx;
int rc = 0;
/* Emit a line loop for each rectangle */
for (idx = 0; idx < count; ++idx) {
for (idx = 0; (rc == 0) && (idx < count); ++idx) {
const SDL_FRect *rect = &rects[idx];
GLfloat xMin = rect->x;
@ -1738,16 +1833,18 @@ GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count)
vertices[6] = xMax;
vertices[7] = yMax;
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8);
GLES2_InitSolidDrawCommand(renderer, GL_TRIANGLE_STRIP, 0, 4);
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8);
if (rc == 0) {
rc = GLES2_AddSolidDrawCommand(renderer, GL_TRIANGLE_STRIP, 0, 4);
}
}
return 0;
}
static GLES2_RenderCommand *
GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uint8 attrs)
static int
GLES2_AddCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Uint8 attrs)
{
GLES2_RenderCommand *cmd = NULL;
GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
@ -1820,8 +1917,7 @@ GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Ui
sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES;
break;
default:
SDL_SetError("Unsupported texture format");
return NULL;
return SDL_SetError("Unsupported texture format");
}
} else {
sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; /* Texture formats match, use the non color mapping shader (even if the formats are not ABGR) */
@ -1854,14 +1950,16 @@ GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Ui
sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES;
break;
default:
SDL_SetError("Unsupported texture format");
return NULL;
return SDL_SetError("Unsupported texture format");
}
}
((GLES2_TextureData *)texture->driverdata)->last_cmd_generation = data->command_generation;
cmd = &data->render_commands[data->current_render_command++];
cmd = GLES2_AllocateRenderCommand(renderer);
if (cmd == NULL) {
return -1;
}
cmd->cmd = GLES2_RENDERCMD_DRAW;
cmd->data.draw.mode = GL_TRIANGLE_STRIP;
cmd->data.draw.first = 0;
@ -1875,7 +1973,7 @@ GLES2_InitCopyDrawCommand(SDL_Renderer *renderer, SDL_Texture *texture, const Ui
cmd->data.draw.imgsrc = sourceType;
cmd->data.draw.texture = texture;
return cmd;
return 0;
}
static int
@ -1883,6 +1981,7 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
const SDL_FRect *dstrect)
{
GLfloat vertices[8];
int rc;
/* Emit the textured quad */
vertices[0] = dstrect->x;
@ -1893,20 +1992,25 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s
vertices[5] = (dstrect->y + dstrect->h);
vertices[6] = (dstrect->x + dstrect->w);
vertices[7] = (dstrect->y + dstrect->h);
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8);
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8);
vertices[0] = srcrect->x / (GLfloat)texture->w;
vertices[1] = srcrect->y / (GLfloat)texture->h;
vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[3] = srcrect->y / (GLfloat)texture->h;
vertices[4] = srcrect->x / (GLfloat)texture->w;
vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8);
if (rc == 0) {
vertices[0] = srcrect->x / (GLfloat)texture->w;
vertices[1] = srcrect->y / (GLfloat)texture->h;
vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[3] = srcrect->y / (GLfloat)texture->h;
vertices[4] = srcrect->x / (GLfloat)texture->w;
vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8);
GLES2_InitCopyDrawCommand(renderer, texture, 0);
return 0;
if (rc == 0) {
rc = GLES2_AddCopyDrawCommand(renderer, texture, 0);
}
}
return rc;
}
static int
@ -1915,50 +2019,60 @@ GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect
{
const float radian_angle = (float)(M_PI * (360.0 - angle) / 180.0);
GLfloat vertices[8];
int rc;
vertices[0] = vertices[2] = vertices[4] = vertices[6] = (GLfloat)SDL_sin(radian_angle);
/* render expects cos value - 1 (see GLES2_VertexSrc_Default_) */
vertices[1] = vertices[3] = vertices[5] = vertices[7] = (GLfloat)SDL_cos(radian_angle) - 1.0f;
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_ANGLE, vertices, 8);
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_ANGLE, vertices, 8);
/* Calculate the center of rotation */
vertices[0] = vertices[2] = vertices[4] = vertices[6] = (center->x + dstrect->x);
vertices[1] = vertices[3] = vertices[5] = vertices[7] = (center->y + dstrect->y);
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_CENTER, vertices, 8);
if (rc == 0) {
vertices[0] = vertices[2] = vertices[4] = vertices[6] = (center->x + dstrect->x);
vertices[1] = vertices[3] = vertices[5] = vertices[7] = (center->y + dstrect->y);
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_CENTER, vertices, 8);
if (rc == 0) {
/* Emit the textured quad */
vertices[0] = dstrect->x;
vertices[1] = dstrect->y;
vertices[2] = (dstrect->x + dstrect->w);
vertices[3] = dstrect->y;
vertices[4] = dstrect->x;
vertices[5] = (dstrect->y + dstrect->h);
vertices[6] = (dstrect->x + dstrect->w);
vertices[7] = (dstrect->y + dstrect->h);
if (flip & SDL_FLIP_HORIZONTAL) {
const GLfloat tmp = vertices[0];
vertices[0] = vertices[4] = vertices[2];
vertices[2] = vertices[6] = tmp;
}
if (flip & SDL_FLIP_VERTICAL) {
const GLfloat tmp = vertices[1];
vertices[1] = vertices[3] = vertices[5];
vertices[5] = vertices[7] = tmp;
}
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8);
/* Emit the textured quad */
vertices[0] = dstrect->x;
vertices[1] = dstrect->y;
vertices[2] = (dstrect->x + dstrect->w);
vertices[3] = dstrect->y;
vertices[4] = dstrect->x;
vertices[5] = (dstrect->y + dstrect->h);
vertices[6] = (dstrect->x + dstrect->w);
vertices[7] = (dstrect->y + dstrect->h);
if (flip & SDL_FLIP_HORIZONTAL) {
const GLfloat tmp = vertices[0];
vertices[0] = vertices[4] = vertices[2];
vertices[2] = vertices[6] = tmp;
if (rc == 0) {
vertices[0] = srcrect->x / (GLfloat)texture->w;
vertices[1] = srcrect->y / (GLfloat)texture->h;
vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[3] = srcrect->y / (GLfloat)texture->h;
vertices[4] = srcrect->x / (GLfloat)texture->w;
vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
rc = GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8);
if (rc == 0) {
GLES2_AddCopyDrawCommand(renderer, texture, (1 << GLES2_ATTRIBUTE_CENTER) | (1 << GLES2_ATTRIBUTE_ANGLE));
}
}
}
}
if (flip & SDL_FLIP_VERTICAL) {
const GLfloat tmp = vertices[1];
vertices[1] = vertices[3] = vertices[5];
vertices[5] = vertices[7] = tmp;
}
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8);
vertices[0] = srcrect->x / (GLfloat)texture->w;
vertices[1] = srcrect->y / (GLfloat)texture->h;
vertices[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[3] = srcrect->y / (GLfloat)texture->h;
vertices[4] = srcrect->x / (GLfloat)texture->w;
vertices[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
vertices[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
vertices[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
GLES2_AddVertices(renderer, GLES2_ATTRIBUTE_TEXCOORD, vertices, 8);
GLES2_InitCopyDrawCommand(renderer, texture, (1 << GLES2_ATTRIBUTE_CENTER) | (1 << GLES2_ATTRIBUTE_ANGLE));
return 0;
return rc;
}
static int
@ -2158,6 +2272,15 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
renderer->driverdata = data;
renderer->window = window;
data->vertex_data_allocation = 512;
data->vertex_data = (GLfloat *) SDL_malloc(data->vertex_data_allocation * sizeof (GLfloat));
if (data->vertex_data == NULL) {
GLES2_DestroyRenderer(renderer);
SDL_OutOfMemory();
goto error;
}
printf("malloc'd %p\n", data->vertex_data);
/* Create an OpenGL ES 2.0 context */
data->context = SDL_GL_CreateContext(window);
if (!data->context) {