diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..7950bfe --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,18 @@ +[ + { + "arguments": [ + "/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/cc", + "-c", + "-Ivendor/", + "-g", + "-Wall", + "-Wextra", + "-o", + "bin/an_editor.o", + "src/main.c" + ], + "directory": "/home/patrick/Documents/an_editor", + "file": "/home/patrick/Documents/an_editor/src/main.c", + "output": "/home/patrick/Documents/an_editor/bin/an_editor.o" + } +] diff --git a/flake.nix b/flake.nix index 4989bd1..226bfac 100755 --- a/flake.nix +++ b/flake.nix @@ -14,14 +14,21 @@ }; naga-cli = pkgs.rustPlatform.buildRustPackage rec { pname = "naga-cli"; - version = "0.19.0"; + version = "v0.19.0"; - src = pkgs.fetchCrate { - inherit pname version; - hash = "sha256-zR7Al5aMG8VTdjZwaZtjeDFI6WFD0N6MCrrLP/9PeZ8="; + src = pkgs.fetchFromGitHub { + name = "wgpu"; + owner = "gfx-rs"; + repo = "wgpu"; + rev = "a63bec8cd67b4abe3b9717e1926a94d1035b830a"; # trunk as of 2024-03-15 + hash = "sha256-AzsR24NAVYgrC/kWGXiWxyMERiLJ/Jink21P4oh8lOw="; }; + buildAndTestSubdir = "naga-cli"; - cargoHash = "sha256-/5srWh4CjD8S/hRFRTJE//X6TgIfbwnMKmgRMjX3084="; + cargoLock = { + lockFile = "${src}/Cargo.lock"; + allowBuiltinFetchGit = true; + }; }; in { @@ -30,13 +37,31 @@ pkg-config binutils clang + bear naga-cli darwin.apple_sdk.frameworks.Kernel darwin.apple_sdk.frameworks.CoreVideo darwin.apple_sdk.frameworks.Metal darwin.apple_sdk.frameworks.MetalKit darwin.apple_sdk.frameworks.Cocoa - ] else throw "unsupported system" ); + ] else if pkgs.system == "x86_64-linux" then [ + pkg-config + binutils + clang + bear + naga-cli + libGL + mesa + gf + xorg.libX11 + xorg.libXi + xorg.xinput + xorg.libXcursor + xorg.libXrandr + xorg.libXinerama + pkgs.nixgl.nixGLIntel + ] + else throw "unsupported system" ); }; } ); diff --git a/justfile b/justfile index d7790af..ffb30eb 100644 --- a/justfile +++ b/justfile @@ -3,13 +3,14 @@ alias r := run build: transpile_shaders_metal mkdir -p bin - cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor + # cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor + cc -Ivendor/ -g -Wall -Wextra src/*.c -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor + # cc bin/*.o -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor run: build - ./bin/an_editor + nixGLIntel ./bin/an_editor transpile_shaders_metal: mkdir -p bin/transpiled_shaders - ../wgpu/target/debug/naga shaders/vertex.wgsl bin/transpiled_shaders/vertex.metal --metal-version 1.2 - ../wgpu/target/debug/naga shaders/fragment.wgsl bin/transpiled_shaders/fragment.metal --metal-version 1.2 - + naga shaders/vertex.wgsl bin/transpiled_shaders/vertex.metal --metal-version 1.2 + naga shaders/fragment.wgsl bin/transpiled_shaders/fragment.metal --metal-version 1.2 diff --git a/shaders/fragment.frag b/shaders/fragment.frag new file mode 100644 index 0000000..a66f6ac --- /dev/null +++ b/shaders/fragment.frag @@ -0,0 +1,21 @@ +#version 330 core +struct VertexOutput { + vec4 position; + vec2 tex_coord; +}; +uniform sampler2D _group_0_binding_0_fs; + +smooth in vec2 _vs2fs_location0; +layout(location = 0) out vec4 _fs2p_location0; + +void main() { + VertexOutput input_ = VertexOutput(gl_FragCoord, _vs2fs_location0); + float text_color = 0.0; + vec4 _e8 = texture(_group_0_binding_0_fs, vec2(vec2(input_.tex_coord.x, input_.tex_coord.y))); + text_color = _e8.x; + float _e11 = text_color; + float _e17 = text_color; + _fs2p_location0 = vec4((_e11 * vec3(1.0, 1.0, 1.0)), _e17); + return; +} + diff --git a/shaders/vertex.vert b/shaders/vertex.vert new file mode 100644 index 0000000..4fa2f7f --- /dev/null +++ b/shaders/vertex.vert @@ -0,0 +1,49 @@ +#version 330 core +uniform uint naga_vs_first_instance; + +struct VertexInput { + vec3 position; + vec2 tex_coord; + vec2 atlas_position; + vec2 size; + vec2 target_position; + float y_offset; + uint glyph_id; +}; +struct VertexOutput { + vec4 position; + vec2 tex_coord; +}; +uniform vec4 screen_size; + +layout(location = 0) in vec3 _p2vs_location0; +layout(location = 1) in vec2 _p2vs_location1; +layout(location = 2) in vec2 _p2vs_location2; +layout(location = 3) in vec2 _p2vs_location3; +layout(location = 4) in vec2 _p2vs_location4; +layout(location = 5) in float _p2vs_location5; +smooth out vec2 _vs2fs_location0; + +vec4 to_device_position(vec2 position, vec2 size) { + return vec4((((position / size) * 2.0) - vec2(1.0)), 1.0, 1.0); +} + +void main() { + VertexInput input_ = VertexInput(_p2vs_location0, _p2vs_location1, _p2vs_location2, _p2vs_location3, _p2vs_location4, _p2vs_location5, (uint(gl_InstanceID) + naga_vs_first_instance)); + VertexOutput out_ = VertexOutput(vec4(0.0), vec2(0.0)); + vec4 vertex_pos = vec4(0.0); + vec2 atlas_position = vec2(0.0); + vec4 _e28 = to_device_position((((((input_.position.xy + vec2(1.0)) / vec2(2.0)) * (input_.size / vec2(2.0))) + input_.target_position) + vec2(0.0, ((input_.y_offset / 2.0) + 32.0))), screen_size.xy); + vertex_pos = _e28; + atlas_position = (((((input_.position.xy + vec2(1.0)) / vec2(2.0)) * input_.size) + input_.atlas_position) / vec2(1024.0)); + vec4 _e47 = vertex_pos; + out_.position = _e47; + vec2 _e49 = atlas_position; + out_.tex_coord = _e49; + VertexOutput _e50 = out_; + gl_Position = _e50.position; + _vs2fs_location0 = _e50.tex_coord; + gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w); + return; +} + diff --git a/shaders/vertex.wgsl b/shaders/vertex.wgsl index b6ce346..84ad7d3 100644 --- a/shaders/vertex.wgsl +++ b/shaders/vertex.wgsl @@ -15,23 +15,23 @@ struct VertexOutput { @location(0) tex_coord: vec2, } -struct Params { - screen_size: vec2, -} +// struct Params { +// screen_size: vec4, +// } fn to_device_position(position: vec2, size: vec2) -> vec4 { return vec4((((position / size) * 2.) - 1.), 1., 1.); } -@group(0) @binding(1) -var params: Params; +@group(0) @binding(0) +var screen_size: vec4; @vertex fn vs_main(input: VertexInput) -> VertexOutput { var out: VertexOutput; - var vertex_pos = to_device_position(((input.position.xy + 1.) / 2.) * (input.size/2.0) + input.target_position + vec2(0., (input.y_offset/2.0)+32), params.screen_size); - vertex_pos.y = -vertex_pos.y; + var vertex_pos = to_device_position(((input.position.xy + 1.) / 2.) * (input.size/2.0) + input.target_position + vec2(0., (input.y_offset/2.0)+32), screen_size.xy); + // vertex_pos.y = -vertex_pos.y; var atlas_position = (((input.position.xy + 1.) / 2.) * input.size + input.atlas_position) / vec2(1024); out.position = vertex_pos; diff --git a/src/compile_flags.txt b/src/compile_flags.txt index c71b0a0..e0d0f6a 100644 --- a/src/compile_flags.txt +++ b/src/compile_flags.txt @@ -1,2 +1,5 @@ +-DED_UI_IMPLEMENTATION +-DED_HT_IMPLEMENTATION +-DED_STRING_IMPLEMENTATION -I../vendor/ -ObjC diff --git a/src/ht.h b/src/ht.h index 5017615..1e39ebe 100644 --- a/src/ht.h +++ b/src/ht.h @@ -2,21 +2,108 @@ #ifndef ED_HT_INCLUDED #define ED_HT_INCLUDED + +#include #include +#include #include +#include + #include "string.h" // see -#define FNV_OFFSET 14695981039346656037UL -#define FNV_PRIME 1099511628211UL +#define FNV_OFFSET 14695981039346656037ULL +#define FNV_PRIME 1099511628211ULL typedef struct { - + string key; } ed_ht_slot; typedef struct { - ed_ht_slot *slots; + ed_ht_slot *key_slots; + void *value_slots; + size_t value_size; size_t capacity; } ed_ht; +typedef uint64_t ht_hash_t; + +#ifdef ED_HT_IMPLEMENTATION + +static ht_hash_t ht_hash(string key, uint64_t mod) { + ht_hash_t hash = FNV_OFFSET; + + for (size_t i=0; icapacity); + + for (size_t i=hash; icapacity; ++i) { + if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) { + ht->key_slots[i].key = key; + + memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size); + return true; + } + } + + return false; +} + +void *ht_get_slot(ed_ht *ht, size_t slot) { + void *value = ht->value_slots+slot*ht->value_size; + + if (slot >= ht->capacity || !value) + return NULL; + + return value; +} + +void *ht_get(ed_ht *ht, string key) { + ht_hash_t hash = ht_hash(key, ht->capacity); + + for (size_t i=hash; icapacity; ++i) { + if (ht->key_slots[i].key.data == NULL) { + return NULL; + } + + if (string_eq(ht->key_slots[i].key, key)) { + return ht_get_slot(ht, i); + } + } + + return NULL; +} + +void ht_destroy(ed_ht *ht) { + // TODO: destroy the hash table + free(ht->key_slots); + free(ht->value_slots); + + ht->key_slots = NULL; + ht->value_slots = NULL; +} + +#endif #endif diff --git a/src/main.c b/src/main.c index 4c98781..d1cee40 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,10 @@ #include +#include #include +// #define ARENA_IMPLEMENTATION +// #include + #define SOKOL_DEBUG #define SOKOL_APP_IMPL @@ -8,7 +12,11 @@ #define SOKOL_GLUE_IMPL #define SOKOL_FETCH_IMPL #define SOKOL_LOG_IMPL -#define SOKOL_METAL + +// TODO: condition compilation +// #define SOKOL_METAL +#define SOKOL_GLCORE33 + #include #include #include @@ -18,21 +26,22 @@ #define STB_TRUETYPE_IMPLEMENTATION #include -#define ARENA_IMPLEMENTATION -#include - -#include "string.h" - +#define ED_STRING_IMPLEMENTATION +#define ED_HT_IMPLEMENTATION #define ED_UI_IMPLEMENTATION +#include "string.h" +#include "ht.h" #include "ui.h" -static Arena default_arena = {0}; -static Arena temporary_arena = {0}; -static Arena *context_arena = &default_arena; + +// static Arena default_arena = {0}; +// static Arena temporary_arena = {0}; +// static Arena *context_arena = &default_arena; void *context_alloc(size_t size) { // assert(context_arena); - return arena_alloc(context_arena, size); + // return arena_alloc(context_arena, size); + return malloc(size); } typedef struct { @@ -49,7 +58,7 @@ typedef struct { } GpuGlyph; typedef struct { - float screen_size[2]; + float screen_size[4]; } GpuUniformParams; typedef struct { @@ -94,35 +103,35 @@ static struct { U8Array glyph_cache; bool should_exit; + + ui_context ui_cx; } state; void queue_text(string text, float position[2]) { float x = 0; for (size_t i=0; i < text.len; ++i) { - if (text.data[i] >= 32 && text.data[i] <= 32+95) { + if (text.data[i] >= 32) { GpuGlyph glyph = *((GpuGlyph *)(state.glyph_cache.data+((text.data[i] - 32) * sizeof(GpuGlyph)))); glyph.position[0] = x+position[0]; glyph.position[1] = position[1]; x += glyph.size[0]/2; - push_u8array(&state.gpu_glyphs, &glyph, sizeof(GpuGlyph)); + push_u8array(&state.gpu_glyphs, (uint8_t *)(&glyph), sizeof(GpuGlyph)); } } } void vertex_shader_loaded(const sfetch_response_t *response) { if (response->fetched) { - state.scratch_shader_desc.vs.source = response->data.ptr; - state.scratch_shader_desc.vs.entry = "vs_main"; state.scratch_shader_desc.vs = (sg_shader_stage_desc) { .source = response->data.ptr, .entry = "vs_main", .uniform_blocks[0] = { .size = sizeof(GpuUniformParams), - .layout = SG_UNIFORMLAYOUT_NATIVE, + .layout = SG_UNIFORMLAYOUT_STD140, .uniforms = { - [0] = { .name = "screen_size", .type = SG_UNIFORMTYPE_FLOAT2 }, + [0] = { .name = "screen_size", .type = SG_UNIFORMTYPE_FLOAT4 }, }, }, }; @@ -139,7 +148,7 @@ void fragment_shader_loaded(const sfetch_response_t *response) { .entry = "fs_main", .images[0].used = true, .samplers[0].used = true, - .image_sampler_pairs[0] = { .used = true, .image_slot = 0, .sampler_slot = 0 }, + .image_sampler_pairs[0] = { .glsl_name = "_group_0_binding_0_fs", .used = true, .image_slot = 0, .sampler_slot = 0 }, }; state.scratch_shader_desc.fs.source = response->data.ptr; state.scratch_shader_desc.fs.entry = "fs_main"; @@ -151,11 +160,12 @@ void fragment_shader_loaded(const sfetch_response_t *response) { void ed_init() { uint8_t ttf_buffer[1<<20]; - FILE *ttf_file = fopen("../c_editor/JetBrainsMono-Medium.ttf", "rb"); + // TODO: grab default font from the system + FILE *ttf_file = fopen("./bin/JetBrainsMono-Medium.ttf", "rb"); if (!ttf_file) { exit(1); } - fread(ttf_buffer, 1, 1<<20, ttf_file); + assert(fread(ttf_buffer, 1, 1<<20, ttf_file)); fclose(ttf_file); stbtt_fontinfo font; @@ -163,10 +173,11 @@ void ed_init() { sg_setup(&(sg_desc) { .environment = sglue_environment(), + .logger.func = slog_func, }); float vertices[] = { - // positions colors + // positions texture coords -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, @@ -188,12 +199,24 @@ void ed_init() { char fs_source[8000] = { 0 }; sfetch_handle_t vs_handle = sfetch_send(&(sfetch_request_t) { +#if defined (__APPLE__) .path = "./bin/transpiled_shaders/vertex.metal", +#elif defined (__linux__) || defined (__unix__) + .path = "./shaders/vertex.vert", +#else +#error "Unsupported platform for shaders" +#endif .callback = vertex_shader_loaded, .buffer = { .ptr = vs_source, .size = sizeof(vs_source) }, }); sfetch_handle_t fs_handle = sfetch_send(&(sfetch_request_t) { +#if defined (__APPLE__) .path = "./bin/transpiled_shaders/fragment.metal", +#elif defined (__linux__) || defined (__unix__) + .path = "./shaders/fragment.frag", +#else +#error "Unsupported platform for shaders" +#endif .callback = fragment_shader_loaded, .buffer = { .ptr = fs_source, .size = sizeof(fs_source) }, }); @@ -221,12 +244,12 @@ void ed_init() { } } // manually add glyph for SPACE - push_u8array(&state.glyph_cache, &(GpuGlyph){ + push_u8array(&state.glyph_cache, (uint8_t *)(&(GpuGlyph){ .atlas_position = { 0 }, .size = { rasterized_font_height/4, rasterized_font_height }, .position = { 0 }, .y_offset = -rasterized_font_height, - }, 1 * sizeof(GpuGlyph)); + }), 1 * sizeof(GpuGlyph)); int x = rasterized_font_height/4; int y = 0; @@ -239,10 +262,10 @@ void ed_init() { y += (int)((float)(ascent - descent + line_gap)*scale); } - int xxx = x; - for (size_t xx=0; xx < width; ++xx) { - int yyy = y; - for (size_t yy=0; yy < height; ++yy) { + size_t xxx = x; + for (size_t xx=0; xx < (size_t)width; ++xx) { + size_t yyy = y; + for (size_t yy=0; yy < (size_t)height; ++yy) { font_bitmap[xxx + yyy * font_bitmap_size] = bitmap[xx + yy * width]; yyy += 1; @@ -251,12 +274,12 @@ void ed_init() { xxx += 1; } - push_u8array(&state.glyph_cache, &(GpuGlyph){ + push_u8array(&state.glyph_cache, (uint8_t *)(&(GpuGlyph){ .atlas_position = { (float)(x), (float)(y) }, .size = { (float)width, (float)height }, .position = { (float)x, (float)y }, .y_offset = (float)(yoff), - }, 1 * sizeof(GpuGlyph)); + }), 1 * sizeof(GpuGlyph)); x += width; } @@ -304,6 +327,28 @@ void ed_init() { queue_text(_String("But what even is text! []!@#$%^&*()_=+"), (float[]){ 0, 0 }); queue_text(_String("v0.1.0"), (float[]){ 32, 128 }); queue_text(_String("an_editor - what even"), (float[]){ 32, 256 }); + + state.ui_cx = init_ui_context(); + + string label = _String("Number 1"); + ht_set(&state.ui_cx.cached_elements, label, &(ui_element_cache_data) { + .label = label, + .size = { + .axis = UI_AXIS_HORIZONTAL, + .computed_size = { 200, 256 }, + } + }); + + for (size_t i = 0; i < state.ui_cx.cached_elements.capacity; ++i) { + if (state.ui_cx.cached_elements.key_slots[i].key.data != NULL) { + string text = state.ui_cx.cached_elements.key_slots[i].key; + + ui_element_cache_data *value = ht_get(&state.ui_cx.cached_elements, text); + if (value) { + queue_text(text, (float[]){ (float)value->size.computed_size[0], (float)value->size.computed_size[1] }); + } + } + } } void ed_frame() { if (state.gpu_glyphs.size > 0) { @@ -315,8 +360,10 @@ void ed_frame() { GpuUniformParams gpu_uniform_params = { .screen_size = { - sapp_width(), - sapp_height() + sapp_widthf(), + sapp_heightf(), + 0, + 0 }, }; diff --git a/src/string.h b/src/string.h index 6571cbd..f9186d3 100644 --- a/src/string.h +++ b/src/string.h @@ -2,12 +2,25 @@ #define ED_STRING_INCLUDED #include +#include -#define _String(text) ((string) { .data = text, .len = sizeof(text) }) +#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text) }) typedef struct { - char *data; + uint8_t *data; size_t len; } string; -#endif +#ifdef ED_STRING_IMPLEMENTATION +bool string_eq(string a, string b) { + if (a.len != b.len) return false; + + for (size_t i=0; i #include #include -#include "string.h" + +#include "ht.h" typedef enum { UI_AXIS_HORIZONTAL, @@ -72,10 +73,19 @@ typedef struct { } ui_element_frame_data; typedef struct { - ed_ht *cached_elements; + ed_ht cached_elements; } ui_context; -#endif #ifdef ED_UI_IMPLEMENTATION + +ui_context init_ui_context() { + ed_ht cached_elements = ht_create(32, sizeof(ui_element_cache_data)); + + return (ui_context) { + .cached_elements = cached_elements, + }; +} + +#endif #endif