schedule user update from sys scheduler

This commit is contained in:
jacob 2025-07-10 18:32:26 -05:00
parent b270737bec
commit 66bae61b1a
17 changed files with 469 additions and 413 deletions

View File

@ -23,20 +23,10 @@
#include "host.h"
#include "bitbuff.h"
struct exit_callback {
app_exit_callback_func *func;
struct exit_callback *next;
};
GLOBAL struct {
struct arena *arena;
struct string write_path;
struct snc_counter exit_fence;
/* Exit callbacks */
struct snc_mutex exit_callbacks_mutex;
struct arena *exit_callbacks_arena;
struct exit_callback *exit_callbacks_head;
} G = ZI, DEBUG_ALIAS(G, G_app);
/* ========================== *
@ -97,20 +87,6 @@ INTERNAL struct sys_window_settings default_window_settings(struct sys_window *w
};
}
/* ========================== *
* Exit callbacks
* ========================== */
void app_register_exit_callback(app_exit_callback_func *func)
{
struct snc_lock lock = snc_lock_e(&G.exit_callbacks_mutex);
struct exit_callback *callback = arena_push(G.exit_callbacks_arena, struct exit_callback);
callback->func = func;
callback->next = G.exit_callbacks_head;
G.exit_callbacks_head = callback;
snc_unlock(&lock);
}
/* ========================== *
* Args
* ========================== */
@ -217,6 +193,17 @@ void sys_app_entry(struct string args_str)
}
}
(UNUSED)args;
(UNUSED)logfile_name;
(UNUSED)settings_file_name;
(UNUSED)connect_address;
(UNUSED)default_window_settings;
#if !RTC
/* Verify test modes aren't left on by accident in release mode */
STATIC_ASSERT(BITBUFF_DEBUG == 0);
@ -227,7 +214,6 @@ void sys_app_entry(struct string args_str)
bitbuff_test();
#endif
G.exit_callbacks_arena = arena_alloc(GIBI(64));
G.arena = arena_alloc(GIBI(64));
G.write_path = initialize_write_directory(G.arena, LIT(WRITE_DIR));
@ -249,9 +235,7 @@ void sys_app_entry(struct string args_str)
logf_info("Parsed arg: key = \"%F\", value = \"%F\"", FMT_STR(arg->key), FMT_STR(arg->value));
}
/* Create window */
struct sys_window *window = sys_window_alloc();
#if 0
/* Read window settings from file */
{
struct arena_temp temp = arena_temp_begin(scratch.arena);
@ -292,45 +276,31 @@ void sys_app_entry(struct string args_str)
arena_temp_end(temp);
}
#endif
/* Startup systems */
struct resource_startup_receipt resource_sr = resource_startup();
gp_startup();
struct sock_startup_receipt sock_sr = sock_startup();
struct host_startup_receipt host_sr = host_startup(&sock_sr);
struct gp_startup_receipt gp_sr = gp_startup();
struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup();
struct ttf_startup_receipt ttf_sr = ttf_startup();
struct font_startup_receipt font_sr = font_startup(&gp_sr, &asset_cache_sr, &ttf_sr, &resource_sr);
struct sprite_startup_receipt sprite_sr = sprite_startup(&gp_sr, &resource_sr);
struct font_startup_receipt font_sr = font_startup(&asset_cache_sr, &ttf_sr, &resource_sr);
struct sprite_startup_receipt sprite_sr = sprite_startup(&resource_sr);
struct mixer_startup_receipt mixer_sr = mixer_startup();
struct sound_startup_receipt sound_sr = sound_startup(&asset_cache_sr, &resource_sr);
struct draw_startup_receipt draw_sr = draw_startup(&gp_sr, &font_sr);
struct draw_startup_receipt draw_sr = draw_startup(&font_sr);
struct sim_startup_receipt sim_sr = sim_startup();
struct user_startup_receipt user_sr = user_startup(&gp_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &host_sr, &sim_sr, connect_address, window);
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
struct user_startup_receipt user_sr = user_startup(&font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &host_sr, &sim_sr, connect_address);
(UNUSED)user_sr;
(UNUSED)playback_sr;
/* Show window */
sys_window_show(window);
/* Wait for app_exit() */
snc_counter_wait(&G.exit_fence);
/* Run exit callbacks */
/* FIXME: Only wait on shutdown for a certain period of time before
* forcing process exit (to prevent process hanging in the background
* if something gets stuck) */
{
__profn("Run exit callbacks");
struct snc_lock lock = snc_lock_e(&G.exit_callbacks_mutex);
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {
callback->func();
}
snc_unlock(&lock);
}
#if 0
/* Write window settings to file */
{
__profn("Write settings file");
@ -350,8 +320,11 @@ void sys_app_entry(struct string args_str)
arena_temp_end(temp);
}
#endif
#if 0
sys_window_release(window);
#endif
logf_info("Program exited normally");
scratch_end(scratch);

View File

@ -1,9 +1,6 @@
#ifndef APP_H
#define APP_H
#define APP_EXIT_CALLBACK_FUNC_DEF(name) void name(void)
typedef APP_EXIT_CALLBACK_FUNC_DEF(app_exit_callback_func);
enum app_dedicated_worker_id {
APP_DEDICATED_WORKER_ID_USER = 0,
APP_DEDICATED_WORKER_ID_SIM = 1,
@ -14,9 +11,6 @@ enum app_dedicated_worker_id {
struct string app_write_path_cat(struct arena *arena, struct string filename);
/* Register a function that will be called when the application exits */
void app_register_exit_callback(app_exit_callback_func *func);
void app_exit(void);
#endif

View File

@ -83,5 +83,5 @@
/* TODO: Move these to user-configurable settings */
#define AUDIO_ENABLED 0
#define VSYNC_ENABLED 1
#define VSYNC 1
#define USER_FPS_LIMIT 300

View File

@ -1,6 +1,5 @@
#include "draw.h"
#include "arena.h"
#include "gp.h"
#include "math.h"
#include "font.h"
#include "sprite.h"
@ -14,10 +13,8 @@ GLOBAL struct {
* Startup
* ========================== */
struct draw_startup_receipt draw_startup(struct gp_startup_receipt *gp_sr,
struct font_startup_receipt *font_sr)
struct draw_startup_receipt draw_startup(struct font_startup_receipt *font_sr)
{
(UNUSED)gp_sr;
(UNUSED)font_sr;
u32 pixel_white = 0xFFFFFFFF;
G.solid_white_texture = gp_texture_alloc(GP_TEXTURE_FORMAT_R8G8B8A8_UNORM, 0, V2I32(1, 1), &pixel_white);

View File

@ -7,8 +7,7 @@ struct font;
struct font_startup_receipt;
struct draw_startup_receipt { i32 _; };
struct draw_startup_receipt draw_startup(struct gp_startup_receipt *gp_sr,
struct font_startup_receipt *font_sr);
struct draw_startup_receipt draw_startup(struct font_startup_receipt *font_sr);
/* ========================== *
* Texture

View File

@ -39,12 +39,10 @@ GLOBAL struct {
* Startup
* ========================== */
struct font_startup_receipt font_startup(struct gp_startup_receipt *gp_sr,
struct asset_cache_startup_receipt *asset_cache_sr,
struct font_startup_receipt font_startup(struct asset_cache_startup_receipt *asset_cache_sr,
struct ttf_startup_receipt *ttf_sr,
struct resource_startup_receipt *resource_sr)
{
(UNUSED)gp_sr;
(UNUSED)asset_cache_sr;
(UNUSED)ttf_sr;
(UNUSED)resource_sr;

View File

@ -2,10 +2,8 @@
#define FONT_H
#include "util.h"
#include "gp.h"
struct asset;
struct gp_startup_receipt;
struct asset_cache_startup_receipt;
struct ttf_startup_receipt;
struct resource_startup_receipt;
@ -30,8 +28,7 @@ struct font {
};
struct font_startup_receipt { i32 _; };
struct font_startup_receipt font_startup(struct gp_startup_receipt *gp_sr,
struct asset_cache_startup_receipt *asset_cache_sr,
struct font_startup_receipt font_startup(struct asset_cache_startup_receipt *asset_cache_sr,
struct ttf_startup_receipt *ttf_sr,
struct resource_startup_receipt *resource_sr);

View File

@ -7,8 +7,7 @@ struct sys_window;
* Startup
* ========================== */
struct gp_startup_receipt { i32 _; };
struct gp_startup_receipt gp_startup(void);
void gp_startup(void);
/* ========================== *
* Resource
@ -119,6 +118,18 @@ struct gp_memory_info {
struct gp_memory_info gp_query_memory_info(void);
/* ========================== *
* Swapchain
* ========================== */
struct gp_swapchain *gp_swapchain_alloc(struct sys_window *window, struct v2i32 resolution);
void gp_swapchain_release(struct gp_swapchain *gp_swapchain);
/* Waits until a new backbuffer is ready to be written to.
* This should be called before rendering for minimum latency. */
void gp_swapchain_wait(struct gp_swapchain *gp_swapchain);
/* ========================== *
* Present
* ========================== */
@ -126,6 +137,6 @@ struct gp_memory_info gp_query_memory_info(void);
/* 1. Clears the backbuffer and ensures it's at size `backbuffer_resolution`
* 2. Blits `texture` to the backbuffer using `texture_xf` (applied to centered unit square)
* 3. Presents the backbuffer */
void gp_present(struct sys_window *window, struct v2i32 backbuffer_resolution, struct gp_resource *texture, struct xform texture_xf, i32 vsync);
void gp_present(struct gp_swapchain *gp_swapchain, struct v2i32 backbuffer_resolution, struct gp_resource *texture, struct xform texture_xf, i32 vsync);
#endif

View File

@ -233,6 +233,8 @@ struct swapchain {
HANDLE waitable;
struct v2i32 resolution;
struct swapchain_buffer buffers[DX12_SWAPCHAIN_BUFFER_COUNT];
struct swapchain *next_free;
};
struct cpu_descriptor_heap {
@ -266,6 +268,8 @@ struct fenced_release_data {
* ========================== */
GLOBAL struct {
struct atomic_i32 initialized;
/* Descriptor heaps pool */
struct snc_mutex command_descriptor_heaps_mutex;
struct arena *command_descriptor_heaps_arena;
@ -282,6 +286,11 @@ GLOBAL struct {
struct arena *resources_arena;
struct dx12_resource *first_free_resource;
/* Swapchains pool */
struct snc_mutex swapchains_mutex;
struct arena *swapchains_arena;
struct swapchain *first_free_swapchain;
/* Pipeline cache */
struct snc_mutex pipelines_mutex;
struct arena *pipelines_arena;
@ -317,9 +326,6 @@ GLOBAL struct {
struct snc_mutex global_submit_mutex;
struct command_queue *command_queues[DX12_NUM_QUEUES];
/* Swapchain */
struct swapchain swapchain;
/* Evictor thread */
struct atomic_i32 evictor_thread_shutdown;
HANDLE evictor_thread_wake_event;
@ -330,7 +336,7 @@ GLOBAL struct {
* Startup
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gp_shutdown);
INTERNAL SYS_EXIT_FUNC(gp_shutdown);
INTERNAL void dx12_init_device(void);
INTERNAL void dx12_init_objects(void);
INTERNAL void dx12_init_pipelines(void);
@ -344,9 +350,12 @@ INTERNAL void fenced_release(void *data, enum fenced_release_kind kind);
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(pipeline_resource_watch_callback, name);
#endif
struct gp_startup_receipt gp_startup(void)
void gp_startup(void)
{
__prof;
if (atomic_i32_fetch_test_set(&G.initialized, 0, 1) != 0) {
sys_panic(LIT("GP layer already initialized"));
}
/* Initialize command descriptor heaps pool */
G.command_descriptor_heaps_arena = arena_alloc(GIBI(64));
@ -358,6 +367,9 @@ struct gp_startup_receipt gp_startup(void)
/* Initialize resources pool */
G.resources_arena = arena_alloc(GIBI(64));
/* Initialize swapchains pool */
G.swapchains_arena = arena_alloc(GIBI(64));
/* Initialize pipeline cache */
G.pipelines_arena = arena_alloc(GIBI(64));
G.pipeline_descs = dict_init(G.pipelines_arena, 1024);
@ -376,17 +388,14 @@ struct gp_startup_receipt gp_startup(void)
#if RESOURCE_RELOADING
resource_register_watch_callback(pipeline_resource_watch_callback);
#endif
app_register_exit_callback(gp_shutdown);
sys_on_exit(gp_shutdown);
/* Start evictor thread */
G.evictor_thread_wake_event = CreateEvent(0, 0, 0, 0);
G.evictor_thread = sys_thread_alloc(evictor_thread_entry_point, 0, LIT("GPU resource evictor thread"), PROF_THREAD_GROUP_EVICTORS);
struct gp_startup_receipt res = ZI;
return res;
}
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gp_shutdown)
INTERNAL SYS_EXIT_FUNC(gp_shutdown)
{
__prof;
#if 0
@ -2560,50 +2569,43 @@ struct gp_memory_info gp_query_memory_info(void)
* Swapchain
* ========================== */
INTERNAL struct swapchain_buffer *update_swapchain(struct swapchain *swapchain, struct sys_window *window, struct v2i32 resolution)
INTERNAL void swapchain_init_resources(struct swapchain *swapchain)
{
__prof;
resolution.x = max_i32(resolution.x, 1);
resolution.y = max_i32(resolution.y, 1);
b32 should_rebuild = !v2i32_eq(swapchain->resolution, resolution);
if (should_rebuild) {
HRESULT hr = 0;
struct command_queue *cq = G.command_queues[DX12_QUEUE_DIRECT];
HWND hwnd = (HWND)sys_window_get_internal_handle(window);
if (swapchain->swapchain) {
ASSERT(hwnd == swapchain->hwnd);
/* Lock direct queue submissions (in case any write to backbuffer) */
/* TODO: Less overkill approach - Only flush present_blit since we know it's the only operation targeting backbuffer */
struct snc_lock lock = snc_lock_e(&cq->submit_fence_mutex);
//DEBUGBREAKABLE;
//struct snc_lock lock = snc_lock_e(&G.global_command_list_record_mutex);
{
/* Flush direct queue */
//ID3D12CommandQueue_Signal(cq->cq, cq->submit_fence, ++cq->submit_fence_target);
{
HANDLE event = CreateEvent(0, 0, 0, 0);
ID3D12Fence_SetEventOnCompletion(cq->submit_fence, cq->submit_fence_target, event);
WaitForSingleObject(event, INFINITE);
CloseHandle(event);
}
/* Release buffers */
for (u32 i = 0; i < countof(swapchain->buffers); ++i) {
struct swapchain_buffer *sb = &swapchain->buffers[i];
descriptor_release(sb->rtv_descriptor);
ID3D12Resource_Release(sb->resource);
}
/* Resize buffers */
hr = IDXGISwapChain_ResizeBuffers(swapchain->swapchain, 0, resolution.x, resolution.y, DXGI_FORMAT_UNKNOWN, DX12_SWAPCHAIN_FLAGS);
ID3D12Resource *resource = 0;
HRESULT hr = IDXGISwapChain3_GetBuffer(swapchain->swapchain, i, &IID_ID3D12Resource, (void **)&resource);
if (FAILED(hr)) {
/* TODO: Don't panic */
sys_panic(LIT("Failed to resize swapchain"));
sys_panic(LIT("Failed to get swapchain buffer"));
}
struct swapchain_buffer *sb = &swapchain->buffers[i];
MEMZERO_STRUCT(sb);
sb->swapchain = swapchain;
sb->resource = resource;
sb->rtv_descriptor = descriptor_alloc(G.rtv_heap);
sb->state = D3D12_RESOURCE_STATE_COMMON;
ID3D12Device_CreateRenderTargetView(G.device, sb->resource, 0, sb->rtv_descriptor->handle);
}
}
struct gp_swapchain *gp_swapchain_alloc(struct sys_window *window, struct v2i32 resolution)
{
HRESULT hr = 0;
HWND hwnd = (HWND)sys_window_get_internal_handle(window);
struct command_queue *cq = G.command_queues[DX12_QUEUE_DIRECT];
struct swapchain *swapchain = NULL;
{
struct snc_lock lock = snc_lock_e(&G.swapchains_mutex);
if (G.first_free_swapchain) {
swapchain = G.first_free_swapchain;
G.first_free_swapchain = swapchain->next_free;
} else {
swapchain = arena_push(G.swapchains_arena, struct swapchain);
}
snc_unlock(&lock);
} else {
}
/* Create swapchain1 */
IDXGISwapChain1 *swapchain1 = 0;
{
@ -2643,24 +2645,67 @@ INTERNAL struct swapchain_buffer *update_swapchain(struct swapchain *swapchain,
IDXGISwapChain1_Release(swapchain1);
swapchain->hwnd = hwnd;
swapchain_init_resources(swapchain);
return (struct gp_swapchain *)swapchain;
}
void gp_swapchain_release(struct gp_swapchain *gp_swapchain)
{
/* TODO */
(UNUSED)gp_swapchain;
}
void gp_swapchain_wait(struct gp_swapchain *gp_swapchain)
{
struct swapchain *swapchain = (struct swapchain *)gp_swapchain;
if (swapchain->waitable) {
WaitForSingleObjectEx(swapchain->waitable, 1000, 1);
}
}
INTERNAL struct swapchain_buffer *update_swapchain(struct swapchain *swapchain, struct v2i32 resolution)
{
__prof;
resolution.x = max_i32(resolution.x, 1);
resolution.y = max_i32(resolution.y, 1);
b32 should_rebuild = !v2i32_eq(swapchain->resolution, resolution);
if (should_rebuild) {
HRESULT hr = 0;
struct command_queue *cq = G.command_queues[DX12_QUEUE_DIRECT];
/* Lock direct queue submissions (in case any write to backbuffer) */
/* TODO: Less overkill approach - Only flush present_blit since we know it's the only operation targeting backbuffer */
struct snc_lock lock = snc_lock_e(&cq->submit_fence_mutex);
//DEBUGBREAKABLE;
//struct snc_lock lock = snc_lock_e(&G.global_command_list_record_mutex);
{
/* Flush direct queue */
//ID3D12CommandQueue_Signal(cq->cq, cq->submit_fence, ++cq->submit_fence_target);
{
HANDLE event = CreateEvent(0, 0, 0, 0);
ID3D12Fence_SetEventOnCompletion(cq->submit_fence, cq->submit_fence_target, event);
WaitForSingleObject(event, INFINITE);
CloseHandle(event);
}
/* Allocate swapchain resources */
/* Release buffers */
for (u32 i = 0; i < countof(swapchain->buffers); ++i) {
ID3D12Resource *resource = 0;
hr = IDXGISwapChain3_GetBuffer(swapchain->swapchain, i, &IID_ID3D12Resource, (void **)&resource);
struct swapchain_buffer *sb = &swapchain->buffers[i];
descriptor_release(sb->rtv_descriptor);
ID3D12Resource_Release(sb->resource);
}
/* Resize buffers */
hr = IDXGISwapChain_ResizeBuffers(swapchain->swapchain, 0, resolution.x, resolution.y, DXGI_FORMAT_UNKNOWN, DX12_SWAPCHAIN_FLAGS);
if (FAILED(hr)) {
/* TODO: Don't panic */
sys_panic(LIT("Failed to get swapchain buffer"));
sys_panic(LIT("Failed to resize swapchain"));
}
struct swapchain_buffer *sb = &swapchain->buffers[i];
MEMZERO_STRUCT(sb);
sb->swapchain = swapchain;
sb->resource = resource;
sb->rtv_descriptor = descriptor_alloc(G.rtv_heap);
sb->state = D3D12_RESOURCE_STATE_COMMON;
ID3D12Device_CreateRenderTargetView(G.device, sb->resource, 0, sb->rtv_descriptor->handle);
}
snc_unlock(&lock);
swapchain_init_resources(swapchain);
swapchain->resolution = resolution;
}
@ -2768,18 +2813,18 @@ INTERNAL void present_blit(struct swapchain_buffer *dst, struct dx12_resource *s
pipeline_scope_end(pipeline_scope);
}
void gp_present(struct sys_window *window, struct v2i32 backbuffer_resolution, struct gp_resource *texture, struct xform texture_xf, i32 vsync)
void gp_present(struct gp_swapchain *gp_swapchain, struct v2i32 backbuffer_resolution, struct gp_resource *texture, struct xform texture_xf, i32 vsync)
{
__prof;
struct swapchain *swapchain = &G.swapchain;
struct swapchain_buffer *swapchain_buffer = update_swapchain(swapchain, window, backbuffer_resolution);
struct swapchain *swapchain = (struct swapchain *)gp_swapchain;
struct swapchain_buffer *swapchain_buffer = update_swapchain(swapchain, backbuffer_resolution);
struct dx12_resource *texture_resource = (struct dx12_resource *)texture;
/* Blit */
present_blit(swapchain_buffer, texture_resource, texture_xf);
u32 present_flags = 0;
if (!vsync) {
if (vsync == 0) {
present_flags |= (DXGI_PRESENT_ALLOW_TEARING * DX12_ALLOW_TEARING);
}
@ -2790,18 +2835,21 @@ void gp_present(struct sys_window *window, struct v2i32 backbuffer_resolution, s
if (!SUCCEEDED(hr)) {
ASSERT(0);
}
__profframe(0);
}
#if PROFILING_D3D
{
__profframe(0);
__profn("Mark queue frames");
/* Lock because frame marks shouldn't occur while command lists are recording */
struct snc_lock lock = snc_lock_e(&G.global_command_list_record_mutex);
for (u32 i = 0; i < countof(G.command_queues); ++i) {
{
struct command_queue *cq = G.command_queues[i];
__prof_dx12_new_frame(cq->prof);
}
}
snc_unlock(&lock);
}
{
@ -2812,13 +2860,6 @@ void gp_present(struct sys_window *window, struct v2i32 backbuffer_resolution, s
}
}
#endif
/* Wait on swapchain */
/* TODO: Move this to system layer */
if (swapchain->waitable) {
__profn("Present wait");
WaitForSingleObjectEx(swapchain->waitable, 1000, 1);
}
}
/* ========================== *

View File

@ -52,7 +52,7 @@ GLOBAL struct {
* ========================== */
INTERNAL void wasapi_initialize(void);
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(playback_shutdown);
INTERNAL SYS_EXIT_FUNC(playback_shutdown);
INTERNAL SYS_THREAD_DEF(playback_scheduler_entry, _);
struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *mixer_sr)
@ -61,12 +61,12 @@ struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *m
wasapi_initialize();
G.playback_scheduler_thread = sys_thread_alloc(playback_scheduler_entry, 0, LIT("Playback scheduler thread"), PROF_THREAD_GROUP_SCHEDULER);
app_register_exit_callback(&playback_shutdown);
sys_on_exit(&playback_shutdown);
return (struct playback_startup_receipt) { 0 };
}
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(playback_shutdown)
INTERNAL SYS_EXIT_FUNC(playback_shutdown)
{
__prof;
atomic_i32_fetch_set(&G.shutdown, 1);

View File

@ -47,7 +47,7 @@ GLOBAL struct {
#if RESOURCE_RELOADING
INTERNAL SYS_THREAD_DEF(resource_watch_monitor_thread_entry_point, _);
INTERNAL SYS_THREAD_DEF(resource_watch_dispatcher_thread_entry_point, _);
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(resource_shutdown);
INTERNAL SYS_EXIT_FUNC(resource_shutdown);
#endif
struct resource_startup_receipt resource_startup(void)
@ -72,7 +72,7 @@ struct resource_startup_receipt resource_startup(void)
G.watch_dispatcher_info_arena = arena_alloc(GIBI(64));
app_register_exit_callback(&resource_shutdown);
sys_on_exit(&resource_shutdown);
G.resource_watch_monitor_thread = sys_thread_alloc(resource_watch_monitor_thread_entry_point, 0, LIT("Resource watch monitor"), PROF_THREAD_GROUP_IO);
G.resource_watch_dispatch_thread = sys_thread_alloc(resource_watch_dispatcher_thread_entry_point, 0, LIT("Resource watch dispatcher"), PROF_THREAD_GROUP_IO);
#endif
@ -154,7 +154,7 @@ void resource_close(struct resource *res_ptr)
#if RESOURCE_RELOADING
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(resource_shutdown)
INTERNAL SYS_EXIT_FUNC(resource_shutdown)
{
__prof;
atomic_i32_fetch_set(&G.watch_shutdown, 1);

View File

@ -200,7 +200,7 @@ INTERNAL struct image_rgba generate_purple_black_image(struct arena *arena, u32
* Startup
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown);
INTERNAL SYS_EXIT_FUNC(sprite_shutdown);
INTERNAL SYS_JOB_DEF(sprite_load_job, arg);
INTERNAL SYS_JOB_DEF(sprite_evictor_job, _);
@ -208,10 +208,8 @@ INTERNAL SYS_JOB_DEF(sprite_evictor_job, _);
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, info);
#endif
struct sprite_startup_receipt sprite_startup(struct gp_startup_receipt *gp_sr,
struct resource_startup_receipt *resource_sr)
struct sprite_startup_receipt sprite_startup(struct resource_startup_receipt *resource_sr)
{
(UNUSED)gp_sr;
(UNUSED)resource_sr;
G.perm_arena = arena_alloc(MEBI(1));
@ -251,13 +249,13 @@ struct sprite_startup_receipt sprite_startup(struct gp_startup_receipt *gp_sr,
sys_run(1, sprite_evictor_job, 0, SYS_PRIORITY_BACKGROUND, &G.shutdown_counter);
app_register_exit_callback(&sprite_shutdown);
sys_on_exit(&sprite_shutdown);
resource_register_watch_callback(&sprite_resource_watch_callback);
return (struct sprite_startup_receipt) { 0 };
}
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown)
INTERNAL SYS_EXIT_FUNC(sprite_shutdown)
{
__prof;
/* Signal evictor shutdown */
@ -1025,20 +1023,17 @@ INTERNAL void *data_from_tag_internal(struct sprite_scope *scope, struct sprite_
struct sprite_texture *sprite_texture_from_tag_await(struct sprite_scope *scope, struct sprite_tag tag)
{
__prof;
return (struct sprite_texture *)data_from_tag_internal(scope, tag, CACHE_ENTRY_KIND_TEXTURE, 1);
}
struct sprite_texture *sprite_texture_from_tag_async(struct sprite_scope *scope, struct sprite_tag tag)
{
__prof;
return (struct sprite_texture *)data_from_tag_internal(scope, tag, CACHE_ENTRY_KIND_TEXTURE, 0);
}
void sprite_texture_from_tag_prefetch(struct sprite_scope *scope, struct sprite_tag tag)
{
__prof;
data_from_tag_internal(scope, tag, CACHE_ENTRY_KIND_TEXTURE, 0);
}
@ -1048,26 +1043,22 @@ void sprite_texture_from_tag_prefetch(struct sprite_scope *scope, struct sprite_
struct sprite_sheet *sprite_sheet_from_tag_await(struct sprite_scope *scope, struct sprite_tag tag)
{
__prof;
return (struct sprite_sheet *)data_from_tag_internal(scope, tag, CACHE_ENTRY_KIND_SHEET, 1);
}
struct sprite_sheet *sprite_sheet_from_tag_async(struct sprite_scope *scope, struct sprite_tag tag)
{
__prof;
return (struct sprite_sheet *)data_from_tag_internal(scope, tag, CACHE_ENTRY_KIND_SHEET, 0);
}
void sprite_sheet_from_tag_prefetch(struct sprite_scope *scope, struct sprite_tag tag)
{
__prof;
data_from_tag_internal(scope, tag, CACHE_ENTRY_KIND_SHEET, 0);
}
struct sprite_sheet_frame sprite_sheet_get_frame(struct sprite_sheet *sheet, u32 index)
{
__prof;
if (index < sheet->frames_count ) {
return sheet->frames[index];
}
@ -1080,7 +1071,6 @@ struct sprite_sheet_frame sprite_sheet_get_frame(struct sprite_sheet *sheet, u32
struct sprite_sheet_span sprite_sheet_get_span(struct sprite_sheet *sheet, struct string name)
{
__prof;
struct sprite_sheet_span res = ZI;
if (sheet->spans_count > 0) {
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name);
@ -1371,7 +1361,6 @@ INTERNAL SYS_JOB_DEF(sprite_evictor_job, _)
/* Evictor sleep */
{
__profn("Sprite evictor wait");
struct snc_lock lock = snc_lock_e(&G.evictor_scheduler_mutex);
{
if (!G.evictor_scheduler_shutdown) {

View File

@ -1,10 +1,8 @@
#ifndef SPRITE_H
#define SPRITE_H
#include "gp.h"
#include "util.h"
struct gp_startup_receipt;
struct resource_startup_receipt;
struct sprite_sheet_span;
struct sprite_sheet_slice_group;
@ -14,8 +12,7 @@ struct sprite_sheet_slice_group;
* ========================== */
struct sprite_startup_receipt { i32 _; };
struct sprite_startup_receipt sprite_startup(struct gp_startup_receipt *gp_sr,
struct resource_startup_receipt *resource_sr);
struct sprite_startup_receipt sprite_startup(struct resource_startup_receipt *resource_sr);
/* ========================== *
* Tag

201
src/sys.h
View File

@ -1,7 +1,110 @@
#ifndef SYS_H
#define SYS_H
struct thread_local_store;
/* ========================== *
* On exit
* ========================== */
/* Functions to be called for graceful shutdown (in reverse order) */
#define SYS_EXIT_FUNC(name) void name(void)
typedef SYS_EXIT_FUNC(sys_exit_func);
void sys_on_exit(sys_exit_func *func);
/* ========================== *
* Scheduler
* ========================== */
i64 sys_current_scheduler_period_ns(void);
/* ========================== *
* Wait
* ========================== */
/* Futex-like wait & wake */
void sys_wait(void *addr, void *cmp, u32 size, i64 timeout_ns);
void sys_wake(void *addr, i32 count);
/* ========================== *
* Fiber
* ========================== */
#define SYS_MAX_FIBERS 4096
i16 sys_current_fiber_id(void);
/* ========================== *
* Job
* ========================== */
enum sys_priority {
SYS_PRIORITY_INHERIT = -1,
SYS_PRIORITY_CRITICAL = 0,
SYS_PRIORITY_HIGH = 1,
SYS_PRIORITY_NORMAL = 2,
SYS_PRIORITY_BACKGROUND = 3,
NUM_SYS_PRIORITIES
};
struct sys_job_data {
i32 id;
void *sig;
};
#define SYS_JOB_DEF(job_name, arg_name) void job_name(struct sys_job_data arg_name)
typedef SYS_JOB_DEF(sys_job_func, job_data);
struct snc_counter;
void sys_run(i32 count, sys_job_func *func, void *sig, enum sys_priority priority, struct snc_counter *counter);
/* ========================== *
* Scratch context
* ========================== */
#define SYS_SCRATCH_ARENAS_PER_CTX 2
struct sys_scratch_ctx {
struct arena *arenas[SYS_SCRATCH_ARENAS_PER_CTX];
};
struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i16 fiber_id);
/* ========================== *
* App entry point
* ========================== */
/* Must be defined by app */
void sys_app_entry(struct string args_str);
/* ========================== *
* Events
@ -316,10 +419,6 @@ enum sys_window_flags {
SYS_WINDOW_FLAG_SHOWING = 0x02
};
#define SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(name, arg_name) void name(struct sys_event arg_name)
typedef SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(sys_window_event_callback_func, event);
/* sys_window_update_settings should be used when altering settings values */
struct sys_window_settings {
char title[256];
@ -335,11 +434,17 @@ struct sys_window_settings {
i32 floating_height;
};
struct sys_window *sys_window_alloc(void);
void sys_window_release(struct sys_window *sys_window);
struct sys_window_event_job_sig {
struct sys_window *window;
struct sys_event event;
};
void sys_window_register_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func);
void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func);
struct sys_window_present_job_sig {
struct sys_window *window;
};
struct sys_window *sys_window_alloc(sys_job_func *event_job, sys_job_func *present_job);
void sys_window_release(struct sys_window *sys_window);
void sys_window_update_settings(struct sys_window *sys_window, struct sys_window_settings *settings);
struct sys_window_settings sys_window_get_settings(struct sys_window *sys_window);
@ -358,6 +463,8 @@ void sys_window_cursor_hide(struct sys_window *sys_window);
void sys_window_cursor_enable_clip(struct sys_window *sys_window, struct rect bounds);
void sys_window_cursor_disable_clip(struct sys_window *sys_window);
struct gp_swapchain *sys_window_get_swapchain(struct sys_window *window);
/* ========================== *
* Threads
* ========================== */
@ -422,80 +529,4 @@ void sys_panic(struct string msg);
b32 sys_run_command(struct string cmd);
/* ========================== *
* Scheduler
* ========================== */
i64 sys_current_scheduler_period_ns(void);
/* ========================== *
* Wait
* ========================== */
/* Futex-like wait & wake */
void sys_wait(void *addr, void *cmp, u32 size, i64 timeout_ns);
void sys_wake(void *addr, i32 count);
/* ========================== *
* Fiber
* ========================== */
#define SYS_MAX_FIBERS 4096
i16 sys_current_fiber_id(void);
/* ========================== *
* Job
* ========================== */
enum sys_priority {
SYS_PRIORITY_INHERIT = -1,
SYS_PRIORITY_CRITICAL = 0,
SYS_PRIORITY_HIGH = 1,
SYS_PRIORITY_NORMAL = 2,
SYS_PRIORITY_BACKGROUND = 3,
NUM_SYS_PRIORITIES
};
struct sys_job_data {
i32 id;
void *sig;
};
#define SYS_JOB_DEF(job_name, arg_name) void job_name(struct sys_job_data arg_name)
typedef SYS_JOB_DEF(sys_job_func, job_data);
struct snc_counter;
void sys_run(i32 count, sys_job_func *func, void *sig, enum sys_priority priority, struct snc_counter *counter);
/* ========================== *
* Scratch context
* ========================== */
#define SYS_SCRATCH_ARENAS_PER_CTX 2
struct sys_scratch_ctx {
struct arena *arenas[SYS_SCRATCH_ARENAS_PER_CTX];
};
struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i16 fiber_id);
/* ========================== *
* App entry point
* ========================== */
/* Must be defined by app */
void sys_app_entry(struct string args_str);
#endif

View File

@ -8,6 +8,7 @@
#include "math.h"
#include "util.h"
#include "uni.h"
#include "gp.h"
#pragma warning(push, 0)
# define UNICODE
@ -42,6 +43,12 @@
#define FIBER_NAME_SUFFIX_CSTR "]"
#define FIBER_NAME_MAX_SIZE 64
#define NUM_WAIT_ADDR_BINS 65536
#define NUM_WAIT_TIME_BINS 1024
#define MAX_EXIT_FUNCS 1024
/* Arbitrary threshold for determining when to fall back from a looped WakeByAddressSingle to WakeByAddressAll */
#define WAKE_ALL_THRESHOLD 8
@ -96,20 +103,18 @@ struct win32_window {
struct v2 cursor_set_position;
struct rect cursor_clip_bounds;
struct atomic_i32 event_thread_shutdown;
struct gp_swapchain *swapchain;
sys_job_func *event_job;
sys_job_func *present_job;
struct sys_thread *event_thread;
struct sys_thread *present_thread;
struct snc_mutex event_callbacks_mutex;
sys_window_event_callback_func *event_callbacks[SYS_WINDOW_EVENT_LISTENERS_MAX];
u64 event_callbacks_count;
struct atomic_i32 shutdown;
struct win32_window *next_free;
};
#define NUM_WAIT_ADDR_BINS 65536
#define NUM_WAIT_TIME_BINS 1024
struct alignas(64) wait_list {
u64 value;
i16 first_waiter;
@ -275,7 +280,9 @@ GLOBAL struct {
/* Exit funcs */
struct atomic_i32 num_exit_funcs;
sys_exit_func *exit_funcs[MAX_EXIT_FUNCS];
/* Scheduler */
struct atomic_i64_padded current_scheduler_cycle;
@ -341,6 +348,27 @@ INTERNAL void tm_unlock(struct ticket_mutex *tm)
atomic_i64_fetch_add(&tm->serving.v, 1);
}
/* ========================== *
* On exit
* ========================== */
void sys_on_exit(sys_exit_func *func)
{
i32 index = atomic_i32_fetch_add(&G.num_exit_funcs, 1);
if (index >= MAX_EXIT_FUNCS) {
sys_panic(LIT("Maximum on exit functions registered"));
}
G.exit_funcs[index] = func;
}
/* ========================== *
* Scheduler
* ========================== */
@ -834,9 +862,11 @@ INTERNAL SYS_THREAD_DEF(job_worker_entry, worker_ctx_arg)
success = SetThreadPriority(thread_handle, priority);
ASSERT(success);
#if 0
u64 affinity_mask = (u64)1 << (ctx->id * 2);
success = SetThreadAffinityMask(thread_handle, affinity_mask) != 0;
ASSERT(success);
#endif
}
i32 worker_fiber_id = sys_current_fiber_id();
@ -857,7 +887,7 @@ INTERNAL SYS_THREAD_DEF(job_worker_entry, worker_ctx_arg)
void *job_sig = 0;
struct snc_counter *job_counter = 0;
{
//__profnc("Pull job", RGB32_F(0.75, 0.75, 0));
__profnc("Pull job", RGB32_F(0.75, 0.75, 0));
for (u32 queue_index = 0; queue_index < countof(queues) && !job_func; ++queue_index) {
struct job_queue *queue = queues[queue_index];
if (queue) {
@ -1091,7 +1121,7 @@ INTERNAL SYS_THREAD_DEF(job_worker_entry, worker_ctx_arg)
{
shutdown = atomic_i32_fetch(&G.workers_shutdown.v);
while (atomic_i64_fetch(&G.num_jobs_in_queue.v) <= 0 && !shutdown) {
//__profnc("Wait for job", RGB32_F(0.75, 0.75, 0));
__profnc("Wait for job", RGB32_F(0.75, 0.75, 0));
snc_cv_wait(&G.workers_wake_cv, &wake_lock);
shutdown = atomic_i32_fetch(&G.workers_shutdown.v);
}
@ -1365,7 +1395,7 @@ INTERNAL SYS_THREAD_DEF(test_entry, _)
/* Start workers */
//G.num_worker_threads = 1;
G.num_worker_threads = 6;
G.num_worker_threads = 4;
G.worker_threads_arena = arena_alloc(GIBI(64));
G.worker_threads = arena_push_array(G.worker_threads_arena, struct sys_thread *, G.num_worker_threads);
G.worker_contexts = arena_push_array(G.worker_threads_arena, struct worker_ctx, G.num_worker_threads);
@ -2173,12 +2203,15 @@ INTERNAL void win32_window_wake(struct win32_window *window);
INTERNAL void win32_window_process_event(struct win32_window *window, struct sys_event event)
{
if (window->event_job) {
__prof;
struct snc_lock lock = snc_lock_e(&window->event_callbacks_mutex);
for (u64 i = 0; i < window->event_callbacks_count; ++i) {
window->event_callbacks[i](event);
struct snc_counter counter = ZI;
struct sys_window_event_job_sig sig = ZI;
sig.window = (struct sys_window *)window;
sig.event = event;
sys_run(1, window->event_job, &sig, SYS_PRIORITY_NORMAL, &counter);
snc_counter_wait(&counter);
}
snc_unlock(&lock);
}
INTERNAL HWND win32_create_window(struct win32_window *window)
@ -2218,7 +2251,7 @@ INTERNAL HWND win32_create_window(struct win32_window *window)
return hwnd;
}
INTERNAL SYS_THREAD_DEF(window_thread_entry_point, arg)
INTERNAL SYS_THREAD_DEF(window_event_thread_entry_point, arg)
{
struct win32_window *window = (struct win32_window *)arg;
@ -2230,14 +2263,14 @@ INTERNAL SYS_THREAD_DEF(window_thread_entry_point, arg)
snc_counter_add(&window->ready_fence, -1);
while (!atomic_i32_fetch(&window->event_thread_shutdown)) {
while (!atomic_i32_fetch(&window->shutdown)) {
MSG msg = ZI;
{
GetMessageW(&msg, 0, 0, 0);
}
{
__profn("Process window message");
if (atomic_i32_fetch(&window->event_thread_shutdown)) {
if (atomic_i32_fetch(&window->shutdown)) {
break;
}
@ -2304,7 +2337,29 @@ INTERNAL SYS_THREAD_DEF(window_thread_entry_point, arg)
DestroyWindow(window->hwnd);
}
INTERNAL struct win32_window *win32_window_alloc(void)
INTERNAL SYS_THREAD_DEF(window_present_thread_entry_point, arg)
{
struct win32_window *window = (struct win32_window *)arg;
/* Show window */
sys_window_show((struct sys_window *)window);
while (!atomic_i32_fetch(&window->shutdown)) {
{
__profn("Swapchain wait");
gp_swapchain_wait(window->swapchain);
}
{
struct snc_counter counter = ZI;
struct sys_window_present_job_sig sig = ZI;
sig.window = (struct sys_window *)window;
sys_run(1, window->present_job, &sig, SYS_PRIORITY_HIGH, &counter);
snc_counter_wait(&counter);
}
}
}
INTERNAL struct win32_window *win32_window_alloc(sys_job_func *event_job, sys_job_func *present_job)
{
struct win32_window *window = 0;
{
@ -2318,12 +2373,23 @@ INTERNAL struct win32_window *win32_window_alloc(void)
snc_unlock(&lock);
}
MEMZERO_STRUCT(window);
window->event_job = event_job;
window->present_job = present_job;
/* Start window thread for processing events */
/* Start window event thread */
snc_counter_add(&window->ready_fence, 1);
window->event_thread = sys_thread_alloc(&window_thread_entry_point, window, LIT("Window thread"), PROF_THREAD_GROUP_WINDOW);
window->event_thread = sys_thread_alloc(&window_event_thread_entry_point, window, LIT("Window event thread"), PROF_THREAD_GROUP_WINDOW);
snc_counter_wait(&window->ready_fence);
/* Start window present thread */
if (present_job) {
window->swapchain = gp_swapchain_alloc((struct sys_window *)window, V2I32(100, 100));
window->present_thread = sys_thread_alloc(&window_present_thread_entry_point, window, LIT("Window present thread"), PROF_THREAD_GROUP_WINDOW);
}
/* Release swapchain */
gp_swapchain_release(window->swapchain);
return window;
}
@ -2334,9 +2400,10 @@ INTERNAL void win32_window_release(struct win32_window *window)
window->next_free = G.first_free_window;
G.first_free_window = window;
/* Stop window thread */
atomic_i32_fetch_set(&window->event_thread_shutdown, 1);
/* Stop window threads */
atomic_i32_fetch_set(&window->shutdown, 1);
win32_window_wake(window);
sys_thread_wait_release(window->present_thread);
sys_thread_wait_release(window->event_thread);
snc_unlock(&lock);
@ -2702,10 +2769,10 @@ INTERNAL LRESULT CALLBACK win32_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
return result;
}
struct sys_window *sys_window_alloc(void)
struct sys_window *sys_window_alloc(sys_job_func *event_job, sys_job_func *present_job)
{
__prof;
return (struct sys_window *)win32_window_alloc();
return (struct sys_window *)win32_window_alloc(event_job, present_job);
}
void sys_window_release(struct sys_window *sys_window)
@ -2715,42 +2782,6 @@ void sys_window_release(struct sys_window *sys_window)
win32_window_release(window);
}
void sys_window_register_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func)
{
struct win32_window *window = (struct win32_window *)sys_window;
struct snc_lock lock = snc_lock_e(&window->event_callbacks_mutex);
{
if (window->event_callbacks_count + 1 > countof(window->event_callbacks)) {
sys_panic(LIT("Too many window event callbacks registered"));
} else {
window->event_callbacks[window->event_callbacks_count++] = func;
}
}
snc_unlock(&lock);
}
void sys_window_unregister_event_callback(struct sys_window *sys_window, sys_window_event_callback_func *func)
{
struct win32_window *window = (struct win32_window *)sys_window;
struct snc_lock lock = snc_lock_e(&window->event_callbacks_mutex);
{
u64 count = window->event_callbacks_count;
sys_window_event_callback_func *last = count > 0 ? window->event_callbacks[count - 1] : 0;
for (u64 i = 0; i < window->event_callbacks_count; ++i) {
if (window->event_callbacks[i] == func) {
if (func != last) {
/* Swap with last element */
window->event_callbacks[i] = last;
}
--window->event_callbacks_count;
}
}
}
snc_unlock(&lock);
}
void sys_window_update_settings(struct sys_window *sys_window, struct sys_window_settings *settings)
{
__prof;
@ -2845,6 +2876,12 @@ void sys_window_cursor_disable_clip(struct sys_window *sys_window)
win32_window_wake(window);
}
/* TODO: Remove this */
struct gp_swapchain *sys_window_get_swapchain(struct sys_window *window)
{
return ((struct win32_window *)window)->swapchain;
}
/* ========================== *
* Threads
* ========================== */
@ -3264,7 +3301,6 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
MEMCPY(G.cmdline_args_wstr, cmdline_wstr, cmdline_len * sizeof(*cmdline_wstr));
G.cmdline_args_wstr[cmdline_len] = 0;
G.main_thread_id = GetCurrentThreadId();
SetThreadDescription(GetCurrentThread(), L"Main thread");
@ -3325,6 +3361,9 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
}
}
/* Start test thread */
struct sys_thread *test_thread = sys_thread_alloc(test_entry, 0, LIT("Test thread"), PROF_THREAD_GROUP_APP);
/* ========================== *
* App thread setup
* ========================== */
@ -3334,9 +3373,6 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
/* Start app thread */
struct sys_thread *app_thread = sys_thread_alloc(&win32_app_thread_entry_point, 0, LIT("App thread"), PROF_THREAD_GROUP_APP);
/* Start test thread */
struct sys_thread *test_thread = sys_thread_alloc(test_entry, 0, LIT("Test thread"), PROF_THREAD_GROUP_APP);
/* Get app thread handle */
HANDLE app_thread_handle = 0;
{
@ -3360,6 +3396,17 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
ASSERT(res != WAIT_FAILED);
(UNUSED)res;
}
}
/* Run exit callbacks */
{
__profn("Run exit callbacks");
i32 num_funcs = atomic_i32_fetch(&G.num_exit_funcs);
for (i32 i = num_funcs - 1; i >= 0; --i) {
sys_exit_func *func = G.exit_funcs[i];
func();
}
}
/* Shutdown test thread */
{
@ -3369,7 +3416,6 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
snc_unlock(&lock);
}
sys_thread_wait_release(test_thread);
}
/* Find any dangling threads that haven't exited gracefully by now */
if (!atomic_i32_fetch(&G.panicking)) {

View File

@ -50,11 +50,11 @@ struct console_log {
GLOBAL struct {
struct atomic_i32 shutdown;
struct snc_counter shutdown_job_counters;
struct sys_window *window;
struct sim_ctx *local_sim_ctx;
struct arena *arena;
struct sys_window *window;
struct string connect_address_str;
struct sim_client_store *user_client_store;
@ -190,14 +190,11 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
* Startup
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown);
INTERNAL SYS_EXIT_FUNC(user_shutdown);
INTERNAL LOG_EVENT_CALLBACK_FUNC_DEF(debug_console_log_callback, log);
INTERNAL SYS_JOB_DEF(user_job, _);
INTERNAL SYS_JOB_DEF(local_sim_job , _);
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event);
struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
struct font_startup_receipt *font_sr,
struct user_startup_receipt user_startup(struct font_startup_receipt *font_sr,
struct sprite_startup_receipt *sprite_sr,
struct draw_startup_receipt *draw_sr,
struct asset_cache_startup_receipt *asset_cache_sr,
@ -205,10 +202,8 @@ struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
struct mixer_startup_receipt *mixer_sr,
struct host_startup_receipt *host_sr,
struct sim_startup_receipt *sim_sr,
struct string connect_address_str,
struct sys_window *window)
struct string connect_address_str)
{
(UNUSED)gp_sr;
(UNUSED)font_sr;
(UNUSED)sprite_sr;
(UNUSED)draw_sr;
@ -249,21 +244,19 @@ struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
//log_register_callback(debug_console_log_callback, LOG_LEVEL_SUCCESS);
log_register_callback(debug_console_log_callback, LOG_LEVEL_DEBUG);
G.window = window;
sys_window_register_event_callback(G.window, &window_event_callback);
/* Start jobs */
sys_run(1, local_sim_job, 0, SYS_PRIORITY_HIGH, &G.shutdown_job_counters);
sys_run(1, user_job, 0, SYS_PRIORITY_HIGH, &G.shutdown_job_counters);
app_register_exit_callback(&user_shutdown);
sys_on_exit(&user_shutdown);
G.window = sys_window_alloc(user_event_job, user_present_job);
return (struct user_startup_receipt) { 0 };
}
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown)
INTERNAL SYS_EXIT_FUNC(user_shutdown)
{
__prof;
sys_window_unregister_event_callback(G.window, &window_event_callback);
sys_window_release(G.window);
/* Signal shutdown */
atomic_i32_fetch_set(&G.shutdown, 1);
/* Wait for jobs shutdown */
@ -289,11 +282,13 @@ INTERNAL struct sys_event_array pop_sys_events(struct arena *arena)
return array;
}
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event)
SYS_JOB_DEF(user_event_job, job)
{
__prof;
struct sys_window_event_job_sig *sig = job.sig;
struct snc_lock lock = snc_lock_e(&G.sys_events_mutex);
{
*arena_push_no_zero(G.sys_events_arena, struct sys_event) = event;
*arena_push_no_zero(G.sys_events_arena, struct sys_event) = sig->event;
}
snc_unlock(&lock);
}
@ -598,9 +593,12 @@ INTERNAL SORT_COMPARE_FUNC_DEF(ent_draw_order_cmp, arg_a, arg_b, udata)
* Update
* ========================== */
INTERNAL void user_update(void)
SYS_JOB_DEF(user_present_job, job)
{
__prof;
struct sys_window_present_job_sig *sig = job.sig;
struct sys_window *window = sig->window;
struct arena_temp scratch = scratch_begin_no_conflict();
/* ========================== *
@ -609,7 +607,7 @@ INTERNAL void user_update(void)
G.real_dt_ns = sys_time_ns() - G.real_time_ns;
G.real_time_ns += G.real_dt_ns;
G.screen_size = sys_window_get_size(G.window);
G.screen_size = sys_window_get_size(window);
struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
@ -838,9 +836,9 @@ INTERNAL void user_update(void)
{
struct bind_state state = G.bind_states[USER_BIND_KIND_FULLSCREEN];
if (state.num_presses) {
struct sys_window_settings settings = sys_window_get_settings(G.window);
struct sys_window_settings settings = sys_window_get_settings(window);
settings.flags ^= SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN;
sys_window_update_settings(G.window, &settings);
sys_window_update_settings(window, &settings);
}
}
@ -2082,7 +2080,8 @@ INTERNAL void user_update(void)
}
/* Present user texture */
gp_present(G.window, backbuffer_resolution, G.user_texture, XFORM_TRS(.t = v2_mul(G.screen_size, 0.5), .s = G.user_size), VSYNC_ENABLED);
struct gp_swapchain *swapchain = sys_window_get_swapchain(window);
gp_present(swapchain, backbuffer_resolution, G.user_texture, XFORM_TRS(.t = v2_mul(G.screen_size, 0.5), .s = G.user_size), VSYNC);
}
/* ========================== *
@ -2094,25 +2093,8 @@ INTERNAL void user_update(void)
scratch_end(scratch);
}
/* ========================== *
* User thread
* ========================== */
INTERNAL SYS_JOB_DEF(user_job, _)
{
(UNUSED)_;
i64 last_frame_ns = 0;
i64 target_dt_ns = NS_FROM_SECONDS(USER_FPS_LIMIT > (0) ? (1.0 / USER_FPS_LIMIT) : 0);
while (!atomic_i32_fetch(&G.shutdown)) {
{
__profn("User sleep");
sleep_frame(last_frame_ns, target_dt_ns);
}
last_frame_ns = sys_time_ns();
user_update();
}
}
/* ========================== *
* Local sim thread

View File

@ -1,8 +1,8 @@
#ifndef USER_H
#define USER_H
struct sys_window;
struct gp_startup_receipt;
#include "sys.h"
struct font_startup_receipt;
struct sprite_startup_receipt;
struct draw_startup_receipt;
@ -60,8 +60,7 @@ enum user_bind_kind {
};
struct user_startup_receipt { i32 _; };
struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
struct font_startup_receipt *font_sr,
struct user_startup_receipt user_startup(struct font_startup_receipt *font_sr,
struct sprite_startup_receipt *sprite_sr,
struct draw_startup_receipt *draw_sr,
struct asset_cache_startup_receipt *asset_cache_sr,
@ -69,7 +68,9 @@ struct user_startup_receipt user_startup(struct gp_startup_receipt *gp_sr,
struct mixer_startup_receipt *mixer_sr,
struct host_startup_receipt *host_sr,
struct sim_startup_receipt *sim_sr,
struct string connect_address_str,
struct sys_window *window);
struct string connect_address_str);
SYS_JOB_DEF(user_event_job, job);
SYS_JOB_DEF(user_present_job, job);
#endif