libobs/graphics: Improve sprite drawing perf

Sprites were unnecessarily updating a dynamic vertex buffer every single
draw call. This creates static vertex buffers for common vertex buffer
dimensions/uvs and only uses a dynamic vertex buffer when the texture
coordinates actually change.

Porbably not a significant perf change considering the very few number
of traingles we draw per frame but still nice to do.
This commit is contained in:
Lain
2025-04-23 15:23:29 -04:00
committed by Ryan Foster
parent d25e2c18e4
commit 7b8b51b29f
2 changed files with 64 additions and 21 deletions
+2
View File
@@ -311,6 +311,8 @@ struct graphics_subsystem {
struct gs_effect *cur_effect;
gs_vertbuffer_t *sprite_buffer;
gs_vertbuffer_t *flipped_sprite_buffer;
gs_vertbuffer_t *subregion_buffer;
bool using_immediate;
struct gs_vb_data *vbd;
+62 -21
View File
@@ -114,25 +114,46 @@ static bool graphics_init_immediate_vb(struct graphics_subsystem *graphics)
return true;
}
static bool graphics_init_sprite_vb(struct graphics_subsystem *graphics)
static bool graphics_init_sprite_vbs(struct graphics_subsystem *graphics)
{
struct gs_vb_data *vbd;
vbd = gs_vbdata_create();
vbd->num = 4;
vbd->points = bmalloc(sizeof(struct vec3) * 4);
vbd->points = bzalloc(sizeof(struct vec3) * 4);
vbd->num_tex = 1;
vbd->tvarray = bmalloc(sizeof(struct gs_tvertarray));
vbd->tvarray = bzalloc(sizeof(struct gs_tvertarray));
vbd->tvarray[0].width = 2;
vbd->tvarray[0].array = bmalloc(sizeof(struct vec2) * 4);
vbd->tvarray[0].array = bzalloc(sizeof(struct vec2) * 4);
memset(vbd->points, 0, sizeof(struct vec3) * 4);
memset(vbd->tvarray[0].array, 0, sizeof(struct vec2) * 4);
vbd->points[1].x = 1.0f;
vbd->points[2].y = 1.0f;
vbd->points[3].x = 1.0f;
vbd->points[3].y = 1.0f;
graphics->sprite_buffer = graphics->exports.device_vertexbuffer_create(graphics->device, vbd, GS_DYNAMIC);
struct vec2 *uvs = vbd->tvarray[0].array;
uvs[1].x = 1.0f;
uvs[2].y = 1.0f;
uvs[3].x = 1.0f;
uvs[3].y = 1.0f;
graphics->sprite_buffer = gs_vertexbuffer_create(vbd, GS_DUP_BUFFER);
if (!graphics->sprite_buffer)
return false;
graphics->subregion_buffer = gs_vertexbuffer_create(vbd, GS_DUP_BUFFER | GS_DYNAMIC);
if (!graphics->subregion_buffer)
return false;
uvs[0].y = 1.0f;
uvs[1].y = 1.0f;
uvs[2].y = 0.0f;
uvs[3].y = 0.0f;
graphics->flipped_sprite_buffer = gs_vertexbuffer_create(vbd, 0);
if (!graphics->flipped_sprite_buffer)
return false;
return true;
}
@@ -145,9 +166,11 @@ static bool graphics_init(struct graphics_subsystem *graphics)
graphics->exports.device_enter_context(graphics->device);
thread_graphics = graphics;
if (!graphics_init_immediate_vb(graphics))
return false;
if (!graphics_init_sprite_vb(graphics))
if (!graphics_init_sprite_vbs(graphics))
return false;
if (pthread_mutex_init(&graphics->mutex, NULL) != 0)
return false;
@@ -168,6 +191,7 @@ static bool graphics_init(struct graphics_subsystem *graphics)
graphics->exports.device_leave_context(graphics->device);
gs_init_image_deps();
thread_graphics = NULL;
return true;
}
@@ -227,6 +251,8 @@ void gs_destroy(graphics_t *graphics)
effect = next;
}
graphics->exports.gs_vertexbuffer_destroy(graphics->subregion_buffer);
graphics->exports.gs_vertexbuffer_destroy(graphics->flipped_sprite_buffer);
graphics->exports.gs_vertexbuffer_destroy(graphics->sprite_buffer);
graphics->exports.gs_vertexbuffer_destroy(graphics->immediate_vertbuffer);
graphics->exports.device_destroy(graphics->device);
@@ -1000,23 +1026,30 @@ void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t h
fcx = width ? (float)width : (float)gs_texture_get_width(tex);
fcy = height ? (float)height : (float)gs_texture_get_height(tex);
data = gs_vertexbuffer_get_data(graphics->sprite_buffer);
if (tex && gs_texture_is_rect(tex))
build_sprite_rect(data, tex, fcx, fcy, flip);
else
build_sprite_norm(data, fcx, fcy, flip);
gs_matrix_push();
gs_matrix_scale3f(fcx, fcy, 1.0f);
gs_vertexbuffer_flush(graphics->sprite_buffer);
gs_load_vertexbuffer(graphics->sprite_buffer);
gs_load_indexbuffer(NULL);
gs_draw(GS_TRISTRIP, 0, 0);
if (tex && gs_texture_is_rect(tex)) {
data = gs_vertexbuffer_get_data(graphics->subregion_buffer);
build_sprite_rect(data, tex, 1.0f, 1.0f, flip);
gs_vertexbuffer_flush(graphics->subregion_buffer);
gs_load_vertexbuffer(graphics->subregion_buffer);
gs_draw(GS_TRISTRIP, 0, 0);
} else {
gs_load_vertexbuffer(flip ? graphics->flipped_sprite_buffer : graphics->sprite_buffer);
gs_draw(GS_TRISTRIP, 0, 0);
}
gs_matrix_pop();
}
void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, uint32_t sub_x, uint32_t sub_y, uint32_t sub_cx,
uint32_t sub_cy)
{
graphics_t *graphics = thread_graphics;
uint32_t cx, cy;
float fcx, fcy;
struct gs_vb_data *data;
@@ -1027,14 +1060,22 @@ void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, uint32_t sub_x,
}
}
fcx = (float)gs_texture_get_width(tex);
fcy = (float)gs_texture_get_height(tex);
cx = gs_texture_get_width(tex);
cy = gs_texture_get_height(tex);
data = gs_vertexbuffer_get_data(graphics->sprite_buffer);
if (sub_x == 0 && sub_y == 0 && sub_cx == cx && sub_cy == cy) {
gs_draw_sprite(tex, flip, 0, 0);
return;
}
fcx = (float)cx;
fcy = (float)cy;
data = gs_vertexbuffer_get_data(graphics->subregion_buffer);
build_subsprite_norm(data, (float)sub_x, (float)sub_y, (float)sub_cx, (float)sub_cy, fcx, fcy, flip);
gs_vertexbuffer_flush(graphics->sprite_buffer);
gs_load_vertexbuffer(graphics->sprite_buffer);
gs_vertexbuffer_flush(graphics->subregion_buffer);
gs_load_vertexbuffer(graphics->subregion_buffer);
gs_load_indexbuffer(NULL);
gs_draw(GS_TRISTRIP, 0, 0);