summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-12-27 11:35:01 +1100
committerquou <quou@disroot.org>2024-12-27 11:36:20 +1100
commit6736966974aea334e119cdb0b6c330066061bf91 (patch)
treea16cc9a3d12e6f9468dbb8fe30146acd78f4af64
parent5f9100b935a27bbb6d5ac8ab2050b19ba2894087 (diff)
basic text rendering
-rw-r--r--Makefile10
-rw-r--r--c2.cpp21
-rw-r--r--intermediate/ui.glsl56
-rw-r--r--todo.txt2
-rw-r--r--ui.cpp275
-rw-r--r--ui.hpp60
6 files changed, 418 insertions, 6 deletions
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>("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 <string.h>
+
+#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