From 693a133cb1a0356f3257f2879be4e23cbb67cbe3 Mon Sep 17 00:00:00 2001 From: quou Date: Mon, 30 Dec 2024 10:18:41 +1100 Subject: better ui renderer --- ui.cpp | 329 +++++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 206 insertions(+), 123 deletions(-) (limited to 'ui.cpp') diff --git a/ui.cpp b/ui.cpp index bd7a90d..2060a78 100644 --- a/ui.cpp +++ b/ui.cpp @@ -8,9 +8,9 @@ extern "C" { #include -#define mesh_size (1024 * 8) +#define vertex_buffer_count (256) -static int font_w = 960; +static constexpr int font_w = 960; static unsigned font_data[] = { 0x00000000, 0x00003000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, @@ -74,9 +74,10 @@ static unsigned font_data[] = { 0x0003c01e, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; -static Texture_Id create_atlas(Device* d, Arena* a) { +static Texture_Id create_atlas(Device* d) { int x, y; - int size = 10 * font_w; + int w = font_w + 10; + int size = 10 * w; Buffer_Id buf = d->create_buffer( "font atlas stage", size, @@ -88,9 +89,16 @@ static Texture_Id create_atlas(Device* d, Arena* a) { for (y = 0; y < 10; y++) { for (x = 0; x < font_w; x++) { int si = x + y * font_w; + int di = x + y * w; unsigned bits = font_data[si >> 5]; int bit = bits & 1 << (si & 0x1f); - pixels[si] = bit? 0xff: 0x00; + pixels[di] = bit? 0xff: 0x00; + } + } + /* for solid colours, one white square at the end */ + for (y = 0; y < 10; y++) { + for (x = font_w; x < w; x++) { + pixels[x + y * w] = 0xff; } } d->unmap_buffer(buf); @@ -98,12 +106,11 @@ static Texture_Id create_atlas(Device* d, Arena* a) { "font atlas", texture_format_r8i, Texture_Flags::sampleable | Texture_Flags::copy_dst, - font_w, + w, 10, buf ); d->destroy_bufferi(buf); - clear_arena(a); return tex; } @@ -116,10 +123,176 @@ static Sampler_Id create_clamped_point(Device* dev) { return dev->create_sampler("clamped point", s); } +UI::Colour::Colour(unsigned rgb, uint8_t a): + r((rgb >> 16) & 0xff), + g((rgb >> 8) & 0xff), + b((rgb) & 0xff), + a(a) {} + +void UI::Vertex_Buffer::init(Device* dev) { + buf.init( + dev, + "UI mesh", + vertex_buffer_count * sizeof(Vertex) * 4, + Buffer_Flags::vertex_buffer + ); + init_indices(dev); + usage = 0; + next = 0; +} + +void UI::Vertex_Buffer::init_indices(Device* dev) { + auto create_ind = [](uint16_t* target){ + int i, i2, e = vertex_buffer_count * 6; + for (i = 0, i2 = 0; i < e; i += 6, i2 += 4) { + target[i ] = (uint16_t)(i2 ); + target[i + 1] = (uint16_t)(i2 + 1); + target[i + 2] = (uint16_t)(i2 + 2); + target[i + 3] = (uint16_t)(i2 + 1); + target[i + 4] = (uint16_t)(i2 + 3); + target[i + 5] = (uint16_t)(i2 + 2); + } + }; + int s = vertex_buffer_count * 6 * sizeof(uint16_t); + Buffer_Id stage = dev->create_buffer( + "UI mesh indices stage", + s, + Buffer_Flags::copy_src | + Buffer_Flags::cpu_readwrite + ); + void* mem = dev->map_buffer(stage, 0, s); + create_ind((uint16_t*)mem); + indices = dev->create_buffer( + "UI indices", + s, + Buffer_Flags::copy_dst | + Buffer_Flags::index_buffer + ); + Context& ctx = dev->acquire(); + ctx.copy(indices, stage); + dev->submit(ctx); + dev->unmap_buffer(stage); + dev->destroy_bufferi(stage); +} + +void UI::Vertex_Buffer::destroy(UI* ui) { + Device* dev = ui->device; + if (next) { + next->destroy(ui); + heap_free(ui->heap, next); + } + dev->destroy_buffer(indices); + buf.destroy(dev); +} + +void UI::Vertex_Buffer::add_quad( + UI* ui, + int x, + int y, + int w, + int h, + float u0, + float v0, + float u1, + float v1, + Colour col +) { + Vertex* verts; + int index = usage * 4; + float r, g, b, a; + if (usage >= vertex_buffer_count) { + if (!next) { + next = (Vertex_Buffer*)heap_alloc(ui->heap, sizeof *next); + next->init(ui->device); + } + next->add_quad(ui, x, y, w, h, u0, v0, u1, v1, col); + return; + } + r = col.r_f(); + g = col.g_f(); + b = col.b_f(); + a = col.a_f(); + verts = &((Vertex*)buf.map(ui->device))[index]; + verts[0] = Vertex { (float)x, (float)y, u0, v0, r, g, b, a }; + verts[1] = Vertex { (float)x + w, (float)y, u1, v0, r, g, b, a }; + verts[2] = Vertex { (float)x, (float)y + h, u0, v1, r, g, b, a }; + verts[3] = Vertex { (float)x + w, (float)y + h, u1, v1, r, g, b, a }; + buf.unmap(ui->device); + usage++; +} + +void UI::Vertex_Buffer::add_rect( + UI* ui, + int x, + int y, + int w, + int h, + Colour col +) { + constexpr float white_uv_x = (float)font_w / (float)(font_w + 10); + add_quad(ui, x, y, w, h, white_uv_x, 0.0f, 1.0f, 1.0f, col); +} + +void UI::Vertex_Buffer::add_char( + UI* ui, + int x, + int y, + char ch, + Colour col +) { + int off = (ch - ' ') * 10; + float w = (float)(font_w + 10); + float u0 = (float)off / w; + float u1 = u0 + (10.0f / w); + add_quad(ui, x, y, 10, 10, u0, 0.0f, u1, 1.0f, col); +} + +void UI::Vertex_Buffer::add_text( + UI* ui, + int x, + int y, + const char* txt, + Colour col +) { + for (; *txt; txt++) + add_char(ui, x += 10, y, *txt, col); +} + +void UI::Vertex_Buffer::update_buffer(Context& ctx) { + buf.update(ctx); + if (next) + next->update_buffer(ctx); +} + +void UI::Vertex_Buffer::draw(UI* ui, Context& ctx, Pipeline& pip, Render_Pass& rp) { + Vertex_Buffer_Binding binding[] = {{ + .id = buf.gpuonly, + .offset = 0, + .target = ui->shader_info.vert_binding + }, {}}; + Index_Buffer_Binding indb = { + .id = indices, + .offset = 0 + }; + Draw d{}; + d.verts = binding; + d.inds = indb; + d.vertex_count = usage * 6; + d.instance_count = 1; + ctx.submit(d, pip, rp); + if (next) + next->draw(ui, ctx, pip, rp); + usage = 0; +} + UI* UI::create(Device* dev, Arena* a, Shader_Id sh) { - Texture_Id atlas = create_atlas(dev, a); + int hs; + Texture_Id atlas = create_atlas(dev); UI* u = (UI*)arena_alloc(a, sizeof *u); - u->init(dev, a, atlas, sh); + Heap* h = (Heap*)arena_alloc(a, sizeof *h); + hs = a->size - a->ptr - allocation_default_alignment - 1; + init_heap(h, arena_alloc(a, hs), hs); + u->init(dev, h, atlas, sh); return u; } @@ -134,24 +307,23 @@ int UI::text_height(const char* t) { return 10; } -void UI::init(Device* dev, Arena* a, Texture_Id at, Shader_Id sh) { +void UI::init( + Device* dev, + Heap* h, + Texture_Id at, + Shader_Id sh +) { Shader* sp; - arena = a; + heap = h; device = dev; - tree = 0; atlas = at; shader = sh; - mesh = device->create_buffer( - "UI Mesh", - mesh_size, - Buffer_Flags::vertex_buffer | - Buffer_Flags::cpu_readwrite - ); - config_buf = device->create_buffer( - "UI Cbuffer", + mesh.init(device); + cbuffer.init( + device, + "UI CBuffer", sizeof(UI_CBuffer), - Buffer_Flags::constant_buffer | - Buffer_Flags::cpu_readwrite + Buffer_Flags::constant_buffer ); sp = &dev->get_shader(sh); vertex_format = sp->vf; @@ -163,110 +335,30 @@ void UI::init(Device* dev, Arena* a, Texture_Id at, Shader_Id sh) { void UI::destroy() { device->destroy_texture(atlas); - device->destroy_buffer(mesh); - device->destroy_buffer(config_buf); device->destroy_sampler(sampler); -} - -int UI::Element::size(Type t) { - switch (t) { - case Type::label: - return sizeof(Label); - } - assert(0); - return 0; -} - -UI::Element* UI::create_element(Element::Type t) { - Element* e = (Element*)arena_alloc(arena, Element::size(t)); - e->type = t; - e->next = 0; - if (tree) - tree->next = e; - else - tree = e; - return e; -} - -UI::Label* UI::create_label(const char* t, int x, int y) { - Label* l = (Label*)create_element(Element::Type::label); - l->text = dup_string(arena, t); - l->len = string_len(t); - l->x = x; - l->y = y; - l->w = text_width(t); - l->h = text_height(t); - return l; + mesh.destroy(this); + cbuffer.destroy(device); } void UI::update(Arena* s) { (void)s; } -int UI::render_label(const Label* l, int off) { - int vc = 6 * l->len, i; - int x = l->x; - int y = l->y; - const char* c; - float o = 10.0f; - Vertex* verts = (Vertex*)device->map_buffer( - mesh, - off * sizeof(Vertex), - vc * sizeof(Vertex) - ); - for (i = 0, c = l->text; i < vc; i += 6, c++) { - float uo = (float)((*c - ' ') * 10) / (float)font_w; - float w = 10.0f / (float)font_w; - float uv[2] = { - uo, uo + w - }; - verts[i] = Vertex { - (float)x, (float)y, uv[0], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f - }; - verts[i + 1] = Vertex { - (float)x, (float)y + o, uv[0], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - }; - verts[i + 2] = Vertex { - (float)x + o, (float)y + o, uv[1], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - }; - verts[i + 3] = Vertex { - (float)x, (float)y, uv[0], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f - }; - verts[i + 4] = Vertex { - (float)x + o, (float)y, uv[1], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f - }; - verts[i + 5] = Vertex { - (float)x + o, (float)y + o, uv[1], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - }; - x += 10; - } - device->unmap_buffer(mesh); - return vc; -} - void UI::render(Arena* s, Texture_Id target) { - Element* e = tree; - int vc = 0; + int i, c = 2000; + Context& ctx = device->get_ctx(); Texture& t = device->get_texture(target); - UI_CBuffer* config = (UI_CBuffer*)device->map_buffer( - config_buf, - 0, - sizeof(UI_CBuffer) - ); + UI_CBuffer* config = (UI_CBuffer*)cbuffer.map(device); config->projection = m4f::orth( 0.0f, (float)t.w, 0.0f, (float)t.h, -1.0f, 1.0f ); - device->unmap_buffer(config_buf); - for (; e; e = e->next) { - switch (e->type) { - case Element::Type::label: - vc += render_label((Label*)e, vc); - break; - } - } + cbuffer.unmap(device); + cbuffer.update(ctx); + + mesh.add_text(this, 10, 10, "Hello, world!", 0xff00ff); Pipeline_Builder pb(s, device); pb.begin_rp(); @@ -281,19 +373,10 @@ void UI::render(Arena* s, Texture_Id target) { Blend_Factor::src_alpha, Blend_Factor::inv_src_colour ); - pb.cbuffer(shader_info.config_binding, config_buf); + pb.cbuffer(shader_info.config_binding, cbuffer.gpuonly); pb.texture(shader_info.atlas_binding, atlas, sampler); Pipeline& pip = pb.build(); - Vertex_Buffer_Binding binding[] = {{ - .id = mesh, - .offset = 0, - .target = shader_info.vert_binding - }, {}}; - - Draw draw{}; - draw.verts = binding; - draw.vertex_count = vc; - draw.instance_count = 1; - device->get_ctx().submit(draw, pip, pass); + mesh.update_buffer(ctx); + mesh.draw(this, ctx, pip, pass); } -- cgit v1.2.3-54-g00ecf