draw multiple quads
This commit is contained in:
parent
3002123ef7
commit
e3d87525d4
2
Makefile
2
Makefile
@ -1,3 +1,3 @@
|
|||||||
test_renderer: test_renderer.c3 src/renderer.c3 resources/shaders/source/*
|
test_renderer: test_renderer.c3 src/renderer.c3 resources/shaders/source/*
|
||||||
scripts/compile_shaders.sh
|
scripts/compile_shaders.sh
|
||||||
c3c compile-run -g -O0 test_renderer.c3 src/renderer.c3 --libdir ../sdl3.c3l --lib sdl3
|
c3c compile -g -O0 test_renderer.c3 src/renderer.c3 --libdir ../sdl3.c3l --lib sdl3
|
||||||
|
12
TODO
12
TODO
@ -60,8 +60,8 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
[x] rect commads should have:
|
[x] rect commads should have:
|
||||||
- border width
|
- border width
|
||||||
- border radius
|
- border radius
|
||||||
[x] add a command to update an atlas
|
[x] add a command to update an atlas
|
||||||
[ ] New window command, useful for popups
|
[ ] New window command, useful for popups
|
||||||
|
|
||||||
@ -107,3 +107,11 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
queried by the user for later use. This allows for smaller caches and in general
|
queried by the user for later use. This allows for smaller caches and in general
|
||||||
reduces some load, since most of the stuff is recomputed for every frame.
|
reduces some load, since most of the stuff is recomputed for every frame.
|
||||||
|
|
||||||
|
## SDL3 Renderer
|
||||||
|
|
||||||
|
- smart batching
|
||||||
|
- maybe use instancing since we are always drawing the same geometry. With instancing every
|
||||||
|
different quad could have its coulour, border and radius with much better performance than
|
||||||
|
issuing a draw call for every quad (and uploading it)
|
||||||
|
https://rastertek.com/dx11win10tut48.html
|
||||||
|
https://www.braynzarsoft.net/viewtutorial/q16390-33-instancing-with-indexed-primitives
|
@ -28,11 +28,13 @@ struct Texture {
|
|||||||
uint id;
|
uint id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// gpu buffer that contains a single quad
|
// The GPU buffers that contain quad info, the size is determined by MAX_QUAD_BATCH
|
||||||
|
const int MAX_QUAD_BATCH = 16;
|
||||||
struct QuadBuffer {
|
struct QuadBuffer {
|
||||||
sdl::GPUBuffer* vert_buf;
|
sdl::GPUBuffer* vert_buf;
|
||||||
sdl::GPUBuffer* idx_buf;
|
sdl::GPUBuffer* idx_buf;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
int count;
|
||||||
}
|
}
|
||||||
|
|
||||||
alias ShaderList = List{Shader};
|
alias ShaderList = List{Shader};
|
||||||
@ -93,6 +95,8 @@ $if DEBUG == 0:
|
|||||||
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "wayland");
|
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "wayland");
|
||||||
}
|
}
|
||||||
$else
|
$else
|
||||||
|
// in debug mode set the video driver to X11 because renderdoc/
|
||||||
|
// doesn't support debugging in wayland yet.
|
||||||
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "x11");
|
sdl::set_hint(sdl::HINT_VIDEO_DRIVER, "x11");
|
||||||
$endif
|
$endif
|
||||||
|
|
||||||
@ -120,16 +124,18 @@ $endif
|
|||||||
unreachable("failed to claim window for use with gpu: %s", sdl::get_error());
|
unreachable("failed to claim window for use with gpu: %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// initialize the quad buffer
|
// initialize the quad buffer
|
||||||
|
// ==========================
|
||||||
self.quad_buffer.vert_buf = sdl::create_gpu_buffer(self.gpu,
|
self.quad_buffer.vert_buf = sdl::create_gpu_buffer(self.gpu,
|
||||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_VERTEX, .size = Quad.vertices.sizeof}
|
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_VERTEX, .size = Quad.vertices.sizeof * MAX_QUAD_BATCH}
|
||||||
);
|
);
|
||||||
if (self.quad_buffer.vert_buf == null) {
|
if (self.quad_buffer.vert_buf == null) {
|
||||||
unreachable("failed to initialize quad buffer (vertex): %s", sdl::get_error());
|
unreachable("failed to initialize quad buffer (vertex): %s", sdl::get_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.quad_buffer.idx_buf = sdl::create_gpu_buffer(self.gpu,
|
self.quad_buffer.idx_buf = sdl::create_gpu_buffer(self.gpu,
|
||||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_INDEX, .size = Quad.indices.sizeof}
|
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_INDEX, .size = Quad.indices.sizeof * MAX_QUAD_BATCH}
|
||||||
);
|
);
|
||||||
if (self.quad_buffer.idx_buf == null) {
|
if (self.quad_buffer.idx_buf == null) {
|
||||||
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
|
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
|
||||||
@ -476,9 +482,13 @@ fn void Renderer.update_texture(&self, String name, char[] pixels, ushort width,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// an highly inefficient way to draw a single quad, no batching, per-quad upload
|
// Push a quad into the quad buffer, return true on success and false on failure
|
||||||
fn void Renderer.draw_rect(&self, short x, short y, short w, short h, uint color, String shader_name)
|
fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color)
|
||||||
{
|
{
|
||||||
|
if (self.quad_buffer.count >= MAX_QUAD_BATCH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// upload the quad data to the gpu
|
// upload the quad data to the gpu
|
||||||
if (self.quad_buffer.initialized == false) {
|
if (self.quad_buffer.initialized == false) {
|
||||||
unreachable("quad buffer not initialized");
|
unreachable("quad buffer not initialized");
|
||||||
@ -512,13 +522,11 @@ fn void Renderer.draw_rect(&self, short x, short y, short w, short h, uint color
|
|||||||
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .col.u = color};
|
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .col.u = color};
|
||||||
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .col.u = color};
|
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .col.u = color};
|
||||||
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .col.u = color};
|
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .col.u = color};
|
||||||
|
// triangle 1 indices
|
||||||
|
|
||||||
// triangle 1
|
|
||||||
quad.indices.i1 = 0; // v1
|
quad.indices.i1 = 0; // v1
|
||||||
quad.indices.i2 = 1; // v2
|
quad.indices.i2 = 1; // v2
|
||||||
quad.indices.i3 = 3; // v4
|
quad.indices.i3 = 3; // v4
|
||||||
// triangle 2
|
// triangle 2 indices
|
||||||
quad.indices.i4 = 1; // v2
|
quad.indices.i4 = 1; // v2
|
||||||
quad.indices.i5 = 2; // v3
|
quad.indices.i5 = 2; // v3
|
||||||
quad.indices.i6 = 3; // v4
|
quad.indices.i6 = 3; // v4
|
||||||
@ -532,15 +540,16 @@ fn void Renderer.draw_rect(&self, short x, short y, short w, short h, uint color
|
|||||||
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
||||||
|
|
||||||
// upload vertices
|
// upload vertices
|
||||||
|
QuadBuffer* qb = &self.quad_buffer;
|
||||||
sdl::upload_to_gpu_buffer(cpy,
|
sdl::upload_to_gpu_buffer(cpy,
|
||||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.vertices.offsetof},
|
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.vertices.offsetof},
|
||||||
&&(GPUBufferRegion){.buffer = self.quad_buffer.vert_buf, .offset = 0, .size = Quad.vertices.sizeof},
|
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = qb.count * Quad.vertices.sizeof, .size = Quad.vertices.sizeof},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
// upload indices
|
// upload indices
|
||||||
sdl::upload_to_gpu_buffer(cpy,
|
sdl::upload_to_gpu_buffer(cpy,
|
||||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.indices.offsetof},
|
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.indices.offsetof},
|
||||||
&&(GPUBufferRegion){.buffer = self.quad_buffer.idx_buf, .offset = 0, .size = Quad.indices.sizeof},
|
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = qb.count * Quad.indices.sizeof, .size = Quad.indices.sizeof},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -551,40 +560,43 @@ fn void Renderer.draw_rect(&self, short x, short y, short w, short h, uint color
|
|||||||
sdl::release_gpu_transfer_buffer(self.gpu, buf);
|
sdl::release_gpu_transfer_buffer(self.gpu, buf);
|
||||||
sdl::wait_for_gpu_idle(self.gpu);
|
sdl::wait_for_gpu_idle(self.gpu);
|
||||||
|
|
||||||
/*
|
qb.count++;
|
||||||
// now finally draw the quad
|
|
||||||
// if we are not in a render pass then we can't render shit
|
|
||||||
if (self.render_cmd == null) {
|
|
||||||
unreachable("start rendering first before trying to render a quad");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this could be done at the start of rendering
|
return true;
|
||||||
GPUTexture* t;
|
}
|
||||||
if (!sdl::wait_and_acquire_gpu_swapchain_texture(self.render_cmd, self.win, &t, null, null)) {
|
|
||||||
unreachable("failed to acquire swapchain texture: %s", sdl::get_error());
|
// draw all quads in the quad buffer, since uniforms are per-drawcall it makes no sense
|
||||||
}
|
// to draw them one a the time
|
||||||
|
fn void Renderer.draw_quads(&self, GPURenderPass* pass)
|
||||||
// TODO: begin render pass
|
{
|
||||||
|
QuadBuffer* qb = &self.quad_buffer;
|
||||||
Pipeline* p = self.pipelines.get_from_name(shader_name);
|
sdl::bind_gpu_vertex_buffers(pass, 0, (GPUBufferBinding[]){{.buffer = qb.vert_buf, .offset = 0}}, 1);
|
||||||
if (p == null) {
|
sdl::bind_gpu_index_buffer(pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = 0}, GPU_INDEXELEMENTSIZE_16BIT);
|
||||||
unreachable("no pipeline named: %s", shader_name);
|
|
||||||
}
|
// we need instancing to not do this
|
||||||
|
for (int i = 0; i < qb.count; i++) {
|
||||||
// bind the data
|
sdl::draw_gpu_indexed_primitives(pass, 6, 1, i*6, i*4, 0);
|
||||||
sdl::bind_gpu_graphics_pipeline(self.render_pass, pipeline);
|
}
|
||||||
sdl::bind_gpu_vertex_buffer(self.render_pass, 0,
|
|
||||||
&&(GPUBufferBinding){.buffer = self.quad_buffer.vert_buf, .offset = 0}, 1
|
qb.count = 0;
|
||||||
);
|
|
||||||
sdl::bind_gpu_index_buffer(self.render_pass, 0,
|
|
||||||
&&(GPUBufferBinding){.buffer = self.quad_buffer.idx_buf, .offset = 0}, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
sdl::draw_gpu_indexed_primitives(self.render_pass, 6, 1, 0, 0, 0);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fn Renderer.draw_quad, it has to use a vertex buffer and an index buffer
|
|
||||||
// TODO: fn Renderer.draw_sprite, same as draw_quad but also bind the texture
|
// TODO: fn Renderer.draw_sprite, same as draw_quad but also bind the texture
|
||||||
// TODO: fn Renderer.begin_render
|
// TODO: fn Renderer.begin_render
|
||||||
// TODO: fn Renderer.end_render
|
// TODO: fn Renderer.end_render
|
||||||
|
|
||||||
|
/// === NOTES ===
|
||||||
|
/* 1. The uniform data is per-render pass. So you can do:
|
||||||
|
* - push uniform
|
||||||
|
* - draw 1
|
||||||
|
* - draw 2
|
||||||
|
* But not:
|
||||||
|
* - push uniform
|
||||||
|
* - draw
|
||||||
|
* - push new uniform
|
||||||
|
* - draw
|
||||||
|
* And not even:
|
||||||
|
* - draw
|
||||||
|
* - push uniform
|
||||||
|
* - draw
|
||||||
|
*/
|
@ -47,7 +47,11 @@ fn int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rect 1
|
// rect 1
|
||||||
ren.draw_rect(100,100,100,100,0xff00ff00,"");
|
ren.push_quad(100,100,100,100,0xff00ff00);
|
||||||
|
// rect 2
|
||||||
|
ren.push_quad(0,0,20,20,0xff0000ff);
|
||||||
|
// rect 3
|
||||||
|
ren.push_quad(200,300,50,50,0xffff0000);
|
||||||
|
|
||||||
GPUGraphicsPipeline* p = ren.pipelines.get_from_name("rect shader").pipeline;
|
GPUGraphicsPipeline* p = ren.pipelines.get_from_name("rect shader").pipeline;
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
@ -55,29 +59,14 @@ fn int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
sdl::bind_gpu_graphics_pipeline(pass, p);
|
sdl::bind_gpu_graphics_pipeline(pass, p);
|
||||||
sdl::bind_gpu_vertex_buffers(pass, 0, (GPUBufferBinding[]){{.buffer = ren.quad_buffer.vert_buf, .offset = 0}}, 1);
|
|
||||||
sdl::bind_gpu_index_buffer(pass, &&(GPUBufferBinding){.buffer = ren.quad_buffer.idx_buf, .offset = 0}, GPU_INDEXELEMENTSIZE_16BIT);
|
|
||||||
Viewsize v = {.w = 640, .h = 480};
|
Viewsize v = {.w = 640, .h = 480};
|
||||||
v.ox = 50*i;
|
v.ox = 50*i;
|
||||||
v.oy = 50*i;
|
v.oy = 50*i;
|
||||||
|
|
||||||
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &v, Viewsize.sizeof);
|
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &v, Viewsize.sizeof);
|
||||||
|
|
||||||
sdl::draw_gpu_indexed_primitives(pass, 6, 1, 0, 0, 0);
|
ren.draw_quads(pass);
|
||||||
|
|
||||||
/*
|
|
||||||
// rect 2
|
|
||||||
ren.draw_rect(0,0,50,50,0xffff0000,"");
|
|
||||||
|
|
||||||
sdl::bind_gpu_graphics_pipeline(pass, p);
|
|
||||||
sdl::bind_gpu_vertex_buffers(pass, 0, (GPUBufferBinding[]){{.buffer = ren.quad_buffer.vert_buf, .offset = 0}}, 1);
|
|
||||||
sdl::bind_gpu_index_buffer(pass, &&(GPUBufferBinding){.buffer = ren.quad_buffer.idx_buf, .offset = 0}, GPU_INDEXELEMENTSIZE_16BIT);
|
|
||||||
Viewsize w = {.w = 640, .h = 480};
|
|
||||||
w.ox = 25*i;
|
|
||||||
//w.oy = 25*i;
|
|
||||||
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &w, Viewsize.sizeof);
|
|
||||||
|
|
||||||
sdl::draw_gpu_indexed_primitives(pass, 6, 1, 0, 0, 0);
|
|
||||||
*/
|
|
||||||
|
|
||||||
sdl::end_gpu_render_pass(pass);
|
sdl::end_gpu_render_pass(pass);
|
||||||
sdl::submit_gpu_command_buffer(cmdbuf);
|
sdl::submit_gpu_command_buffer(cmdbuf);
|
||||||
|
Loading…
Reference in New Issue
Block a user