From 74e8d3f0278a65fdf86a1185fec8a6016e628e88 Mon Sep 17 00:00:00 2001 From: quou Date: Sun, 19 Jan 2025 21:04:51 +1100 Subject: render UI in software --- app.hpp | 2 +- c2.cpp | 66 +++++- intermediate/ui.glsl | 23 +- ui.cpp | 605 ++++++++++++++++++++++----------------------------- ui.hpp | 124 ++++------- video.cpp | 8 + 6 files changed, 379 insertions(+), 449 deletions(-) diff --git a/app.hpp b/app.hpp index df16bd9..d26003a 100644 --- a/app.hpp +++ b/app.hpp @@ -1,7 +1,7 @@ #ifndef app_hpp #define app_hpp -#define app_memory_size (1024 * 1024 * 32) +#define app_memory_size (1024 * 1024 * 512) #include #include diff --git a/c2.cpp b/c2.cpp index e8e6c82..3c862a6 100644 --- a/c2.cpp +++ b/c2.cpp @@ -17,7 +17,7 @@ extern "C" { #define video_arena_size (1024 * 1024 * 16) #define asset_arena_size (1024 * 1024 * 4) -#define ui_arena_size (1024 * 16) +#define ui_arena_size (1024 * 1024 * 64) #define scene_arena_size (1024) #define per_frame_memory_size (1024 * 1024) @@ -645,6 +645,8 @@ struct C2 : public App { Buffer_Id vbo, cbuf; Sampler_Id clamped_linear; Texture_Id hdr_target; + Texture_Id ui_texture; + Buffer_Id ui_buffer; Line_Renderer lr; World* world; UI* ui; @@ -683,6 +685,7 @@ struct C2 : public App { dev = Device::create(&video_arena, this); default_texture = make_default_texture(dev); make_hdr_target(); + make_ui_texture(); model_loader.init(dev, &assets); mat_loader.init(&assets, default_texture); register_asset_loader("MODL", &model_loader); @@ -705,7 +708,7 @@ struct C2 : public App { clamped_linear = create_clamped_linear(dev); world = (World*)arena_alloc(arena, sizeof *world); world->init(arena); - ui = UI::create(dev, this, &ui_arena, ui_shader->id); + ui = UI::create(this, &ui_arena); init_editor(ui, world); lr.init(dev, &assets); assert(per_frame != 0); @@ -876,7 +879,39 @@ struct C2 : public App { ctx.debug_pop(); ctx.debug_push("ui"); - ui->render(&frame_arena, dev->get_backbuffer()); + ui->render(&frame_arena); + { + int s = ui->ren.bound.w * ui->ren.bound.h * 4; + void* pixels = dev->map_buffer(ui_buffer, 0, s); + memcpy(pixels, ui->ren.pixels, s); + dev->unmap_buffer(ui_buffer); + ctx.copy(ui_texture, ui_buffer); + + pb.begin_rp(); + pb.rp_target(dev->get_backbuffer(), Clear_Mode::restore); + Render_Pass& ui_pass = pb.build_rp(); + + pb.begin(); + pb.shader(ui_shader->id); + pb.vertex_format(ui_shader->vf); + pb.blend( + Blend_Mode::add, + Blend_Factor::src_alpha, + Blend_Factor::inv_src_alpha + ); + pb.texture( + ui_shader->descriptor_binding("atlas"), + ui_texture, + clamped_linear + ); + Pipeline& ui_pip = pb.build(); + quad.render( + ctx, + ui_pip, + ui_pass, + ui_shader->binding_index("verts") + ); + } ctx.debug_pop(); if (mjp(mbtn_left)) { @@ -920,6 +955,8 @@ struct C2 : public App { assets.destroy(); dev->destroy_texture(default_texture); dev->destroy_texture(hdr_target); + dev->destroy_texture(ui_texture); + dev->destroy_buffer(ui_buffer); dev->destroy_sampler(clamped_linear); dev->destroy_buffer(vbo); dev->destroy_buffer(cbuf); @@ -930,7 +967,10 @@ struct C2 : public App { ui->layout(w, h); dev->on_resize(); dev->destroy_texture(hdr_target); + dev->destroy_texture(ui_texture); + dev->destroy_buffer(ui_buffer); make_hdr_target(); + make_ui_texture(); } void make_hdr_target() { @@ -947,6 +987,26 @@ struct C2 : public App { ); } + void make_ui_texture() { + ui_texture = dev->create_texture( + "UI texture", + texture_format_rgba8i, + Texture_Flags::sampleable | Texture_Flags::copy_dst, + w, + h, + 1, + 1, + 1, + Buffer_Id(0) + ); + ui_buffer = dev->create_buffer( + "UI Buffer", + w * h * 4, + Buffer_Flags::copy_src | + Buffer_Flags::cpu_readwrite + ); + } + void on_text_input(const char* buf) override { ui->text_input(buf); } diff --git a/intermediate/ui.glsl b/intermediate/ui.glsl index 095d4df..7dd361d 100644 --- a/intermediate/ui.glsl +++ b/intermediate/ui.glsl @@ -13,28 +13,11 @@ type: vec2 [attribute] name: uv type: vec2 -[attribute] -name: colour -type: vec4 -[interpolator] -name: colour -type: vec4 [interpolator] name: uv type: vec2 -[struct] -name: Config -[variable] -name: projection -type: mat4 - -[cbuffer] -name: config_buffer -type: Config -stage: vertex - [texture] name: atlas stage: fragment @@ -49,9 +32,8 @@ type: vec4 #ifdef VERTEX_SHADER void main() { - interpolator.colour = colour; interpolator.uv = uv; - gl_Position = config_buffer.projection * vec4(position, 0.0, 1.0); + gl_Position = vec4(position, 1.0, 1.0); } #endif @@ -59,8 +41,7 @@ void main() { #ifdef FRAGMENT_SHADER void main() { - vec4 c = texture(atlas, interpolator.uv).r.xxxx; - c *= interpolator.colour; + vec4 c = texture(atlas, interpolator.uv).rgba; colour = c; } diff --git a/ui.cpp b/ui.cpp index 6b282f9..387b5ac 100644 --- a/ui.cpp +++ b/ui.cpp @@ -14,6 +14,8 @@ extern "C" { #define vertex_buffer_count (2048) #define ui_padding 5 +#define DEBUG_CLEARS 0 + static constexpr int font_w = 960; static unsigned font_data[] = { 0x00000000, 0x00003000, 0x00000000, 0x00000000, 0x00000000, @@ -78,58 +80,6 @@ static unsigned font_data[] = { 0x0003c01e, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; -static Texture_Id create_atlas(Device* d) { - int x, y; - int w = font_w + 10; - int size = 10 * w; - Buffer_Id buf = d->create_buffer( - "font atlas stage", - size, - Buffer_Flags::copy_src | - Buffer_Flags::cpu_readwrite - ); - Texture_Id tex; - uint8_t* pixels = (uint8_t*)d->map_buffer(buf, 0, size); - 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[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); - tex = d->create_texture( - "font atlas", - texture_format_r8i, - Texture_Flags::sampleable | Texture_Flags::copy_dst, - w, - 10, - 1, - 1, - 1, - buf - ); - d->destroy_bufferi(buf); - return tex; -} - -static Sampler_Id create_clamped_point(Device* dev) { - Sampler_State s{}; - s.min = Filter_Mode::point; - s.mag = Filter_Mode::point; - s.address_u = Address_Mode::clamp; - s.address_v = Address_Mode::clamp; - return dev->create_sampler("clamped point", s); -} - UI::Colour::Colour(unsigned rgb, uint8_t a): r((rgb >> 16) & 0xff), g((rgb >> 8) & 0xff), @@ -157,6 +107,30 @@ void UI::Rect::clip(const Rect& other) { if (h < 0) h = 0; } +void UI::Rect::clip_src(const Rect& other, Rect& src) { + int n; + if ((n = other.x - x) > 0) { + w -= n; + x += n; + src.w -= n; + src.x += n; + } + if ((n = other.y - y) > 0) { + h -= n; + y += n; + src.h -= n; + src.y += n; + } + if ((n = x + w - (other.x + other.w)) > 0) { + w -= n; + src.w -= n; + } + if ((n = y + h - (other.y + other.h)) > 0) { + h -= n; + src.h -= n; + } +} + void UI::Rect::shrink(int a) { x += a; y += a; @@ -170,236 +144,248 @@ bool UI::Rect::contains(int px, int py) { return px >= x && py >= y && px < x + w && py < y + h; } -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; - start = 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); +static inline UI::Colour blend(UI::Colour dst, UI::Colour src) { + int ia; + ia = 0xff - src.a; + dst.r = (uint8_t)(((src.r * src.a) + (dst.r * ia)) >> 8); + dst.g = (uint8_t)(((src.g * src.a) + (dst.g * ia)) >> 8); + dst.b = (uint8_t)(((src.b * src.a) + (dst.b * ia)) >> 8); + dst.a = (uint8_t)(((src.a * src.a) + (dst.a * ia)) >> 8); + return dst; } -void UI::Vertex_Buffer::add_quad( - UI* ui, + +void UI::Renderer::blit_rect( int x, int y, int w, int h, - float u0, - float v0, - float u1, - float v1, Colour col ) { - Vertex* verts; - int index = usage * 4; - int* scissor = ui->pipeline->scissor; - 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); + int i, j, ex, ey, s; + Rect r = { x, y, w, h }; + r.clip(clip); + Colour* dst = pixels + r.x + r.y * bound.w; + ex = r.x + r.w; + ey = r.y + r.h; + s = bound.w - r.w; + for (j = r.y; j < ey; j++) { + for (i = r.x; i < ex; i++) { + *dst = blend(*dst, col); + dst++; } - next->add_quad(ui, x, y, w, h, u0, v0, u1, v1, col); - return; + dst += s; } - if ( - clip.x < scissor[0] || - clip.y < scissor[1] || - clip.x + clip.w > scissor[0] + scissor[2] || - clip.y + clip.h > scissor[1] + scissor[3] || - (( - clip.x != scissor[0] || - clip.y != scissor[1] || - clip.w != scissor[2] || - clip.h != scissor[3] - ) && ( - x < clip.x || - y < clip.y || - x + w > clip.x + clip.w || - y + h > clip.y + clip.h - )) - ) { - ui->mesh.draw( - ui, - ui->device->get_ctx(), - *ui->pipeline, - *ui->render_pass - ); - scissor[0] = clip.x; - scissor[1] = clip.y; - scissor[2] = clip.w; - scissor[3] = clip.h; - ui->pipeline->hash(); +} + +void UI::Renderer::blit_char(char ch, int x, int y, Colour col) { + Rect re, su; + int i, j, sx, sy, ex, ey, s; + Colour* dst; + re.x = x; + re.y = y; + re.w = 10; + re.h = 10; + su.x = (ch - ' ') * 10; + su.y = 0; + su.w = 10; + su.h = 10; + re.clip_src(clip, su); + ex = re.x + re.w; + ey = re.y + re.h; + dst = pixels + re.x + re.y * bound.w; + s = bound.w - re.w; + for (j = re.y, sy = su.y; j < ey; j++, sy++) { + for (i = re.x, sx = su.x; i < ex; i++, sx++) { + int si = (sx + sy * font_w); + int bit = (1 << (si & 0x1f)); + bit &= font_data[si >> 5]; + if (bit) + *dst = blend(*dst, col); + dst++; + } + dst += s; } - 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, +} + +UI::Renderer::Cmd* UI::Renderer::add_cmd(int size) { + Cmd* r; + size = align_size(size, 8); + assert(cmd_buf_ptr + size <= cmd_buf_size); + r = (Cmd*)&cmd_buf[cmd_buf_ptr]; + r->size = size; + cmd_buf_ptr += size; + return r; +} + +void UI::Renderer::commit_cmd(Cmd* c) { + int x1 = std::min(c->r.x / grid_cell_size[0], grid_size); + int y1 = std::min(c->r.y / grid_cell_size[1], grid_size); + int x2 = std::min((c->r.x + c->r.w + grid_cell_size[0]) / grid_cell_size[0], grid_size); + int y2 = std::min((c->r.y + c->r.h + grid_cell_size[1]) / grid_cell_size[1], grid_size); + int s = grid_size - (x2 - x1); + uint64_t* h = grid + x1 + y1 * grid_size; + int i, j; + for (j = y1; j < y2; j++) { + for (i = x1; i < x2; i++) { + *h = fnv1a64_2(*h, (uint8_t*)c, c->size); + h++; + } + h += s; + } +} + +void UI::Renderer::add_rect( 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); + Cmd* c = add_cmd(sizeof *c); + c->type = Cmd::RECT; + c->col = col; + c->r.x = x; + c->r.y = y; + c->r.w = w; + c->r.h = h; + commit_cmd(c); } -void UI::Vertex_Buffer::add_char( - UI* ui, +void UI::Renderer::add_text( int x, int y, - char ch, + const char* txt, 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); + int len = string_len(txt); + Cmd* c = add_cmd(sizeof *c + len + 1); + c->type = Cmd::TEXT; + c->col = col; + c->r.x = x; + c->r.y = y; + c->r.w = 10 * len; + c->r.h = 10; + string_copy((char*)(c + 1), txt); + commit_cmd(c); +} + +void UI::Renderer::reset(const Rect& cl) { + int i, e; + uint64_t* dst = grid; + clip = cl; + e = grid_size * grid_size; + for (i = 0; i < e; i++, dst++) + *dst = fnv1a64(0, 0); + grid_cell_size[0] = cl.w / grid_size; + grid_cell_size[1] = cl.h / grid_size; + cmd_buf_ptr = 0; +} + +void UI::Renderer::set_clip(const Rect& r) { + Cmd* c = add_cmd(sizeof *c); + c->type = Cmd::CLIP; + c->col = Colour(0, 0); + c->r = r; + clip = r; } -void UI::Vertex_Buffer::add_text( - UI* ui, - int x, - int y, - const char* txt, - Colour col -) { - for (; *txt; txt++, x += 10) - add_char(ui, x, y, *txt, col); +void UI::Renderer::clear(const Rect& r) { + int x, y; + int ex = r.x + r.w; + int ey = r.y + r.h; + int s = bound.w - r.w; + Colour* dst = pixels + r.x + r.y * bound.w; +#if DEBUG_CLEARS + Colour col(rand(), 0x30); +#else + Colour col(0x000000, 0x00); +#endif + for (y = r.y; y < ey; y++) { + for (x = r.x; x < ex; x++) { + *dst = col; + dst++; + } + dst += s; + } } -void UI::Vertex_Buffer::update_buffer(Context& ctx) { - buf.update(ctx); +const App* g_app; +void UI::Renderer::flush(int x, int y) { + Cmd* cmd = (Cmd*)cmd_buf; + Cmd* last = (Cmd*)(cmd_buf + cmd_buf_ptr); + Rect cell = Rect( + x * grid_cell_size[0], + y * grid_cell_size[1], + grid_cell_size[0], + grid_cell_size[1] + ); + clip = cell; + clear(cell); + while (cmd != last) { + switch (cmd->type) { + case Cmd::RECT: { + Rect& r = cmd->r; + blit_rect(r.x, r.y, r.w, r.h, cmd->col); + } break; + case Cmd::TEXT: { + int x = cmd->r.x; + char* txt = (char*)(cmd + 1); + for (; *txt; txt++, x += 10) + blit_char(*txt, x, cmd->r.y, cmd->col); + } break; + case Cmd::CLIP: { + clip = cmd->r; + clip.clip(cell); + break; + } + } + cmd = (Cmd*)((uint8_t*)cmd + cmd->size); + } } -void UI::Vertex_Buffer::reset(const Rect& c) { - start = 0; - usage = 0; - dirty = true; - clip = c; - if (next) - next->reset(c); +void UI::Renderer::flush() { + uint64_t* next = grid == grid_a? grid_b: grid_a; + uint64_t* h = grid; + uint64_t* p = next; + int i, j; + for (j = 0; j < grid_size; j++) { + for (i = 0; i < grid_size; i++) { + if (*p != *h) { + flush(i, j); + } + h++; + p++; + } + } + grid = next; } -void UI::Vertex_Buffer::set_clip(const Rect& r) { - clip = r; - assert(r.x >= 0 && r.y >= 0); - assert(r.w >= 0 && r.h >= 0); - if (next) - next->set_clip(r); +void UI::Renderer::resize(int w, int h) { + heap_free(arena, pixels); + pixels = (Colour*)heap_alloc(arena, w * h * sizeof *pixels); + assert(pixels != 0); + bound = Rect(0, 0, w, h); } -void UI::Vertex_Buffer::draw( - UI* ui, - Context& ctx, - Pipeline& pip, - Render_Pass& rp -) { - if (usage == start) { - if (next) - next->draw(ui, ctx, pip, rp); - return; - } - if (dirty) { - update_buffer(ctx); - dirty = false; - } - 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 - start) * 6; - d.instance_count = 1; - d.first_vertex = start * 6; - ctx.submit(d, pip, rp); - if (next) - next->draw(ui, ctx, pip, rp); - start = usage; +void UI::Renderer::init(Heap* heap, int w, int h) { + arena = heap; + pixels = (Colour*)heap_alloc(heap, w * h * sizeof *pixels); + assert(pixels != 0); + bound = Rect(0, 0, w, h); + grid = grid_a; } UI* UI::create( - Device* dev, const App* app, - Arena* a, - Shader_Id sh + Arena* a ) { int hs; - Texture_Id atlas = create_atlas(dev); UI* u = (UI*)arena_alloc(a, sizeof *u); 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, app, h, atlas, sh); + u->init(app, h); return u; } @@ -415,44 +401,21 @@ int UI::text_height(const char* t) { } void UI::init( - Device* dev, const App* a, - Heap* h, - Texture_Id at, - Shader_Id sh + Heap* h ) { - Shader* sp; heap = h; - device = dev; app = a; - atlas = at; - shader = sh; hot = 0; hovered = 0; active = 0; - mesh.init(device); - cbuffer.init( - device, - "UI CBuffer", - sizeof(UI_CBuffer), - Buffer_Flags::constant_buffer - ); - sp = &dev->get_shader(sh); - vertex_format = sp->vf; - shader_info.vert_binding = sp->binding_index("verts"); - shader_info.atlas_binding = sp->descriptor_binding("atlas"); - shader_info.config_binding = sp->descriptor_binding("config_buffer"); - sampler = create_clamped_point(device); + ren.init(heap, a->w, a->h); root = create_element(0); ((Container*)root)->padding = 0; } void UI::destroy() { root->destroy(); - device->destroy_texture(atlas); - device->destroy_sampler(sampler); - mesh.destroy(this); - cbuffer.destroy(device); } void UI::layout(int w, int h) { @@ -516,93 +479,60 @@ void UI::update(Arena* s) { layout(area.w, area.h); } -void UI::render(Arena* s, Texture_Id target) { - Context& ctx = device->get_ctx(); - Texture& t = device->get_texture(target); - 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 - ); - cbuffer.unmap(device); - cbuffer.update(ctx); - - Pipeline_Builder pb(s, device); - pb.begin_rp(); - pb.rp_target(device->get_backbuffer(), Clear_Mode::restore); - render_pass = &pb.build_rp(); - - pb.begin(); - pb.shader(shader); - pb.vertex_format(vertex_format); - pb.blend( - Blend_Mode::add, - Blend_Factor::src_alpha, - Blend_Factor::inv_src_alpha - ); - pb.cbuffer(shader_info.config_binding, cbuffer.gpuonly); - pb.texture(shader_info.atlas_binding, atlas, sampler); - pipeline = &pb.build(); - +void UI::render(Arena* s) { + if (area.w != ren.bound.w || area.h != ren.bound.h) + ren.resize(area.w, area.h); + ren.reset(ren.bound); root->render(); - mesh.draw(this, ctx, *pipeline, *render_pass); - mesh.reset(Rect(0, 0, t.w, t.h)); + g_app = app; + ren.flush(); } void UI::draw_container(const Rect& bound, Colour c) { /* todo line function lmao */ - mesh.add_rect( - this, + ren.add_rect( bound.x, bound.y, bound.w, bound.h, c ); - mesh.add_rect( - this, + ren.add_rect( bound.x, bound.y, bound.w, 1, 0x000000 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + 1, bound.y + 1, bound.w - 1, 1, 0xa7a7a7 ); - mesh.add_rect( - this, + ren.add_rect( bound.x, bound.y, 1, bound.h, 0x000000 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + 1, bound.y + 1, 1, bound.h - 1, 0xa7a7a7 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + bound.w - 1, bound.y + 1, 1, bound.h - 2, 0x000000 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + 1, bound.y + bound.h - 1, bound.w - 1, @@ -612,56 +542,49 @@ void UI::draw_container(const Rect& bound, Colour c) { } void UI::draw_containeri(const Rect& bound, Colour c) { - mesh.add_rect( - this, + ren.add_rect( bound.x, bound.y, bound.w, bound.h, c ); - mesh.add_rect( - this, + ren.add_rect( bound.x, bound.y, bound.w, 1, 0x000000 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + 1, bound.y + bound.h - 2, bound.w - 2, 1, 0xa7a7a7 ); - mesh.add_rect( - this, + ren.add_rect( bound.x, bound.y, 1, bound.h, 0x000000 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + bound.w - 2, bound.y + 1, 1, bound.h - 2, 0xa7a7a7 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + bound.w - 1, bound.y + 1, 1, bound.h - 2, 0x000000 ); - mesh.add_rect( - this, + ren.add_rect( bound.x + 1, bound.y + bound.h - 1, bound.w - 1, @@ -728,12 +651,12 @@ void UI::Element::on_update() { void UI::Element::render() { Element* child; - Rect old_clip = ui->mesh.clip; + Rect old_clip = ui->ren.clip; on_render(); for (child = children; child; child = child->next) { child->render(); } - ui->mesh.clip = old_clip; + ui->ren.set_clip(old_clip); } void UI::Element::on_message(const Message& msg) { @@ -838,7 +761,7 @@ UI::Rect UI::Container::layout(const Rect& avail) { } void UI::Container::on_render() { - ui->mesh.set_clip(clip); + ui->ren.set_clip(clip); } UI::Table::Table(UI* ui, Element* parent, const int* rs): @@ -921,7 +844,7 @@ UI::Rect UI::Toolbar::layout(const Rect& avail) { } void UI::Toolbar::on_render() { - ui->mesh.set_clip(clip); + ui->ren.set_clip(clip); ui->draw_container( bound, 0xa7a7a7 @@ -958,9 +881,8 @@ void UI::Button::on_render() { ui->draw_containeri(bound, 0xffffff); } else ui->draw_container(bound, 0xffffff); - ui->mesh.set_clip(clip); - ui->mesh.add_text( - ui, + ui->ren.set_clip(clip); + ui->ren.add_text( bound.x + ui_padding + toff[0], bound.y + ui_padding + toff[1], text, @@ -1014,8 +936,7 @@ void UI::Input::on_render() { if ((n = c.x - (clip.x + clip.w)) > 0) tx -= n; c.x = tx + w; - ui->mesh.add_rect( - ui, + ui->ren.add_rect( c.x, c.y, c.w, @@ -1024,9 +945,8 @@ void UI::Input::on_render() { ); } if (buf) { - ui->mesh.set_clip(clip); - ui->mesh.add_text( - ui, + ui->ren.set_clip(clip); + ui->ren.add_text( tx, bound.y + ui_padding, buf, @@ -1155,16 +1075,14 @@ void UI::Slider::on_render() { hc = 0x4f4f4f; Rect sr = track_rect(); Rect ha = handle_rect(); - ui->mesh.add_rect( - ui, + ui->ren.add_rect( sr.x, sr.y, sr.w, sr.h, 0xa7a7a7 ); - ui->mesh.add_rect( - ui, + ui->ren.add_rect( ha.x, ha.y, ha.w, @@ -1271,9 +1189,8 @@ UI::Rect UI::Label::layout(const Rect& avail) { } void UI::Label::on_render() { - ui->mesh.set_clip(clip); - ui->mesh.add_text( - ui, + ui->ren.set_clip(clip); + ui->ren.add_text( bound.x + ui_padding, bound.y + ui_padding, text, @@ -1363,7 +1280,7 @@ UI::Rect UI::Modal::layout(const Rect& avail) { } void UI::Modal::on_render() { - ui->mesh.set_clip(clip); + ui->ren.set_clip(clip); ui->draw_container( contents->bound, Colour(0xffffff, 0x80) diff --git a/ui.hpp b/ui.hpp index ea51717..37726cb 100644 --- a/ui.hpp +++ b/ui.hpp @@ -3,16 +3,12 @@ #include "app.hpp" #include "maths.hpp" -#include "video.hpp" #include struct Arena; +struct Heap; struct UI { - struct Vertex { - float x, y, u, v, r, g, b, a; - }; - struct Colour { uint8_t r, g, b, a; Colour(unsigned rgb, uint8_t a = 0xff); @@ -29,109 +25,77 @@ struct UI { Rect(int x, int y, int w, int h); void clip(const Rect& other); + void clip_src(const Rect& other, Rect& src); void shrink(int a); bool contains(int px, int py); }; - struct Vertex_Buffer { + struct Renderer { + static constexpr int grid_size = 16; + static constexpr int cmd_buf_size = 1024 * 8; + Rect bound; Rect clip; - bool dirty; - Staged_Buffer buf; - Buffer_Id indices; - int start, usage; - - void init(Device* dev); - void init_indices(Device* dev); - void destroy(UI* ui); - void update_buffer(Context& ctx); - void reset(const Rect& clip); - void set_clip(const Rect& clip); - - void add_quad( - UI* ui, - int x, - int y, - int w, - int h, - float u0, - float v0, - float u1, - float v1, - Colour col - ); - void add_rect( - UI* ui, - int x, - int y, - int w, - int h, - Colour col - ); - void add_char(UI* ui, int x, int y, char ch, Colour col); - void add_text( - UI* ui, - int x, - int y, - const char* txt, - Colour col - ); - void draw( - UI* ui, - Context& ctx, - Pipeline& pip, - Render_Pass& rp - ); - Vertex_Buffer* next; - }; + Colour* pixels; + Heap* arena; + uint8_t cmd_buf[cmd_buf_size]; + int cmd_buf_ptr; + int grid_cell_size[2]; + uint64_t grid_a[grid_size * grid_size]; + uint64_t grid_b[grid_size * grid_size]; + uint64_t* grid; + + struct Cmd { + enum { + RECT, + TEXT, + CLIP + }; + int type, size; + Colour col; + Rect r; + }; + + void blit_rect(int x, int y, int w, int h, Colour col); + void blit_char(char ch, int x, int y, Colour col); + + Cmd* add_cmd(int size); + void commit_cmd(Cmd* c); + void add_rect(int x, int y, int w, int h, Colour col); + void add_text(int x, int y, const char* txt, Colour col); + void reset(const Rect& cl); + void set_clip(const Rect& r); + void clear(const Rect& r); + void flush(int x, int y); + void flush(); + void resize(int w, int h); + void init(Heap* heap, int w, int h); + } ren; struct Element; Heap* heap; - Device* device; const App* app; - Texture_Id atlas; - Shader_Id shader; - Vertex_Format_Id vertex_format; - Sampler_Id sampler; - Staged_Buffer cbuffer; - Vertex_Buffer mesh; - Pipeline* pipeline; - Render_Pass* render_pass; Element* root, * hot, * hovered, * active; Rect area; bool layout_dirty; - struct UI_CBuffer { - m4f projection; - }; - struct { - int vert_binding; - int atlas_binding; - int config_binding; - } shader_info; - static int text_width(const char* t); static int text_height(const char* t); static UI* create( - Device* dev, const App* app, - Arena* a, - Shader_Id sh + Arena* a ); void init( - Device* dev, const App* app, - Heap* h, - Texture_Id atlas, - Shader_Id sh + Heap* h ); void destroy(); void layout(int w, int h); void text_input(const char* buf); void update(Arena* s); - void render(Arena* s, Texture_Id target); + void render(Arena* s); void draw_container(const Rect& r, Colour c); void draw_containeri(const Rect& r, Colour c); diff --git a/video.cpp b/video.cpp index 6d52a57..82dd0f7 100644 --- a/video.cpp +++ b/video.cpp @@ -2454,6 +2454,14 @@ void Context::transition(Texture_Id id, Resource_State state) { b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; src_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else if ( + src_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && + dst_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + ) { + b.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + b.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dst_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; } else if ( src_layout == VK_IMAGE_LAYOUT_UNDEFINED && dst_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL -- cgit v1.2.3-54-g00ecf