diff --git a/src/base/base_bitbuff.c b/src/base/base_bitbuff.c index e71a556f..5590b9ce 100644 --- a/src/base/base_bitbuff.c +++ b/src/base/base_bitbuff.c @@ -115,7 +115,9 @@ b32 BB_CheckWriterOverflowBits(BB_Writer *bw, u64 num_bits) if (bytes_needed > max_len) { /* Writer overflowed fixed buffer */ +#if BITBUFF_DEBUG Assert(0); +#endif result = 1; bw->cur_bit = max_len << 3; bw->overflowed = 1; @@ -447,7 +449,9 @@ b32 BB_CheckReaderOverflowBits(BB_Reader *br, u64 num_bits) if (bits_needed > base_len_bits) { /* Tried to read past bitbuff memory */ +#if BITBUFF_DEBUG Assert(0); +#endif result = 1; br->cur_bit = base_len_bits; br->overflowed = 1; diff --git a/src/gpu/gpu.lay b/src/gpu/gpu.lay index c9642760..d6e91344 100644 --- a/src/gpu/gpu.lay +++ b/src/gpu/gpu.lay @@ -1,6 +1,7 @@ @Layer gpu //- Dependencies +@Dep window @Dep platform //- Api diff --git a/src/gpu/gpu_core.h b/src/gpu/gpu_core.h index a2df9cb9..ee6fc609 100644 --- a/src/gpu/gpu_core.h +++ b/src/gpu/gpu_core.h @@ -3,6 +3,7 @@ Struct(GPU_Resource); Struct(GPU_CommandList); +Struct(GPU_Swapchain); //////////////////////////////////////////////////////////// //~ Queue types @@ -468,3 +469,19 @@ void GPU_CopyBytesToFootprint(void *dst, void *src, GPU_Resource *footprint_refe //~ @hookdecl Memory info operations GPU_MemoryInfo GPU_QueryMemoryInfo(void); + +//////////////////////////////////////////////////////////// +//~ @hookdecl Swapchain operations + +GPU_Swapchain *GPU_AcquireSwapchain(WND_Handle window, GPU_Format format, Vec2I32 size); +void GPU_ReleaseSwapchain(GPU_Swapchain *swapchain); + +/* Waits until a new backbuffer is ready to be written to. + * This should be called before rendering for minimum latency. */ +void GPU_YieldOnSwapchain(GPU_Swapchain *swapchain); + +/* 1. Ensures the backbuffer size matches `backbuffer_size` + * 2. Blits `texture` into position `dst` in the backbuffer + * 3. Presents the backbuffer + * 4. Returns the value that the Direct queue fence will reach once GPU completes blitting (`texture` shouldn't be released while blit is in flight) */ +i64 GPU_PresentSwapchain(GPU_Swapchain *swapchain, GPU_Resource *texture, Vec2I32 backbuffer_size, Vec2I32 dst, i32 vsync); diff --git a/src/gpu/gpu_dx12/gpu_dx12.c b/src/gpu/gpu_dx12/gpu_dx12.c index f20cde27..f872b612 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.c +++ b/src/gpu/gpu_dx12/gpu_dx12.c @@ -2036,12 +2036,13 @@ i64 GPU_D12_BlitToSwapchain(GPU_D12_SwapchainBuffer *dst, GPU_D12_Resource *text } //////////////////////////////////////////////////////////// -//~ Swapchain operations +//~ @hookdef Swapchain operations -GPU_D12_Swapchain *GPU_D12_AcquireSwapchain(HWND hwnd, GPU_Format format, Vec2I32 size) +GPU_Swapchain *GPU_AcquireSwapchain(WND_Handle window, GPU_Format format, Vec2I32 size) { GPU_D12_SharedState *g = &GPU_D12_shared_state; HRESULT hr = 0; + HWND hwnd = WND_W32_WindowFromHandle(window)->hwnd; GPU_D12_Queue *queue = GPU_D12_QueueFromKind(GPU_QueueKind_Direct); GPU_D12_Swapchain *swapchain = 0; @@ -2110,15 +2111,15 @@ GPU_D12_Swapchain *GPU_D12_AcquireSwapchain(HWND hwnd, GPU_Format format, Vec2I3 GPU_D12_InitSwapchainResources(swapchain); - return (GPU_D12_Swapchain *)swapchain; + return (GPU_Swapchain *)swapchain; } -void GPU_D12_ReleaseSwapchain(GPU_D12_Swapchain *swapchain) +void GPU_ReleaseSwapchain(GPU_Swapchain *gpu_swapchain) { /* TODO */ } -void GPU_D12_YieldOnSwapchain(GPU_D12_Swapchain *gpu_swapchain) +void GPU_YieldOnSwapchain(GPU_Swapchain *gpu_swapchain) { /* TODO: Actually yield, don't block */ GPU_D12_Swapchain *swapchain = (GPU_D12_Swapchain *)gpu_swapchain; @@ -2128,7 +2129,7 @@ void GPU_D12_YieldOnSwapchain(GPU_D12_Swapchain *gpu_swapchain) } } -i64 GPU_D12_PresentSwapchain(GPU_D12_Swapchain *gpu_swapchain, GPU_Resource *gpu_texture, Vec2I32 backbuffer_size, Vec2I32 dst, i32 vsync) +i64 GPU_PresentSwapchain(GPU_Swapchain *gpu_swapchain, GPU_Resource *gpu_texture, Vec2I32 backbuffer_size, Vec2I32 dst, i32 vsync) { GPU_D12_Swapchain *swapchain = (GPU_D12_Swapchain *)gpu_swapchain; GPU_D12_Resource *texture = (GPU_D12_Resource *)gpu_texture; @@ -2156,6 +2157,12 @@ i64 GPU_D12_PresentSwapchain(GPU_D12_Swapchain *gpu_swapchain, GPU_Resource *gpu present_flags |= DXGI_PRESENT_ALLOW_TEARING; } + if (vsync != 0) + { + /* FIXME: Don't flush in fullscreen mode? */ + // DwmFlush(); + } + /* Present */ { __profn("Present"); diff --git a/src/gpu/gpu_dx12/gpu_dx12.h b/src/gpu/gpu_dx12/gpu_dx12.h index bded8f9f..2a45097d 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.h +++ b/src/gpu/gpu_dx12/gpu_dx12.h @@ -13,7 +13,7 @@ #define GPU_D12_TearingIsAllowed 1 #define GPU_D12_FrameLatency 2 -#define GPU_D12_SwapchainBufferCount 3 +#define GPU_D12_SwapchainBufferCount 2 #define GPU_D12_SwapchainFlags (((GPU_D12_TearingIsAllowed != 0) * DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) \ | ((GPU_D12_FrameLatency != 0) * DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)) @@ -375,22 +375,6 @@ void GPU_D12_InitSwapchainResources(GPU_D12_Swapchain *swapchain); GPU_D12_SwapchainBuffer *GPU_D12_UpdateSwapchain(GPU_D12_Swapchain *swapchain, Vec2I32 resolution); i64 GPU_D12_BlitToSwapchain(GPU_D12_SwapchainBuffer *swapchain_buffer, GPU_D12_Resource *texture, Vec2I32 dst_pos); -//////////////////////////////////////////////////////////// -//~ Swapchain operations - -GPU_D12_Swapchain *GPU_D12_AcquireSwapchain(HWND hwnd, GPU_Format format, Vec2I32 size); -void GPU_D12_ReleaseSwapchain(GPU_D12_Swapchain *swapchain); - -/* Waits until a new backbuffer is ready to be written to. - * This should be called before rendering for minimum latency. */ -void GPU_D12_YieldOnSwapchain(GPU_D12_Swapchain *swapchain); - -/* 1. Ensures the backbuffer size matches `backbuffer_size` - * 2. Blits `texture` into position `dst` in the backbuffer - * 3. Presents the backbuffer - * 4. Returns the value that the Direct queue fence will reach once GPU completes blitting (`texture` shouldn't be released while blit is in flight) */ -i64 GPU_D12_PresentSwapchain(GPU_D12_Swapchain *swapchain, GPU_Resource *texture, Vec2I32 backbuffer_size, Vec2I32 dst, i32 vsync); - //////////////////////////////////////////////////////////// //~ Sync job diff --git a/src/gpu/gpu_dx12/gpu_dx12.lay b/src/gpu/gpu_dx12/gpu_dx12.lay index 47557a18..ffd9189f 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.lay +++ b/src/gpu/gpu_dx12/gpu_dx12.lay @@ -1,5 +1,8 @@ @Layer gpu_dx12 +//- Dependencies +@Dep window_win32 + //- Api @IncludeC gpu_dx12.h diff --git a/src/pp/pp.c b/src/pp/pp.c index 8e7d91a9..034af6ce 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -10,12 +10,12 @@ void StartupUser(void) SetGstat(GSTAT_DEBUG_STEPS, U64Max); - g->arena = AcquireArena(Gibi(64)); + Arena *perm = PermArena(); g->real_time_ns = TimeNs(); /* TODO: Remove this */ String connect_address_str = Lit(""); - g->connect_address_str = PushString(g->arena, connect_address_str); + g->connect_address_str = PushString(perm, connect_address_str); /* Initialize average dt to a reasonable value */ g->average_local_to_user_snapshot_publish_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; @@ -30,25 +30,26 @@ void StartupUser(void) g->local_to_user_client_store = AcquireClientStore(); g->local_to_user_client = AcquireClient(g->local_to_user_client_store); - /* Default persistent state */ - { - g->world_to_ui_xf = XformIdentity; - g->world_to_render_xf = XformIdentity; - // g->desired_window.flags = WND_Flag_Fullscreen; - g->desired_window.restore_p1 = VEC2I32(1920, 1080); - } - /* Init from swap */ if (IsSwappedIn()); { TempArena scratch = BeginScratchNoConflict(); - String swap_str = SwappedStateFromName(scratch.arena, Lit("pp_user")); + String swap_encoded = SwappedStateFromName(scratch.arena, Lit("pp_user")); + + BB_Buff bb = BB_BuffFromString(swap_encoded); + BB_Reader br = BB_ReaderFromBuff(&bb); + + String swap_str = BB_ReadString(scratch.arena, &br); + g->window_restore = BB_ReadString(perm, &br); + if (swap_str.len == sizeof(SwappedUserState)) { - SwappedUserState *swap = (SwappedUserState *)swap_str.text; + SwappedUserState *swap = PushStructNoZero(scratch.arena, SwappedUserState); + CopyBytes(swap, swap_str.text, swap_str.len); SharedUserState *old = &swap->s; CopyStructRegion(g, old, AUTO_PERSIST_START, AUTO_PERSIST_END); } + EndScratch(scratch); } @@ -80,10 +81,17 @@ ExitFuncDef(ShutdownUser) if (IsSwappingOut()) { TempArena scratch = BeginScratchNoConflict(); - - SwappedUserState *swap = PushStruct(scratch.arena, SwappedUserState); - swap->s = *g; - WriteSwappedState(Lit("pp_user"), StringFromStruct(swap)); + u64 max_size = Mebi(16); + u8 *bytes = PushStructsNoZero(scratch.arena, u8, max_size); + BB_Buff bb = BB_BuffFromString(STRING(max_size, bytes)); + { + BB_Writer bw = BB_WriterFromBuff(&bb); + SwappedUserState *swap = PushStruct(scratch.arena, SwappedUserState); + swap->s = *g; + BB_WriteString(&bw, StringFromStruct(swap)); + BB_WriteString(&bw, g->window_restore); + WriteSwappedState(Lit("pp_user"), STRING(BB_GetNumBytesWritten(&bw), BB_GetWrittenRaw(&bw))); + } EndScratch(scratch); } } @@ -487,24 +495,39 @@ void UpdateUser(void) SharedUserState *g = &shared_user_state; TempArena scratch = BeginScratchNoConflict(); - //- Begin frame - WND_Event window_event = WND_BeginUpdate(scratch.arena); - - u64 inputs_count = window_event.inputs_count; - Input *inputs = window_event.inputs; - g->real_dt_ns = TimeNs() - g->real_time_ns; g->real_time_ns += g->real_dt_ns; - //- Begin window - WND_Settings cur_window = window_event.settings; - if (window_event.os_gen > g->window_os_gen && window_event.cmd_gen > 0) + if (g->swapchain) { - /* Window was modified outside of program. Reflect this in our own state. */ - g->desired_window = cur_window; + GPU_YieldOnSwapchain(g->swapchain); } - g->window_os_gen = window_event.os_gen; - g->screen_size = SubVec2I32(window_event.draw_p1, window_event.draw_p0); + + //- Begin window frame + WND_Frame window_frame = WND_BeginFrame(scratch.arena); + if (g->user_tick == 0) + { + String restore = g->window_restore; + if (restore.len > 0) + { + WND_PushCmd(window_frame, .kind = WND_CmdKind_Restore, .restore = restore); + } + } + { + String src = window_frame.restore; + if (src.len > g->window_restore.len) + { + Arena *perm = PermArena(); + g->window_restore = PushString(perm, src); + } + CopyBytes(g->window_restore.text, src.text, src.len); + g->window_restore.len = src.len; + } + + u64 inputs_count = window_frame.inputs_count; + Input *inputs = window_frame.inputs; + + g->screen_size = window_frame.draw_size; g->screen_size.x = MaxI32(g->screen_size.x, 1); g->screen_size.y = MaxI32(g->screen_size.y, 1); @@ -760,7 +783,7 @@ void UpdateUser(void) { if (g->bind_states[BindKind_Fullscreen].num_presses && g->bind_states[BindKind_FullscreenMod].is_held) { - g->desired_window.flags ^= WND_Flag_Fullscreen; + WND_PushCmd(window_frame, .kind = WND_CmdKind_SetFullscreen, .v = !window_frame.fullscreen); } } @@ -771,7 +794,7 @@ void UpdateUser(void) if (g->bind_states[BindKind_DebugToggleTopmost].num_presses > 0) { - g->desired_window.flags ^= WND_Flag_ForcedTop; + WND_PushCmd(window_frame, .kind = WND_CmdKind_SetForcedTop, .v = !window_frame.forced_top); P_LogSuccessF("Toggle topmost"); } @@ -2352,18 +2375,18 @@ void UpdateUser(void) g->gpu_submit_fence_target = UI_EndBuild(g->ui_target); ////////////////////////////// - //- End window update + //- Present & end frame + { - WND_Cmd cmd = ZI; - cmd.desired_settings = g->desired_window; - cmd.texture = g->ui_target; - cmd.vsync = VSYNC; + if (!g->swapchain) { - Vec2 backbuffer_dst_f = MulXformV2(g->ui_to_screen_xf, VEC2(0, 0)); - cmd.backbuffer_dst = VEC2I32(RoundF32ToI32(backbuffer_dst_f.x), RoundF32ToI32(backbuffer_dst_f.y)); + g->swapchain = GPU_AcquireSwapchain(window_frame.window_handle, GPU_Format_R8G8B8A8_Unorm, g->screen_size); } - g->gpu_submit_fence_target = WND_EndUpdateAndPresent(cmd); + Vec2 backbuffer_dst_f = MulXformV2(g->ui_to_screen_xf, VEC2(0, 0)); + Vec2I32 backbuffer_dst = VEC2I32(RoundF32ToI32(backbuffer_dst_f.x), RoundF32ToI32(backbuffer_dst_f.y)); + g->gpu_submit_fence_target = GPU_PresentSwapchain(g->swapchain, g->ui_target, g->screen_size, backbuffer_dst, VSYNC); } + WND_EndFrame(window_frame); ++g->user_tick; EndScratch(scratch); diff --git a/src/pp/pp.h b/src/pp/pp.h index d78b02fe..f16dcd3c 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -240,14 +240,16 @@ Struct(SharedUserState) i64 real_dt_ns; i64 real_time_ns; + //- Window + + WND_Handle window; + GPU_Swapchain *swapchain; + String window_restore; + ////////////////////////////// //- Persist start StructRegion(AUTO_PERSIST_START); - //- Window - - WND_Settings desired_window; - //- Debug camera EntityId debug_following; diff --git a/src/window/window.h b/src/window/window.h index 010bdec7..24713a60 100644 --- a/src/window/window.h +++ b/src/window/window.h @@ -1,55 +1,64 @@ //////////////////////////////////////////////////////////// -//~ Window types +//~ Handle types -Enum(WND_Flag) +Struct(WND_Handle) { - WND_Flag_None = (0), - WND_Flag_ForcedTop = (1 << 0), - WND_Flag_RestoreToMaximized = (1 << 1), - WND_Flag_Fullscreen = (1 << 2), - WND_Flag_Maximized = (1 << 3), - WND_Flag_Minimized = (1 << 4), + u64 v; }; -Struct(WND_Settings) +//////////////////////////////////////////////////////////// +//~ Cmd types + +Enum(WND_CmdKind) { - WND_Flag flags; - Vec2I32 restore_p0; - Vec2I32 restore_p1; -}; - -Struct(WND_Event) -{ - WND_Settings settings; - u64 inputs_count; - Input *inputs; - - Vec2I32 monitor_p0; - Vec2I32 monitor_p1; - - Vec2I32 draw_p0; - Vec2I32 draw_p1; - - u64 cmd_gen; /* How many times has a window cmd changed the window state */ - u64 os_gen; /* How many times has the OS changed window state (e.g. mouse drags OS window border) */ + WND_CmdKind_None, + WND_CmdKind_SetMinimized, + WND_CmdKind_SetMaximized, + WND_CmdKind_SetFullscreen, + WND_CmdKind_SetForcedTop, + WND_CmdKind_Restore, }; Struct(WND_Cmd) { - WND_Settings desired_settings; - - GPU_Resource *texture; - Vec2I32 backbuffer_dst; - i32 vsync; + WND_CmdKind kind; + String restore; + b32 v; }; //////////////////////////////////////////////////////////// -//~ @hookdecl Startup hooks +//~ Frame types + +Struct(WND_Frame) +{ + WND_Handle window_handle; + + /* User input since last update */ + u64 inputs_count; + Input *inputs; + + /* Window info */ + Vec2I32 draw_size; + String restore; + b32 minimized; + b32 maximized; + b32 fullscreen; + b32 forced_top; +}; + +//////////////////////////////////////////////////////////// +//~ @hookdecl Startup void WND_Startup(void); //////////////////////////////////////////////////////////// -//~ @hookdecl Window hooks +//~ @hookdecl Command operations -WND_Event WND_BeginUpdate(Arena *arena); -i64 WND_EndUpdateAndPresent(WND_Cmd cmd); +#define WND_PushCmd(frame, ...) WND_PushCmd_((frame), (WND_Cmd) { __VA_ARGS__ }) +void WND_PushCmd_(WND_Frame frame, WND_Cmd desc); + +//////////////////////////////////////////////////////////// +//~ @hookdecl Frame + +WND_Frame WND_BeginFrame(Arena *arena); +void WND_EndFrame(WND_Frame frame); diff --git a/src/window/window.lay b/src/window/window.lay index 50d33b16..8daddfc9 100644 --- a/src/window/window.lay +++ b/src/window/window.lay @@ -1,8 +1,5 @@ @Layer window -//- Dependencies -@Dep gpu - //- Api @IncludeC window.h diff --git a/src/window/window_win32/window_win32.c b/src/window/window_win32/window_win32.c index d4a171df..99024f55 100644 --- a/src/window/window_win32/window_win32.c +++ b/src/window/window_win32/window_win32.c @@ -90,6 +90,14 @@ void WND_Startup(void) RunJob(WND_W32_ProcessMessagesForever, .pool = message_job_pool); } +//////////////////////////////////////////////////////////// +//~ Window helpers + +WND_W32_Window *WND_W32_WindowFromHandle(WND_Handle handle) +{ + return (WND_W32_Window *)handle.v; +} + //////////////////////////////////////////////////////////// //~ Initialization @@ -133,6 +141,7 @@ JobDef(WND_W32_ProcessMessagesForever, sig, id) DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode)); /* Set window as userdata */ + /* FIXME: Necessary? */ SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window); } window->hwnd = hwnd; @@ -370,6 +379,7 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l EndScratch(scratch); } break; +#if 0 //- Process command case WND_CmdMsgId: { @@ -543,25 +553,7 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l window->first_shown = 1; } } break; - - //- Detect changes - case WM_WINDOWPOSCHANGED: - { - if (window->cmd_depth == 0) - { - /* This change occurred outside of a user window command, so it must come from the OS */ - Atomic64FetchAdd(&window->os_gen, 1); - P_LogInfoF("OS change"); - } - else - { - /* FIXME: In order for this to truly be the result of a user cmd we should clear the message queue after cmd submission */ - P_LogDebugF("Cmd change"); - Atomic64FetchAdd(&window->cmd_gen, 1); - --window->cmd_depth; - } - result = DefWindowProcW(hwnd, msg, wparam, lparam); - } break; +#endif } } @@ -569,25 +561,44 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l } //////////////////////////////////////////////////////////// -//~ @hookdef Update +//~ @hookdef Cmds -WND_Event WND_BeginUpdate(Arena *arena) +void WND_PushCmd_(WND_Frame frame, WND_Cmd desc) +{ + WND_W32_Window *window = WND_W32_WindowFromHandle(frame.window_handle); + WND_W32_CmdNode *n = PushStruct(window->cmds_arena, WND_W32_CmdNode); + n->cmd = desc; + QueuePush(window->first_cmd, window->last_cmd, n); + if (desc.kind == WND_CmdKind_Restore) + { + n->cmd.restore = PushString(window->cmds_arena, desc.restore); + } +} + +//////////////////////////////////////////////////////////// +//~ @hookdef Frame + +WND_Frame WND_BeginFrame(Arena *arena) { WND_W32_SharedState *g = &WND_W32_shared_state; WND_W32_Window *window = &g->window; - WND_Event result = ZI; + WND_Frame result = ZI; while (!Atomic32Fetch(&window->is_ready)) { _mm_pause(); } HWND hwnd = window->hwnd; + result.window_handle.v = (u64)window; - /* Wait on swapchain */ - if (window->swapchain) + /* Reset cmds */ + if (!window->cmds_arena) { - GPU_D12_YieldOnSwapchain(window->swapchain); + window->cmds_arena = AcquireArena(Gibi(64)); } + ResetArena(window->cmds_arena); + window->first_cmd = 0; + window->last_cmd = 0; /* Pop inputs */ { @@ -603,124 +614,199 @@ WND_Event WND_BeginUpdate(Arena *arena) } /* Grab monitor info */ + RECT monitor_rect = ZI; { MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); - RECT monitor_rect = monitor_info.rcMonitor; - result.monitor_p0.x = monitor_rect.left; - result.monitor_p0.y = monitor_rect.top; - result.monitor_p1.x = monitor_rect.right; - result.monitor_p1.y = monitor_rect.bottom; + monitor_rect = monitor_info.rcMonitor; } - /* Grab window info */ - WND_Settings settings = ZI; + /* Client rect */ + RECT client_rect = ZI; + GetClientRect(hwnd, (LPRECT)&client_rect); + + /* Screen rect */ + RECT screen_rect = client_rect; + ClientToScreen(hwnd, (LPPOINT)&screen_rect.left); + ClientToScreen(hwnd, (LPPOINT)&screen_rect.right); + result.draw_size = VEC2I32(screen_rect.right - screen_rect.left, screen_rect.bottom - screen_rect.top); + + /* Minimized / maximized / fullscreen */ + DWORD style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); + DWORD ex_style = (DWORD)GetWindowLongPtr(hwnd, GWL_EXSTYLE); + WINDOWPLACEMENT placement = { .length = sizeof(placement) }; + GetWindowPlacement(hwnd, &placement); { - /* Draw size */ - { - RECT screen_rect = ZI; - GetClientRect(hwnd, (LPRECT)&screen_rect); - ClientToScreen(hwnd, (LPPOINT)&screen_rect.left); - ClientToScreen(hwnd, (LPPOINT)&screen_rect.right); - result.draw_p0 = VEC2I32(screen_rect.left, screen_rect.top); - result.draw_p1 = VEC2I32(screen_rect.right, screen_rect.bottom); - } - - /* Clamp */ - // { - // RECT virt_rect = ZI; - // virt_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); - // virt_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); - // virt_rect.right = virt_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN); - // virt_rect.bottom = virt_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN); - // result.draw_p0.x = MaxI32(result.draw_p0.x, virt_rect.left); - // result.draw_p0.y = MaxI32(result.draw_p0.y, virt_rect.top); - // result.draw_p1.x = MinI32(result.draw_p1.x, virt_rect.right); - // result.draw_p1.y = MinI32(result.draw_p1.y, virt_rect.bottom); - // } - // result.draw_p1.x = MaxI32(result.draw_p1.x, result.draw_p0.x + 320); - // result.draw_p1.y = MaxI32(result.draw_p1.y, result.draw_p0.y + 180); - - /* Minimized / maximized */ - WINDOWPLACEMENT placement = { .length = sizeof(placement) }; - GetWindowPlacement(hwnd, &placement); if (placement.showCmd == SW_MAXIMIZE) { - settings.flags |= WND_Flag_Maximized; + result.maximized = 1; } if (placement.showCmd == SW_MINIMIZE) { - settings.flags |= WND_Flag_Minimized; + result.minimized = 1; } - if (placement.flags & WPF_RESTORETOMAXIMIZED) + if (screen_rect.left == monitor_rect.left && + screen_rect.top == monitor_rect.top && + screen_rect.right == monitor_rect.right && + screen_rect.bottom == monitor_rect.bottom) { - settings.flags |= WND_Flag_RestoreToMaximized; + result.fullscreen = 1; } - - /* Restore size */ - { - RECT placement_rect = placement.rcNormalPosition; - settings.restore_p0 = VEC2I32(placement_rect.left, placement_rect.top); - settings.restore_p1 = VEC2I32(placement_rect.right, placement_rect.bottom); - } - - /* Fullscreen */ - /* TODO: Verify WS_POPUP as well */ - if (EqVec2I32(result.draw_p0, result.monitor_p0) && EqVec2I32(result.draw_p1, result.monitor_p1)) - { - settings.flags |= WND_Flag_Fullscreen; - } - - result.settings = settings; } - result.os_gen = Atomic64Fetch(&window->os_gen); - result.cmd_gen = Atomic64Fetch(&window->cmd_gen); + result.forced_top = window->is_forced_top; + result.fullscreen = window->is_fullscreen; + + /* Generate restore data */ + { + WND_W32_RestorableData restore = ZI; + { + restore.magic = WND_W32_RestoreMagic; + restore.placement = placement; + restore.style = style; + restore.ex_style = ex_style; + restore.is_forced_top = window->is_forced_top; + restore.is_fullscreen = window->is_fullscreen; + restore.fullscreen_restore_rect = window->fullscreen_restore_rect; + if (IsWindowArranged(hwnd)) + { + restore.is_snapped = 1; + restore.snapped_screen_rect = screen_rect; + } + } + result.restore = PushString(window->cmds_arena, StringFromStruct(&restore)); + } - window->update_begin_event = result; return result; } -i64 WND_EndUpdateAndPresent(WND_Cmd cmd) +void WND_EndFrame(WND_Frame frame) { + TempArena scratch = BeginScratchNoConflict(); WND_W32_SharedState *g = &WND_W32_shared_state; - WND_W32_Window *window = &g->window; + WND_W32_Window *window = WND_W32_WindowFromHandle(frame.window_handle); HWND hwnd = window->hwnd; - WND_Event begin_event = window->update_begin_event; - WND_Settings draw_settings = begin_event.settings; - Vec2I32 draw_size = SubVec2I32(begin_event.draw_p1, begin_event.draw_p0); - - if (!window->swapchain) + /* Process cmds */ + for (WND_W32_CmdNode *n = window->first_cmd; n; n = n->next) { - window->swapchain = GPU_D12_AcquireSwapchain(window->hwnd, GPU_Format_R8G8B8A8_Unorm, draw_size); - } - - /* Present */ - i64 present_fence_target = 0; - if (cmd.texture != 0) - { - Vec2I32 backbuffer_dst = cmd.backbuffer_dst; - if (cmd.vsync != 0) + WND_Cmd cmd = n->cmd; + switch(cmd.kind) { - /* FIXME: Don't flush in fullscreen mode? */ - DwmFlush(); + //- Minimize + case WND_CmdKind_SetMinimized: + { + ShowWindow(hwnd, SW_MINIMIZE); + } break; + + //- Maximize + case WND_CmdKind_SetMaximized: + { + ShowWindow(hwnd, SW_MAXIMIZE); + } break; + + //- Fullscreen + case WND_CmdKind_SetFullscreen: + { + if (cmd.v != window->is_fullscreen) + { + DWORD old_style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); + RECT old_rect = ZI; + { + GetClientRect(hwnd, (LPRECT)&old_rect); + ClientToScreen(hwnd, (LPPOINT)&old_rect.left); + ClientToScreen(hwnd, (LPPOINT)&old_rect.right); + AdjustWindowRect(&old_rect, old_style, 0); + } + RECT new_rect = ZI; + HWND new_hwnd = 0; + DWORD new_style = old_style; + if (cmd.v) + { + /* Enter fullscreen */ + { + MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; + GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); + new_rect = monitor_info.rcMonitor; + } + new_style &= ~WS_OVERLAPPEDWINDOW; + new_style |= WS_POPUP; + window->fullscreen_restore_rect = old_rect; + new_hwnd = HWND_TOP; + } + else + { + /* Exit fullscreen */ + new_rect = window->fullscreen_restore_rect; + new_style &= ~WS_POPUP; + new_style |= WS_OVERLAPPEDWINDOW; + new_hwnd = window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST; + } + window->is_fullscreen = cmd.v; + SetWindowLongPtr(hwnd, GWL_STYLE, new_style); + SetWindowPos(hwnd, new_hwnd, new_rect.left, new_rect.top, new_rect.right - new_rect.left, new_rect.bottom - new_rect.top, 0); + } + } break; + + //- Force top + case WND_CmdKind_SetForcedTop: + { + window->is_forced_top = cmd.v; + SetWindowPos(hwnd, window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } break; + + //- Restore + case WND_CmdKind_Restore: + { + /* FIXME: Cap bounds */ + String restore_str = cmd.restore; + if (restore_str.len == sizeof(WND_W32_RestorableData)) + { + WND_W32_RestorableData *restore = PushStructNoZero(scratch.arena, WND_W32_RestorableData); + CopyBytes(restore, restore_str.text, restore_str.len); + if (restore->magic == WND_W32_RestoreMagic) + { + WINDOWPLACEMENT placement = restore->placement; + SetWindowPlacement(hwnd, &placement); + SetWindowLongPtr(hwnd, GWL_STYLE, restore->style); + SetWindowLongPtr(hwnd, GWL_EXSTYLE, restore->ex_style); + + if (restore->is_fullscreen) + { + MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; + GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); + RECT rect = monitor_info.rcMonitor; + window->is_fullscreen = 1; + window->fullscreen_restore_rect = restore->fullscreen_restore_rect; + SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0); + } + + if (restore->is_forced_top) + { + window->is_forced_top = 1; + SetWindowPos(hwnd, window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + + if (restore->is_snapped) + { + RECT rect = restore->snapped_screen_rect; + HWND pos_hwnd = window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(hwnd, pos_hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0); + } + } + } + } break; } - present_fence_target = GPU_D12_PresentSwapchain(window->swapchain, cmd.texture, draw_size, backbuffer_dst, cmd.vsync); } - /* Push cmd to window */ - b32 desires_change = !EqStruct(&cmd.desired_settings, &begin_event.settings); - if (desires_change) + /* Bring window to front on first show */ + if (!window->first_shown) { - LockTicketMutex(&window->u2w_tm); - { - window->u2w_update_end_cmd = cmd; - window->u2w_update_end_event = begin_event; - PostMessageW(window->hwnd, WND_CmdMsgId, 0, 0); - } - UnlockTicketMutex(&window->u2w_tm); + ShowWindow(hwnd, SW_SHOW); + SetForegroundWindow(hwnd); + BringWindowToTop(hwnd); + window->first_shown = 1; } - return present_fence_target; + EndScratch(scratch); } diff --git a/src/window/window_win32/window_win32.h b/src/window/window_win32/window_win32.h index 9ba9c3a0..93ea83b7 100644 --- a/src/window/window_win32/window_win32.h +++ b/src/window/window_win32/window_win32.h @@ -1,36 +1,58 @@ //////////////////////////////////////////////////////////// //~ Window types -#define WND_CmdMsgId (WM_USER + 1) - Struct(WND_W32_Window) { HWND hwnd; - GPU_D12_Swapchain *swapchain; Atomic32 is_ready; - Atomic64 cmd_gen; - Atomic64 os_gen; - /* Window state */ u16 previous_utf16_high_surrogate; - b32 first_shown; - i64 cmd_depth; /* User state */ - WND_Event update_begin_event; + Arena *cmds_arena; + struct WND_W32_CmdNode *first_cmd; + struct WND_W32_CmdNode *last_cmd; + b32 first_shown; + b32 is_forced_top; + b32 is_fullscreen; + RECT fullscreen_restore_rect; /* Window -> User */ /* Reads outside of window thread must lock */ TicketMutex w2u_tm; Arena *w2u_inputs_arena; +}; +//////////////////////////////////////////////////////////// +//~ Restore types - /* User -> Window */ - /* Reads outside of user thread must lock */ - TicketMutex u2w_tm; - WND_Event u2w_update_end_event; /* The user's processed event at end of update */ - WND_Cmd u2w_update_end_cmd; /* The user's cmd at the end of update */ +#define WND_W32_RestoreMagic 0xc4b4842a + +Struct(WND_W32_RestorableData) +{ + u32 magic; + + WINDOWPLACEMENT placement; + DWORD style; + DWORD ex_style; + + b32 is_forced_top; + + b32 is_fullscreen; + RECT fullscreen_restore_rect; + + b32 is_snapped; + RECT snapped_screen_rect; +}; + +//////////////////////////////////////////////////////////// +//~ Cmd types + +Struct(WND_W32_CmdNode) +{ + WND_W32_CmdNode *next; + WND_Cmd cmd; }; //////////////////////////////////////////////////////////// @@ -42,9 +64,14 @@ Struct(WND_W32_SharedState) { Btn vk_to_btn[256]; WNDCLASSEXW window_class; - WND_W32_Window window; /* Only single-window for now */ + WND_W32_Window window; /* Single-window for now */ } extern WND_W32_shared_state; +//////////////////////////////////////////////////////////// +//~ Window helpers + +WND_W32_Window *WND_W32_WindowFromHandle(WND_Handle handle); + //////////////////////////////////////////////////////////// //~ Initialization diff --git a/src/window/window_win32/window_win32.lay b/src/window/window_win32/window_win32.lay index 28c8c5cc..89997fa3 100644 --- a/src/window/window_win32/window_win32.lay +++ b/src/window/window_win32/window_win32.lay @@ -1,8 +1,5 @@ @Layer window_win32 -//- Dependencies -@Dep gpu_dx12 - //- Api @IncludeC window_win32.h