using the new renderer

This commit is contained in:
Alessandro Mauri 2025-06-12 19:49:43 +02:00
parent c4a3dd3a26
commit 458c45d2b9
4 changed files with 142 additions and 232 deletions

View File

@ -206,7 +206,7 @@ fn void? Ctx.init(&ctx)
ctx.style.margin = {2, 2, 2, 2};
ctx.style.border = {2, 2, 2, 2};
ctx.style.padding = {1, 1, 1, 1};
ctx.style.radius = 5;
ctx.style.radius = 12;
ctx.style.bgcolor = 0x282828ffu.to_rgba();
ctx.style.fgcolor = 0xfbf1c7ffu.to_rgba();
ctx.style.brcolor = 0xd79921ffu.to_rgba();
@ -303,18 +303,10 @@ $if DEBUG == 1:
.rect.color = 0xff00ffffu.to_rgba()
};
ctx.cmd_queue.enqueue(&cmd)!;
// dump the command buffer
// io::printn("Command Buffer Dump:");
// foreach(idx, c: ctx.cmd_queue) {
// io::printfn("\t [%d] = {%s}", idx, c);
// }
ctx.cmd_queue.sort()!;
// io::printn("Sorted Command Buffer Dump:");
// foreach(idx, c: ctx.cmd_queue) {
// io::printfn("\t [%d] = {%s}", idx, c);
// }
$endif
// sort the command buffer by the z-index
ctx.cmd_queue.sort()!;
}
<*

View File

@ -2,7 +2,7 @@
"langrev": "1",
"warnings": ["no-unused"],
"dependency-search-paths": ["lib", "lib/vendor/libraries"],
"dependencies": ["raylib55", "sdl3", "ugui"],
"dependencies": ["sdl3", "ugui"],
"features": [],
"authors": ["Alessandro Mauri <ale@shitposting.expert>"],
"version": "0.1.0",

View File

@ -2,10 +2,11 @@ import std::io;
import vtree;
import cache;
import ugui;
import raylib5::rl;
import std::time;
import std::collections::ringbuffer;
import std::core::string;
import sdlrenderer::ren;
import sdl3::sdl;
alias Times = ringbuffer::RingBuffer{time::NanoDuration[128]};
@ -44,84 +45,14 @@ fn TimeStats Times.get_stats(&times)
}
const ZString MSDF_FS = `
#version 330
const char[*] MSDF_FS_PATH = "resources/shaders/compiled/msdf.frag.spv";
const char[*] SPRITE_FS_PATH = "resources/shaders/compiled/sprite.frag.spv";
const char[*] FONT_FS_PATH = "resources/shaders/compiled/font.frag.spv";
const char[*] RECT_FS_PATH = "resources/shaders/compiled/rect.frag.spv";
in vec2 fragTexCoord;
out vec4 fragColor;
uniform sampler2D texture0;
uniform vec4 colDiffuse;
const char[*] SPRITE_VS_PATH = "resources/shaders/compiled/sprite.vert.spv";
const char[*] RECT_VS_PATH = "resources/shaders/compiled/rect.vert.spv";
const float pxRange = 4.0f;
float screenPxRange() {
vec2 unitRange = vec2(pxRange)/vec2(textureSize(texture0, 0));
vec2 screenTexSize = vec2(1.0)/fwidth(fragTexCoord);
return max(0.5*dot(unitRange, screenTexSize), 1.0);
}
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
vec3 msd = texture(texture0, fragTexCoord).rgb;
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = screenPxRange()*(sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
fragColor = colDiffuse * opacity;
}
`;
const ZString FONT_FS = `
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Output fragment color
out vec4 finalColor;
void main()
{
vec4 alpha = texture(texture0, fragTexCoord);
finalColor = colDiffuse*fragColor;
finalColor.a *= alpha.r;
}
`;
// SDF based rect rendering
const ZString RECT_FS = `
#version 330
in vec2 fragTexCoord;
in vec4 fragColor;
out vec4 finalColor;
uniform float radius;
uniform float border;
uniform vec2 size;
void main()
{
}
`;
macro rl::Color ugui::Color.conv(color)
{
return {.r = color.r, .g = color.g, .b = color.b, .a = color.a};
}
macro rl::Rectangle Rect.conv(rect)
{
return {.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h};
}
fn int main(String[] args)
{
@ -129,18 +60,57 @@ fn int main(String[] args)
ui.init()!!;
defer ui.free();
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
ui.sprite_atlas_create("icons", AtlasType.ATLAS_R8G8B8A8, 512, 512)!!;
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
ui.import_sprite_file_qoi("tick", "resources/tick_sdf.qoi", SpriteType.SPRITE_MSDF)!!;
ren::Renderer ren;
ren.init("Ugui Test", 640, 480);
defer ren.free();
ui.input_window_size(640, 480)!!;
//
// FONT LOADING
//
{
// import font in the ui context
ui.load_font("font1", "resources/hack-nerd.ttf", 16)!!;
// create the rendering pipeline
ren.font_atlas_id = ui.get_font_id("font1");
ren.load_spirv_shader_from_file("UGUI_PIPELINE_FONT", SPRITE_VS_PATH, FONT_FS_PATH, 1, 0);
ren.create_pipeline("UGUI_PIPELINE_FONT", SPRITE);
// send the atlas to the gpu
Atlas* font_atlas = ui.get_font_atlas("font1")!!;
ren.new_texture("font1", JUST_ALPHA, font_atlas.buffer, font_atlas.width, font_atlas.height);
}
//
// ICON LOADING
//
{
// create the atlas and upload some icons
ui.sprite_atlas_create("icons", AtlasType.ATLAS_R8G8B8A8, 512, 512)!!;
ui.import_sprite_file_qoi("tux", "resources/tux.qoi")!!;
ui.import_sprite_file_qoi("tick", "resources/tick_sdf.qoi", SpriteType.SPRITE_MSDF)!!;
// create the rendering pipelines
ren.sprite_atlas_id = ui.get_sprite_atlas_id("icons");
// normal sprite pipeline
ren.load_spirv_shader_from_file("UGUI_PIPELINE_SPRITE", SPRITE_VS_PATH, SPRITE_FS_PATH, 1, 0);
ren.create_pipeline("UGUI_PIPELINE_SPRITE", SPRITE);
// msdf sprite pipeline
ren.load_spirv_shader_from_file("UGUI_PIPELINE_SPRITE_MSDF", SPRITE_VS_PATH, MSDF_FS_PATH, 1, 0);
ren.create_pipeline("UGUI_PIPELINE_SPRITE_MSDF", SPRITE);
// upload the atlas to the gpu
Atlas atlas = ui.sprite_atlas.atlas;
ren.new_texture("icons", FULL_COLOR, atlas.buffer, atlas.width, atlas.height);
}
//
// RECT PIPELINE
//
ren.load_spirv_shader_from_file("UGUI_PIPELINE_RECT", RECT_VS_PATH, RECT_FS_PATH, 0, 0);
ren.create_pipeline("UGUI_PIPELINE_RECT", RECT);
short width = 800;
short height = 450;
rl::setConfigFlags(rl::FLAG_WINDOW_RESIZABLE);
rl::initWindow(width, height, "Ugui Test");
ui.input_window_size(width, height)!!;
rl::setTargetFPS(60);
rl::enableEventWaiting();
isz frame;
bool toggle = true;
@ -148,25 +118,49 @@ fn int main(String[] args)
Times ui_times;
Times draw_times;
// font stuff
rl::Shader font_shader = rl::loadShaderFromMemory(null, FONT_FS);
rl::Image font_atlas;
rl::Texture2D font_texture;
ugui::Id font_id = ui.get_font_id("font1");
// sprite stuff
rl::Shader msdf_shader = rl::loadShaderFromMemory(null, MSDF_FS);
rl::Image sprite_atlas;
rl::Texture2D sprite_texture;
ugui::Id sprite_id = ui.get_sprite_atlas_id("icons");
// Main loop
while (!rl::windowShouldClose()) {
//
// MAIN LOOP
//
sdl::Event e;
bool quit = false;
while (!quit) {
clock.mark();
// FIXME: modkeys input is broken
ugui::ModKeys mod;
mod.rctrl = rl::isKeyDown(rl::KEY_RIGHT_CONTROL);
mod.lctrl = rl::isKeyDown(rl::KEY_LEFT_CONTROL);
while (sdl::poll_event(&e)) {
switch (e.type) {
case EVENT_QUIT:
quit = true;
case EVENT_KEY_DOWN:
mod.rctrl = !!(e.key.key == K_RCTRL);
mod.lctrl = !!(e.key.key == K_LCTRL);
case EVENT_WINDOW_RESIZED:
ui.input_window_size((short)e.window.data1, (short)e.window.data2)!!;
case EVENT_WINDOW_FOCUS_GAINED:
ui.input_changefocus(true);
case EVENT_WINDOW_FOCUS_LOST:
ui.input_changefocus(false);
case EVENT_MOUSE_MOTION:
ui.input_mouse_abs((short)e.motion.x, (short)e.motion.y);
case EVENT_MOUSE_WHEEL:
ui.input_mouse_wheel((short)e.wheel.integer_x, (short)e.wheel.integer_y);
case EVENT_MOUSE_BUTTON_DOWN: nextcase;
case EVENT_MOUSE_BUTTON_UP:
sdl::MouseButtonFlags mb = sdl::get_mouse_state(null, null);
ugui::MouseButtons b = {
.btn_left = !!(mb & BUTTON_LMASK),
.btn_right = !!(mb & BUTTON_RMASK),
.btn_middle = !!(mb & BUTTON_MMASK),
.btn_4 = !!(mb & BUTTON_X1MASK),
.btn_5 = !!(mb & BUTTON_X2MASK),
};
ui.input_mouse_button(b);
default:
io::eprintfn("unhandled event: %s", e.type);
}
}
ui.input_mod_keys(mod);
/*
@ -177,33 +171,20 @@ fn int main(String[] args)
}
*/
/* Start Input Handling */
if (rl::isWindowResized()) {
width = (short)rl::getScreenWidth();
height = (short)rl::getScreenHeight();
ui.input_window_size(width, height)!!;
}
ui.input_changefocus(rl::isWindowFocused());
rl::Vector2 mpos = rl::getMousePosition();
ui.input_mouse_abs((short)mpos.x, (short)mpos.y);
rl::Vector2 mwheel = rl::getMouseWheelMoveV();
ui.input_mouse_wheel((short)mwheel.x, (short)mwheel.y);
ugui::MouseButtons buttons = {
.btn_left = rl::isMouseButtonDown(rl::MouseButton.LEFT),
.btn_right = rl::isMouseButtonDown(rl::MouseButton.RIGHT),
.btn_middle = rl::isMouseButtonDown(rl::MouseButton.MIDDLE),
};
ui.input_mouse_button(buttons);
/* End Input Handling */
/* Start UI Handling */
ui.frame_begin()!!;
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) break;
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) quit = true;
/*
ui.div_begin("main", ugui::DIV_FILL)!!;
ui.button("button0", {0,0,30,30})!!;
ui.draw_sprite("sprite1", "tux")!!;
ui.text_unbounded("text1", "Ciao")!!;
ui.div_end()!!;
*/
ui.div_begin("main", {.w=-100})!!;
{
@ -292,86 +273,24 @@ fn int main(String[] args)
};
ui.div_end()!!;
ui.frame_end()!!;
/* End UI Handling */
ui_times.push(clock.mark());
//ui_times.print_stats();
/* Start UI Drawing */
rl::beginDrawing();
// ClearBackground(BLACK);
for (Cmd* cmd; (cmd = ui.cmd_queue.dequeue() ?? null) != null;) {
switch (cmd.type) {
case CmdType.CMD_RECT:
Rect r = cmd.rect.rect;
float rad = cmd.rect.radius;
// for some weird-ass reason the straight forward inverse formula does not work
float roundness = r.w > r.h ? (2.1f*rad)/(float)r.h : (2.1f*rad)/(float)r.w;
rl::drawRectangleRounded(cmd.rect.rect.conv(), roundness, 0, cmd.rect.color.conv());
case CmdType.CMD_UPDATE_ATLAS:
if (cmd.update_atlas.id == font_id) {
//rl::unload_image(font_atlas);
font_atlas.data = cmd.update_atlas.raw_buffer;
font_atlas.width = cmd.update_atlas.width;
font_atlas.height = cmd.update_atlas.height;
font_atlas.mipmaps = 1;
font_atlas.format = rl::PixelFormat.UNCOMPRESSED_GRAYSCALE;
if (rl::isTextureValid(font_texture)) {
rl::unloadTexture(font_texture);
}
font_texture = rl::loadTextureFromImage(font_atlas);
} else if (cmd.update_atlas.id == sprite_id) {
sprite_atlas.data = cmd.update_atlas.raw_buffer;
sprite_atlas.width = cmd.update_atlas.width;
sprite_atlas.height = cmd.update_atlas.height;
sprite_atlas.mipmaps = 1;
sprite_atlas.format = rl::PixelFormat.UNCOMPRESSED_R8G8B8A8;
if (rl::isTextureValid(sprite_texture)) {
rl::unloadTexture(sprite_texture);
}
sprite_texture = rl::loadTextureFromImage(sprite_atlas);
}
case CmdType.CMD_SPRITE:
if (cmd.sprite.texture_id == font_id) {
rl::Vector2 position = {
.x = cmd.sprite.rect.x,
.y = cmd.sprite.rect.y,
};
rl::beginShaderMode(font_shader);
rl::drawTextureRec(font_texture, cmd.sprite.texture_rect.conv(), position, cmd.sprite.hue.conv());
rl::endShaderMode();
} else if (cmd.sprite.texture_id == sprite_id) {
// FIXME: THIS CODE IS SHIT, REAL DOO DOO
{
if (cmd.sprite.type == SpriteType.SPRITE_MSDF) {
rl::beginShaderMode(msdf_shader);
defer rl::endShaderMode();
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
} else {
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
}
};
} else {
io::printfn("unknown texture id: %d", cmd.sprite.texture_id);
}
case CmdType.CMD_SCISSOR:
if (cmd.scissor.rect.w == 0 && cmd.scissor.rect.h == 0) {
rl::endScissorMode();
} else {
rl::beginScissorMode(cmd.scissor.rect.x, cmd.scissor.rect.y, cmd.scissor.rect.w, cmd.scissor.rect.h);
}
default:
io::printfn("Unknown cmd type: %s", cmd.type);
}
}
/* Start UI Drawing */
ren.begin_render(true);
ren.render_ugui(&ui.cmd_queue);
ren.end_render();
draw_times.push(clock.mark());
//draw_times.print_stats();
rl::endDrawing();
/* End Drawing */
}
rl::closeWindow();
return 0;
}

View File

@ -55,6 +55,7 @@ const int MAX_QUAD_BATCH = 256;
struct QuadBuffer {
sdl::GPUBuffer* vert_buf;
sdl::GPUBuffer* idx_buf;
sdl::GPUTransferBuffer* transfer_buffer;
bool initialized;
int count;
int off; // the offset to draw from
@ -173,6 +174,13 @@ $endif
unreachable("failed to initialize quad buffer (index): %s", sdl::get_error());
}
self.quad_buffer.transfer_buffer = sdl::create_gpu_transfer_buffer(self.gpu,
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
);
if (self.quad_buffer.transfer_buffer == null) {
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
}
self.quad_buffer.initialized = true;
}
@ -189,6 +197,8 @@ fn void Renderer.free(&self)
}
self.pipelines.free();
// FIXME: release the quad buffer
sdl::release_window_from_gpu_device(self.gpu, self.win);
sdl::destroy_gpu_device(self.gpu);
sdl::destroy_window(self.win);
@ -511,7 +521,7 @@ 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)
fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u, short v, uint color = 0xffffffff)
{
Quad quad;
/* v1 v4
@ -526,10 +536,10 @@ fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u,
* +-------------+
* v2 v3
*/
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = u, .v = v}};
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = u, .v = v+h}};
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = u+w, .v = v+h}};
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = u+w, .v = v}};
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
@ -582,27 +592,21 @@ fn bool Renderer.upload_quad(&self, Quad* source_quad)
if (self.quad_buffer.count >= MAX_QUAD_BATCH || source_quad == null) {
return false;
}
QuadBuffer* qb = &self.quad_buffer;
// upload the quad data to the gpu
if (self.quad_buffer.initialized == false) {
if (qb.initialized == false) {
unreachable("quad buffer not initialized");
}
GPUTransferBuffer* buf = sdl::create_gpu_transfer_buffer(self.gpu,
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
);
if (buf == null) {
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
}
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, buf, false);
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, qb.transfer_buffer, true);
if (quad == null) {
unreachable("failed to map gpu transfer buffer: %s", sdl::get_error());
}
*quad = *source_quad;
sdl::unmap_gpu_transfer_buffer(self.gpu, buf);
sdl::unmap_gpu_transfer_buffer(self.gpu, qb.transfer_buffer);
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
if (cmd == null) {
@ -611,15 +615,14 @@ fn bool Renderer.upload_quad(&self, Quad* source_quad)
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
// upload vertices
QuadBuffer* qb = &self.quad_buffer;
sdl::upload_to_gpu_buffer(cpy,
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.vertices.offsetof},
&&(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 = buf, .offset = Quad.indices.offsetof},
&&(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
);
@ -628,8 +631,6 @@ fn bool Renderer.upload_quad(&self, Quad* source_quad)
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, buf);
//sdl::wait_for_gpu_idle(self.gpu);
qb.count++;
@ -788,7 +789,6 @@ fn void Renderer.reset_scissor(&self)
fn void Renderer.render_ugui(&self, CmdQueue* queue)
{
io::printn("---------------------------------------");
Cmd* last_command;
for (Cmd* cmd; (cmd = queue.dequeue() ?? null) != null;) {
if (last_command == null || last_command.type != cmd.type) {
@ -800,11 +800,10 @@ fn void Renderer.render_ugui(&self, CmdQueue* queue)
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);
io::printn(r);
case CMD_SPRITE:
// TODO: support hue in sprite
CmdSprite s = cmd.sprite;
self.push_sprite(s.rect.x, s.rect.y, s.rect.w, s.rect.h, s.texture_rect.x, s.texture_rect.y);
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;