#include "ui.hpp" extern "C" { #include "memory.h" #include "plat.h" #include "str.h" } #include #define mesh_size (1024 * 8) static int font_w = 960; static unsigned font_data[] = { 0x00000000, 0x00003000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0cc0c000, 0x8000301b, 0x60601803, 0x00000000, 0x20000000, 0x0780c078, 0x83f0e01e, 0xe0783f07, 0x60000001, 0x1e018000, 0x07e0c078, 0xe3f87e1f, 0xe0cc1e0f, 0x1e638f01, 0x1f0c6318, 0x07e1f07e, 0xc330fc1e, 0x30cc318c, 0x021e0fc3, 0x000101e0, 0x00e00000, 0x0000e000, 0xc00e0007, 0x3c038c00, 0x00000000, 0x00000000, 0x00001000, 0x00000000, 0x30380000, 0x0000c070, 0x0cc1e000, 0xc630f81b, 0xc0301806, 0x00000cc0, 0x30000000, 0x0cc0f0cc, 0xc030f033, 0x30cc330c, 0x30000003, 0x33030000, 0x8cc1e0cc, 0xc230cc33, 0xc0cc3308, 0x0c630600, 0x318ce3b8, 0x0cc318cc, 0xc330b433, 0x30cc318c, 0x06060cc3, 0x00038180, 0x00c00018, 0x8000c000, 0x000c000d, 0x30030000, 0x00000000, 0x00000000, 0x00001800, 0x00000000, 0x300c0000, 0x0409e0c0, 0x8481e000, 0xc3300c3f, 0x80180c06, 0x000c0781, 0x18000000, 0x0c00c0ec, 0xc1f0d830, 0x30cc1800, 0x180c0303, 0x30060000, 0x8cc330ec, 0xc0318c01, 0xc0cc3180, 0x0c330600, 0x318de3f8, 0x0cc318cc, 0xc3303003, 0x3078318c, 0x0c060643, 0x0006c180, 0x07c0f018, 0x81e0f81e, 0xf06c3701, 0x30330f00, 0x1e07c1f8, 0x0ee37076, 0xc330fc3e, 0x30c6318c, 0x300c0fc3, 0x0e0f20c0, 0x0001e000, 0x8180781b, 0x80180013, 0x000c1fe1, 0x0c000000, 0x0600c0dc, 0xc300cc1c, 0xe0780c07, 0x0c0c0303, 0x180c03f0, 0x87c330ec, 0xc1f18c01, 0xc0fc0187, 0x0c1f0600, 0x318fe358, 0x07c3187c, 0xc330300e, 0xe030358c, 0x18060301, 0x000c6180, 0x0cc18030, 0xc330cc33, 0xc0dc1987, 0x301b0c00, 0x330cc358, 0x0dc198cc, 0xc3301803, 0x306c358c, 0x00060643, 0x1b060180, 0x0000c000, 0xc0c0c01b, 0x8018001e, 0x003f0781, 0x060003f0, 0x0300c0cc, 0xc301fc30, 0x00cc0c0c, 0x0c000003, 0x0c0c0000, 0x8cc3f00c, 0xc0318c01, 0xc0cc3980, 0x0c330660, 0x318f6318, 0x06c3980c, 0xc3303018, 0xc078358c, 0x30060180, 0x00000180, 0x0cc1f000, 0x83f0cc03, 0xc0cc1981, 0x301f0c00, 0x330cc358, 0x00c198cc, 0xc330181e, 0x3038358c, 0x300c0303, 0x318000c0, 0x80000000, 0xc6607c3f, 0xc030000c, 0x300c0cc0, 0x03030000, 0x0180c0cc, 0xc330c033, 0x30cc0c0c, 0x180c0303, 0x000603f0, 0x8cc3308c, 0xc230cc33, 0xc0cc3300, 0x8c630660, 0x318e6318, 0x0cc1f00c, 0x83303033, 0xc0cc1f07, 0x600609c0, 0x00000180, 0x0cc19800, 0x8030cc33, 0xc0cc1f01, 0x30330cc0, 0x330cc358, 0x00c198cc, 0x8330d830, 0xe06c1f07, 0x300c0981, 0x318000c0, 0x0000c000, 0x8630301b, 0x6060001b, 0x300c0000, 0x01830000, 0x0fc3f078, 0x81e0c01e, 0xe0780c07, 0x300c0301, 0x0c030000, 0x07e33078, 0xe3f87e1f, 0xe0cc3e01, 0xfe6383c1, 0x1f0c6318, 0x0ce3c01e, 0x01e0781e, 0xe0cc1b03, 0xc01e0fc1, 0x000001e0, 0x07e3f000, 0xc1e0f81e, 0xf0ce1803, 0xfc338783, 0x1e0cc318, 0x01e1f07c, 0x06e0701f, 0x80c61b03, 0x30380fc1, 0x3f800070, 0x0000c000, 0x0000301b, 0x00000000, 0x18000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x60060000, 0x00018000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x7f800000, 0x00000000, 0x00000000, 0x00000f00, 0x00000000, 0x00000000, 0x0001800c, 0x00000000, 0xf0000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0003c01e, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; static Texture_Id create_atlas(Device* d, Arena* a) { int x, y; int size = 10 * font_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; unsigned bits = font_data[si >> 5]; int bit = bits & 1 << (si & 0x1f); pixels[si] = bit? 0xff: 0x00; } } d->unmap_buffer(buf); tex = d->create_texture( "font atlas", texture_format_r8i, Texture_Flags::sampleable | Texture_Flags::copy_dst, font_w, 10, buf ); d->destroy_bufferi(buf); clear_arena(a); 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* UI::create(Device* dev, Arena* a, Shader_Id sh) { Texture_Id atlas = create_atlas(dev, a); UI* u = (UI*)arena_alloc(a, sizeof *u); u->init(dev, a, atlas, sh); return u; } int UI::text_width(const char* t) { int w; for (w = 0; *t; t++) w += 10; return w; } int UI::text_height(const char* t) { (void)t; return 10; } void UI::init(Device* dev, Arena* a, Texture_Id at, Shader_Id sh) { Shader* sp; arena = a; 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", sizeof(UI_CBuffer), Buffer_Flags::constant_buffer | Buffer_Flags::cpu_readwrite ); 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); } 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; } 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; Texture& t = device->get_texture(target); UI_CBuffer* config = (UI_CBuffer*)device->map_buffer( config_buf, 0, sizeof(UI_CBuffer) ); 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; } } Pipeline_Builder pb(s, device); pb.begin_rp(); pb.rp_target(device->get_backbuffer(), Clear_Mode::restore); Render_Pass& 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_colour ); pb.cbuffer(shader_info.config_binding, config_buf); 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); }