get working on linux (kinda), implement hash table

main
Patrick Cleavelin 2024-03-15 17:56:35 -05:00
parent 112d0bbb8e
commit 11a53fa087
11 changed files with 333 additions and 59 deletions

18
compile_commands.json Normal file
View File

@ -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"
}
]

View File

@ -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" );
};
}
);

View File

@ -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

21
shaders/fragment.frag Normal file
View File

@ -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;
}

49
shaders/vertex.vert Normal file
View File

@ -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;
}

View File

@ -15,23 +15,23 @@ struct VertexOutput {
@location(0) tex_coord: vec2<f32>,
}
struct Params {
screen_size: vec2<f32>,
}
// struct Params {
// screen_size: vec4<f32>,
// }
fn to_device_position(position: vec2<f32>, size: vec2<f32>) -> vec4<f32> {
return vec4<f32>((((position / size) * 2.) - 1.), 1., 1.);
}
@group(0) @binding(1)
var<uniform> params: Params;
@group(0) @binding(0)
var<uniform> screen_size: vec4<f32>;
@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<f32>(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<f32>(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<f32>(1024);
out.position = vertex_pos;

View File

@ -1,2 +1,5 @@
-DED_UI_IMPLEMENTATION
-DED_HT_IMPLEMENTATION
-DED_STRING_IMPLEMENTATION
-I../vendor/
-ObjC

View File

@ -2,21 +2,108 @@
#ifndef ED_HT_INCLUDED
#define ED_HT_INCLUDED
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "string.h"
// see <https://en.wikipedia.org/wiki/FowlerNollVo_hash_function>
#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; i<key.len; ++i) {
hash *= FNV_PRIME;
hash ^= key.data[i];
}
return hash % mod;
}
ed_ht ht_create(size_t max_entries, size_t value_size) {
ed_ht_slot *key_slots = (ed_ht_slot *)malloc(max_entries * sizeof(ed_ht_slot));
void *value_slots = malloc(max_entries * value_size);
memset(key_slots, 0, max_entries * sizeof(ed_ht_slot));
memset(value_slots, 0, max_entries * value_size);
return (ed_ht) {
.key_slots = key_slots,
.value_slots = value_slots,
.value_size = value_size,
.capacity = max_entries,
};
}
bool ht_set(ed_ht *ht, string key, void *value) {
ht_hash_t hash = ht_hash(key, ht->capacity);
for (size_t i=hash; i<ht->capacity; ++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; i<ht->capacity; ++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

View File

@ -1,6 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
// #define ARENA_IMPLEMENTATION
// #include <tsoding/arena.h>
#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 <sokol/sokol_log.h>
#include <sokol/sokol_gfx.h>
#include <sokol/sokol_app.h>
@ -18,21 +26,22 @@
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb/std_truetype.h>
#define ARENA_IMPLEMENTATION
#include <tsoding/arena.h>
#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
},
};

View File

@ -2,12 +2,25 @@
#define ED_STRING_INCLUDED
#include <stddef.h>
#include <stdint.h>
#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<a.len; ++i) {
if (a.data[i] != b.data[i]) return false;
}
return true;
}
#endif
#endif

View File

@ -6,7 +6,8 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#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