Compare commits
No commits in common. "b411718c9406ba6decdbab8c0841e7f74172504f" and "10ee643a0cef5272159384290370e32c369c0a2f" have entirely different histories.
b411718c94
...
10ee643a0c
@ -68,7 +68,7 @@ const Rect DIV_FILL = { .x = 0, .y = 0, .w = 0, .h = 0 };
|
||||
|
||||
const uint STACK_STEP = 10;
|
||||
const uint MAX_ELEMS = 128;
|
||||
const uint MAX_CMDS = 2048;
|
||||
const uint MAX_CMDS = 256;
|
||||
const uint ROOT_ID = 1;
|
||||
const uint TEXT_MAX = 64;
|
||||
|
||||
@ -197,7 +197,7 @@ fn void? Ctx.init(&ctx)
|
||||
ctx.cache.init()!;
|
||||
defer catch { (void)ctx.cache.free(); }
|
||||
|
||||
ctx.cmd_queue.init(MAX_CMDS)!;
|
||||
ctx.cmd_queue.init(MAX_ELEMENTS)!;
|
||||
defer catch { (void)ctx.cmd_queue.free(); }
|
||||
|
||||
ctx.active_div = 0;
|
||||
|
@ -13,7 +13,7 @@ layout(location = 0) out vec4 fragColor;
|
||||
void main()
|
||||
{
|
||||
ivec2 ts = textureSize(tx, 0);
|
||||
vec2 fts = vec2(ts);
|
||||
vec2 fts = vec2(float(ts.x), float(ts.y));
|
||||
vec2 real_uv = uv / fts;
|
||||
|
||||
vec4 opacity = texture(tx, real_uv);
|
||||
|
@ -24,7 +24,7 @@ float median(float r, float g, float b) {
|
||||
|
||||
void main() {
|
||||
ivec2 ts = textureSize(tx, 0);
|
||||
vec2 fts = vec2(ts);
|
||||
vec2 fts = vec2(float(ts.x), float(ts.y));
|
||||
vec2 real_uv = uv / fts;
|
||||
|
||||
vec3 msd = texture(tx, real_uv).rgb;
|
||||
|
@ -4,24 +4,56 @@ layout(set = 3, binding = 0) uniform Viewport {
|
||||
ivec2 view;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec4 in_color;
|
||||
layout(location = 1) in vec4 in_quad_size; // x,y, w,h
|
||||
layout(location = 2) in float in_radius;
|
||||
layout(location = 0) in vec4 color;
|
||||
layout(location = 1) in vec2 local_position;
|
||||
layout(location = 2) in vec2 global_position;
|
||||
layout(location = 3) in float radius;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
// SDF for a rounded rectangle given the centerpoint, half size and radius, all in pixels
|
||||
float sdf_rr(vec2 p, vec2 half_size, float radius) {
|
||||
float sdf_rr(vec2 p, vec2 center, vec2 half_size, float radius) {
|
||||
// Translate fragment position to rectangle's coordinate system
|
||||
p -= center;
|
||||
// Adjust for rounded corners: shrink the rectangle by the radius
|
||||
vec2 q = abs(p) - half_size + radius;
|
||||
// Combine distance components:
|
||||
// - max(q, 0.0) handles regions outside the rounded corners
|
||||
// - min(max(q.x, q.y), 0.0) handles regions inside the rectangle
|
||||
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 centerpoint = in_quad_size.xy + in_quad_size.zw * 0.5;
|
||||
vec2 half_size = in_quad_size.zw * 0.5;
|
||||
float distance = sdf_rr(vec2(gl_FragCoord) - centerpoint, half_size, in_radius);
|
||||
float alpha = 1.0 - smoothstep(0.0, 1.0, distance);
|
||||
// local_position are normalized coordinates in the rectangle, passed from the
|
||||
// vertex shader
|
||||
/*
|
||||
* Window
|
||||
* +-----------------------+
|
||||
* | (1,1) |
|
||||
* | +----------x |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | | Rect | |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | x----------+ |
|
||||
* | (-1,-1) |
|
||||
* | |
|
||||
* +-----------------------+
|
||||
*/
|
||||
|
||||
fragColor = vec4(in_color.rgb, in_color.a * alpha);
|
||||
vec2 dx = dFdx(local_position);
|
||||
vec2 dy = dFdy(local_position);
|
||||
// Conversion from normalized coordinates to pixels
|
||||
vec2 norm_to_px = 1.0 / vec2(length(dx), length(dy));
|
||||
|
||||
vec2 centerpoint = global_position - local_position * norm_to_px;
|
||||
// the half size of the rectangle is also norm_to_px
|
||||
vec2 half_size = 1.0 * norm_to_px;
|
||||
|
||||
float distance = sdf_rr(global_position, centerpoint, half_size, radius);
|
||||
float alpha = 1.0 - smoothstep(0.0, 1.0, max(distance, 0.0));
|
||||
|
||||
fragColor = vec4(color.rgb, color.a * alpha);
|
||||
}
|
@ -5,25 +5,26 @@ layout(set = 1, binding = 0) uniform Viewport {
|
||||
};
|
||||
|
||||
layout(location = 0) in ivec2 position;
|
||||
layout(location = 1) in ivec4 attr; // quad x,y,w,h
|
||||
layout(location = 2) in ivec2 uv; // x,y in the texture
|
||||
layout(location = 3) in ivec4 color;
|
||||
layout(location = 1) in ivec2 uv;
|
||||
layout(location = 2) in ivec4 color;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_quad_size;
|
||||
layout(location = 2) out float out_radius;
|
||||
layout(location = 0) out vec4 col;
|
||||
layout(location = 1) out vec2 local_position;
|
||||
layout(location = 2) out vec2 global_position;
|
||||
layout(location = 3) out float radius;
|
||||
|
||||
void main()
|
||||
{
|
||||
// vertex position
|
||||
ivec2 px_pos = attr.xy + position.xy * attr.zw;
|
||||
vec2 clip_pos;
|
||||
clip_pos.x = float(px_pos.x)*2.0 / view.x - 1.0;
|
||||
clip_pos.y = -(float(px_pos.y)*2.0 / view.y - 1.0);
|
||||
vec2 pos;
|
||||
pos.x = float(position.x)*2.0 / view.x - 1.0;
|
||||
pos.y = -(float(position.y)*2.0 / view.y - 1.0);
|
||||
|
||||
gl_Position = vec4(clip_pos, 0.0, 1.0);
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
|
||||
out_color = vec4(color) / 255.0;
|
||||
out_quad_size = vec4(attr);
|
||||
out_radius = float(abs(uv.x));
|
||||
local_position = vec2(sign(uv));
|
||||
global_position = gl_Position.xy;
|
||||
radius = abs(float(uv.x));
|
||||
|
||||
col = vec4(color) / 255.0;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ layout(set = 2, binding = 0) uniform sampler2D tx;
|
||||
void main()
|
||||
{
|
||||
ivec2 ts = textureSize(tx, 0);
|
||||
vec2 fts = vec2(ts);
|
||||
vec2 fts = vec2(float(ts.x), float(ts.y));
|
||||
vec2 real_uv = uv / fts;
|
||||
fragColor = texture(tx, real_uv);
|
||||
}
|
@ -5,24 +5,19 @@ layout(set = 1, binding = 0) uniform Viewport {
|
||||
};
|
||||
|
||||
layout(location = 0) in ivec2 position;
|
||||
layout(location = 1) in ivec4 attr; // quad x,y,w,h
|
||||
layout(location = 2) in ivec2 in_uv;
|
||||
layout(location = 3) in ivec4 color;
|
||||
layout(location = 1) in ivec2 in_uv;
|
||||
layout(location = 2) in ivec4 color;
|
||||
|
||||
layout(location = 0) out vec2 out_uv;
|
||||
layout(location = 1) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
// vertex position
|
||||
ivec2 px_pos = attr.xy + position.xy * attr.zw;
|
||||
vec2 clip_pos;
|
||||
clip_pos.x = float(px_pos.x)*2.0 / view.x - 1.0;
|
||||
clip_pos.y = -(float(px_pos.y)*2.0 / view.y - 1.0);
|
||||
vec2 pos;
|
||||
pos.x = float(position.x)*2.0 / view.x - 1.0;
|
||||
pos.y = -(float(position.y)*2.0 / view.y - 1.0);
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
|
||||
gl_Position = vec4(clip_pos, 0.0, 1.0);
|
||||
|
||||
vec2 px_uv = in_uv.xy + position.xy * attr.zw;
|
||||
out_uv = vec2(px_uv);
|
||||
out_uv = vec2(float(in_uv.x), float(in_uv.y));
|
||||
out_color = vec4(color) / 255.0;
|
||||
}
|
||||
|
10
src/main.c3
10
src/main.c3
@ -61,7 +61,7 @@ fn int main(String[] args)
|
||||
defer ui.free();
|
||||
|
||||
ren::Renderer ren;
|
||||
ren.init("Ugui Test", 640, 480, false);
|
||||
ren.init("Ugui Test", 640, 480);
|
||||
defer ren.free();
|
||||
ui.input_window_size(640, 480)!!;
|
||||
|
||||
@ -113,10 +113,8 @@ fn int main(String[] args)
|
||||
|
||||
|
||||
isz frame;
|
||||
double fps;
|
||||
bool toggle = true;
|
||||
time::Clock clock;
|
||||
time::Clock fps_clock;
|
||||
Times ui_times;
|
||||
Times draw_times;
|
||||
|
||||
@ -128,7 +126,6 @@ fn int main(String[] args)
|
||||
bool quit = false;
|
||||
while (!quit) {
|
||||
clock.mark();
|
||||
fps_clock.mark();
|
||||
|
||||
// FIXME: modkeys input is broken
|
||||
ugui::ModKeys mod;
|
||||
@ -271,7 +268,6 @@ fn int main(String[] args)
|
||||
ui.div_begin("fps", {0, ui.height-100, -300, 100})!!;
|
||||
{
|
||||
ui.layout_set_column()!!;
|
||||
ui.text_unbounded("frame number", string::tformat("frame %d, fps = %.2f", frame, fps))!!;
|
||||
ui.text_unbounded("draw times", string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
|
||||
ui.text_unbounded("ui text input", (String)ui.input.keyboard.text[..])!!;
|
||||
};
|
||||
@ -294,10 +290,6 @@ fn int main(String[] args)
|
||||
draw_times.push(clock.mark());
|
||||
//draw_times.print_stats();
|
||||
/* End Drawing */
|
||||
|
||||
|
||||
fps = 1.0 / fps_clock.mark().to_sec();
|
||||
frame++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
373
src/renderer.c3
373
src/renderer.c3
@ -51,20 +51,14 @@ struct Texture {
|
||||
}
|
||||
|
||||
// The GPU buffers that contain quad info, the size is determined by MAX_QUAD_BATCH
|
||||
const int MAX_QUAD_BATCH = 2048;
|
||||
const int MAX_QUAD_BATCH = 256;
|
||||
struct QuadBuffer {
|
||||
sdl::GPUBuffer* vert_buf; // on-gpu vertex buffer
|
||||
sdl::GPUBuffer* idx_buf; // on-gpu index buffer
|
||||
sdl::GPUBuffer* attr_buf; // on-gpu quad attribute buffer
|
||||
|
||||
sdl::GPUTransferBuffer* attr_ts;
|
||||
|
||||
QuadAttributes[] attr_ts_mapped;
|
||||
|
||||
// how many quads are currently stored
|
||||
int count;
|
||||
|
||||
sdl::GPUBuffer* vert_buf;
|
||||
sdl::GPUBuffer* idx_buf;
|
||||
sdl::GPUTransferBuffer* transfer_buffer;
|
||||
bool initialized;
|
||||
int count;
|
||||
int off; // the offset to draw from
|
||||
}
|
||||
|
||||
alias ShaderList = IdList{Shader};
|
||||
@ -85,28 +79,26 @@ struct Renderer {
|
||||
|
||||
Id sprite_atlas_id;
|
||||
Id font_atlas_id;
|
||||
|
||||
uint scissor_x, scissor_y, scissor_w, scissor_h;
|
||||
}
|
||||
|
||||
// How each vertex is represented in the gpu
|
||||
struct Vertex {
|
||||
short x, y;
|
||||
}
|
||||
|
||||
// Attributes of each quad instance
|
||||
struct QuadAttributes {
|
||||
// how each vertex is represented in the gpu
|
||||
struct Vertex @packed {
|
||||
struct pos {
|
||||
short x, y, w, h;
|
||||
short x, y;
|
||||
}
|
||||
struct uv {
|
||||
short u, v;
|
||||
}
|
||||
uint color;
|
||||
struct col { // FIXME: this is shit
|
||||
union {
|
||||
char r, g, b, a;
|
||||
char[4] arr;
|
||||
uint u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A single quad
|
||||
struct Quad {
|
||||
struct Quad @packed {
|
||||
struct vertices {
|
||||
Vertex v1,v2,v3,v4;
|
||||
}
|
||||
@ -122,7 +114,7 @@ struct ViewsizeUniform @align(16) {
|
||||
const int DEBUG = 1;
|
||||
const bool CYCLE = true;
|
||||
|
||||
fn void Renderer.init(&self, ZString title, uint width, uint height, bool vsync)
|
||||
fn void Renderer.init(&self, ZString title, uint width, uint height)
|
||||
{
|
||||
// set wayland hint automagically
|
||||
$if DEBUG == 0:
|
||||
@ -167,115 +159,33 @@ $endif
|
||||
}
|
||||
|
||||
// set swapchain parameters, like vsync
|
||||
GPUPresentMode present_mode = vsync ? GPU_PRESENTMODE_VSYNC : GPU_PRESENTMODE_IMMEDIATE;
|
||||
sdl::set_gpu_swapchain_parameters(self.gpu, self.win, GPU_SWAPCHAINCOMPOSITION_SDR, present_mode);
|
||||
sdl::set_gpu_swapchain_parameters(self.gpu, self.win, GPU_SWAPCHAINCOMPOSITION_SDR, GPU_PRESENTMODE_IMMEDIATE);
|
||||
|
||||
//
|
||||
// initialize the quad buffer
|
||||
// ==========================
|
||||
QuadBuffer* qb = &self.quad_buffer;
|
||||
|
||||
// since instanced rendering is used, on the gpu there is only one mesh, a single quad.
|
||||
|
||||
// create the vertex and index buffer on the gpu
|
||||
qb.vert_buf = sdl::create_gpu_buffer(self.gpu,
|
||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_VERTEX, .size = Quad.vertices.sizeof}
|
||||
self.quad_buffer.vert_buf = sdl::create_gpu_buffer(self.gpu,
|
||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_VERTEX, .size = Quad.vertices.sizeof * MAX_QUAD_BATCH}
|
||||
);
|
||||
if (qb.vert_buf == null) {
|
||||
if (self.quad_buffer.vert_buf == null) {
|
||||
unreachable("failed to initialize quad buffer (vertex): %s", sdl::get_error());
|
||||
}
|
||||
|
||||
qb.idx_buf = sdl::create_gpu_buffer(self.gpu,
|
||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_INDEX, .size = Quad.indices.sizeof}
|
||||
self.quad_buffer.idx_buf = sdl::create_gpu_buffer(self.gpu,
|
||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_INDEX, .size = Quad.indices.sizeof * MAX_QUAD_BATCH}
|
||||
);
|
||||
if (qb.idx_buf == null) {
|
||||
if (self.quad_buffer.idx_buf == null) {
|
||||
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
|
||||
}
|
||||
|
||||
qb.attr_buf = sdl::create_gpu_buffer(self.gpu,
|
||||
&&(GPUBufferCreateInfo){.usage = GPU_BUFFERUSAGE_VERTEX, .size = QuadAttributes.sizeof * MAX_QUAD_BATCH}
|
||||
);
|
||||
if (qb.attr_buf == null) {
|
||||
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
|
||||
}
|
||||
|
||||
|
||||
// upload the quad mesh
|
||||
GPUTransferBuffer *ts = sdl::create_gpu_transfer_buffer(self.gpu,
|
||||
self.quad_buffer.transfer_buffer = sdl::create_gpu_transfer_buffer(self.gpu,
|
||||
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
|
||||
);
|
||||
if (ts == null) {
|
||||
if (self.quad_buffer.transfer_buffer == null) {
|
||||
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
|
||||
}
|
||||
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, ts, false);
|
||||
|
||||
/* v1 v4
|
||||
* +-------------+
|
||||
* | _/|
|
||||
* | _/ |
|
||||
* | 1 _/ |
|
||||
* | _/ |
|
||||
* | _/ |
|
||||
* | _/ 2 |
|
||||
* |/ |
|
||||
* +-------------+
|
||||
* v2 v3
|
||||
*/
|
||||
quad.vertices.v1 = {.x = 0, .y = 0};
|
||||
quad.vertices.v2 = {.x = 0, .y = 1};
|
||||
quad.vertices.v3 = {.x = 1, .y = 1};
|
||||
quad.vertices.v4 = {.x = 1, .y = 0};
|
||||
// triangle 1 indices
|
||||
quad.indices.i1 = 0; // v1
|
||||
quad.indices.i2 = 1; // v2
|
||||
quad.indices.i3 = 3; // v4
|
||||
// triangle 2 indices
|
||||
quad.indices.i4 = 1; // v2
|
||||
quad.indices.i5 = 2; // v3
|
||||
quad.indices.i6 = 3; // v4
|
||||
|
||||
sdl::unmap_gpu_transfer_buffer(self.gpu, ts);
|
||||
|
||||
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
|
||||
if (cmd == null) {
|
||||
unreachable("failed to upload quad at acquiring command buffer: %s", sdl::get_error());
|
||||
}
|
||||
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
||||
|
||||
// upload vertices
|
||||
sdl::upload_to_gpu_buffer(cpy,
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = ts, .offset = Quad.vertices.offsetof},
|
||||
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = 0, .size = Quad.vertices.sizeof},
|
||||
false
|
||||
);
|
||||
// upload indices
|
||||
sdl::upload_to_gpu_buffer(cpy,
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = ts, .offset = Quad.indices.offsetof},
|
||||
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = 0, .size = Quad.indices.sizeof},
|
||||
false
|
||||
);
|
||||
|
||||
sdl::end_gpu_copy_pass(cpy);
|
||||
if (!sdl::submit_gpu_command_buffer(cmd)) {
|
||||
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
||||
}
|
||||
sdl::release_gpu_transfer_buffer(self.gpu, ts);
|
||||
|
||||
|
||||
// create and map the quad attributes transfer buffer
|
||||
qb.attr_ts = sdl::create_gpu_transfer_buffer(self.gpu,
|
||||
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = QuadAttributes.sizeof * MAX_QUAD_BATCH}
|
||||
);
|
||||
if (qb.attr_ts == null) {
|
||||
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
|
||||
}
|
||||
qb.attr_ts_mapped = ((QuadAttributes*)sdl::map_gpu_transfer_buffer(self.gpu, qb.attr_ts, false))[:MAX_QUAD_BATCH];
|
||||
if (qb.attr_ts_mapped.ptr == null) {
|
||||
unreachable("failed to map vertex or index buffers: %s", sdl::get_error());
|
||||
}
|
||||
|
||||
|
||||
qb.initialized = true;
|
||||
self.quad_buffer.initialized = true;
|
||||
}
|
||||
|
||||
fn void Renderer.free(&self)
|
||||
@ -416,47 +326,35 @@ fn void Renderer.create_pipeline(&self, String shader_name, PipelineType type)
|
||||
// is represented by two floats, the color as 32 bit rgba and the uv also as intgers.
|
||||
.vertex_input_state = {
|
||||
// the description of each vertex buffer, for now I use only one buffer
|
||||
.vertex_buffer_descriptions = (GPUVertexBufferDescription[]){
|
||||
{ // first slot, per-vertex attributes
|
||||
.slot = 0,
|
||||
.pitch = Vertex.sizeof,
|
||||
.input_rate = GPU_VERTEXINPUTRATE_VERTEX,
|
||||
},
|
||||
{ // second slot, per-instance attributes
|
||||
.slot = 1,
|
||||
.pitch = QuadAttributes.sizeof,
|
||||
.input_rate = GPU_VERTEXINPUTRATE_INSTANCE,
|
||||
}
|
||||
},
|
||||
.num_vertex_buffers = 2,
|
||||
// the description of each vertex
|
||||
.vertex_buffer_descriptions = (GPUVertexBufferDescription[]){{
|
||||
.slot = 0,
|
||||
.pitch = Vertex.sizeof,
|
||||
.input_rate = GPU_VERTEXINPUTRATE_VERTEX,
|
||||
.instance_step_rate = 0,
|
||||
}},
|
||||
.num_vertex_buffers = 1,
|
||||
// the description of each vertex, each vertex has three properties
|
||||
.vertex_attributes = (GPUVertexAttribute[]){
|
||||
{ // at location zero there is the position of the vertex
|
||||
.location = 0,
|
||||
.buffer_slot = 0, // buffer slot zero so per-vertex
|
||||
.format = GPU_VERTEXELEMENTFORMAT_SHORT2, // x,y
|
||||
.offset = 0,
|
||||
},
|
||||
{ // at location one there is the per-quad position
|
||||
.location = 1,
|
||||
.buffer_slot = 1, // buffer slot one so per-instance
|
||||
.format = GPU_VERTEXELEMENTFORMAT_SHORT4, // x,y,w,h
|
||||
.offset = QuadAttributes.pos.offsetof,
|
||||
},
|
||||
{ // at location two there are the per-quad uv coordinates
|
||||
.location = 2,
|
||||
.buffer_slot = 1,
|
||||
.buffer_slot = 0, // only one buffer so always slot zero
|
||||
.format = GPU_VERTEXELEMENTFORMAT_SHORT2,
|
||||
.offset = QuadAttributes.uv.offsetof,
|
||||
.offset = Vertex.pos.offsetof,
|
||||
},
|
||||
{ // at location three there is the quad color
|
||||
.location = 3,
|
||||
.buffer_slot = 1,
|
||||
.format = GPU_VERTEXELEMENTFORMAT_UBYTE4,
|
||||
.offset = QuadAttributes.color.offsetof,
|
||||
{ // at location one there are the uv coordinates
|
||||
.location = 1,
|
||||
.buffer_slot = 0,
|
||||
.format = GPU_VERTEXELEMENTFORMAT_SHORT2,
|
||||
.offset = Vertex.uv.offsetof,
|
||||
},
|
||||
{ // at location two there is the color
|
||||
.location = 2,
|
||||
.buffer_slot = 0,
|
||||
.format = GPU_VERTEXELEMENTFORMAT_UBYTE4, // 4x8bit unsigned rgba format
|
||||
.offset = Vertex.col.offsetof,
|
||||
}
|
||||
},
|
||||
.num_vertex_attributes = 4,
|
||||
.num_vertex_attributes = 3,
|
||||
},
|
||||
// the pipeline's primitive type and rasterizer state differs based on what needs to
|
||||
// be drawn
|
||||
@ -629,31 +527,73 @@ fn void Renderer.update_texture_by_id(&self, Id id, char[] pixels, uint width, u
|
||||
|
||||
fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u, short v, uint color = 0xffffffff)
|
||||
{
|
||||
QuadAttributes qa = {
|
||||
.pos = {.x = x, .y = y, .w = w, .h = h},
|
||||
.uv = {.u = u, .v = v},
|
||||
.color = color
|
||||
};
|
||||
Quad quad;
|
||||
/* v1 v4
|
||||
* +-------------+
|
||||
* | _/|
|
||||
* | _/ |
|
||||
* | 1 _/ |
|
||||
* | _/ |
|
||||
* | _/ |
|
||||
* | _/ 2 |
|
||||
* |/ |
|
||||
* +-------------+
|
||||
* v2 v3
|
||||
*/
|
||||
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = u, .v = v}, .col.u = color};
|
||||
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = u, .v = v+h}, .col.u = color};
|
||||
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = u+w, .v = v+h}, .col.u = color};
|
||||
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = u+w, .v = v}, .col.u = color};
|
||||
// triangle 1 indices
|
||||
quad.indices.i1 = 0; // v1
|
||||
quad.indices.i2 = 1; // v2
|
||||
quad.indices.i3 = 3; // v4
|
||||
// triangle 2 indices
|
||||
quad.indices.i4 = 1; // v2
|
||||
quad.indices.i5 = 2; // v3
|
||||
quad.indices.i6 = 3; // v4
|
||||
|
||||
return self.map_quad(qa);
|
||||
return self.upload_quad(&quad);
|
||||
}
|
||||
|
||||
// Push a quad into the quad buffer, return true on success and false on failure
|
||||
fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color, ushort radius = 0)
|
||||
{
|
||||
QuadAttributes qa = {
|
||||
.pos = {.x = x, .y = y, .w = w, .h = h},
|
||||
.uv = {.u = radius, .v = radius},
|
||||
.color = color
|
||||
};
|
||||
Quad quad;
|
||||
/* v1 v4
|
||||
* +-------------+
|
||||
* | _/|
|
||||
* | _/ |
|
||||
* | 1 _/ |
|
||||
* | _/ |
|
||||
* | _/ |
|
||||
* | _/ 2 |
|
||||
* |/ |
|
||||
* +-------------+
|
||||
* v2 v3
|
||||
*/
|
||||
// the wanted radius is pushed into the uv coordinates, the vertex shader then extracts the absolute value
|
||||
// and passes it to the fragment shader, then it uses the sign to give the fragment shader local coordinates
|
||||
// into the quad.
|
||||
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = -radius, .v = +radius}, .col.u = color};
|
||||
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = -radius, .v = -radius}, .col.u = color};
|
||||
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = +radius, .v = -radius}, .col.u = color};
|
||||
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = +radius, .v = +radius}, .col.u = color};
|
||||
// triangle 1 indices
|
||||
quad.indices.i1 = 0; // v1
|
||||
quad.indices.i2 = 1; // v2
|
||||
quad.indices.i3 = 3; // v4
|
||||
// triangle 2 indices
|
||||
quad.indices.i4 = 1; // v2
|
||||
quad.indices.i5 = 2; // v3
|
||||
quad.indices.i6 = 3; // v4
|
||||
|
||||
return self.map_quad(qa);
|
||||
return self.upload_quad(&quad);
|
||||
}
|
||||
|
||||
// this does not upload a quad, but it simply copies the quad data to the correct transfer buffers.
|
||||
// Data transfer to the GPU only happens in draw_quads() to save time
|
||||
fn bool Renderer.map_quad(&self, QuadAttributes qa)
|
||||
fn bool Renderer.upload_quad(&self, Quad* source_quad)
|
||||
{
|
||||
if (self.quad_buffer.count >= MAX_QUAD_BATCH) {
|
||||
if (self.quad_buffer.count >= MAX_QUAD_BATCH || source_quad == null) {
|
||||
return false;
|
||||
}
|
||||
QuadBuffer* qb = &self.quad_buffer;
|
||||
@ -663,16 +603,14 @@ fn bool Renderer.map_quad(&self, QuadAttributes qa)
|
||||
unreachable("quad buffer not initialized");
|
||||
}
|
||||
|
||||
qb.attr_ts_mapped[qb.count] = qa;
|
||||
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, qb.transfer_buffer, CYCLE);
|
||||
if (quad == null) {
|
||||
unreachable("failed to map gpu transfer buffer: %s", sdl::get_error());
|
||||
}
|
||||
|
||||
qb.count++;
|
||||
*quad = *source_quad;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void Renderer.upload_quads(&self)
|
||||
{
|
||||
QuadBuffer* qb = &self.quad_buffer;
|
||||
sdl::unmap_gpu_transfer_buffer(self.gpu, qb.transfer_buffer);
|
||||
|
||||
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
|
||||
if (cmd == null) {
|
||||
@ -680,10 +618,16 @@ fn void Renderer.upload_quads(&self)
|
||||
}
|
||||
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
||||
|
||||
// upload quad attributes
|
||||
// upload vertices
|
||||
sdl::upload_to_gpu_buffer(cpy,
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = qb.attr_ts, .offset = 0},
|
||||
&&(GPUBufferRegion){.buffer = qb.attr_buf, .offset = 0, .size = QuadAttributes.sizeof * qb.count},
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = qb.transfer_buffer, .offset = Quad.vertices.offsetof},
|
||||
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = qb.count * Quad.vertices.sizeof, .size = Quad.vertices.sizeof},
|
||||
false
|
||||
);
|
||||
// upload indices
|
||||
sdl::upload_to_gpu_buffer(cpy,
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = qb.transfer_buffer, .offset = Quad.indices.offsetof},
|
||||
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = qb.count * Quad.indices.sizeof, .size = Quad.indices.sizeof},
|
||||
false
|
||||
);
|
||||
|
||||
@ -691,32 +635,35 @@ fn void Renderer.upload_quads(&self)
|
||||
if (!sdl::submit_gpu_command_buffer(cmd)) {
|
||||
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
||||
}
|
||||
|
||||
qb.count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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, uint off, uint count)
|
||||
fn void Renderer.draw_quads(&self)
|
||||
{
|
||||
QuadBuffer* qb = &self.quad_buffer;
|
||||
|
||||
// too many quads to draw
|
||||
if (off >= qb.count || count > qb.count - off) {
|
||||
unreachable("too many quads, have %d, requested %d, offset %d", qb.count, count, off);
|
||||
if (qb.off == qb.count) return;
|
||||
|
||||
sdl::bind_gpu_vertex_buffers(self.render_pass, 0, (GPUBufferBinding[]){{.buffer = qb.vert_buf, .offset = qb.off*Quad.vertices.sizeof}}, 1);
|
||||
sdl::bind_gpu_index_buffer(self.render_pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = qb.off*Quad.indices.sizeof}, GPU_INDEXELEMENTSIZE_16BIT);
|
||||
|
||||
// we need instancing to not do this
|
||||
for (int i = 0; i < qb.count - qb.off; i++) {
|
||||
sdl::draw_gpu_indexed_primitives(self.render_pass, 6, 1, i*6, i*4, 0);
|
||||
}
|
||||
|
||||
sdl::bind_gpu_vertex_buffers(self.render_pass, 0,
|
||||
(GPUBufferBinding[]){
|
||||
{.buffer = qb.vert_buf, .offset = 0},
|
||||
{.buffer = qb.attr_buf, .offset = 0},
|
||||
}, 2);
|
||||
sdl::bind_gpu_index_buffer(self.render_pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = 0}, GPU_INDEXELEMENTSIZE_16BIT);
|
||||
|
||||
sdl::draw_gpu_indexed_primitives(self.render_pass, 6, count, 0, 0, off);
|
||||
qb.off = qb.count;
|
||||
}
|
||||
|
||||
fn void Renderer.reset_quads(&self)
|
||||
{
|
||||
self.quad_buffer.count = 0;
|
||||
self.quad_buffer.off = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -846,53 +793,35 @@ fn void Renderer.reset_scissor(&self)
|
||||
|
||||
fn void Renderer.render_ugui(&self, CmdQueue* queue)
|
||||
{
|
||||
// upload pass
|
||||
foreach (&c : queue) {
|
||||
if (c.type == CMD_RECT) {
|
||||
CmdRect r = c.rect;
|
||||
self.push_quad(r.rect.x, r.rect.y, r.rect.w, r.rect.h, r.color.to_uint(), r.radius);
|
||||
} else if (c.type == CMD_SPRITE) {
|
||||
CmdSprite s = c.sprite;
|
||||
self.push_sprite(s.rect.x, s.rect.y, s.texture_rect.w, s.texture_rect.h, s.texture_rect.x, s.texture_rect.y, s.hue.to_uint());
|
||||
}
|
||||
}
|
||||
self.upload_quads();
|
||||
|
||||
Cmd* last_command;
|
||||
uint off;
|
||||
uint count;
|
||||
for (Cmd* cmd; (cmd = queue.dequeue() ?? null) != null;) {
|
||||
if (last_command == null || last_command.type != cmd.type) {
|
||||
self.end_command(last_command, off, count);
|
||||
self.end_command(last_command);
|
||||
self.begin_command(cmd);
|
||||
off += count;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
switch (cmd.type) {
|
||||
case CMD_RECT: nextcase;
|
||||
case CMD_RECT:
|
||||
CmdRect r = cmd.rect;
|
||||
self.push_quad(r.rect.x, r.rect.y, r.rect.w, r.rect.h, r.color.to_uint(), r.radius);
|
||||
case CMD_SPRITE:
|
||||
count++;
|
||||
// TODO: support hue in sprite
|
||||
CmdSprite s = cmd.sprite;
|
||||
self.push_sprite(s.rect.x, s.rect.y, s.texture_rect.w, s.texture_rect.h, s.texture_rect.x, s.texture_rect.y);
|
||||
case CMD_UPDATE_ATLAS:
|
||||
// TODO: verify the correct type
|
||||
CmdUpdateAtlas u = cmd.update_atlas;
|
||||
char[] pixels = u.raw_buffer[..u.width*u.height*u.bpp];
|
||||
self.update_texture(u.id, pixels, u.width, u.height);
|
||||
case CMD_SCISSOR:
|
||||
case CMD_SCISSOR: break; // FIXME: ugui sends a scissor event before any rect event, this cannot be done and needs different handling
|
||||
ugui::Rect s = cmd.scissor.rect;
|
||||
if (s.x == 0 && s.y == 0 && s.w == 0 && s.h == 0) {
|
||||
self.get_window_size((int*)&s.w, (int*)&s.h);
|
||||
}
|
||||
self.scissor_x = s.x;
|
||||
self.scissor_y = s.y;
|
||||
self.scissor_w = s.w;
|
||||
self.scissor_h = s.h;
|
||||
self.set_scissor(s.x, s.y, s.w, s.h);
|
||||
default: unreachable("unknown command: %s", cmd.type);
|
||||
}
|
||||
|
||||
last_command = cmd;
|
||||
}
|
||||
self.end_command(last_command, off, count);
|
||||
self.end_command(last_command);
|
||||
}
|
||||
|
||||
fn void Renderer.begin_command(&self, Cmd* cmd)
|
||||
@ -902,7 +831,6 @@ fn void Renderer.begin_command(&self, Cmd* cmd)
|
||||
switch (cmd.type) {
|
||||
case CMD_RECT:
|
||||
self.start_render_pass("UGUI_PIPELINE_RECT");
|
||||
self.set_scissor(self.scissor_x, self.scissor_y, self.scissor_w, self.scissor_h);
|
||||
case CMD_SPRITE:
|
||||
// TODO: support multiple sprite and font atlases
|
||||
CmdSprite s = cmd.sprite;
|
||||
@ -923,21 +851,20 @@ fn void Renderer.begin_command(&self, Cmd* cmd)
|
||||
}
|
||||
self.start_render_pass(pipeline);
|
||||
self.bind_texture(texture);
|
||||
self.set_scissor(self.scissor_x, self.scissor_y, self.scissor_w, self.scissor_h);
|
||||
case CMD_UPDATE_ATLAS: break;
|
||||
case CMD_SCISSOR: break;
|
||||
default: unreachable("unknown command: %s", cmd.type);
|
||||
}
|
||||
}
|
||||
|
||||
fn void Renderer.end_command(&self, Cmd* cmd, uint off, uint count)
|
||||
fn void Renderer.end_command(&self, Cmd* cmd)
|
||||
{
|
||||
if (cmd == null) return;
|
||||
|
||||
switch (cmd.type) {
|
||||
case CMD_RECT: nextcase;
|
||||
case CMD_SPRITE:
|
||||
self.draw_quads(off, count);
|
||||
self.draw_quads();
|
||||
self.end_render_pass();
|
||||
case CMD_UPDATE_ATLAS: break;
|
||||
case CMD_SCISSOR: break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user