This commit is contained in:
Krzosa Karol
2025-11-22 15:37:59 +01:00
parent cd8d166c0c
commit 2e087824a3
4 changed files with 728 additions and 716 deletions

View File

@@ -2,7 +2,6 @@
for %%a in (%*) do set "%%~a=1" for %%a in (%*) do set "%%~a=1"
set common=-I ../src/ -g -fdiagnostics-absolute-paths -Wno-unsequenced -Wno-single-bit-bitfield-constant-conversion -Wall -Wno-missing-braces -Wextra -Wno-missing-field-initializers set common=-I ../src/ -g -fdiagnostics-absolute-paths -Wno-unsequenced -Wno-single-bit-bitfield-constant-conversion -Wall -Wno-missing-braces -Wextra -Wno-missing-field-initializers
set wasm_flags=--target=wasm32 -nostdlib -mbulk-memory -msimd128 -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296 set wasm_flags=--target=wasm32 -nostdlib -mbulk-memory -msimd128 -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296

View File

@@ -1,470 +1,470 @@
#include "core_platform.h" #include "core_platform.h"
#include "core_ctx.h" #include "core_ctx.h"
#include "stb_sprintf.h"
#if PLATFORM_WASM && PLATFORM_EMSCRIPTEN == 0
#define gb_wasm_export __attribute__((visibility("default"))) #if PLATFORM_WASM && PLATFORM_EMSCRIPTEN == 0
#define fn_wasm_export __attribute__((visibility("default"))) #define gb_wasm_export __attribute__((visibility("default")))
#define fn_wasm_import #define fn_wasm_export __attribute__((visibility("default")))
#define fn_wasm_import
fn_wasm_import void wasm_alert(isize ptr, i32 len);
fn_wasm_import f64 wasm_parse_float(isize str, i32 len); fn_wasm_import void wasm_alert(isize ptr, i32 len);
fn_wasm_import void wasm_write_to_console(isize str, i32 len); fn_wasm_import f64 wasm_parse_float(isize str, i32 len);
fn_wasm_import void wasm_write_to_console(isize str, i32 len);
extern char __heap_base;
extern char __heap_base;
fn void *os_vmem_reserve(usize size) {
unused(size); fn void *os_vmem_reserve(usize size) {
return NULL; unused(size);
} return NULL;
}
fn b32 os_vmem_commit(void *p, usize size) {
unused(p); fn b32 os_vmem_commit(void *p, usize size) {
unused(size); unused(p);
return false; unused(size);
} return false;
}
fn b32 os_vmem_release(void *p) {
unused(p); fn b32 os_vmem_release(void *p) {
return true; unused(p);
} return true;
}
fn b32 os_vmem_decommit(void *p, usize size) {
unused(p); fn b32 os_vmem_decommit(void *p, usize size) {
unused(size); unused(p);
return true; unused(size);
} return true;
}
fn void os_error_box(char *str) {
wasm_alert((isize)str, str_len(str)); fn void os_error_box(char *str) {
} wasm_alert((isize)str, str_len(str));
}
fn void os_console_log(char *str) {
s8_t string = s8_from_char(str); fn void os_console_log(char *str) {
if (s8_ends_with(string, s8("\n"))) { s8_t string = s8_from_char(str);
string = s8_chop(string, 1); if (s8_ends_with(string, s8("\n"))) {
} string = s8_chop(string, 1);
wasm_write_to_console((isize)string.str, (i32)string.len); }
} wasm_write_to_console((isize)string.str, (i32)string.len);
}
fn f64 os_parse_float(char *str) {
return wasm_parse_float((isize)str, str_len((char *)str)); fn f64 os_parse_float(char *str) {
} return wasm_parse_float((isize)str, str_len((char *)str));
}
fn void os_core_small_init(ma_arena_t *arena) {
tcx = ma_push_type(arena, thread_ctx_t); fn void os_core_small_init(ma_arena_t *arena) {
tcx->log.break_on_error = true; tcx = ma_push_type(arena, thread_ctx_t);
tcx->log.break_on_fatal = true; tcx->log.break_on_error = true;
tcx->log.break_on_warning = true; tcx->log.break_on_fatal = true;
tcx->log.print_file_and_line = true; tcx->log.break_on_warning = true;
tcx->log.log_proc = default_log_proc; tcx->log.print_file_and_line = true;
} tcx->log.log_proc = default_log_proc;
}
fn void os_core_init(void) {
ma_arena_t perm = {0}; fn void os_core_init(void) {
isize page_size = kib(64); ma_arena_t perm = {0};
isize page_count = __builtin_wasm_memory_size(0); isize page_size = kib(64);
u8 *memory = (u8 *)&__heap_base; isize page_count = __builtin_wasm_memory_size(0);
usize memory_size = page_count * (page_size) - (isize)memory; u8 *memory = (u8 *)&__heap_base;
perm.data = memory; usize memory_size = page_count * (page_size) - (isize)memory;
perm.commit = perm.reserve = memory_size; perm.data = memory;
os_core_small_init(&perm); perm.commit = perm.reserve = memory_size;
tcx->perm = perm; os_core_small_init(&perm);
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(16)); tcx->perm = perm;
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2)); ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(64));
ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256)); ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2));
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64)); ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256));
debugf("memory size = %lld", memory_size); ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
} debugf("memory size = %lld", memory_size);
#elif PLATFORM_WINDOWS }
#pragma comment(lib, "user32.lib") #elif PLATFORM_WINDOWS
#pragma comment(lib, "DbgHelp.lib") #pragma comment(lib, "user32.lib")
#pragma comment(lib, "Shell32.lib") #pragma comment(lib, "DbgHelp.lib")
#pragma comment(lib, "Ole32.lib") #pragma comment(lib, "Shell32.lib")
#define NOMINMAX #pragma comment(lib, "Ole32.lib")
#define WIN32_LEAN_AND_MEAN #define NOMINMAX
#include <windows.h> #define WIN32_LEAN_AND_MEAN
#include <DbgHelp.h> #include <windows.h>
#include <TlHelp32.h> #include <DbgHelp.h>
#include <Shlobj.h> #include <TlHelp32.h>
#include <math.h> #include <Shlobj.h>
#include <string.h> #include <math.h>
#include <stdio.h> #include <string.h>
#include <stdlib.h> #include <stdio.h>
#include <signal.h> #include <stdlib.h>
#include <signal.h>
typedef struct b_stacktrace_tag* b_stacktrace_handle;
typedef struct b_stacktrace_tag* b_stacktrace_handle;
/*
Returns a stack-trace handle from the point of view of the caller which /*
can be expanded to a string via b_stacktrace_to_string. Returns a stack-trace handle from the point of view of the caller which
The handle is allocated with `malloc` and needs to be freed with `free` can be expanded to a string via b_stacktrace_to_string.
*/ The handle is allocated with `malloc` and needs to be freed with `free`
fn b_stacktrace_handle b_stacktrace_get(); */
fn b_stacktrace_handle b_stacktrace_get();
/*
Converts a stack-trace handle to a human-readable string. /*
The string is allocated with `malloc` and needs to be freed with `free` Converts a stack-trace handle to a human-readable string.
*/ The string is allocated with `malloc` and needs to be freed with `free`
fn char* b_stacktrace_to_string(b_stacktrace_handle stacktrace); */
fn char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
/*
Returns a human-readable stack-trace string from the point of view of the /*
caller. Returns a human-readable stack-trace string from the point of view of the
The string is allocated with `malloc` and needs to be freed with `free` caller.
*/ The string is allocated with `malloc` and needs to be freed with `free`
fn char* b_stacktrace_get_string(void); */
fn char* b_stacktrace_get_string(void);
/* version */
/* version */
typedef struct print_buf {
char* buf; typedef struct print_buf {
int pos; char* buf;
int size; int pos;
} print_buf; int size;
} print_buf;
static print_buf buf_init(void) {
print_buf ret = { static print_buf buf_init(void) {
(char*) malloc(1024), print_buf ret = {
0, (char*) malloc(1024),
1024 0,
}; 1024
return ret; };
} return ret;
}
static void buf_printf(print_buf* b, const char* fmt, ...) {
va_list args; static void buf_printf(print_buf* b, const char* fmt, ...) {
va_start(args, fmt); va_list args;
const int len = vsnprintf(NULL, 0, fmt, args) + 1; va_start(args, fmt);
va_end(args); const int len = stbsp_vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args);
const int new_end = b->pos + len;
const int new_end = b->pos + len;
if (new_end > b->size) {
while (new_end > b->size) b->size *= 2; if (new_end > b->size) {
b->buf = (char*)realloc(b->buf, b->size); while (new_end > b->size) b->size *= 2;
} b->buf = (char*)realloc(b->buf, b->size);
}
va_start(args, fmt);
b->pos += vsnprintf(b->buf + b->pos, len, fmt, args); va_start(args, fmt);
va_end(args); b->pos += stbsp_vsnprintf(b->buf + b->pos, len, fmt, args);
} va_end(args);
}
char* b_stacktrace_get_string(void) {
b_stacktrace_handle h = b_stacktrace_get(); char* b_stacktrace_get_string(void) {
char* ret = b_stacktrace_to_string(h); b_stacktrace_handle h = b_stacktrace_get();
free(h); char* ret = b_stacktrace_to_string(h);
return ret; free(h);
} return ret;
}
#define B_STACKTRACE_MAX_DEPTH 1024
#define B_STACKTRACE_MAX_DEPTH 1024
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63) #define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
typedef struct b_stacktrace_entry { typedef struct b_stacktrace_entry {
DWORD64 AddrPC_Offset; DWORD64 AddrPC_Offset;
DWORD64 AddrReturn_Offset; DWORD64 AddrReturn_Offset;
} b_stacktrace_entry; } b_stacktrace_entry;
static int SymInitialize_called = 0; static int SymInitialize_called = 0;
b_stacktrace_handle b_stacktrace_get(void) { b_stacktrace_handle b_stacktrace_get(void) {
HANDLE process = GetCurrentProcess(); HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread(); HANDLE thread = GetCurrentThread();
CONTEXT context; CONTEXT context;
STACKFRAME64 frame; /* in/out stackframe */ STACKFRAME64 frame; /* in/out stackframe */
DWORD imageType; DWORD imageType;
b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry)); b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry));
int i = 0; int i = 0;
if (!SymInitialize_called) { if (!SymInitialize_called) {
SymInitialize(process, NULL, TRUE); SymInitialize(process, NULL, TRUE);
SymInitialize_called = 1; SymInitialize_called = 1;
} }
RtlCaptureContext(&context); RtlCaptureContext(&context);
memset(&frame, 0, sizeof(frame)); memset(&frame, 0, sizeof(frame));
#ifdef _M_IX86 #ifdef _M_IX86
imageType = IMAGE_FILE_MACHINE_I386; imageType = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip; frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat; frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp; frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp; frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64 #elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64; imageType = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip; frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat; frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp; frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp; frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64 #elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64; imageType = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP; frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat; frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp; frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP; frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat; frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp; frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat;
#else #else
#error "Platform not supported!" #error "Platform not supported!"
#endif #endif
while (1) { while (1) {
b_stacktrace_entry* cur = ret + i++; b_stacktrace_entry* cur = ret + i++;
if (i == B_STACKTRACE_MAX_DEPTH) { if (i == B_STACKTRACE_MAX_DEPTH) {
cur->AddrPC_Offset = 0; cur->AddrPC_Offset = 0;
cur->AddrReturn_Offset = 0; cur->AddrReturn_Offset = 0;
break; break;
} }
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
cur->AddrPC_Offset = frame.AddrPC.Offset; cur->AddrPC_Offset = frame.AddrPC.Offset;
cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */ cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */
cur->AddrReturn_Offset |= GetLastError(); cur->AddrReturn_Offset |= GetLastError();
break; break;
} }
cur->AddrPC_Offset = frame.AddrPC.Offset; cur->AddrPC_Offset = frame.AddrPC.Offset;
cur->AddrReturn_Offset = frame.AddrReturn.Offset; cur->AddrReturn_Offset = frame.AddrReturn.Offset;
if (frame.AddrReturn.Offset == 0) { if (frame.AddrReturn.Offset == 0) {
break; break;
} }
} }
return (b_stacktrace_handle)(ret); return (b_stacktrace_handle)(ret);
} }
char* b_stacktrace_to_string(b_stacktrace_handle h) { char* b_stacktrace_to_string(b_stacktrace_handle h) {
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h; const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
int i = 0; int i = 0;
HANDLE process = GetCurrentProcess(); HANDLE process = GetCurrentProcess();
print_buf out = buf_init(); print_buf out = buf_init();
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024); IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024);
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = 1024; symbol->MaxNameLength = 1024;
while (1) { while (1) {
IMAGEHLP_LINE64 lineData; IMAGEHLP_LINE64 lineData;
DWORD lineOffset = 0; DWORD lineOffset = 0;
DWORD64 symOffset = 0; DWORD64 symOffset = 0;
const b_stacktrace_entry* cur = entries + i++; const b_stacktrace_entry* cur = entries + i++;
if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) { if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) {
DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF; DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF;
buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset); buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset);
break; break;
} }
if (cur->AddrPC_Offset == cur->AddrReturn_Offset) { if (cur->AddrPC_Offset == cur->AddrReturn_Offset) {
buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset); buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset);
break; break;
} }
SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData); SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData);
buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber); buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber);
if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) { if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) {
buf_printf(&out, "%s\n", symbol->Name); buf_printf(&out, "%s\n", symbol->Name);
} }
else { else {
buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset); buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset);
} }
if (cur->AddrReturn_Offset == 0) { if (cur->AddrReturn_Offset == 0) {
break; break;
} }
} }
free(symbol); free(symbol);
return out.buf; return out.buf;
} }
fn void *os_vmem_reserve(usize size) { fn void *os_vmem_reserve(usize size) {
void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
return result; return result;
} }
fn b32 os_vmem_commit(void *p, usize size) { fn b32 os_vmem_commit(void *p, usize size) {
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE); void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
return result ? true : false; return result ? true : false;
} }
fn b32 os_vmem_release(void *p) { fn b32 os_vmem_release(void *p) {
BOOL result = VirtualFree(p, 0, MEM_RELEASE); BOOL result = VirtualFree(p, 0, MEM_RELEASE);
return result ? true : false; return result ? true : false;
} }
fn b32 os_vmem_decommit(void *p, usize size) { fn b32 os_vmem_decommit(void *p, usize size) {
BOOL result = VirtualFree(p, size, MEM_DECOMMIT); BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
return result ? true : false; return result ? true : false;
} }
fn void os_error_box(char *str) { fn void os_error_box(char *str) {
OutputDebugStringA(str); OutputDebugStringA(str);
fprintf(stderr, "%s", str); fprintf(stderr, "%s", str);
fflush(stderr); fflush(stderr);
MessageBoxA(NULL, str, "fatal error", MB_OK); MessageBoxA(NULL, str, "fatal error", MB_OK);
} }
fn void os_console_log(char *str) { fn void os_console_log(char *str) {
OutputDebugStringA(str); OutputDebugStringA(str);
fputs(str, stdout); fputs(str, stdout);
fflush(stdout); fflush(stdout);
} }
fn f64 os_parse_float(char *str) { fn f64 os_parse_float(char *str) {
return strtod(str, NULL); return strtod(str, NULL);
} }
fn void os_win32_crash_handler(int signal) { fn void os_win32_crash_handler(int signal) {
printf("signal: %d\n", signal); printf("signal: %d\n", signal);
puts(b_stacktrace_get_string()); puts(b_stacktrace_get_string());
exit(1); exit(1);
} }
fn void os_win32_register_crash_handler() { fn void os_win32_register_crash_handler() {
signal(SIGSEGV, os_win32_crash_handler); signal(SIGSEGV, os_win32_crash_handler);
signal(SIGABRT, os_win32_crash_handler); signal(SIGABRT, os_win32_crash_handler);
} }
fn void os_core_small_init(ma_arena_t *arena) { fn void os_core_small_init(ma_arena_t *arena) {
tcx = ma_push_type(arena, thread_ctx_t); tcx = ma_push_type(arena, thread_ctx_t);
tcx->log.break_on_error = true; tcx->log.break_on_error = true;
tcx->log.break_on_fatal = true; tcx->log.break_on_fatal = true;
tcx->log.break_on_warning = true; tcx->log.break_on_warning = true;
tcx->log.print_file_and_line = true; tcx->log.print_file_and_line = true;
tcx->log.log_proc = default_log_proc; tcx->log.log_proc = default_log_proc;
os_win32_register_crash_handler(); os_win32_register_crash_handler();
} }
fn void os_core_init(void) { fn void os_core_init(void) {
ma_arena_t perm = {0}; ma_arena_t perm = {0};
os_core_small_init(&perm); os_core_small_init(&perm);
tcx->perm = perm; tcx->perm = perm;
} }
#elif PLATFORM_POSIX #elif PLATFORM_POSIX
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <signal.h> #include <signal.h>
#include <ucontext.h> #include <ucontext.h>
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h> #include <dlfcn.h>
#if !PLATFORM_EMSCRIPTEN #if !PLATFORM_EMSCRIPTEN
#include <execinfo.h> #include <execinfo.h>
#include <backtrace.h> #include <backtrace.h>
#endif #endif
fn void *os_vmem_reserve(usize size) { fn void *os_vmem_reserve(usize size) {
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
return result == MAP_FAILED ? NULL : result; return result == MAP_FAILED ? NULL : result;
} }
fn b32 os_vmem_commit(void *ptr, usize size) { fn b32 os_vmem_commit(void *ptr, usize size) {
mprotect(ptr, size, PROT_READ|PROT_WRITE); mprotect(ptr, size, PROT_READ|PROT_WRITE);
return true; return true;
} }
fn b32 os_vmem_release(void *ptr) { fn b32 os_vmem_release(void *ptr) {
int result = munmap(ptr, 0); int result = munmap(ptr, 0);
return result == 0 ? true : false; return result == 0 ? true : false;
} }
fn b32 os_vmem_decommit(void *ptr, usize size) { fn b32 os_vmem_decommit(void *ptr, usize size) {
madvise(ptr, size, MADV_DONTNEED); madvise(ptr, size, MADV_DONTNEED);
mprotect(ptr, size, PROT_NONE); mprotect(ptr, size, PROT_NONE);
return true; return true;
} }
fn void os_error_box(char *str) { fn void os_error_box(char *str) {
fprintf(stderr, "%s", str); fprintf(stderr, "%s", str);
fflush(stderr); fflush(stderr);
} }
fn void os_console_log(char *str) { fn void os_console_log(char *str) {
fputs(str, stdout); fputs(str, stdout);
fflush(stdout); fflush(stdout);
} }
fn f64 os_parse_float(char *str) { fn f64 os_parse_float(char *str) {
return strtod(str, NULL); return strtod(str, NULL);
} }
gb struct backtrace_state *backtrace_state = NULL; gb struct backtrace_state *backtrace_state = NULL;
fn void os_core_backtrace_error_callback(void *data, const char *msg, int errnum) { fn void os_core_backtrace_error_callback(void *data, const char *msg, int errnum) {
unused(data); unused(data);
errorf("libbacktrace error: %s (errnum: %d)\n", msg, errnum); errorf("libbacktrace error: %s (errnum: %d)\n", msg, errnum);
} }
fn int os_core_backtrace_print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) { fn int os_core_backtrace_print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
unused(data); unused(data);
unused(pc); unused(pc);
b32 printed = false; b32 printed = false;
if (filename != NULL) { if (filename != NULL) {
char buffer[512]; char buffer[512];
char *f = realpath(filename, buffer); char *f = realpath(filename, buffer);
printf("%s:%d:1: ", f, lineno); printf("%s:%d:1: ", f, lineno);
printed = true; printed = true;
} }
if (function != NULL) { if (function != NULL) {
printf("%s", function); printf("%s", function);
printed = true; printed = true;
} }
if (printed) { if (printed) {
printf("\n"); printf("\n");
} }
return 0; return 0;
} }
#if !PLATFORM_EMSCRIPTEN #if !PLATFORM_EMSCRIPTEN
fn void os_unix_crash_handler(int signal, siginfo_t* info, void* context) { fn void os_unix_crash_handler(int signal, siginfo_t* info, void* context) {
unused(context); unused(context);
unused(signal); unused(signal);
unused(info); unused(info);
backtrace_full(backtrace_state, 2, os_core_backtrace_print_callback, os_core_backtrace_error_callback, NULL); backtrace_full(backtrace_state, 2, os_core_backtrace_print_callback, os_core_backtrace_error_callback, NULL);
exit(1); exit(1);
} }
fn void os_unix_register_crash_handler() { fn void os_unix_register_crash_handler() {
backtrace_state = backtrace_create_state(NULL, 1, os_core_backtrace_error_callback, NULL); backtrace_state = backtrace_create_state(NULL, 1, os_core_backtrace_error_callback, NULL);
struct sigaction sa; struct sigaction sa;
sa.sa_sigaction = os_unix_crash_handler; sa.sa_sigaction = os_unix_crash_handler;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL); sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGABRT, &sa, NULL); sigaction(SIGABRT, &sa, NULL);
sigaction(SIGBUS, &sa, NULL); sigaction(SIGBUS, &sa, NULL);
sigaction(SIGILL, &sa, NULL); sigaction(SIGILL, &sa, NULL);
sigaction(SIGFPE, &sa, NULL); sigaction(SIGFPE, &sa, NULL);
} }
#endif #endif
fn void os_core_small_init(ma_arena_t *arena) { fn void os_core_small_init(ma_arena_t *arena) {
tcx = ma_push_type(arena, thread_ctx_t); tcx = ma_push_type(arena, thread_ctx_t);
tcx->log.break_on_error = true; tcx->log.break_on_error = true;
tcx->log.break_on_fatal = true; tcx->log.break_on_fatal = true;
tcx->log.break_on_warning = true; tcx->log.break_on_warning = true;
tcx->log.print_file_and_line = true; tcx->log.print_file_and_line = true;
tcx->log.log_proc = default_log_proc; tcx->log.log_proc = default_log_proc;
#if !PLATFORM_EMSCRIPTEN #if !PLATFORM_EMSCRIPTEN
os_unix_register_crash_handler(); os_unix_register_crash_handler();
#endif #endif
} }
fn void os_core_init(void) { fn void os_core_init(void) {
ma_arena_t perm = {0}; ma_arena_t perm = {0};
os_core_small_init(&perm); os_core_small_init(&perm);
tcx->perm = perm; tcx->perm = perm;
} }
#endif #endif

View File

@@ -4,57 +4,138 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Canvas</title> <title>Canvas</title>
<style> <style>
html,body{margin:0;height:100%;overflow:hidden;padding:0;font-size: 100px;font-family: FiraCode;} html,body{margin:0;height:100%;overflow:hidden;padding:0;}
canvas{display:block;width:100%;height:250px;border-bottom: dotted;} canvas{display:block;width:100%;height:100%;}
@font-face {
font-family: 'FiraCode';
src: url('./FiraCode-Regular.ttf');
font-weight: normal;
font-style: normal;
font-display: swap;
}
div{
position:relative;
display: block;
width: 100%;
text-align: center;
border-bottom: dotted;
}
</style> </style>
</head> </head>
<body> <body>
<canvas id="glcanvas"></canvas> <canvas id="glcanvas"></canvas>
<div>
<p>Hello world (html)</p>
</div>
<canvas id="2dcanvas"></canvas>
<script> <script>
function createShader(gl, type, src) {
const sh = gl.createShader(type);
gl.shaderSource(sh, src);
gl.compileShader(sh);
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
console.error('Shader compile error:', gl.getShaderInfoLog(sh));
gl.deleteShader(sh);
return null;
}
return sh;
}
function createProgram(gl, vs, fs) { CANVAS = null;
const prg = gl.createProgram(); GL = null;
gl.attachShader(prg, vs); class wglRender {
gl.attachShader(prg, fs); constructor() {
gl.linkProgram(prg); const VERTEX_SRC = `#version 300 es
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) { precision mediump float;
console.error('Program link error:', gl.getProgramInfoLog(prg)); in vec2 a_position;
gl.deleteProgram(prg); out vec2 v_texCoord;
return null; void main() {
v_texCoord = (a_position + 1.0) * 0.5; // [0,1]
v_texCoord.y = (1.0 - v_texCoord.y); // Flip the Y to get the standard memory behavior
gl_Position = vec4(a_position, 0.0, 1.0);
}`;
const FRAGMENT_SRC = `#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 outColor;
void main() {
outColor = texture(u_texture, v_texCoord);
}`;
CANVAS = document.getElementById('glcanvas');
GL = CANVAS.getContext('webgl2');
if (!GL) { alert('WebGL 2 not supported'); return; }
const vs = this.createShader(GL.VERTEX_SHADER, VERTEX_SRC);
const fs = this.createShader(GL.FRAGMENT_SHADER, FRAGMENT_SRC);
this.program = this.createProgram(vs, fs);
this.posLoc = GL.getAttribLocation(this.program, 'a_position');
this.texLoc = GL.getUniformLocation(this.program, 'u_texture');
// Fullscreen quad
const quad = new Float32Array([
-1, -1, // bottom left
1, -1, // bottom right
-1, 1, // top left
-1, 1, // top left
1, -1, // bottom right
1, 1 // top right
]);
this.vao = GL.createVertexArray();
GL.bindVertexArray(this.vao);
this.vbo = GL.createBuffer();
GL.bindBuffer(GL.ARRAY_BUFFER, this.vbo);
GL.bufferData(GL.ARRAY_BUFFER, quad, GL.STATIC_DRAW);
GL.enableVertexAttribArray(this.posLoc);
GL.vertexAttribPointer(this.posLoc, 2, GL.FLOAT, false, 0, 0);
}
render() {
GL.useProgram(this.program);
GL.bindVertexArray(this.vao);
GL.activeTexture(GL.TEXTURE0);
GL.bindTexture(GL.TEXTURE_2D, this.texture);
GL.uniform1i(this.texLoc, 0);
GL.drawArrays(GL.TRIANGLES, 0, 6);
}
updateTextureSize(wasm) {
const dpr = window.devicePixelRatio || 1;
const w = Math.floor(CANVAS.clientWidth * dpr);
const h = Math.floor(CANVAS.clientHeight * dpr);
if (w !== this.w || h !== this.h) {
CANVAS.width = w;
CANVAS.height = h;
GL.viewport(0, 0, w, h);
this.w = w;
this.h = h;
if (this.texture) {
GL.deleteTexture(this.texture);
}
const rawPtr = wasm.exports.update(w, h, window.devicePixelRatio || 1.0);
const pixels = wasm.getU8View(rawPtr, w*h*4);
{
this.texture = GL.createTexture();
GL.bindTexture(GL.TEXTURE_2D, this.texture);
// Allocate an empty texture of the canvas size
GL.texImage2D(
GL.TEXTURE_2D, 0, GL.RGBA8,
w, h, 0,
GL.RGBA, GL.UNSIGNED_BYTE, pixels
);
// Set parameters no filtering needed for empty data
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
}
// GL.activeTexture(GL.TEXTURE0);
// GL.bindTexture(GL.TEXTURE_2D, this.texture);
// GL.texSubImage2D(GL.TEXTURE_2D, 0, 0, 0, w, h, GL.RGBA, GL.UNSIGNED_BYTE,pixels);
}
}
createShader(type, src) {
const sh = GL.createShader(type);
GL.shaderSource(sh, src);
GL.compileShader(sh);
if (!GL.getShaderParameter(sh, GL.COMPILE_STATUS)) {
console.error('Shader compile error:', GL.getShaderInfoLog(sh));
GL.deleteShader(sh);
return null;
}
return sh;
}
createProgram(vs, fs) {
const prg = GL.createProgram();
GL.attachShader(prg, vs);
GL.attachShader(prg, fs);
GL.linkProgram(prg);
if (!GL.getProgramParameter(prg, GL.LINK_STATUS)) {
console.error('Program link error:', GL.getProgramInfoLog(prg));
GL.deleteProgram(prg);
return null;
}
return prg;
} }
return prg;
} }
class WASMMemory { class WASMMemory {
@@ -67,18 +148,23 @@ class WASMMemory {
this.exports = null; this.exports = null;
} }
readString(str, len) { decodeString(str, len) {
const arr = this.u8.subarray(str, str+len); const arr = this.u8.subarray(str, str+len);
const text = this.utf8decoder.decode(arr); const text = this.utf8decoder.decode(arr);
return text; return text;
} }
readUint8(p, len) { getU8View(p, len) {
const arr = this.u8.subarray(p, p+len); const arr = this.u8.subarray(p, p+len);
return arr; return arr;
} }
writeString(ptr, ptr_len, string) { getView(p, len) {
const res = new DataView(this.mem.buffer, p, p+len);
return res;
}
encodeString(ptr, ptr_len, string) {
const bytes = this.utf8encoder.encode(string); const bytes = this.utf8encoder.encode(string);
let i = 0; let i = 0;
for (; i < bytes.length && i < (ptr_len-1); i += 1) { for (; i < bytes.length && i < (ptr_len-1); i += 1) {
@@ -94,10 +180,10 @@ async function WASMInit(name) {
const binary = await request.arrayBuffer(); const binary = await request.arrayBuffer();
const import_table = { const import_table = {
memory: mem.mem, memory: mem.mem,
wasm_parse_float: (str, len) => { return parseFloat(mem.readString(str, len)); }, wasm_parse_float: (str, len) => { return parseFloat(mem.decodeString(str, len)); },
wasm_alert: (str, len) => { alert(mem.readString(str,len)); }, wasm_alert: (str, len) => { alert(mem.decodeString(str,len)); },
wasm_trap: () => { throw new Error(); }, wasm_trap: () => { throw new Error(); },
wasm_write_to_console: (str, len) => { console.log(mem.readString(str, len)); }, wasm_write_to_console: (str, len) => { console.log(mem.decodeString(str, len)); },
}; };
const program = await WebAssembly['instantiate'](binary, { "env": import_table }); const program = await WebAssembly['instantiate'](binary, { "env": import_table });
const instance = program['instance']; const instance = program['instance'];
@@ -107,146 +193,17 @@ async function WASMInit(name) {
} }
(async () => { (async () => {
const render = new wglRender();
const wasm = await WASMInit("main.wasm"); const wasm = await WASMInit("main.wasm");
wasm.exports.init(); wasm.exports.init();
function frameLoop() {
const VERTEX_SRC = `#version 300 es render.updateTextureSize(wasm);
precision mediump float; render.render();
in vec2 a_position; requestAnimationFrame(frameLoop);
out vec2 v_texCoord;
void main() {
v_texCoord = (a_position + 1.0) * 0.5; // [0,1]
v_texCoord.y = (1.0 - v_texCoord.y); // Flip the Y to get the standard memory behavior
gl_Position = vec4(a_position, 0.0, 1.0);
}`;
const FRAGMENT_SRC = `#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 outColor;
void main() {
outColor = texture(u_texture, v_texCoord);
}`;
/* ---------- WebGL setup ---------- */
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl2');
if (!gl) { alert('WebGL 2 not supported'); return; }
const vs = createShader(gl, gl.VERTEX_SHADER, VERTEX_SRC);
const fs = createShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SRC);
const program = createProgram(gl, vs, fs);
const posLoc = gl.getAttribLocation(program, 'a_position');
const texLoc = gl.getUniformLocation(program, 'u_texture');
// Fullscreen quad
const quad = new Float32Array([
-1, -1, // bottom left
1, -1, // bottom right
-1, 1, // top left
-1, 1, // top left
1, -1, // bottom right
1, 1 // top right
]);
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
/* ---------- Dynamic texture handling ---------- */
let texture = null;
let texWidth = 0, texHeight = 0;
let pixels = null;
function createTexture(w, h) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Allocate an empty texture of the canvas size
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA8,
w, h, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null
);
// Set parameters no filtering needed for empty data
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return tex;
} }
function updateTextureSize() { requestAnimationFrame(frameLoop);
const dpr = window.devicePixelRatio || 1;
const w = Math.floor(canvas.clientWidth * dpr);
const h = Math.floor(canvas.clientHeight * dpr);
if (w !== texWidth || h !== texHeight) {
texWidth = w; texHeight = h;
if (texture) gl.deleteTexture(texture);
texture = createTexture(w, h);
let rawPtr = wasm.exports.update(texWidth, texHeight, window.devicePixelRatio || 1.0);
pixels = wasm.readUint8(rawPtr, texWidth*texHeight*4);
{
const canvas = document.getElementById('2dcanvas');
const dpr = window.devicePixelRatio || 1;
const w = Math.floor(canvas.clientWidth * dpr);
const h = Math.floor(canvas.clientHeight * dpr);
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
let size = 100*dpr;
ctx.font = `${size}px FiraCode`;
const text = 'Hello world (canvas)';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
ctx.fillStyle = '#000';
ctx.fillText(text, centerX, centerY);
}
}
}
/* ---------- Canvas resize routine ---------- */
function resizeCanvasToDisplaySize() {
const dpr = window.devicePixelRatio || 1;
const w = Math.floor(canvas.clientWidth * dpr);
const h = Math.floor(canvas.clientHeight * dpr);
if (canvas.width !== w || canvas.height !== h) {
canvas.width = w;
canvas.height = h;
gl.viewport(0, 0, w, h);
}
}
/* ---------- Render loop ---------- */
function render() {
resizeCanvasToDisplaySize();
updateTextureSize();
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, texWidth, texHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
gl.uniform1i(texLoc, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
})(); })();

View File

@@ -2,8 +2,8 @@
#include "core/core.c" #include "core/core.c"
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_ifloor(x) ((int)f64_floor(x)) #define STBTT_ifloor(x) ((i32)f64_floor(x))
#define STBTT_iceil(x) ((int)f64_ceil(x)) #define STBTT_iceil(x) ((i32)f64_ceil(x))
#define STBTT_sqrt(x) (f64_sqrt(x)) #define STBTT_sqrt(x) (f64_sqrt(x))
#define STBTT_fmod(x,y) (f64_mod(x,y)) #define STBTT_fmod(x,y) (f64_mod(x,y))
#define STBTT_pow(x,y) (f64_pow(x,y)) #define STBTT_pow(x,y) (f64_pow(x,y))
@@ -20,32 +20,123 @@
#include "vendor/stb/stb_truetype.h" #include "vendor/stb/stb_truetype.h"
#include "fira_code.c" #include "fira_code.c"
typedef struct glyph_t glyph_t;
struct glyph_t {
u8 *data;
i32 width, height;
i32 xoff, yoff;
i32 left_side_bearing, xadvance;
};
typedef struct font_t font_t;
struct font_t {
glyph_t glyphs[96];
i32 ascent, descent, line_gap, size;
f32 scale;
};
typedef struct bitmap_t bitmap_t; typedef struct bitmap_t bitmap_t;
struct bitmap_t { struct bitmap_t {
u32 *data; u32 *data;
i32 x, y; i32 x, y;
}; };
typedef struct glyph_t glyph_t;
struct glyph_t {
bitmap_t bitmap;
i32 xoff, yoff;
i32 left_side_bearing, xadvance;
};
typedef struct font_t font_t;
struct font_t {
glyph_t glyphs[256];
bitmap_t atlas;
i32 ascent, descent, line_gap, size;
f32 scale;
i32 height;
};
fn font_t create_font(i32 size) {
font_t font = {0};
font.size = size;
stbtt_fontinfo stb_font;
i32 rc = stbtt_InitFont(&stb_font, main_font_data, 0);
assert_expr(rc != 0);
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
font.height = f32_round((font.ascent - font.descent) + font.line_gap) * font.scale;
for (i32 c = 0; c < 256; c += 1) {
glyph_t *glyph = font.glyphs + (c - ' ');
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->bitmap.x, &glyph->bitmap.y, &glyph->xoff, &glyph->yoff);
glyph->bitmap.data = ma_push_array(&tcx->temp, u32, glyph->bitmap.x * glyph->bitmap.y);
for (i32 y = 0; y < glyph->bitmap.y; y += 1) {
for (i32 x = 0; x < glyph->bitmap.x; x += 1) {
glyph->bitmap.data[x + y * glyph->bitmap.x] = temp_data[x + y * glyph->bitmap.x] << 24;
}
}
stbtt_GetCodepointHMetrics(&stb_font, c, &glyph->xadvance, &glyph->left_side_bearing);
}
return font;
}
fn font_t create_font_atlas(i32 size) {
stbtt_fontinfo stb_font;
i32 rc = stbtt_InitFont(&stb_font, main_font_data, 0);
assert_expr(rc != 0);
font_t font = { 0 };
font.size = size;
font.atlas.x = 512;
font.atlas.y = 512;
font.atlas.data = ma_push_array(&tcx->temp, u32, font.atlas.x * font.atlas.y);
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
i32 xiter = 2;
i32 yiter = 2;
i32 max_yiter_for_row = 0;
for (i32 c = ' '; c < '~'; c += 1) {
glyph_t *glyph = font.glyphs + (c - ' ');
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->bitmap.x, &glyph->bitmap.y, &glyph->xoff, &glyph->yoff);
if (xiter + glyph->bitmap.x >= font.atlas.x) {
xiter = 0;
yiter += max_yiter_for_row + 2;
}
max_yiter_for_row = MAX(max_yiter_for_row, glyph->bitmap.y);
for (i32 y = 0; y < glyph->bitmap.y; y += 1) {
for (i32 x = 0; x < glyph->bitmap.x; x += 1) {
font.atlas.data[(xiter + x) + (yiter + y)*font.atlas.x] = temp_data[x + y * glyph->bitmap.x] << 24;
}
}
xiter += glyph->bitmap.x + 2;
}
return font;
}
fn_wasm_export void init(void) { fn_wasm_export void init(void) {
os_core_init(); os_core_init();
} }
fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, int ix, int iy, b32 draw, char *string) { fn void draw_rect(bitmap_t *canvas, bitmap_t *bitmap, i32 x, i32 y) {
i32 x0 = x;
i32 y0 = y;
i32 x1 = x + bitmap->x;
i32 y1 = y + bitmap->y;
i32 cx0 = CLAMP(x0, 0, canvas->x);
i32 cy0 = CLAMP(y0, 0, canvas->y);
i32 cx1 = CLAMP(x1, 0, canvas->x);
i32 cy1 = CLAMP(y1, 0, canvas->y);
if (cx0 >= cx1 || cy0 >= cy1) {
return;
}
i32 sx_off = cx0 - x0;
i32 sy_off = cy0 - y0;
i32 sy = sy_off;
for (i32 y = cy0; y < cy1; y += 1) {
i32 sx = sx_off;
for (i32 x = cx0; x < cx1; x += 1) {
canvas->data[x + y * canvas->x] = bitmap->data[sx + sy * bitmap->x];
sx += 1;
}
sy += 1;
}
}
fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, i32 ix, i32 iy, b32 draw, char *string) {
i32 xiter = 0; i32 xiter = 0;
v2i32_t R = {0}; v2i32_t R = {0};
for (char *it = string; *it; it += 1) { for (char *it = string; *it; it += 1) {
@@ -53,60 +144,25 @@ fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, int ix, int iy, b32 draw,
i32 curr_xiter = xiter; i32 curr_xiter = xiter;
xiter += g->xadvance * font->scale; xiter += g->xadvance * font->scale;
i32 x0 = curr_xiter + g->xoff + ix;
i32 y0 = g->yoff + font->ascent*font->scale + iy;
i32 x1 = g->width + x0;
i32 y1 = g->height + y0;
i32 cx0 = CLAMP(x0, 0, canvas->x);
i32 cy0 = CLAMP(y0, 0, canvas->y);
i32 cx1 = CLAMP(x1, 0, canvas->x);
i32 cy1 = CLAMP(y1, 0, canvas->y);
R.x = xiter; R.x = xiter;
R.y = MAX(R.y, g->height); R.y = MAX(R.y, g->bitmap.y);
if (!draw || (cx0 >= cx1 || cy0 >= cy1)) { if (!draw) {
continue; continue;
} }
i32 sx_off = cx0 - x0; draw_rect(canvas, &g->bitmap, curr_xiter + g->xoff + ix, g->yoff + font->ascent*font->scale + iy);
i32 sy_off = cy0 - y0;
i32 sy = sy_off;
for (i32 y = cy0; y < cy1; y += 1) {
i32 sx = sx_off;
for (i32 x = cx0; x < cx1; x += 1) {
canvas->data[x + y * canvas->x] = g->data[sx + sy * g->width] << 24;
sx += 1;
}
sy += 1;
}
} }
return R; return R;
} }
fn_wasm_export u32 *update(i32 width, i32 height, f32 dpr) { fn_wasm_export u32 *update(i32 width, i32 height, f32 dpr) {
gb font_t font = {0};
font.size = (i32)(130.f * dpr);
stbtt_fontinfo stb_font;
int rc = stbtt_InitFont(&stb_font, main_font_data, stbtt_GetFontOffsetForIndex(main_font_data,0));
assert_expr(rc != 0);
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
for (int c = ' '; c < '~'; c += 1) {
glyph_t *glyph = font.glyphs + (c - ' ');
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->width, &glyph->height, &glyph->xoff, &glyph->yoff);
glyph->data = ma_push_array_copy(&tcx->perm, temp_data, glyph->width*glyph->height);
stbtt_GetCodepointHMetrics(&stb_font, c, &glyph->xadvance, &glyph->left_side_bearing);
}
ma_set0(&tcx->temp); ma_set0(&tcx->temp);
u32 *pixels = ma_push_array(&tcx->temp, u32, width * height); u32 *pixels = ma_push_array(&tcx->temp, u32, width * height);
bitmap_t canvas = {pixels, width, height}; bitmap_t canvas = {pixels, width, height};
v2i32_t size = draw_string(&canvas, &font, 200, 500, false, "Hello world (stb)"); font_t font = create_font_atlas(60);
draw_string(&canvas, &font, (width-size.x)/2, height - size.y*2, true, "Hello world (stb)"); draw_rect(&canvas, &font.atlas, 0, 0);
//v2i32_t size = draw_string(&canvas, &font, 200, 500, false, "Hello world (stb)");
//draw_string(&canvas, &font, (width-size.x)/2, height - size.y*2, true, "Hello world (stb)");
return pixels; return pixels;
} }