opengles2: Use client-side arrays on everything but Emscripten.

Turns out they're much faster!

Fixes #5206.
This commit is contained in:
Ryan C. Gordon 2022-01-11 16:15:45 -05:00
parent bb9ebad74b
commit f9b918ff40

View file

@ -28,6 +28,16 @@
#include "../../video/SDL_blit.h" #include "../../video/SDL_blit.h"
#include "SDL_shaders_gles2.h" #include "SDL_shaders_gles2.h"
/* WebGL doesn't offer client-side arrays, so use Vertex Buffer Objects
on Emscripten, which converts GLES2 into WebGL calls.
In all other cases, attempt to use client-side arrays, as they tend to
be faster. */
#if defined(__EMSCRIPTEN__)
#define USE_VERTEX_BUFFER_OBJECTS 1
#else
#define USE_VERTEX_BUFFER_OBJECTS 0
#endif
/* To prevent unnecessary window recreation, /* To prevent unnecessary window recreation,
* these should match the defaults selected in SDL_GL_ResetAttributes * these should match the defaults selected in SDL_GL_ResetAttributes
*/ */
@ -151,9 +161,12 @@ typedef struct GLES2_RenderData
GLES2_ProgramCache program_cache; GLES2_ProgramCache program_cache;
Uint8 clear_r, clear_g, clear_b, clear_a; Uint8 clear_r, clear_g, clear_b, clear_a;
#if USE_VERTEX_BUFFER_OBJECTS
GLuint vertex_buffers[8]; GLuint vertex_buffers[8];
size_t vertex_buffer_size[8]; size_t vertex_buffer_size[8];
int current_vertex_buffer; int current_vertex_buffer;
#endif
GLES2_DrawStateCache drawstate; GLES2_DrawStateCache drawstate;
} GLES2_RenderData; } GLES2_RenderData;
@ -844,7 +857,7 @@ GLES2_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture
} }
static int static int
SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_ImageSource imgsrc) SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_ImageSource imgsrc, void *vertices)
{ {
SDL_Texture *texture = cmd->data.draw.texture; SDL_Texture *texture = cmd->data.draw.texture;
const SDL_BlendMode blend = cmd->data.draw.blend; const SDL_BlendMode blend = cmd->data.draw.blend;
@ -926,7 +939,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I
} }
if (texture) { if (texture) {
SDL_Vertex *verts = (SDL_Vertex *) (cmd->data.draw.first); SDL_Vertex *verts = (SDL_Vertex *) (((Uint8 *) vertices) + cmd->data.draw.first);
data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *)&verts->tex_coord); data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *)&verts->tex_coord);
} }
@ -960,7 +973,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I
/* all drawing commands use this */ /* all drawing commands use this */
{ {
SDL_VertexSolid *verts = (SDL_VertexSolid *) (cmd->data.draw.first); SDL_VertexSolid *verts = (SDL_VertexSolid *) (((Uint8 *) vertices) + cmd->data.draw.first);
data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *) &verts->position); data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *) &verts->position);
data->glVertexAttribPointer(GLES2_ATTRIBUTE_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE /* Normalized */, stride, (const GLvoid *) &verts->color); data->glVertexAttribPointer(GLES2_ATTRIBUTE_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE /* Normalized */, stride, (const GLvoid *) &verts->color);
} }
@ -969,7 +982,7 @@ SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_I
} }
static int static int
SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd) SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, void *vertices)
{ {
GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata;
GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
@ -1079,7 +1092,7 @@ SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
} }
} }
return SetDrawState(data, cmd, sourceType); return SetDrawState(data, cmd, sourceType, vertices);
} }
static int static int
@ -1087,8 +1100,11 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
{ {
GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata; GLES2_RenderData *data = (GLES2_RenderData *) renderer->driverdata;
const SDL_bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || renderer->target->format == SDL_PIXELFORMAT_RGB888)); const SDL_bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_ARGB8888 || renderer->target->format == SDL_PIXELFORMAT_RGB888));
#if USE_VERTEX_BUFFER_OBJECTS
const int vboidx = data->current_vertex_buffer; const int vboidx = data->current_vertex_buffer;
const GLuint vbo = data->vertex_buffers[vboidx]; const GLuint vbo = data->vertex_buffers[vboidx];
#endif
if (GLES2_ActivateRenderer(renderer) < 0) { if (GLES2_ActivateRenderer(renderer) < 0) {
return -1; return -1;
@ -1106,6 +1122,7 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
} }
} }
#if USE_VERTEX_BUFFER_OBJECTS
/* upload the new VBO data for this set of commands. */ /* upload the new VBO data for this set of commands. */
data->glBindBuffer(GL_ARRAY_BUFFER, vbo); data->glBindBuffer(GL_ARRAY_BUFFER, vbo);
if (data->vertex_buffer_size[vboidx] < vertsize) { if (data->vertex_buffer_size[vboidx] < vertsize) {
@ -1120,6 +1137,8 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
if (data->current_vertex_buffer >= SDL_arraysize(data->vertex_buffers)) { if (data->current_vertex_buffer >= SDL_arraysize(data->vertex_buffers)) {
data->current_vertex_buffer = 0; data->current_vertex_buffer = 0;
} }
vertices = NULL; /* attrib pointers will be offsets into the VBO. */
#endif
while (cmd) { while (cmd) {
switch (cmd->command) { switch (cmd->command) {
@ -1184,7 +1203,7 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
break; break;
case SDL_RENDERCMD_DRAW_LINES: { case SDL_RENDERCMD_DRAW_LINES: {
if (SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID) == 0) { if (SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID, vertices) == 0) {
size_t count = cmd->data.draw.count; size_t count = cmd->data.draw.count;
if (count > 2) { if (count > 2) {
/* joined lines cannot be grouped */ /* joined lines cannot be grouped */
@ -1242,9 +1261,9 @@ GLES2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
} }
if (thistexture) { if (thistexture) {
ret = SetCopyState(renderer, cmd); ret = SetCopyState(renderer, cmd, vertices);
} else { } else {
ret = SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID); ret = SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID, vertices);
} }
if (ret == 0) { if (ret == 0) {
@ -1308,8 +1327,10 @@ GLES2_DestroyRenderer(SDL_Renderer *renderer)
data->framebuffers = nextnode; data->framebuffers = nextnode;
} }
#if USE_VERTEX_BUFFER_OBJECTS
data->glDeleteBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers); data->glDeleteBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers);
GL_CheckError("", renderer); GL_CheckError("", renderer);
#endif
SDL_GL_DeleteContext(data->context); SDL_GL_DeleteContext(data->context);
} }
@ -2071,8 +2092,10 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
renderer->info.max_texture_height = value; renderer->info.max_texture_height = value;
#if USE_VERTEX_BUFFER_OBJECTS
/* we keep a few of these and cycle through them, so data can live for a few frames. */ /* we keep a few of these and cycle through them, so data can live for a few frames. */
data->glGenBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers); data->glGenBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers);
#endif
data->framebuffers = NULL; data->framebuffers = NULL;
data->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &window_framebuffer); data->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &window_framebuffer);