simplify window layer

This commit is contained in:
jacob 2025-10-26 21:57:01 -05:00
parent 6beb910f98
commit 9297e40b0c
13 changed files with 394 additions and 237 deletions

View File

@ -115,7 +115,9 @@ b32 BB_CheckWriterOverflowBits(BB_Writer *bw, u64 num_bits)
if (bytes_needed > max_len) if (bytes_needed > max_len)
{ {
/* Writer overflowed fixed buffer */ /* Writer overflowed fixed buffer */
#if BITBUFF_DEBUG
Assert(0); Assert(0);
#endif
result = 1; result = 1;
bw->cur_bit = max_len << 3; bw->cur_bit = max_len << 3;
bw->overflowed = 1; bw->overflowed = 1;
@ -447,7 +449,9 @@ b32 BB_CheckReaderOverflowBits(BB_Reader *br, u64 num_bits)
if (bits_needed > base_len_bits) if (bits_needed > base_len_bits)
{ {
/* Tried to read past bitbuff memory */ /* Tried to read past bitbuff memory */
#if BITBUFF_DEBUG
Assert(0); Assert(0);
#endif
result = 1; result = 1;
br->cur_bit = base_len_bits; br->cur_bit = base_len_bits;
br->overflowed = 1; br->overflowed = 1;

View File

@ -1,6 +1,7 @@
@Layer gpu @Layer gpu
//- Dependencies //- Dependencies
@Dep window
@Dep platform @Dep platform
//- Api //- Api

View File

@ -3,6 +3,7 @@
Struct(GPU_Resource); Struct(GPU_Resource);
Struct(GPU_CommandList); Struct(GPU_CommandList);
Struct(GPU_Swapchain);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Queue types //~ Queue types
@ -468,3 +469,19 @@ void GPU_CopyBytesToFootprint(void *dst, void *src, GPU_Resource *footprint_refe
//~ @hookdecl Memory info operations //~ @hookdecl Memory info operations
GPU_MemoryInfo GPU_QueryMemoryInfo(void); 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);

View File

@ -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; GPU_D12_SharedState *g = &GPU_D12_shared_state;
HRESULT hr = 0; HRESULT hr = 0;
HWND hwnd = WND_W32_WindowFromHandle(window)->hwnd;
GPU_D12_Queue *queue = GPU_D12_QueueFromKind(GPU_QueueKind_Direct); GPU_D12_Queue *queue = GPU_D12_QueueFromKind(GPU_QueueKind_Direct);
GPU_D12_Swapchain *swapchain = 0; 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); 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 */ /* TODO */
} }
void GPU_D12_YieldOnSwapchain(GPU_D12_Swapchain *gpu_swapchain) void GPU_YieldOnSwapchain(GPU_Swapchain *gpu_swapchain)
{ {
/* TODO: Actually yield, don't block */ /* TODO: Actually yield, don't block */
GPU_D12_Swapchain *swapchain = (GPU_D12_Swapchain *)gpu_swapchain; 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_Swapchain *swapchain = (GPU_D12_Swapchain *)gpu_swapchain;
GPU_D12_Resource *texture = (GPU_D12_Resource *)gpu_texture; 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; present_flags |= DXGI_PRESENT_ALLOW_TEARING;
} }
if (vsync != 0)
{
/* FIXME: Don't flush in fullscreen mode? */
// DwmFlush();
}
/* Present */ /* Present */
{ {
__profn("Present"); __profn("Present");

View File

@ -13,7 +13,7 @@
#define GPU_D12_TearingIsAllowed 1 #define GPU_D12_TearingIsAllowed 1
#define GPU_D12_FrameLatency 2 #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) \ #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)) | ((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); 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); 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 //~ Sync job

View File

@ -1,5 +1,8 @@
@Layer gpu_dx12 @Layer gpu_dx12
//- Dependencies
@Dep window_win32
//- Api //- Api
@IncludeC gpu_dx12.h @IncludeC gpu_dx12.h

View File

@ -10,12 +10,12 @@ void StartupUser(void)
SetGstat(GSTAT_DEBUG_STEPS, U64Max); SetGstat(GSTAT_DEBUG_STEPS, U64Max);
g->arena = AcquireArena(Gibi(64)); Arena *perm = PermArena();
g->real_time_ns = TimeNs(); g->real_time_ns = TimeNs();
/* TODO: Remove this */ /* TODO: Remove this */
String connect_address_str = Lit(""); 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 */ /* Initialize average dt to a reasonable value */
g->average_local_to_user_snapshot_publish_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND; 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_store = AcquireClientStore();
g->local_to_user_client = AcquireClient(g->local_to_user_client_store); 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 */ /* Init from swap */
if (IsSwappedIn()); if (IsSwappedIn());
{ {
TempArena scratch = BeginScratchNoConflict(); 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)) 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; SharedUserState *old = &swap->s;
CopyStructRegion(g, old, AUTO_PERSIST_START, AUTO_PERSIST_END); CopyStructRegion(g, old, AUTO_PERSIST_START, AUTO_PERSIST_END);
} }
EndScratch(scratch); EndScratch(scratch);
} }
@ -80,10 +81,17 @@ ExitFuncDef(ShutdownUser)
if (IsSwappingOut()) if (IsSwappingOut())
{ {
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
u64 max_size = Mebi(16);
SwappedUserState *swap = PushStruct(scratch.arena, SwappedUserState); u8 *bytes = PushStructsNoZero(scratch.arena, u8, max_size);
swap->s = *g; BB_Buff bb = BB_BuffFromString(STRING(max_size, bytes));
WriteSwappedState(Lit("pp_user"), StringFromStruct(swap)); {
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); EndScratch(scratch);
} }
} }
@ -487,24 +495,39 @@ void UpdateUser(void)
SharedUserState *g = &shared_user_state; SharedUserState *g = &shared_user_state;
TempArena scratch = BeginScratchNoConflict(); 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_dt_ns = TimeNs() - g->real_time_ns;
g->real_time_ns += g->real_dt_ns; g->real_time_ns += g->real_dt_ns;
//- Begin window if (g->swapchain)
WND_Settings cur_window = window_event.settings;
if (window_event.os_gen > g->window_os_gen && window_event.cmd_gen > 0)
{ {
/* Window was modified outside of program. Reflect this in our own state. */ GPU_YieldOnSwapchain(g->swapchain);
g->desired_window = cur_window;
} }
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.x = MaxI32(g->screen_size.x, 1);
g->screen_size.y = MaxI32(g->screen_size.y, 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) 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) 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"); P_LogSuccessF("Toggle topmost");
} }
@ -2352,18 +2375,18 @@ void UpdateUser(void)
g->gpu_submit_fence_target = UI_EndBuild(g->ui_target); g->gpu_submit_fence_target = UI_EndBuild(g->ui_target);
////////////////////////////// //////////////////////////////
//- End window update //- Present & end frame
{ {
WND_Cmd cmd = ZI; if (!g->swapchain)
cmd.desired_settings = g->desired_window;
cmd.texture = g->ui_target;
cmd.vsync = VSYNC;
{ {
Vec2 backbuffer_dst_f = MulXformV2(g->ui_to_screen_xf, VEC2(0, 0)); g->swapchain = GPU_AcquireSwapchain(window_frame.window_handle, GPU_Format_R8G8B8A8_Unorm, g->screen_size);
cmd.backbuffer_dst = VEC2I32(RoundF32ToI32(backbuffer_dst_f.x), RoundF32ToI32(backbuffer_dst_f.y));
} }
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; ++g->user_tick;
EndScratch(scratch); EndScratch(scratch);

View File

@ -240,14 +240,16 @@ Struct(SharedUserState)
i64 real_dt_ns; i64 real_dt_ns;
i64 real_time_ns; i64 real_time_ns;
//- Window
WND_Handle window;
GPU_Swapchain *swapchain;
String window_restore;
////////////////////////////// //////////////////////////////
//- Persist start //- Persist start
StructRegion(AUTO_PERSIST_START); StructRegion(AUTO_PERSIST_START);
//- Window
WND_Settings desired_window;
//- Debug camera //- Debug camera
EntityId debug_following; EntityId debug_following;

View File

@ -1,55 +1,64 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Window types //~ Handle types
Enum(WND_Flag) Struct(WND_Handle)
{ {
WND_Flag_None = (0), u64 v;
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),
}; };
Struct(WND_Settings) ////////////////////////////////////////////////////////////
//~ Cmd types
Enum(WND_CmdKind)
{ {
WND_Flag flags; WND_CmdKind_None,
Vec2I32 restore_p0; WND_CmdKind_SetMinimized,
Vec2I32 restore_p1; WND_CmdKind_SetMaximized,
}; WND_CmdKind_SetFullscreen,
WND_CmdKind_SetForcedTop,
Struct(WND_Event) WND_CmdKind_Restore,
{
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) */
}; };
Struct(WND_Cmd) Struct(WND_Cmd)
{ {
WND_Settings desired_settings; WND_CmdKind kind;
String restore;
GPU_Resource *texture; b32 v;
Vec2I32 backbuffer_dst;
i32 vsync;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @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); void WND_Startup(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ @hookdecl Window hooks //~ @hookdecl Command operations
WND_Event WND_BeginUpdate(Arena *arena); #define WND_PushCmd(frame, ...) WND_PushCmd_((frame), (WND_Cmd) { __VA_ARGS__ })
i64 WND_EndUpdateAndPresent(WND_Cmd cmd); void WND_PushCmd_(WND_Frame frame, WND_Cmd desc);
////////////////////////////////////////////////////////////
//~ @hookdecl Frame
WND_Frame WND_BeginFrame(Arena *arena);
void WND_EndFrame(WND_Frame frame);

View File

@ -1,8 +1,5 @@
@Layer window @Layer window
//- Dependencies
@Dep gpu
//- Api //- Api
@IncludeC window.h @IncludeC window.h

View File

@ -90,6 +90,14 @@ void WND_Startup(void)
RunJob(WND_W32_ProcessMessagesForever, .pool = message_job_pool); 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 //~ Initialization
@ -133,6 +141,7 @@ JobDef(WND_W32_ProcessMessagesForever, sig, id)
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode)); DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode));
/* Set window as userdata */ /* Set window as userdata */
/* FIXME: Necessary? */
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window); SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window);
} }
window->hwnd = hwnd; window->hwnd = hwnd;
@ -370,6 +379,7 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l
EndScratch(scratch); EndScratch(scratch);
} break; } break;
#if 0
//- Process command //- Process command
case WND_CmdMsgId: case WND_CmdMsgId:
{ {
@ -543,25 +553,7 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l
window->first_shown = 1; window->first_shown = 1;
} }
} break; } break;
#endif
//- 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;
} }
} }
@ -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_SharedState *g = &WND_W32_shared_state;
WND_W32_Window *window = &g->window; WND_W32_Window *window = &g->window;
WND_Event result = ZI; WND_Frame result = ZI;
while (!Atomic32Fetch(&window->is_ready)) while (!Atomic32Fetch(&window->is_ready))
{ {
_mm_pause(); _mm_pause();
} }
HWND hwnd = window->hwnd; HWND hwnd = window->hwnd;
result.window_handle.v = (u64)window;
/* Wait on swapchain */ /* Reset cmds */
if (window->swapchain) 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 */ /* Pop inputs */
{ {
@ -603,124 +614,199 @@ WND_Event WND_BeginUpdate(Arena *arena)
} }
/* Grab monitor info */ /* Grab monitor info */
RECT monitor_rect = ZI;
{ {
MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) };
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info);
RECT monitor_rect = monitor_info.rcMonitor; 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;
} }
/* Grab window info */ /* Client rect */
WND_Settings settings = ZI; 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) if (placement.showCmd == SW_MAXIMIZE)
{ {
settings.flags |= WND_Flag_Maximized; result.maximized = 1;
} }
if (placement.showCmd == SW_MINIMIZE) 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.forced_top = window->is_forced_top;
result.cmd_gen = Atomic64Fetch(&window->cmd_gen); 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; 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_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; HWND hwnd = window->hwnd;
WND_Event begin_event = window->update_begin_event; /* Process cmds */
WND_Settings draw_settings = begin_event.settings; for (WND_W32_CmdNode *n = window->first_cmd; n; n = n->next)
Vec2I32 draw_size = SubVec2I32(begin_event.draw_p1, begin_event.draw_p0);
if (!window->swapchain)
{ {
window->swapchain = GPU_D12_AcquireSwapchain(window->hwnd, GPU_Format_R8G8B8A8_Unorm, draw_size); WND_Cmd cmd = n->cmd;
} switch(cmd.kind)
/* Present */
i64 present_fence_target = 0;
if (cmd.texture != 0)
{
Vec2I32 backbuffer_dst = cmd.backbuffer_dst;
if (cmd.vsync != 0)
{ {
/* FIXME: Don't flush in fullscreen mode? */ //- Minimize
DwmFlush(); 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 */ /* Bring window to front on first show */
b32 desires_change = !EqStruct(&cmd.desired_settings, &begin_event.settings); if (!window->first_shown)
if (desires_change)
{ {
LockTicketMutex(&window->u2w_tm); ShowWindow(hwnd, SW_SHOW);
{ SetForegroundWindow(hwnd);
window->u2w_update_end_cmd = cmd; BringWindowToTop(hwnd);
window->u2w_update_end_event = begin_event; window->first_shown = 1;
PostMessageW(window->hwnd, WND_CmdMsgId, 0, 0);
}
UnlockTicketMutex(&window->u2w_tm);
} }
return present_fence_target; EndScratch(scratch);
} }

View File

@ -1,36 +1,58 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Window types //~ Window types
#define WND_CmdMsgId (WM_USER + 1)
Struct(WND_W32_Window) Struct(WND_W32_Window)
{ {
HWND hwnd; HWND hwnd;
GPU_D12_Swapchain *swapchain;
Atomic32 is_ready; Atomic32 is_ready;
Atomic64 cmd_gen;
Atomic64 os_gen;
/* Window state */ /* Window state */
u16 previous_utf16_high_surrogate; u16 previous_utf16_high_surrogate;
b32 first_shown;
i64 cmd_depth;
/* User state */ /* 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 */ /* Window -> User */
/* Reads outside of window thread must lock */ /* Reads outside of window thread must lock */
TicketMutex w2u_tm; TicketMutex w2u_tm;
Arena *w2u_inputs_arena; Arena *w2u_inputs_arena;
};
////////////////////////////////////////////////////////////
//~ Restore types
/* User -> Window */ #define WND_W32_RestoreMagic 0xc4b4842a
/* Reads outside of user thread must lock */
TicketMutex u2w_tm; Struct(WND_W32_RestorableData)
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 */ 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]; Btn vk_to_btn[256];
WNDCLASSEXW window_class; 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; } extern WND_W32_shared_state;
////////////////////////////////////////////////////////////
//~ Window helpers
WND_W32_Window *WND_W32_WindowFromHandle(WND_Handle handle);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Initialization //~ Initialization

View File

@ -1,8 +1,5 @@
@Layer window_win32 @Layer window_win32
//- Dependencies
@Dep gpu_dx12
//- Api //- Api
@IncludeC window_win32.h @IncludeC window_win32.h