breaking triangle buffer into batches each time the current image changes

This commit is contained in:
martinfouilleul 2023-02-02 18:57:57 +01:00
parent 9cadc2f23f
commit 19988d045e
6 changed files with 113 additions and 46 deletions

View File

@ -107,7 +107,38 @@ void mg_gles_canvas_update_vertex_layout(mg_gles_canvas_backend* backend)
.indexStride = LAYOUT_INT_SIZE};
}
void mg_gles_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount, mg_color clearColor)
void mg_gles_canvas_begin(mg_canvas_backend* interface)
{
mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface;
mg_gles_surface* surface = mg_gles_canvas_get_surface(backend);
if(!surface)
{
return;
}
glUseProgram(backend->program);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void mg_gles_canvas_end(mg_canvas_backend* interface)
{
//NOTE: nothing to do here...
}
void mg_gles_canvas_clear(mg_canvas_backend* interface, mg_color clearColor)
{
mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface;
mg_gles_surface* surface = mg_gles_canvas_get_surface(backend);
if(!surface)
{
return;
}
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
glClear(GL_COLOR_BUFFER_BIT);
}
void mg_gles_canvas_draw_batch(mg_canvas_backend* interface, u32 vertexCount, u32 indexCount)
{
mg_gles_canvas_backend* backend = (mg_gles_canvas_backend*)interface;
mg_gles_surface* surface = mg_gles_canvas_get_surface(backend);
@ -125,14 +156,11 @@ void mg_gles_canvas_draw_buffers(mg_canvas_backend* interface, u32 vertexCount,
glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->indexBuffer);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glUseProgram(backend->program);
glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->vertexBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->indexBuffer);
glUniform1i(0, indexCount);
glUniform4f(1, clearColor.r, clearColor.g, clearColor.b, clearColor.a);
glDrawArrays(GL_TRIANGLES, 0, 6);
@ -187,7 +215,10 @@ mg_canvas_backend* mg_gles_canvas_create(mg_surface surface)
//NOTE(martin): setup interface functions
backend->interface.destroy = mg_gles_canvas_destroy;
backend->interface.drawBuffers = mg_gles_canvas_draw_buffers;
backend->interface.begin = mg_gles_canvas_begin;
backend->interface.end = mg_gles_canvas_end;
backend->interface.clear = mg_gles_canvas_clear;
backend->interface.drawBatch = mg_gles_canvas_draw_batch;
backend->interface.atlasUpload = mg_gles_canvas_atlas_upload;
mg_surface_prepare(surface);

View File

@ -34,7 +34,6 @@ const char* gles_canvas_fragment =
"} indexBuffer ;\n"
"\n"
"layout(location = 0) uniform int indexCount;\n"
"layout(location = 1) uniform vec4 clearColor;\n"
"\n"
"layout(location = 0) out vec4 fragColor;\n"
"\n"
@ -73,8 +72,8 @@ const char* gles_canvas_fragment =
" {\n"
" currentZIndex[i] = -1;\n"
" flipCount[i] = 0;\n"
" sampleColor[i] = clearColor;\n"
" currentColor[i] = clearColor;\n"
" sampleColor[i] = vec4(0, 0, 0, 0);\n"
" currentColor[i] = vec4(0, 0, 0, 0);\n"
" }\n"
"\n"
" for(int triangleIndex=0; triangleIndex<indexCount; triangleIndex+=3)\n"

View File

@ -21,7 +21,6 @@ layout(binding = 1) buffer indexBufferSSBO {
} indexBuffer ;
layout(location = 0) uniform int indexCount;
layout(location = 1) uniform vec4 clearColor;
layout(location = 0) out vec4 fragColor;
@ -60,8 +59,8 @@ void main()
{
currentZIndex[i] = -1;
flipCount[i] = 0;
sampleColor[i] = clearColor;
currentColor[i] = clearColor;
sampleColor[i] = vec4(0, 0, 0, 0);
currentColor[i] = vec4(0, 0, 0, 0);
}
for(int triangleIndex=0; triangleIndex<indexCount; triangleIndex+=3)

View File

@ -433,9 +433,23 @@ u32 mg_get_next_z_index(mg_canvas_data* canvas)
}
/////////////////////////////////////// WIP /////////////////////////////////////////////////////////////////////////
//TODO(martin): rename with something more explicit
u32 mg_vertices_base_index(mg_canvas_data* canvas)
{
return(canvas->vertexCount);
return(canvas->batchBaseIndex + canvas->vertexCount);
}
int* mg_reserve_indices(mg_canvas_data* canvas, u32 indexCount)
{
mg_vertex_layout* layout = &canvas->backend->vertexLayout;
//TODO: do something here...
ASSERT(canvas->indexCount + indexCount < layout->maxIndexCount);
int* base = ((int*)layout->indexBuffer) + canvas->indexCount;
canvas->indexCount += indexCount;
return(base);
}
void mg_push_textured_vertex(mg_canvas_data* canvas, vec2 pos, vec4 cubic, vec2 uv, mg_color color, u64 zIndex)
@ -461,21 +475,6 @@ void mg_push_vertex(mg_canvas_data* canvas, vec2 pos, vec4 cubic, mg_color color
mg_push_textured_vertex(canvas, pos, cubic, (vec2){0, 0}, color, zIndex);
}
/////////////////////////////////////// WIP /////////////////////////////////////////////////////////////////////////
int* mg_reserve_indices(mg_canvas_data* canvas, u32 indexCount)
{
mg_vertex_layout* layout = &canvas->backend->vertexLayout;
//TODO: do something here...
ASSERT(canvas->indexCount + indexCount < layout->maxIndexCount);
int* base = ((int*)layout->indexBuffer) + canvas->indexCount;
canvas->indexCount += indexCount;
return(base);
}
//-----------------------------------------------------------------------------------------------------------
// Path Filling
//-----------------------------------------------------------------------------------------------------------
@ -2811,6 +2810,18 @@ void mg_do_clip_push(mg_canvas_data* canvas, mp_rect clip)
mg_clip_stack_push(canvas, r);
}
void mg_flush_batch(mg_canvas_data* canvas)
{
if(canvas->backend && canvas->backend->drawBatch)
{
canvas->backend->drawBatch(canvas->backend, canvas->vertexCount, canvas->indexCount);
}
canvas->batchBaseIndex += canvas->vertexCount;
canvas->vertexCount = 0;
canvas->indexCount = 0;
}
void mg_flush()
{
mg_canvas_data* canvas = __mgCurrentCanvas;
@ -2830,6 +2841,10 @@ void mg_flush()
0, 1, 0};
canvas->clip = (mp_rect){-FLT_MAX/2, -FLT_MAX/2, FLT_MAX, FLT_MAX};
mg_image currentImage = mg_image_nil();
canvas->backend->begin(canvas->backend);
for(int i=0; i<count; i++)
{
if(nextIndex >= count)
@ -2840,6 +2855,12 @@ void mg_flush()
mg_primitive* primitive = &(canvas->primitives[nextIndex]);
nextIndex++;
if(i && primitive->attributes.image.h != currentImage.h)
{
mg_flush_batch(canvas);
currentImage = primitive->attributes.image;
}
switch(primitive->cmd)
{
case MG_CMD_CLEAR:
@ -2847,8 +2868,9 @@ void mg_flush()
//NOTE(martin): clear buffers
canvas->vertexCount = 0;
canvas->indexCount = 0;
canvas->batchBaseIndex = 0;
clearColor = primitive->attributes.color;
canvas->backend->clear(canvas->backend, primitive->attributes.color);
} break;
case MG_CMD_FILL:
@ -2949,10 +2971,9 @@ void mg_flush()
}
exit_command_loop: ;
if(canvas->backend && canvas->backend->drawBuffers)
{
canvas->backend->drawBuffers(canvas->backend, canvas->vertexCount, canvas->indexCount, clearColor);
}
mg_flush_batch(canvas);
canvas->backend->end(canvas->backend);
//NOTE(martin): clear buffers
canvas->primitiveCount = 0;
@ -2962,7 +2983,6 @@ void mg_flush()
canvas->path.startIndex = 0;
canvas->path.count = 0;
canvas->primitiveCount = 0;
canvas->frameCounter++;
}
////////////////////////////////////////////////////////////

View File

@ -222,7 +222,10 @@ typedef struct mg_vertex_layout
typedef struct mg_canvas_backend mg_canvas_backend;
typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_draw_buffers_proc)(mg_canvas_backend* backend, u32 vertexCount, u32 indexCount, mg_color clearColor);
typedef void (*mg_canvas_backend_begin_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_end_proc)(mg_canvas_backend* backend);
typedef void (*mg_canvas_backend_clear_proc)(mg_canvas_backend* backend, mg_color clearColor);
typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend, u32 vertexCount, u32 indexCount);
typedef void (*mg_canvas_backend_atlas_upload_proc)(mg_canvas_backend* backend, mp_rect rect, u8* bytes);
typedef struct mg_canvas_backend
@ -230,7 +233,10 @@ typedef struct mg_canvas_backend
mg_vertex_layout vertexLayout;
mg_canvas_backend_destroy_proc destroy;
mg_canvas_backend_draw_buffers_proc drawBuffers;
mg_canvas_backend_begin_proc begin;
mg_canvas_backend_end_proc end;
mg_canvas_backend_clear_proc clear;
mg_canvas_backend_draw_batch_proc drawBatch;
mg_canvas_backend_atlas_upload_proc atlasUpload;
} mg_canvas_backend;
@ -264,6 +270,7 @@ typedef struct mg_canvas_data
u32 vertexCount;
u32 indexCount;
u32 batchBaseIndex;
mg_image_data images[MG_IMAGE_MAX_COUNT];
u32 imageNextIndex;

View File

@ -1,16 +1,16 @@
Various notes/thoughts about the 2D vector graphics renderer
Various notes/thoughts about the 2D vector graphics renderer
Triangle Rasterization
----------------------
https://fgiesen.wordpress.com/2013/02/08/triangle-rasterization-in-practice/
https://github.com/rygorous/trirast/blob/master/main.cpp
https://joshbeam.com/articles/triangle_rasterization/
https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/
https://web.archive.org/web/20120625103536/http://devmaster.net/forums/topic/1145-advanced-rasterization/
https://fgiesen.wordpress.com/2013/02/08/triangle-rasterization-in-practice/
https://github.com/rygorous/trirast/blob/master/main.cpp
https://joshbeam.com/articles/triangle_rasterization/
https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/
https://web.archive.org/web/20120625103536/http://devmaster.net/forums/topic/1145-advanced-rasterization/
Bindless textures
-----------------
@ -28,5 +28,16 @@ This would mean individual textures can be set and used in a frame. So without b
- We'll likely need a batching fallback anyway?
-> Angle doesn't seem to support GL_IMG_bindless_textures for now.
Workaround: we could use a desktop GL 4.3+ context for the canvas renderer on windows, _BUT_ the functions would conflict with the GLES canvas. Except if we use function pointers that are loaded differently for each context (which we probably should but I'd better keep it for later).
-> We'll probably want to do that, or make 1 draw call per changing texture.
Anyway for now, is it possible have an _under the hood_ atlas, and reserve a way to change the API so that we make the atlas explicit / allow using single textures for big images etc?
* We could decide that we can set an atlas, and all mg_images get allocated from that atlas. If no atlas is set a default one is used.
* or, we can expose mg_texture and related APIs for now, as if they were individual, but back them by a hidden atlas. And a bit later expose mg_image/atlas -> maybe better.
* Or just implement breaking the triangle stream into batches now...