From 6736966974aea334e119cdb0b6c330066061bf91 Mon Sep 17 00:00:00 2001 From: quou Date: Fri, 27 Dec 2024 11:35:01 +1100 Subject: basic text rendering --- Makefile | 10 +- c2.cpp | 21 +++- intermediate/ui.glsl | 56 +++++++++++ todo.txt | 2 +- ui.cpp | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++ ui.hpp | 60 +++++++++++ 6 files changed, 418 insertions(+), 6 deletions(-) create mode 100644 intermediate/ui.glsl create mode 100644 ui.cpp create mode 100644 ui.hpp diff --git a/Makefile b/Makefile index ac6149c..fbc4cf4 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,11 @@ target = c2 data_dir = data -shaders = $(data_dir)/triangle.csh +shaders = $(data_dir)/triangle.csh $(data_dir)/ui.csh textures = $(data_dir)/22.tex $(data_dir)/kita.tex packed_files = $(shaders) $(textures) tools = qstd cfg sc -objects = app.o c2.o video.o pipeline.o asset.o +objects = app.o c2.o video.o pipeline.o asset.o ui.o includes = -Iqstd defines = -Dplat_x86 -Dplat_posix -Dplat_x11 -Dallocation_default_alignment=8 cflags = -MMD -MF $(basename $@).d $(includes) $(defines) $(DEBUG_COMPILE_FLAG) @@ -37,6 +37,9 @@ pack: $(packed_files) packer data/triangle.csh: intermediate/triangle.glsl | $(data_dir) sc ./sc/sc intermediate/triangle.glsl $(data_dir)/triangle.csh +data/ui.csh: intermediate/ui.glsl | $(data_dir) sc + ./sc/sc intermediate/ui.glsl $(data_dir)/ui.csh + data/22.tex: intermediate/22.bmp | $(data_dir) convtexture ./convtexture intermediate/22.bmp $(data_dir)/22.tex bc1 @@ -55,6 +58,9 @@ pipeline.o: asset.o: $(CXX) -c $(cflags) asset.cpp -o asset.o +ui.o: + $(CXX) -c $(cflags) ui.cpp -o ui.o + c2.o: $(CXX) -c $(cflags) c2.cpp -o c2.o diff --git a/c2.cpp b/c2.cpp index 3ea05b9..223e0b0 100644 --- a/c2.cpp +++ b/c2.cpp @@ -1,4 +1,5 @@ #include "app.hpp" +#include "ui.hpp" #include "video.hpp" extern "C" { #include "plat.h" @@ -9,6 +10,7 @@ extern "C" { #define video_arena_size (1024 * 1024 * 16) #define asset_arena_size (1024 * 1024 * 4) +#define ui_arena_size (1024 * 16) #define per_frame_memory_size (1024 * 1024) static float verts[] = { @@ -64,15 +66,15 @@ struct Config_Buffer { }; int main() { - Arena video_arena; - Arena asset_arena; + Arena video_arena, asset_arena, ui_arena; Asset_Arena assets; Device* dev; - Shader* shader; + Shader* shader, * ui_shader; Texture* texture; Texture* texture2; Buffer_Id vbo, cbuf; Sampler_Id clamped_linear; + UI* ui; C2* app = App::create("c2"); void* per_frame; int frame = 0; @@ -87,10 +89,16 @@ int main() { arena_alloc(app->arena, asset_arena_size), asset_arena_size ); + init_arena( + &ui_arena, + arena_alloc(app->arena, ui_arena_size), + ui_arena_size + ); assets.init(&asset_arena, "pack"); dev = Device::create(&video_arena, app); app->dev = dev; shader = (Shader*)assets.load("triangle.csh"); + ui_shader = (Shader*)assets.load("ui.csh"); texture = (Texture*)assets.load("22.tex"); texture2 = (Texture*)assets.load("kita.tex"); cbuf = dev->create_buffer( @@ -103,6 +111,8 @@ int main() { per_frame_memory_size ); clamped_linear = create_clamped_linear(dev); + ui = UI::create(dev, &ui_arena, ui_shader->id); + ui->create_label("Hello, world", 10, 15); assert(per_frame != 0); uint8_t r = 0; vbo = upload_verts(dev); @@ -113,6 +123,8 @@ int main() { app->begin(); dev->begin_frame(); + ui->update(&frame_arena); + { void* mem; mem = dev->map_buffer(cbuf, 0, sizeof(Config_Buffer)); @@ -154,11 +166,14 @@ int main() { dev->get_ctx().submit(draw, pip, pass); + ui->render(&frame_arena, dev->get_backbuffer()); + r += 10; frame++; dev->present(); app->end(); } + ui->destroy(); assets.destroy(); dev->destroy_sampler(clamped_linear); dev->destroy_buffer(vbo); diff --git a/intermediate/ui.glsl b/intermediate/ui.glsl new file mode 100644 index 0000000..e40d3d5 --- /dev/null +++ b/intermediate/ui.glsl @@ -0,0 +1,56 @@ +#ifdef DESC +[program] +type: graphics +vertex: main +fragment: main + +[binding] +name: verts +rate: vertex +[attribute] +name: position +type: vec2 +[attribute] +name: uv +type: vec2 +[attribute] +name: colour +type: vec4 + +[interpolator] +name: colour +type: vec4 +[interpolator] +name: uv +type: vec2 + +[texture] +name: atlas +stage: fragment +dimension: 2 + +[target] +name: colour +type: vec4 + +#endif + +#ifdef VERTEX_SHADER + +void main() { + interpolator.colour = colour; + interpolator.uv = uv; + gl_Position = vec4(position, 0.0, 1.0); +} + +#endif + +#ifdef FRAGMENT_SHADER + +void main() { + vec4 c = texture(atlas, interpolator.uv).r.xxxx; + c *= interpolator.colour; + colour = c; +} + +#endif diff --git a/todo.txt b/todo.txt index d17a86d..33dbce5 100644 --- a/todo.txt +++ b/todo.txt @@ -6,7 +6,7 @@ todo list - [x] texture conversion + compression, etc - [x] texture the triangle - [x] refactor pipelines and vertex formats to be more flexible - - [ ] simple text rendering + - [x] simple text rendering - [ ] 3D maths library - [ ] model conversion - [ ] render a model diff --git a/ui.cpp b/ui.cpp new file mode 100644 index 0000000..3f773f5 --- /dev/null +++ b/ui.cpp @@ -0,0 +1,275 @@ +#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; + uint8_t* pixels = (uint8_t*)arena_alloc(a, size); + void* bufmem; + Buffer_Id buf; + Texture_Id tex; + 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; + } + } + buf = d->create_buffer( + size, + Buffer_Flags::copy_src | + Buffer_Flags::cpu_readwrite + ); + bufmem = d->map_buffer(buf, 0, size); + memcpy(bufmem, pixels, size); + d->unmap_buffer(buf); + tex = d->create_texture( + texture_format_r8i, + font_w, + 10, + buf + ); + d->destroy_buffer(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(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( + mesh_size, + Buffer_Flags::vertex_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"); + sampler = create_clamped_point(device); +} + +void UI::destroy() { + device->destroy_texture(atlas); + device->destroy_buffer(mesh); + 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) { + +} + +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 * ndc[0]; + 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 * ndc[0], (float)y * ndc[1], uv[0], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f + }; + verts[i + 1] = Vertex { + (float)x * ndc[0], (float)y * ndc[1] + o, uv[0], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f + }; + verts[i + 2] = Vertex { + (float)x * ndc[0] + o, (float)y * ndc[1] + o, uv[1], 1.0f, 1.0f, 1.0f, 1.0f, 1.0f + }; + verts[i + 3] = Vertex { + (float)x * ndc[0], (float)y * ndc[1], uv[0], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f + }; + verts[i + 4] = Vertex { + (float)x * ndc[0] + o, (float)y * ndc[1], uv[1], 0.0f, 1.0f, 1.0f, 1.0f, 1.0f + }; + verts[i + 5] = Vertex { + (float)x * ndc[0] + o, (float)y * ndc[1] + 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); + ndc[0] = 4.0f / (float)t.w; + ndc[1] = 4.0f / (float)t.h; + for (; e; e = e->next) { + switch (e->type) { + case Element::Type::label: + vc += render_label((Label*)e, vc); + break; + } + } + + Pipeline_Builder pb(s); + pb.begin_rp(); + pb.rp_target(device->get_backbuffer(), Clear_Mode::restore); + Render_Pass& pass = pb.build_rp(); + + pb.begin(device); + pb.shader(shader); + pb.vertex_format(vertex_format); + 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); +} diff --git a/ui.hpp b/ui.hpp new file mode 100644 index 0000000..a181afe --- /dev/null +++ b/ui.hpp @@ -0,0 +1,60 @@ +#ifndef ui_hpp +#define ui_hpp + +#include "video.hpp" + +struct Arena; +struct UI { + struct Rect { + int x, y, w, h; + }; + + struct Element : Rect { + enum class Type { + label + } type; + Element* next; + static int size(Type t); + }; + + struct Label : Element { + char* text; + int len; + int x, y; + }; + + struct Vertex { + float x, y, u, v, r, g, b, a; + }; + + Arena* arena; + Device* device; + Element* tree; + Texture_Id atlas; + Shader_Id shader; + Vertex_Format_Id vertex_format; + Buffer_Id mesh; + Sampler_Id sampler; + struct { + int vert_binding; + int atlas_binding; + } shader_info; + float ndc[2]; + + static int text_width(const char* t); + static int text_height(const char* t); + + static UI* create(Device* dev, Arena* a, Shader_Id sh); + + Element* create_element(Element::Type t); + Label* create_label(const char* t, int x, int y); + + void init(Device* dev, Arena* a, Texture_Id atlas, Shader_Id sh); + void destroy(); + void update(Arena* s); + void render(Arena* s, Texture_Id target); + + int render_label(const Label* l, int off); +}; + +#endif -- cgit v1.2.3-54-g00ecf