orca/src/main.c

582 lines
14 KiB
C
Raw Normal View History

2023-04-12 14:21:03 +00:00
/************************************************************//**
*
* @file: main.c
* @author: Martin Fouilleul
* @date: 11/04/2023
*
*****************************************************************/
#include<stdio.h>
2023-04-14 09:48:36 +00:00
#include<errno.h>
2023-04-12 14:21:03 +00:00
#include<pthread.h>
#include<math.h>
#define MG_INCLUDE_GL_API
#include"milepost.h"
#include"orca_runtime.h"
#include"memory_impl.c"
2023-04-12 14:21:03 +00:00
#define LOG_SUBSYSTEM "Orca"
2023-04-18 12:33:47 +00:00
/*
2023-04-18 08:45:16 +00:00
void orca_log(int len, const char* ptr)
2023-04-12 14:21:03 +00:00
{
log_info("%.*s", len, ptr);
2023-04-18 12:33:47 +00:00
}
*/
void orca_log_entry(log_level level,
int fileLen,
char* file,
int functionLen,
char* function,
int line,
int msgLen,
char* msg)
2023-04-18 12:33:47 +00:00
{
log_entry(level,
str8_from_buffer(fileLen, file),
str8_from_buffer(functionLen, function),
line,
"%.*s\n",
msgLen,
msg);
2023-04-18 12:33:47 +00:00
}
2023-04-14 09:48:36 +00:00
void mg_matrix_push_flat(float a11, float a12, float a13,
float a21, float a22, float a23)
{
mg_mat2x3 m = {a11, a12, a13, a21, a22, a23};
mg_matrix_push(m);
}
2023-04-18 08:45:16 +00:00
int orca_assert(const char* file, const char* function, int line, const char* src, const char* note)
{
2023-04-18 08:45:16 +00:00
mem_arena* scratch = mem_scratch();
str8 msg = str8_pushf(scratch,
"Assertion failed in function %s() in file \"%s\", line %i:\n%s\nNote: %s\n",
function,
file,
line,
src,
note);
const char* msgCStr = str8_to_cstring(scratch, msg);
log_error(msgCStr);
2023-04-18 08:45:16 +00:00
const char* options[] = {"OK"};
mp_alert_popup("Assertion Failed", msgCStr, 1, options);
//TODO: should terminate more gracefully...
exit(-1);
}
mg_font orca_font_create(const char* resourcePath)
2023-04-14 09:48:36 +00:00
{
//NOTE(martin): create default font
str8 fontPath = mp_app_get_resource_path(mem_scratch(), resourcePath);
2023-04-14 09:48:36 +00:00
char* fontPathCString = str8_to_cstring(mem_scratch(), fontPath);
FILE* fontFile = fopen(fontPathCString, "r");
if(!fontFile)
{
log_error("Could not load font file '%s': %s\n", fontPathCString, strerror(errno));
2023-04-14 09:48:36 +00:00
return(mg_font_nil());
}
unsigned char* fontData = 0;
fseek(fontFile, 0, SEEK_END);
u32 fontDataSize = ftell(fontFile);
rewind(fontFile);
fontData = (unsigned char*)malloc(fontDataSize);
fread(fontData, 1, fontDataSize, fontFile);
fclose(fontFile);
unicode_range ranges[5] = {UNICODE_RANGE_BASIC_LATIN,
UNICODE_RANGE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
UNICODE_RANGE_LATIN_EXTENDED_A,
UNICODE_RANGE_LATIN_EXTENDED_B,
UNICODE_RANGE_SPECIALS};
mg_font font = mg_font_create_from_memory(fontDataSize, fontData, 5, ranges);
free(fontData);
return(font);
}
mg_font mg_font_create_default()
{
return(orca_font_create("../resources/OpenSansLatinSubset.ttf"));
}
2023-04-12 14:21:03 +00:00
#include"bindgen_core_api.c"
2023-04-14 09:48:36 +00:00
#include"canvas_api_bind.c"
2023-04-12 14:21:03 +00:00
#include"bindgen_gles_api.c"
#include"manual_gles_api.c"
typedef struct orca_debug_overlay
{
bool show;
mg_surface surface;
mg_canvas canvas;
mg_font font;
ui_context ui;
} orca_debug_overlay;
void debug_overlay_toogle(orca_debug_overlay* overlay)
{
overlay->show = !overlay->show;
mg_surface_set_hidden(overlay->surface, !overlay->show);
}
2023-04-12 14:21:03 +00:00
typedef struct orca_app
{
mp_window window;
mg_surface surface;
mg_canvas canvas;
orca_runtime runtime;
2023-04-12 14:21:03 +00:00
orca_debug_overlay debugOverlay;
} orca_app;
2023-04-12 14:21:03 +00:00
char m3_type_to_tag(M3ValueType type)
{
switch(type)
{
case c_m3Type_none:
return('v');
case c_m3Type_i32:
return('i');
case c_m3Type_i64:
return('I');
case c_m3Type_f32:
return('f');
case c_m3Type_f64:
return('d');
case c_m3Type_unknown:
return('!');
}
}
void orca_runtime_init(orca_runtime* runtime)
2023-04-12 14:21:03 +00:00
{
memset(runtime, 0, sizeof(orca_runtime));
2023-04-12 14:21:03 +00:00
mem_base_allocator* allocator = mem_base_allocator_default();
runtime->wasmMemory.committed = 0;
runtime->wasmMemory.reserved = 4ULL<<30;
runtime->wasmMemory.ptr = mem_base_reserve(allocator, runtime->wasmMemory.reserved);
2023-04-12 14:21:03 +00:00
}
orca_app __orcaApp;
2023-04-12 14:21:03 +00:00
orca_runtime* orca_runtime_get()
2023-04-12 14:21:03 +00:00
{
return(&__orcaApp.runtime);
2023-04-12 14:21:03 +00:00
}
void* orca_runloop(void* user)
{
orca_app* app = &__orcaApp;
orca_runtime_init(&app->runtime);
2023-04-12 14:21:03 +00:00
//NOTE: loads wasm module
const char* bundleNameCString = "module";
str8 modulePath = mp_app_get_resource_path(mem_scratch(), "../wasm/module.wasm");
const char* modulePathCString = str8_to_cstring(mem_scratch(), modulePath);
FILE* file = fopen(modulePathCString, "rb");
if(!file)
{
log_error("Couldn't load wasm module at %s\n", modulePathCString);
2023-04-12 14:21:03 +00:00
return((void*)-1);
}
fseek(file, 0, SEEK_END);
u64 wasmSize = ftell(file);
rewind(file);
app->runtime.wasmBytecode.len = wasmSize;
app->runtime.wasmBytecode.ptr = malloc_array(char, wasmSize);
fread(app->runtime.wasmBytecode.ptr, 1, app->runtime.wasmBytecode.len, file);
2023-04-12 14:21:03 +00:00
fclose(file);
u32 stackSize = 65536;
app->runtime.m3Env = m3_NewEnvironment();
2023-04-12 14:21:03 +00:00
app->runtime.m3Runtime = m3_NewRuntime(app->runtime.m3Env, stackSize, NULL);
m3_RuntimeSetMemoryCallbacks(app->runtime.m3Runtime, wasm_memory_resize_callback, wasm_memory_free_callback, &app->runtime.wasmMemory);
2023-04-12 14:21:03 +00:00
//NOTE: host memory will be freed when runtime is freed.
//TODO check errors
m3_ParseModule(app->runtime.m3Env, &app->runtime.m3Module, (u8*)app->runtime.wasmBytecode.ptr, app->runtime.wasmBytecode.len);
m3_LoadModule(app->runtime.m3Runtime, app->runtime.m3Module);
m3_SetModuleName(app->runtime.m3Module, bundleNameCString);
2023-04-12 14:21:03 +00:00
mem_scratch_clear();
//NOTE: bind orca APIs
bindgen_link_core_api(app->runtime.m3Module);
bindgen_link_canvas_api(app->runtime.m3Module);
bindgen_link_gles_api(app->runtime.m3Module);
manual_link_gles_api(app->runtime.m3Module);
2023-04-12 14:21:03 +00:00
//NOTE: compile
M3Result res = m3_CompileModule(app->runtime.m3Module);
2023-04-12 14:21:03 +00:00
if(res)
{
M3ErrorInfo errInfo = {};
m3_GetErrorInfo(app->runtime.m3Runtime, &errInfo);
2023-04-12 14:21:03 +00:00
log_error("wasm error: %s\n", errInfo.message);
return((void*)-1);
2023-04-12 14:21:03 +00:00
}
//NOTE: Find heap base
u32 heapBase = 0;
{
IM3Global global = m3_FindGlobal(app->runtime.m3Module, "__heap_base");
2023-04-12 14:21:03 +00:00
if(global)
{
M3TaggedValue val;
M3Result res = m3_GetGlobal(global, &val);
if(!res && val.type == c_m3Type_i32)
{
heapBase = val.value.i32;
}
else
{
log_error("couldn't get value of __heap_base\n");
2023-04-12 14:21:03 +00:00
return((void*)-1);
}
}
else
{
log_error("couldn't locate __heap_base\n");
2023-04-12 14:21:03 +00:00
return((void*)-1);
}
}
//NOTE: align heap base on 16Bytes
heapBase = AlignUpOnPow2(heapBase, 16);
log_info("mem_size = %u, __heap_base = %u\n", m3_GetMemorySize(app->runtime.m3Runtime), heapBase);
2023-04-12 14:21:03 +00:00
//NOTE: Find and type check event handlers.
2023-04-12 14:21:03 +00:00
for(int i=0; i<G_EVENT_COUNT; i++)
{
const g_event_handler_desc* desc = &G_EVENT_HANDLER_DESC[i];
IM3Function handler = 0;
m3_FindFunction(&handler, app->runtime.m3Runtime, desc->name.ptr);
2023-04-12 14:21:03 +00:00
if(handler)
{
bool checked = false;
//NOTE: check function signature
int retCount = m3_GetRetCount(handler);
int argCount = m3_GetArgCount(handler);
if(retCount == desc->retTags.len && argCount == desc->argTags.len)
{
checked = true;
for(int retIndex = 0; retIndex < retCount; retIndex++)
{
M3ValueType m3Type = m3_GetRetType(handler, retIndex);
char tag = m3_type_to_tag(m3Type);
if(tag != desc->retTags.ptr[retIndex])
{
checked = false;
break;
}
}
if(checked)
{
for(int argIndex = 0; argIndex < argCount; argIndex++)
{
M3ValueType m3Type = m3_GetArgType(handler, argIndex);
char tag = m3_type_to_tag(m3Type);
if(tag != desc->argTags.ptr[argIndex])
{
checked = false;
break;
}
}
}
}
if(checked)
{
app->runtime.eventHandlers[i] = handler;
2023-04-12 14:21:03 +00:00
}
else
{
log_error("type mismatch for event handler %.*s\n", (int)desc->name.len, desc->name.ptr);
2023-04-12 14:21:03 +00:00
}
}
}
//NOTE: setup ui context
2023-04-12 14:21:03 +00:00
//NOTE: prepare GL surface
mg_surface_prepare(app->surface);
IM3Function* eventHandlers = app->runtime.eventHandlers;
2023-04-12 14:21:03 +00:00
//NOTE: call init handler
if(eventHandlers[G_EVENT_START])
{
m3_Call(eventHandlers[G_EVENT_START], 0, 0);
}
if(eventHandlers[G_EVENT_FRAME_RESIZE])
{
mp_rect frame = mg_surface_get_frame(app->surface);
u32 width = (u32)frame.w;
u32 height = (u32)frame.h;
const void* args[2] = {&width, &height};
m3_Call(eventHandlers[G_EVENT_FRAME_RESIZE], 2, args);
}
ui_set_context(&app->debugOverlay.ui);
2023-04-12 14:21:03 +00:00
while(!mp_should_quit())
{
mp_event* event = 0;
while((event = mp_next_event(mem_scratch())) != 0)
2023-04-12 14:21:03 +00:00
{
if(app->debugOverlay.show)
{
ui_process_event(event);
}
switch(event->type)
2023-04-12 14:21:03 +00:00
{
case MP_EVENT_WINDOW_CLOSE:
{
mp_request_quit();
} break;
case MP_EVENT_WINDOW_RESIZE:
{
mp_rect frame = {0, 0, event->frame.rect.w, event->frame.rect.h};
mg_surface_set_frame(app->surface, frame);
2023-04-14 09:48:36 +00:00
2023-04-12 14:21:03 +00:00
if(eventHandlers[G_EVENT_FRAME_RESIZE])
{
u32 width = (u32)event->frame.rect.w;
u32 height = (u32)event->frame.rect.h;
2023-04-12 14:21:03 +00:00
const void* args[2] = {&width, &height};
m3_Call(eventHandlers[G_EVENT_FRAME_RESIZE], 2, args);
}
} break;
case MP_EVENT_MOUSE_BUTTON:
{
if(event->key.action == MP_KEY_PRESS)
2023-04-12 14:21:03 +00:00
{
if(eventHandlers[G_EVENT_MOUSE_DOWN])
{
int key = event->key.code;
2023-04-12 14:21:03 +00:00
const void* args[1] = {&key};
m3_Call(eventHandlers[G_EVENT_MOUSE_DOWN], 1, args);
}
}
else
{
if(eventHandlers[G_EVENT_MOUSE_UP])
{
int key = event->key.code;
2023-04-12 14:21:03 +00:00
const void* args[1] = {&key};
m3_Call(eventHandlers[G_EVENT_MOUSE_UP], 1, args);
}
}
} break;
case MP_EVENT_MOUSE_MOVE:
{
if(eventHandlers[G_EVENT_MOUSE_MOVE])
{
const void* args[4] = {&event->move.x, &event->move.y, &event->move.deltaX, &event->move.deltaY};
2023-04-12 14:21:03 +00:00
m3_Call(eventHandlers[G_EVENT_MOUSE_MOVE], 4, args);
}
} break;
case MP_EVENT_KEYBOARD_KEY:
{
if(event->key.action == MP_KEY_PRESS)
2023-04-12 14:21:03 +00:00
{
if(event->key.code == MP_KEY_D && (event->key.mods & (MP_KEYMOD_SHIFT | MP_KEYMOD_CMD)))
{
debug_overlay_toogle(&app->debugOverlay);
}
2023-04-12 14:21:03 +00:00
if(eventHandlers[G_EVENT_KEY_DOWN])
{
const void* args[1] = {&event->key.code};
2023-04-12 14:21:03 +00:00
m3_Call(eventHandlers[G_EVENT_KEY_DOWN], 1, args);
}
}
else if(event->key.action == MP_KEY_RELEASE)
2023-04-12 14:21:03 +00:00
{
if(eventHandlers[G_EVENT_KEY_UP])
{
const void* args[1] = {&event->key.code};
2023-04-12 14:21:03 +00:00
m3_Call(eventHandlers[G_EVENT_KEY_UP], 1, args);
}
}
} break;
default:
break;
}
}
2023-04-14 09:48:36 +00:00
mg_canvas_prepare(app->canvas);
if(eventHandlers[G_EVENT_FRAME_REFRESH])
{
m3_Call(eventHandlers[G_EVENT_FRAME_REFRESH], 0, 0);
}
mg_present();
if(app->debugOverlay.show)
{
ui_style debugUIDefaultStyle = {.bgColor = {0},
.color = {1, 1, 1, 1},
.font = app->debugOverlay.font,
.fontSize = 16,
.borderColor = {1, 0, 0, 1},
.borderSize = 2};
ui_style_mask debugUIDefaultMask = UI_STYLE_BG_COLOR
| UI_STYLE_COLOR
| UI_STYLE_BORDER_COLOR
| UI_STYLE_BORDER_SIZE
| UI_STYLE_FONT
| UI_STYLE_FONT_SIZE;
ui_frame(&debugUIDefaultStyle, debugUIDefaultMask)
{
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
.size.height = {UI_SIZE_PARENT, 1, 1}},
UI_STYLE_SIZE);
ui_container("overlay area", 0)
{
//...
}
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
.size.height = {UI_SIZE_PARENT, 0.4},
.bgColor = {0, 0, 0, 0.5}},
UI_STYLE_SIZE
|UI_STYLE_BG_COLOR);
ui_container("log console", UI_FLAG_DRAW_BACKGROUND)
{
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
.size.height = {UI_SIZE_PARENT, 1}},
UI_STYLE_SIZE);
ui_panel("log console", UI_FLAG_CLICKABLE)
{
ui_style_next(&(ui_style){.size.width = {UI_SIZE_PARENT, 1},
.size.height = {UI_SIZE_PIXELS, 800}},
UI_STYLE_SIZE);
ui_container("contents", 0)
{
}
}
}
}
mg_canvas_prepare(app->debugOverlay.canvas);
ui_draw();
/*
mg_set_font(app->debugOverlay.font);
mg_set_font_size(32);
mg_set_color_rgba(0.2, 0.2, 0.2, 1);
mg_move_to(30, 30);
mg_text_outlines(STR8("Debug Overlay"));
mg_fill();
*/
mg_present();
}
2023-04-12 14:21:03 +00:00
mem_scratch_clear();
}
return(0);
}
int main(int argc, char** argv)
{
2023-04-18 12:33:47 +00:00
log_set_level(LOG_LEVEL_INFO);
2023-04-12 14:21:03 +00:00
mp_init();
mp_clock_init();
orca_app* orca = &__orcaApp;
2023-04-12 14:21:03 +00:00
mp_rect windowRect = {.x = 100, .y = 100, .w = 810, .h = 610};
orca->window = mp_window_create(windowRect, "orca", 0);
orca->surface = mg_surface_create_for_window(orca->window, MG_BACKEND_DEFAULT);
orca->canvas = mg_canvas_create(orca->surface);
mg_surface_swap_interval(orca->surface, 1);
2023-04-12 14:21:03 +00:00
orca->debugOverlay.show = false;
orca->debugOverlay.surface = mg_surface_create_for_window(orca->window, MG_BACKEND_DEFAULT);
orca->debugOverlay.canvas = mg_canvas_create(orca->debugOverlay.surface);
orca->debugOverlay.font = orca_font_create("../resources/Andale Mono.ttf");
2023-04-12 14:21:03 +00:00
mg_surface_set_hidden(orca->debugOverlay.surface, true);
//WARN: this is a workaround to avoid stalling the first few times we acquire drawables from
// the surfaces... This should probably be fixed in the implementation of mtl_surface!
for(int i=0; i<4; i++)
2023-04-12 14:21:03 +00:00
{
mg_canvas_prepare(orca->canvas);
mg_present();
mg_canvas_prepare(orca->debugOverlay.canvas);
mg_present();
2023-04-12 14:21:03 +00:00
}
ui_init(&orca->debugOverlay.ui);
2023-04-14 09:48:36 +00:00
//NOTE: show window and start runloop
mp_window_bring_to_front(orca->window);
mp_window_focus(orca->window);
2023-04-12 14:21:03 +00:00
pthread_t runloopThread;
pthread_create(&runloopThread, 0, orca_runloop, 0);
2023-04-12 14:21:03 +00:00
while(!mp_should_quit())
{
mp_pump_events(0);
//TODO: what to do with mem scratch here?
}
void* res;
pthread_join(runloopThread, &res);
mg_canvas_destroy(orca->canvas);
mg_surface_destroy(orca->surface);
mp_window_destroy(orca->window);
2023-04-12 14:21:03 +00:00
mp_terminate();
return(0);
}
#undef LOG_SUBSYSTEM