diff --git a/src/app.c b/src/app.c index a39aeda3..827507e0 100644 --- a/src/app.c +++ b/src/app.c @@ -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); diff --git a/src/app.h b/src/app.h index b0c66be8..cd95a058 100644 --- a/src/app.h +++ b/src/app.h @@ -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 diff --git a/src/config.h b/src/config.h index 910c37b2..9787e1ec 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/draw.c b/src/draw.c index b3ad691e..8a239704 100644 --- a/src/draw.c +++ b/src/draw.c @@ -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); diff --git a/src/draw.h b/src/draw.h index 3e2ebb07..c02752d1 100644 --- a/src/draw.h +++ b/src/draw.h @@ -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 diff --git a/src/font.c b/src/font.c index b9fa5d9b..36a8463f 100644 --- a/src/font.c +++ b/src/font.c @@ -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; diff --git a/src/font.h b/src/font.h index cef1c00d..45933ecf 100644 --- a/src/font.h +++ b/src/font.h @@ -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); diff --git a/src/gp.h b/src/gp.h index 76ba97f4..e8e4da93 100644 --- a/src/gp.h +++ b/src/gp.h @@ -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 diff --git a/src/gp_dx12.c b/src/gp_dx12.c index 2f712bf6..7ba061e6 100644 --- a/src/gp_dx12.c +++ b/src/gp_dx12.c @@ -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,7 +2569,103 @@ 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) +{ + for (u32 i = 0; i < countof(swapchain->buffers); ++i) { + 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 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); + } + + /* Create swapchain1 */ + IDXGISwapChain1 *swapchain1 = 0; + { + DXGI_SWAP_CHAIN_DESC1 desc = ZI; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.Width = resolution.x; + desc.Height = resolution.y; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = DX12_SWAPCHAIN_BUFFER_COUNT; + desc.Scaling = DXGI_SCALING_NONE; + desc.Flags = DX12_SWAPCHAIN_FLAGS; + desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + hr = IDXGIFactory2_CreateSwapChainForHwnd(G.factory, (IUnknown *)cq->cq, hwnd, &desc, 0, 0, &swapchain1); + if (FAILED(hr)) { + sys_panic(LIT("Failed to create IDXGISwapChain1")); + } + } + + /* Upgrade to swapchain3 */ + hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain3, (void **)&swapchain->swapchain); + if (FAILED(hr)) { + sys_panic(LIT("Failed to create IDXGISwapChain3")); + } + + /* Create waitable object */ +#if DX12_WAIT_FRAME_LATENCY + IDXGISwapChain3_SetMaximumFrameLatency(swapchain->swapchain, 1); + swapchain->waitable = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain->swapchain); + ASSERT(swapchain->waitable); +#endif + + /* Disable Alt+Enter changing monitor resolution to match window size */ + IDXGIFactory_MakeWindowAssociation(G.factory, hwnd, DXGI_MWA_NO_ALT_ENTER); + + 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); @@ -2569,98 +2674,38 @@ INTERNAL struct swapchain_buffer *update_swapchain(struct swapchain *swapchain, 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); + /* 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); { - /* 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); - if (FAILED(hr)) { - /* TODO: Don't panic */ - sys_panic(LIT("Failed to resize swapchain")); - } - } - snc_unlock(&lock); - } else { - /* Create swapchain1 */ - IDXGISwapChain1 *swapchain1 = 0; - { - DXGI_SWAP_CHAIN_DESC1 desc = ZI; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.Width = resolution.x; - desc.Height = resolution.y; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = DX12_SWAPCHAIN_BUFFER_COUNT; - desc.Scaling = DXGI_SCALING_NONE; - desc.Flags = DX12_SWAPCHAIN_FLAGS; - desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - hr = IDXGIFactory2_CreateSwapChainForHwnd(G.factory, (IUnknown *)cq->cq, hwnd, &desc, 0, 0, &swapchain1); - if (FAILED(hr)) { - sys_panic(LIT("Failed to create IDXGISwapChain1")); - } + HANDLE event = CreateEvent(0, 0, 0, 0); + ID3D12Fence_SetEventOnCompletion(cq->submit_fence, cq->submit_fence_target, event); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); } - /* Upgrade to swapchain3 */ - hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain3, (void **)&swapchain->swapchain); - if (FAILED(hr)) { - sys_panic(LIT("Failed to create IDXGISwapChain3")); + /* 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); } - /* Create waitable object */ -#if DX12_WAIT_FRAME_LATENCY - IDXGISwapChain3_SetMaximumFrameLatency(swapchain->swapchain, 1); - swapchain->waitable = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain->swapchain); - ASSERT(swapchain->waitable); -#endif - - /* Disable Alt+Enter changing monitor resolution to match window size */ - IDXGIFactory_MakeWindowAssociation(G.factory, hwnd, DXGI_MWA_NO_ALT_ENTER); - - IDXGISwapChain1_Release(swapchain1); - swapchain->hwnd = hwnd; - } - - /* Allocate swapchain resources */ - for (u32 i = 0; i < countof(swapchain->buffers); ++i) { - ID3D12Resource *resource = 0; - hr = IDXGISwapChain3_GetBuffer(swapchain->swapchain, i, &IID_ID3D12Resource, (void **)&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,17 +2835,20 @@ 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); + { + 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); - } } /* ========================== * diff --git a/src/playback_wasapi.c b/src/playback_wasapi.c index 42cc28b0..d8e9ba4d 100644 --- a/src/playback_wasapi.c +++ b/src/playback_wasapi.c @@ -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); diff --git a/src/resource.c b/src/resource.c index 1f7e09de..b0e81eb2 100644 --- a/src/resource.c +++ b/src/resource.c @@ -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); diff --git a/src/sprite.c b/src/sprite.c index 167dd945..6c5de8e6 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -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) { diff --git a/src/sprite.h b/src/sprite.h index feac7fe3..3f2946e1 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -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 diff --git a/src/sys.h b/src/sys.h index f8d20d76..a3e0eba0 100644 --- a/src/sys.h +++ b/src/sys.h @@ -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 diff --git a/src/sys_win32.c b/src/sys_win32.c index 0aabd1a4..ee7799f9 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -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) { - __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); + if (window->event_job) { + __prof; + 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,17 +3396,27 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, ASSERT(res != WAIT_FAILED); (UNUSED)res; } - - /* Shutdown test thread */ - { - struct snc_lock lock = snc_lock_e(&G.workers_wake_mutex); - atomic_i32_fetch_set(&G.workers_shutdown.v, 1); - snc_cv_signal(&G.workers_wake_cv, I32_MAX); - snc_unlock(&lock); - } - sys_thread_wait_release(test_thread); } + /* 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 */ + { + struct snc_lock lock = snc_lock_e(&G.workers_wake_mutex); + atomic_i32_fetch_set(&G.workers_shutdown.v, 1); + snc_cv_signal(&G.workers_wake_cv, I32_MAX); + 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)) { struct snc_lock lock = snc_lock_s(&G.threads_mutex); diff --git a/src/user.c b/src/user.c index ee975a3c..1e39b0a2 100644 --- a/src/user.c +++ b/src/user.c @@ -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 diff --git a/src/user.h b/src/user.h index ad5323b3..5819f730 100644 --- a/src/user.h +++ b/src/user.h @@ -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