From 5f7de288aca4da0f4deb76dd4a8a6aab2548c5d0 Mon Sep 17 00:00:00 2001 From: jacob Date: Sat, 25 Oct 2025 10:27:47 -0500 Subject: [PATCH] begin window layer --- src/base/base_inc.h | 1 + src/base/base_input.h | 149 +++++++ src/base/base_memory.h | 2 + src/base/base_win32/base_win32_job.c | 3 +- src/gpu/gpu_core.h | 17 - src/gpu/gpu_dx12/gpu_dx12.c | 533 +++++++++++------------ src/gpu/gpu_dx12/gpu_dx12.h | 16 + src/meta/meta.c | 6 +- src/pp/pp.c | 98 +++-- src/pp/pp.h | 76 ++-- src/pp/pp.lay | 2 + src/window/window.h | 46 ++ src/window/window.lay | 12 + src/window/window_win32/window_win32.c | 480 ++++++++++++++++++++ src/window/window_win32/window_win32.h | 54 +++ src/window/window_win32/window_win32.lay | 10 + 16 files changed, 1136 insertions(+), 369 deletions(-) create mode 100644 src/base/base_input.h create mode 100644 src/window/window.h create mode 100644 src/window/window.lay create mode 100644 src/window/window_win32/window_win32.c create mode 100644 src/window/window_win32/window_win32.h create mode 100644 src/window/window_win32/window_win32.lay diff --git a/src/base/base_inc.h b/src/base/base_inc.h index 97e76fbd..cfb356b4 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -22,6 +22,7 @@ # include "base_entry.h" # include "base_bitbuff.h" # include "base_resource.h" +# include "base_input.h" #elif LanguageIsGpu # include "base_math_gpu.h" #endif diff --git a/src/base/base_input.h b/src/base/base_input.h new file mode 100644 index 00000000..64f4cee3 --- /dev/null +++ b/src/base/base_input.h @@ -0,0 +1,149 @@ +//////////////////////////////////////////////////////////// +//~ Button types + +Enum(Btn) +{ + Btn_None, + + //- Mouse buttons + Btn_M1, + Btn_M2, + Btn_M3, + Btn_M4, + Btn_M5, + + //- Mouse wheel + Btn_MWheelUp, + Btn_MWheelDown, + + //- Keyboard buttons + Btn_Esc, + Btn_F1, + Btn_F2, + Btn_F3, + Btn_F4, + Btn_F5, + Btn_F6, + Btn_F7, + Btn_F8, + Btn_F9, + Btn_F10, + Btn_F11, + Btn_F12, + Btn_F13, + Btn_F14, + Btn_F15, + Btn_F16, + Btn_F17, + Btn_F18, + Btn_F19, + Btn_F20, + Btn_F21, + Btn_F22, + Btn_F23, + Btn_F24, + Btn_GraveAccent, + Btn_0, + Btn_1, + Btn_2, + Btn_3, + Btn_4, + Btn_5, + Btn_6, + Btn_7, + Btn_8, + Btn_9, + Btn_Minus, + Btn_Equal, + Btn_Backspace, + Btn_Delete, + Btn_Tab, + Btn_A, + Btn_B, + Btn_C, + Btn_D, + Btn_E, + Btn_F, + Btn_G, + Btn_H, + Btn_I, + Btn_J, + Btn_K, + Btn_L, + Btn_M, + Btn_N, + Btn_O, + Btn_P, + Btn_Q, + Btn_R, + Btn_S, + Btn_T, + Btn_U, + Btn_V, + Btn_W, + Btn_X, + Btn_Y, + Btn_Z, + Btn_Space, + Btn_Enter, + Btn_Ctrl, + Btn_Shift, + Btn_Alt, + Btn_Up, + Btn_Left, + Btn_Down, + Btn_Right, + Btn_PageUp, + Btn_PageDown, + Btn_Home, + Btn_End, + Btn_ForwardSlash, + Btn_Period, + Btn_Comma, + Btn_Quote, + Btn_LeftBracket, + Btn_RightBracket, + Btn_Insert, + Btn_Semicolon, + + Btn_Count +}; + +//////////////////////////////////////////////////////////// +//~ Input types + +Enum(InputKind) +{ + InputKind_None, + + InputKind_ButtonDown, + InputKind_ButtonUp, + + InputKind_CursorMove, + InputKind_MouseMove, + + InputKind_Text, + + InputKind_Quit, + + InputKind_Count +}; + +Struct(Input) +{ + InputKind kind; + + /* InputKind_ButtonDown */ + /* InputKind_ButtonUp */ + Btn button; + b32 is_repeat; + + /* InputKind_Text */ + u32 text_codepoint; + + /* InputKind_CursorMove */ + Vec2I32 cursor_pos; + + /* InputKind_MouseMove */ + Vec2I32 mouse_delta; +}; diff --git a/src/base/base_memory.h b/src/base/base_memory.h index f1ad9f94..1a6883c1 100644 --- a/src/base/base_memory.h +++ b/src/base/base_memory.h @@ -18,8 +18,10 @@ void SetMemoryReadWrite(void *address, u64 size); //- Wrappers #define ZeroStruct(ptr) ZeroBytes((ptr), sizeof(*(ptr))) +#define ZeroStructs(ptr, n) ZeroBytes((ptr), sizeof(*(ptr)) * (n)) #define ZeroArray(a) Assert(IsArray(a)); ZeroBytes((a), sizeof((a))) #define CopyStruct(ptr_dst, ptr_src) CopyBytes((ptr_dst), (ptr_src), sizeof(*(ptr_dst))) +#define CopyStructs(ptr_dst, ptr_src, n) CopyBytes((ptr_dst), (ptr_src), sizeof(*(ptr_dst)) * (n)) #define EqStruct(p1, p2) EqBytes((p1), (p2), sizeof(*p1)) #define ZeroBytes(ptr, count) SetBytes((ptr), 0, (count)) #define EqBytes(p1, p2, n) (CmpBytes((p1), (p2), (n)) == 0) diff --git a/src/base/base_win32/base_win32_job.c b/src/base/base_win32/base_win32_job.c index 9c018160..8259563d 100644 --- a/src/base/base_win32/base_win32_job.c +++ b/src/base/base_win32/base_win32_job.c @@ -565,8 +565,7 @@ void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids) Atomic8Set(&fiber->status, W32_FiberStatus_None); W32_Task *task = fiber->task; - // if (task->job->flags & JobFlag_Dedicated) - if (0) + if (task->job->flags & JobFlag_Dedicated) { /* TODO: Wake dedicated fiber right now */ WakeByAddressSingle(&fiber->status); diff --git a/src/gpu/gpu_core.h b/src/gpu/gpu_core.h index 8a2aa83f..a2df9cb9 100644 --- a/src/gpu/gpu_core.h +++ b/src/gpu/gpu_core.h @@ -3,7 +3,6 @@ Struct(GPU_Resource); Struct(GPU_CommandList); -Struct(GPU_Swapchain); //////////////////////////////////////////////////////////// //~ Queue types @@ -469,19 +468,3 @@ 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(P_Window *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 3591081a..d2564483 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.c +++ b/src/gpu/gpu_dx12/gpu_dx12.c @@ -696,225 +696,6 @@ u64 GPU_D12_EndRawCommandList(GPU_D12_RawCommandList *cl) return target; } -//////////////////////////////////////////////////////////// -//~ Swapchain helpers - -void GPU_D12_InitSwapchainResources(GPU_D12_Swapchain *swapchain) -{ - GPU_D12_SharedState *g = &GPU_D12_shared_state; - 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 */ - Panic(Lit("Failed to get swapchain buffer")); - } - GPU_D12_SwapchainBuffer *sb = &swapchain->buffers[i]; - ZeroStruct(sb); - sb->swapchain = swapchain; - sb->d3d_resource = resource; - sb->rtv_descriptor = GPU_D12_AcquireDescriptor(g->rtv_heap); - sb->state = D3D12_RESOURCE_STATE_COMMON; - ID3D12Device_CreateRenderTargetView(g->device, sb->d3d_resource, 0, sb->rtv_descriptor->handle); - } -} - -GPU_D12_SwapchainBuffer *GPU_D12_UpdateSwapchain(GPU_D12_Swapchain *swapchain, Vec2I32 resolution) -{ - __prof; - GPU_D12_SharedState *g = &GPU_D12_shared_state; - resolution.x = MaxI32(resolution.x, 1); - resolution.y = MaxI32(resolution.y, 1); - b32 should_rebuild = !EqVec2I32(swapchain->resolution, resolution); - if (should_rebuild) - { - HRESULT hr = 0; - GPU_D12_Queue *queue = GPU_D12_QueueFromKind(GPU_QueueKind_Direct); - /* Lock direct queue submissions (in case any write to backbuffer) */ - /* TODO: Less overkill approach - Only flush GPU_D12_BlitToSwapchain since we know it's the only operation targeting backbuffer */ - Lock lock = LockE(&queue->submit_mutex); - //DEBUGBREAKABLE; - //Lock lock = LockE(&g->global_command_list_record_mutex); - { - /* Flush direct queue */ - //ID3D12CommandQueue_Signal(cq->cq, cq->submit_fence, ++cq->submit_fence_target); - { - HANDLE event = CreateEvent(0, 0, 0, 0); - ID3D12Fence_SetEventOnCompletion(queue->submit_fence, queue->submit_fence_target, event); - WaitForSingleObject(event, INFINITE); - CloseHandle(event); - } - - /* Release buffers */ - for (u32 i = 0; i < countof(swapchain->buffers); ++i) - { - GPU_D12_SwapchainBuffer *sb = &swapchain->buffers[i]; - GPU_D12_ReleaseDescriptor(sb->rtv_descriptor); - ID3D12Resource_Release(sb->d3d_resource); - } - - /* Resize buffers */ - hr = IDXGISwapChain_ResizeBuffers(swapchain->swapchain, 0, resolution.x, resolution.y, DXGI_FORMAT_UNKNOWN, GPU_D12_SwapchainFlags); - if (FAILED(hr)) - { - /* TODO: Don't panic */ - Panic(Lit("Failed to resize swapchain")); - } - } - Unlock(&lock); - - GPU_D12_InitSwapchainResources(swapchain); - - swapchain->resolution = resolution; - } - - u32 backbuffer_index = IDXGISwapChain3_GetCurrentBackBufferIndex(swapchain->swapchain); - return &swapchain->buffers[backbuffer_index]; -} - -i64 GPU_D12_BlitToSwapchain(GPU_D12_SwapchainBuffer *dst, GPU_D12_Resource *texture, Vec2I32 dst_pos) -{ - GPU_D12_SharedState *g = &GPU_D12_shared_state; - - GPU_D12_Swapchain *swapchain = dst->swapchain; - GPU_D12_RawCommandList *dx12_cl = GPU_D12_BeginRawCommandList(GPU_QueueKind_Direct); - ID3D12GraphicsCommandList *rcl = dx12_cl->cl; - D3D12_RESOURCE_STATES old_texture_state = texture->state; - - { - u32 barriers_count = 0; - D3D12_RESOURCE_BARRIER rbs[2] = ZI; - /* Transition backbuffer to RENDER_TARGET */ - { - D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; - rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - rb->Transition.pResource = dst->d3d_resource; - rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - rb->Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - rb->Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - } - ID3D12GraphicsCommandList_ResourceBarrier(rcl, barriers_count, rbs); - } - - /* Clear */ - { - f32 clear_color[4] = ZI; - ID3D12GraphicsCommandList_ClearRenderTargetView(rcl, dst->rtv_descriptor->handle, clear_color, 0, 0); - } - - { - u32 barriers_count = 0; - D3D12_RESOURCE_BARRIER rbs[2] = ZI; - /* Transition backbuffer to COPY_DEST */ - { - D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; - rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - rb->Transition.pResource = dst->d3d_resource; - rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - rb->Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - rb->Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; - } - /* Transition texture to COPY_SRC */ - if (texture->state != D3D12_RESOURCE_STATE_COPY_SOURCE) - { - D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; - rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - rb->Transition.pResource = texture->d3d_resource; - rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - rb->Transition.StateBefore = texture->state; - rb->Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; - texture->state = rb->Transition.StateAfter; - } - ID3D12GraphicsCommandList_ResourceBarrier(rcl, barriers_count, rbs); - } - - /* Copy */ - { - D3D12_TEXTURE_COPY_LOCATION dst_loc = ZI; - dst_loc.pResource = dst->d3d_resource; - dst_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dst_loc.SubresourceIndex = 0; - - D3D12_TEXTURE_COPY_LOCATION src_loc = ZI; - src_loc.pResource = texture->d3d_resource; - src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - src_loc.SubresourceIndex = 0; - - Vec2I32 dst_size = swapchain->resolution; - Vec2I32 src_size = VEC2I32(texture->desc.texture.size.x, texture->desc.texture.size.y); - - i32 dst_left = dst_pos.x; - i32 dst_top = dst_pos.y; - - i32 src_left = 0; - i32 src_top = 0; - i32 src_right = src_size.x; - i32 src_bottom = src_size.y; - - /* Clamp copy src & dst */ - if (dst_left < 0) - { - src_left -= dst_left; - dst_left = 0; - } - if (dst_top < 0) - { - src_top -= dst_top; - dst_top = 0; - } - if (dst_left + (src_left + src_right) > dst_size.x) - { - src_right -= (dst_left + (src_left + src_right)) - dst_size.x; - } - if (dst_top + (src_top + src_bottom) > dst_size.y) - { - src_bottom -= (dst_top + (src_top + src_bottom)) - dst_size.y; - } - - if (src_left < src_right && src_bottom > src_top) - { - D3D12_BOX src_box = ZI; - src_box.left = src_left; - src_box.top = src_top; - src_box.right = src_right; - src_box.bottom = src_bottom; - src_box.back = 1; - ID3D12GraphicsCommandList_CopyTextureRegion(rcl, &dst_loc, dst_left, dst_top, 0, &src_loc, &src_box); - } - } - - { - u32 barriers_count = 0; - D3D12_RESOURCE_BARRIER rbs[2] = ZI; - /* Transition backbuffer to PRESENT */ - { - D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; - rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - rb->Transition.pResource = dst->d3d_resource; - rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - rb->Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; - rb->Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - } - /* Transition texture to original state */ - if (texture->state != old_texture_state) - { - D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; - rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - rb->Transition.pResource = texture->d3d_resource; - rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - rb->Transition.StateBefore = texture->state; - rb->Transition.StateAfter = old_texture_state; - texture->state = rb->Transition.StateAfter; - } - ID3D12GraphicsCommandList_ResourceBarrier(rcl, barriers_count, rbs); - } - - i64 fence_target = GPU_D12_EndRawCommandList(dx12_cl); - return fence_target; -} - //////////////////////////////////////////////////////////// //~ Queue sync job @@ -1091,17 +872,17 @@ GPU_Resource *GPU_AcquireResource(GPU_ResourceDesc desc) }; Assert(!(desc.flags & GPU_ResourceFlag_Renderable)); D3D12_RESOURCE_DESC d3d_desc = ZI; - d3d_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - d3d_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - d3d_desc.Format = DXGI_FORMAT_UNKNOWN; - d3d_desc.Alignment = 0; - d3d_desc.Width = buffer_size; - d3d_desc.Height = 1; - d3d_desc.DepthOrArraySize = 1; - d3d_desc.MipLevels = 1; - d3d_desc.SampleDesc.Count = 1; + d3d_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + d3d_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + d3d_desc.Format = DXGI_FORMAT_UNKNOWN; + d3d_desc.Alignment = 0; + d3d_desc.Width = buffer_size; + d3d_desc.Height = 1; + d3d_desc.DepthOrArraySize = 1; + d3d_desc.MipLevels = 1; + d3d_desc.SampleDesc.Count = 1; d3d_desc.SampleDesc.Quality = 0; - d3d_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS * !!(desc.flags & GPU_ResourceFlag_Writable); + d3d_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS * !!(desc.flags & GPU_ResourceFlag_Writable); r->state = desc.buffer.heap_kind == GPU_HeapKind_Upload ? D3D12_RESOURCE_STATE_GENERIC_READ : D3D12_RESOURCE_STATE_COPY_DEST; HRESULT hr = ID3D12Device_CreateCommittedResource(g->device, &heap_props, heap_flags, &d3d_desc, r->state, 0, &IID_ID3D12Resource, (void **)&r->d3d_resource); if (FAILED(hr)) @@ -1119,27 +900,27 @@ GPU_Resource *GPU_AcquireResource(GPU_ResourceDesc desc) { D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_DEFAULT }; - D3D12_RESOURCE_DESC d3d_desc = ZI; - d3d_desc.Dimension = desc.kind == GPU_ResourceKind_Texture1D ? D3D12_RESOURCE_DIMENSION_TEXTURE1D - : desc.kind == GPU_ResourceKind_Texture2D ? D3D12_RESOURCE_DIMENSION_TEXTURE2D - : D3D12_RESOURCE_DIMENSION_TEXTURE3D; - d3d_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - d3d_desc.Format = GPU_D12_DxgiFormatFromGpuFormat(desc.texture.format); - d3d_desc.Alignment = 0; - d3d_desc.Width = desc.texture.size.x; - d3d_desc.Height = desc.texture.size.y; - d3d_desc.DepthOrArraySize = desc.texture.size.z; - d3d_desc.MipLevels = (desc.flags & GPU_ResourceFlag_MaxMipLevels) ? 0 : MaxI32(desc.texture.mip_levels, 1); - d3d_desc.SampleDesc.Count = 1; - d3d_desc.SampleDesc.Quality = 0; - d3d_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS * !!(desc.flags & GPU_ResourceFlag_Writable); - d3d_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET * !!(desc.flags & GPU_ResourceFlag_Renderable); - r->state = D3D12_RESOURCE_STATE_COPY_DEST; - D3D12_CLEAR_VALUE clear_value = { .Format = d3d_desc.Format, .Color = { 0 } }; - clear_value.Color[0] = desc.clear_color.x; - clear_value.Color[1] = desc.clear_color.y; - clear_value.Color[2] = desc.clear_color.z; - clear_value.Color[3] = desc.clear_color.w; + D3D12_RESOURCE_DESC d3d_desc = ZI; + d3d_desc.Dimension = desc.kind == GPU_ResourceKind_Texture1D ? D3D12_RESOURCE_DIMENSION_TEXTURE1D + : desc.kind == GPU_ResourceKind_Texture2D ? D3D12_RESOURCE_DIMENSION_TEXTURE2D + : D3D12_RESOURCE_DIMENSION_TEXTURE3D; + d3d_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + d3d_desc.Format = GPU_D12_DxgiFormatFromGpuFormat(desc.texture.format); + d3d_desc.Alignment = 0; + d3d_desc.Width = MaxI32(desc.texture.size.x, 1); + d3d_desc.Height = MaxI32(desc.texture.size.y, 1); + d3d_desc.DepthOrArraySize = MaxI32(desc.texture.size.z, 1); + d3d_desc.MipLevels = (desc.flags & GPU_ResourceFlag_MaxMipLevels) ? 0 : MaxI32(desc.texture.mip_levels, 1); + d3d_desc.SampleDesc.Count = 1; + d3d_desc.SampleDesc.Quality = 0; + d3d_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS * !!(desc.flags & GPU_ResourceFlag_Writable); + d3d_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET * !!(desc.flags & GPU_ResourceFlag_Renderable); + r->state = D3D12_RESOURCE_STATE_COPY_DEST; + D3D12_CLEAR_VALUE clear_value = { .Format = d3d_desc.Format, .Color = { 0 } }; + clear_value.Color[0] = desc.clear_color.x; + clear_value.Color[1] = desc.clear_color.y; + clear_value.Color[2] = desc.clear_color.z; + clear_value.Color[3] = desc.clear_color.w; D3D12_CLEAR_VALUE *clear_value_ptr = d3d_desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET ? &clear_value : 0; HRESULT hr = ID3D12Device_CreateCommittedResource(g->device, &heap_props, heap_flags, &d3d_desc, r->state, clear_value_ptr, &IID_ID3D12Resource, (void **)&r->d3d_resource); if (FAILED(hr)) @@ -2035,13 +1816,231 @@ GPU_MemoryInfo GPU_QueryMemoryInfo(void) } //////////////////////////////////////////////////////////// -//~ @hookdef Swapchain hooks +//~ Swapchain helpers -GPU_Swapchain *GPU_AcquireSwapchain(P_Window *window, GPU_Format format, Vec2I32 size) +void GPU_D12_InitSwapchainResources(GPU_D12_Swapchain *swapchain) +{ + GPU_D12_SharedState *g = &GPU_D12_shared_state; + 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 */ + Panic(Lit("Failed to get swapchain buffer")); + } + GPU_D12_SwapchainBuffer *sb = &swapchain->buffers[i]; + ZeroStruct(sb); + sb->swapchain = swapchain; + sb->d3d_resource = resource; + sb->rtv_descriptor = GPU_D12_AcquireDescriptor(g->rtv_heap); + sb->state = D3D12_RESOURCE_STATE_COMMON; + ID3D12Device_CreateRenderTargetView(g->device, sb->d3d_resource, 0, sb->rtv_descriptor->handle); + } +} + +GPU_D12_SwapchainBuffer *GPU_D12_UpdateSwapchain(GPU_D12_Swapchain *swapchain, Vec2I32 resolution) +{ + __prof; + GPU_D12_SharedState *g = &GPU_D12_shared_state; + resolution.x = MaxI32(resolution.x, 1); + resolution.y = MaxI32(resolution.y, 1); + b32 should_rebuild = !EqVec2I32(swapchain->resolution, resolution); + if (should_rebuild) + { + HRESULT hr = 0; + GPU_D12_Queue *queue = GPU_D12_QueueFromKind(GPU_QueueKind_Direct); + /* Lock direct queue submissions (in case any write to backbuffer) */ + /* TODO: Less overkill approach - Only flush GPU_D12_BlitToSwapchain since we know it's the only operation targeting backbuffer */ + Lock lock = LockE(&queue->submit_mutex); + //DEBUGBREAKABLE; + //Lock lock = LockE(&g->global_command_list_record_mutex); + { + /* Flush direct queue */ + //ID3D12CommandQueue_Signal(cq->cq, cq->submit_fence, ++cq->submit_fence_target); + { + HANDLE event = CreateEvent(0, 0, 0, 0); + ID3D12Fence_SetEventOnCompletion(queue->submit_fence, queue->submit_fence_target, event); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); + } + + /* Release buffers */ + for (u32 i = 0; i < countof(swapchain->buffers); ++i) + { + GPU_D12_SwapchainBuffer *sb = &swapchain->buffers[i]; + GPU_D12_ReleaseDescriptor(sb->rtv_descriptor); + ID3D12Resource_Release(sb->d3d_resource); + } + + /* Resize buffers */ + hr = IDXGISwapChain_ResizeBuffers(swapchain->swapchain, 0, resolution.x, resolution.y, DXGI_FORMAT_UNKNOWN, GPU_D12_SwapchainFlags); + if (FAILED(hr)) + { + /* TODO: Don't panic */ + Panic(Lit("Failed to resize swapchain")); + } + } + Unlock(&lock); + + GPU_D12_InitSwapchainResources(swapchain); + + swapchain->resolution = resolution; + } + + u32 backbuffer_index = IDXGISwapChain3_GetCurrentBackBufferIndex(swapchain->swapchain); + return &swapchain->buffers[backbuffer_index]; +} + +i64 GPU_D12_BlitToSwapchain(GPU_D12_SwapchainBuffer *dst, GPU_D12_Resource *texture, Vec2I32 dst_pos) +{ + GPU_D12_SharedState *g = &GPU_D12_shared_state; + + GPU_D12_Swapchain *swapchain = dst->swapchain; + GPU_D12_RawCommandList *dx12_cl = GPU_D12_BeginRawCommandList(GPU_QueueKind_Direct); + ID3D12GraphicsCommandList *rcl = dx12_cl->cl; + D3D12_RESOURCE_STATES old_texture_state = texture->state; + + { + u32 barriers_count = 0; + D3D12_RESOURCE_BARRIER rbs[2] = ZI; + /* Transition backbuffer to RENDER_TARGET */ + { + D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; + rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + rb->Transition.pResource = dst->d3d_resource; + rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + rb->Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + rb->Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + } + ID3D12GraphicsCommandList_ResourceBarrier(rcl, barriers_count, rbs); + } + + /* Clear */ + { + f32 clear_color[4] = ZI; + ID3D12GraphicsCommandList_ClearRenderTargetView(rcl, dst->rtv_descriptor->handle, clear_color, 0, 0); + } + + { + u32 barriers_count = 0; + D3D12_RESOURCE_BARRIER rbs[2] = ZI; + /* Transition backbuffer to COPY_DEST */ + { + D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; + rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + rb->Transition.pResource = dst->d3d_resource; + rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + rb->Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + rb->Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + } + /* Transition texture to COPY_SRC */ + if (texture->state != D3D12_RESOURCE_STATE_COPY_SOURCE) + { + D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; + rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + rb->Transition.pResource = texture->d3d_resource; + rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + rb->Transition.StateBefore = texture->state; + rb->Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + texture->state = rb->Transition.StateAfter; + } + ID3D12GraphicsCommandList_ResourceBarrier(rcl, barriers_count, rbs); + } + + /* Copy */ + { + D3D12_TEXTURE_COPY_LOCATION dst_loc = ZI; + dst_loc.pResource = dst->d3d_resource; + dst_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dst_loc.SubresourceIndex = 0; + + D3D12_TEXTURE_COPY_LOCATION src_loc = ZI; + src_loc.pResource = texture->d3d_resource; + src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + src_loc.SubresourceIndex = 0; + + Vec2I32 dst_size = swapchain->resolution; + Vec2I32 src_size = VEC2I32(texture->desc.texture.size.x, texture->desc.texture.size.y); + + i32 dst_left = dst_pos.x; + i32 dst_top = dst_pos.y; + + i32 src_left = 0; + i32 src_top = 0; + i32 src_right = src_size.x; + i32 src_bottom = src_size.y; + + /* Clamp copy src & dst */ + if (dst_left < 0) + { + src_left -= dst_left; + dst_left = 0; + } + if (dst_top < 0) + { + src_top -= dst_top; + dst_top = 0; + } + if (dst_left + (src_left + src_right) > dst_size.x) + { + src_right -= (dst_left + (src_left + src_right)) - dst_size.x; + } + if (dst_top + (src_top + src_bottom) > dst_size.y) + { + src_bottom -= (dst_top + (src_top + src_bottom)) - dst_size.y; + } + + if (src_left < src_right && src_bottom > src_top) + { + D3D12_BOX src_box = ZI; + src_box.left = src_left; + src_box.top = src_top; + src_box.right = src_right; + src_box.bottom = src_bottom; + src_box.back = 1; + ID3D12GraphicsCommandList_CopyTextureRegion(rcl, &dst_loc, dst_left, dst_top, 0, &src_loc, &src_box); + } + } + + { + u32 barriers_count = 0; + D3D12_RESOURCE_BARRIER rbs[2] = ZI; + /* Transition backbuffer to PRESENT */ + { + D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; + rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + rb->Transition.pResource = dst->d3d_resource; + rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + rb->Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + rb->Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + } + /* Transition texture to original state */ + if (texture->state != old_texture_state) + { + D3D12_RESOURCE_BARRIER *rb = &rbs[barriers_count++]; + rb->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + rb->Transition.pResource = texture->d3d_resource; + rb->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + rb->Transition.StateBefore = texture->state; + rb->Transition.StateAfter = old_texture_state; + texture->state = rb->Transition.StateAfter; + } + ID3D12GraphicsCommandList_ResourceBarrier(rcl, barriers_count, rbs); + } + + i64 fence_target = GPU_D12_EndRawCommandList(dx12_cl); + return fence_target; +} + +//////////////////////////////////////////////////////////// +//~ Swapchain operations + +GPU_D12_Swapchain *GPU_D12_AcquireSwapchain(HWND hwnd, GPU_Format format, Vec2I32 size) { GPU_D12_SharedState *g = &GPU_D12_shared_state; HRESULT hr = 0; - HWND hwnd = (HWND)P_GetInternalWindowHandle(window); GPU_D12_Queue *queue = GPU_D12_QueueFromKind(GPU_QueueKind_Direct); GPU_D12_Swapchain *swapchain = 0; @@ -2070,17 +2069,17 @@ GPU_Swapchain *GPU_AcquireSwapchain(P_Window *window, GPU_Format format, Vec2I32 IDXGISwapChain1 *swapchain1 = 0; { DXGI_SWAP_CHAIN_DESC1 desc = ZI; - desc.Format = GPU_D12_DxgiFormatFromGpuFormat(format); - desc.Width = size.x; - desc.Height = size.y; - desc.SampleDesc.Count = 1; + desc.Format = GPU_D12_DxgiFormatFromGpuFormat(format); + desc.Width = size.x; + desc.Height = size.y; + desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; - desc.BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = GPU_D12_SwapchainBufferCount; - desc.Scaling = DXGI_SCALING_NONE; - desc.Flags = GPU_D12_SwapchainFlags; - desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = GPU_D12_SwapchainBufferCount; + desc.Scaling = DXGI_SCALING_NONE; + desc.Flags = GPU_D12_SwapchainFlags; + desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; hr = IDXGIFactory2_CreateSwapChainForHwnd(g->factory, (IUnknown *)queue->d3d_queue, hwnd, &desc, 0, 0, &swapchain1); if (FAILED(hr)) { @@ -2110,15 +2109,15 @@ GPU_Swapchain *GPU_AcquireSwapchain(P_Window *window, GPU_Format format, Vec2I32 GPU_D12_InitSwapchainResources(swapchain); - return (GPU_Swapchain *)swapchain; + return (GPU_D12_Swapchain *)swapchain; } -void GPU_ReleaseSwapchain(GPU_Swapchain *swapchain) +void GPU_D12_ReleaseSwapchain(GPU_D12_Swapchain *swapchain) { /* TODO */ } -void GPU_YieldOnSwapchain(GPU_Swapchain *gpu_swapchain) +void GPU_D12_YieldOnSwapchain(GPU_D12_Swapchain *gpu_swapchain) { /* TODO: Actually yield, don't block */ GPU_D12_Swapchain *swapchain = (GPU_D12_Swapchain *)gpu_swapchain; @@ -2128,7 +2127,7 @@ void GPU_YieldOnSwapchain(GPU_Swapchain *gpu_swapchain) } } -i64 GPU_PresentSwapchain(GPU_Swapchain *gpu_swapchain, GPU_Resource *gpu_texture, Vec2I32 backbuffer_size, Vec2I32 dst, i32 vsync) +i64 GPU_D12_PresentSwapchain(GPU_D12_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; diff --git a/src/gpu/gpu_dx12/gpu_dx12.h b/src/gpu/gpu_dx12/gpu_dx12.h index b1574b84..bded8f9f 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.h +++ b/src/gpu/gpu_dx12/gpu_dx12.h @@ -375,6 +375,22 @@ 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/meta/meta.c b/src/meta/meta.c index 1881d155..0669ea22 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -1055,9 +1055,9 @@ JobDef(Build, _, __) String exe_file = Lit("pp.exe"); { - /* Wait for exe to become writable (once program closes) */ - i64 timeout_ns = NsFromSeconds(0.5); - OS_File file = OS_OpenFile(exe_file, OS_FileFlag_Write, timeout_ns); + /* Wait for exe to become writable (wait for program to close) */ + f32 timeout = 1.0; + OS_File file = OS_OpenFile(exe_file, OS_FileFlag_Write, NsFromSeconds(timeout)); OS_CloseFile(file); } diff --git a/src/pp/pp.c b/src/pp/pp.c index fdc096ef..8dd77a53 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -50,9 +50,6 @@ void StartupUser(void) g->console_logs_arena = AcquireArena(Gibi(64)); //P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Success); P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Debug); - g->window = P_AcquireWindow(); - g->swapchain = GPU_AcquireSwapchain(g->window, GPU_Format_R8G8B8A8_Unorm, VEC2I32(100, 100)); - P_ShowWindow(g->window); /* Start jobs */ g->shutdown_jobs_count += RunJob(UpdateUserOrSleep, .pool = JobPool_User, .fence = &g->shutdown_jobs_fence); @@ -80,8 +77,6 @@ ExitFuncDef(ShutdownUser) WriteSwappedState(Lit("pp_user"), StringFromStruct(swap)); EndScratch(scratch); } - - P_ReleaseWindow(g->window); } //////////////////////////////////////////////////////////// @@ -480,16 +475,26 @@ MergesortCompareFuncDef(EntitySortCmp, arg_a, arg_b, _) //////////////////////////////////////////////////////////// //~ User update -void UpdateUser(P_Window *window) +void UpdateUser(void) { __prof; SharedUserState *g = &shared_user_state; TempArena scratch = BeginScratchNoConflict(); //- Begin frame + WND_Event win_event = WND_BeginUpdate(scratch.arena); + + u64 inputs_count = win_event.inputs_count; + Input *inputs = win_event.inputs; + g->real_dt_ns = TimeNs() - g->real_time_ns; g->real_time_ns += g->real_dt_ns; - g->screen_size = P_GetWindowSize(window); + + g->screen_size = SubVec2I32(g->window_settings.p1, g->window_settings.p0); + if (EqVec2I32(g->screen_size, VEC2I32(0, 0))) + { + g->screen_size = VEC2I32(1920, 1080); + } //- Begin UI UI_BeginBuild(); @@ -630,12 +635,10 @@ void UpdateUser(P_Window *window) } } - //- Process sys events into user bind state + //- Process inputs into user bind state { - __profn("Process sys events"); - - P_WindowEventArray events = P_PopWindowEvents(scratch.arena, window); + __profn("Process inputs"); /* Reset bind pressed / released states */ for (u32 i = 0; i < countof(g->bind_states); ++i) @@ -645,40 +648,40 @@ void UpdateUser(P_Window *window) }; } - for (u64 ent_index = 0; ent_index < events.count; ++ent_index) + for (u64 idx = 0; idx < inputs_count; ++idx) { - P_WindowEvent *event = &events.events[ent_index]; - if (event->kind == P_WindowEventKind_Quit) + Input *input = &inputs[idx]; + if (input->kind == InputKind_Quit) { SignalExit(0); } - if (event->kind == P_WindowEventKind_ButtonUp) + if (input->kind == InputKind_ButtonUp) { /* Escape quit */ - if (event->button == P_Btn_ESC) + if (input->button == Btn_Esc) { SignalExit(0); } } /* Update mouse pos */ - if (event->kind == P_WindowEventKind_CursorMove) + if (input->kind == InputKind_CursorMove) { - g->screen_cursor = event->cursor_position; + g->screen_cursor = Vec2FromFields(input->cursor_pos); } /* Update bind states */ - if ((event->kind == P_WindowEventKind_ButtonDown || event->kind == P_WindowEventKind_ButtonUp)) + if ((input->kind == InputKind_ButtonDown || input->kind == InputKind_ButtonUp)) { - P_Btn button = event->button; - button = button >= P_Btn_Count ? P_Btn_None : button; + Btn button = input->button; + button = button >= Btn_Count ? Btn_None : button; BindKind bind = g_binds[button]; if (bind) { - b32 pressed = event->kind == P_WindowEventKind_ButtonDown; + b32 pressed = input->kind == InputKind_ButtonDown; #if 0 - b32 out_of_bounds = button >= P_Btn_M1 && button <= P_Btn_M5 && + b32 out_of_bounds = button >= Btn_M1 && button <= Btn_M5 && (g->ui_cursor.x < 0 || g->ui_cursor.y < 0 || g->ui_cursor.x > g->ui_size.x || @@ -692,7 +695,7 @@ void UpdateUser(P_Window *window) if (!out_of_bounds) { ++g->bind_states[bind].num_presses_and_repeats; - if (event->is_repeat) + if (input->is_repeat) { ++g->bind_states[bind].num_repeats; } @@ -748,13 +751,11 @@ void UpdateUser(P_Window *window) //- Update user state from binds - /* Test fullscreen */ + /* Test fullscreen */ { if (g->bind_states[BindKind_Fullscreen].num_presses && g->bind_states[BindKind_FullscreenMod].is_held) { - P_WindowSettings settings = P_GetWindowSettings(window); - settings.flags ^= P_WindowSettingsFlag_Fullscreen; - P_UpdateWindowSettings(window, &settings); + g->window_settings.flags ^= WND_Flag_Fullscreen; } } @@ -765,7 +766,7 @@ void UpdateUser(P_Window *window) if (g->bind_states[BindKind_DebugToggleTopmost].num_presses > 0) { - P_ToggleWindowTopmost(window); + g->window_settings.flags ^= WND_Flag_ForcedTop; P_LogSuccessF("Toggle topmost"); } @@ -2469,6 +2470,7 @@ void UpdateUser(P_Window *window) /* Render UI */ GPU_Resource *ui_render = UI_EndBuild(ui_viewport); +#if 0 ////////////////////////////// //- Present @@ -2476,6 +2478,24 @@ void UpdateUser(P_Window *window) Vec2I32 backbuffer_dst = VEC2I32(RoundF32ToI32(backbuffer_dst_f.x), RoundF32ToI32(backbuffer_dst_f.y)); g->gpu_render_fence_target = GPU_PresentSwapchain(g->swapchain, ui_render, g->screen_size, backbuffer_dst, VSYNC); // g->gpu_render_fence_target = GPU_PresentSwapchain(g->swapchain, g->ui_target, g->screen_size, backbuffer_dst, VSYNC); +#else + ////////////////////////////// + //- End window update + + g->window_settings.vsync = VSYNC; + g->window_settings.p0 = VEC2I32(0, 0); + g->window_settings.p1 = g->screen_size; + { + WND_Cmd cmd = ZI; + cmd.settings = g->window_settings; + cmd.texture = ui_render; + { + 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)); + } + WND_EndUpdate(cmd); + } +#endif EndScratch(scratch); } @@ -2489,22 +2509,14 @@ JobDef(UpdateUserOrSleep, UNUSED sig, UNUSED id) i64 time_ns = TimeNs(); while (!Atomic32Fetch(&g->shutdown)) { - P_Window *window = g->window; - { - __profn("User sleep"); #if FPS_LIMIT > 0 - { - __profn("Frame limiter wait"); - P_SleepFrame(time_ns, 1000000000 / FPS_LIMIT); - time_ns = TimeNs(); - } -#endif - { - __profn("Swapchain wait"); - GPU_YieldOnSwapchain(g->swapchain); - } + { + __profn("User frame limit"); + P_SleepFrame(time_ns, 1000000000 / FPS_LIMIT); + time_ns = TimeNs(); } - UpdateUser(window); +#endif + UpdateUser(); } } diff --git a/src/pp/pp.h b/src/pp/pp.h index 32287d1e..55e20e0b 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -52,46 +52,46 @@ Enum(BindKind) //- Test bindings /* TODO: Remove this */ -Global Readonly BindKind g_binds[P_Btn_Count] = { - [P_Btn_W] = BindKind_MoveUp, - [P_Btn_S] = BindKind_MoveDown, - [P_Btn_A] = BindKind_MoveLeft, - [P_Btn_D] = BindKind_MoveRight, - [P_Btn_M1] = BindKind_Fire, - [P_Btn_M2] = BindKind_AltFire, +Global Readonly BindKind g_binds[Btn_Count] = { + [Btn_W] = BindKind_MoveUp, + [Btn_S] = BindKind_MoveDown, + [Btn_A] = BindKind_MoveLeft, + [Btn_D] = BindKind_MoveRight, + [Btn_M1] = BindKind_Fire, + [Btn_M2] = BindKind_AltFire, #if 0 - [P_Btn_Alt] = BindKind_Walk, + [Btn_Alt] = BindKind_Walk, #endif /* Testing */ - [P_Btn_Z] = BindKind_TestTile, - [P_Btn_M5] = BindKind_DebugDrag, - [P_Btn_M4] = BindKind_DebugDelete, - [P_Btn_F] = BindKind_DebugExplode, - [P_Btn_T] = BindKind_DebugTeleport, - [P_Btn_C] = BindKind_DebugClear, - [P_Btn_1] = BindKind_DebugSpawn1, - [P_Btn_2] = BindKind_DebugSpawn2, - [P_Btn_3] = BindKind_DebugSpawn3, - [P_Btn_4] = BindKind_DebugSpawn4, - [P_Btn_G] = BindKind_DebugWalls, - [P_Btn_N] = BindKind_DebugStep, - [P_Btn_Q] = BindKind_DebugFollow, - [P_Btn_F1] = BindKind_DebugPause, - [P_Btn_F2] = BindKind_DebugCamera, - [P_Btn_F3] = BindKind_DebugDraw, - [P_Btn_F4] = BindKind_DebugToggleTopmost, - [P_Btn_GraveAccent] = BindKind_DebugConsole, - [P_Btn_Alt] = BindKind_FullscreenMod, - [P_Btn_Enter] = BindKind_Fullscreen, - [P_Btn_MWheelUp] = BindKind_ZoomIn, - [P_Btn_MWheelDown] = BindKind_ZoomOut, - [P_Btn_M3] = BindKind_Pan, + [Btn_Z] = BindKind_TestTile, + [Btn_M5] = BindKind_DebugDrag, + [Btn_M4] = BindKind_DebugDelete, + [Btn_F] = BindKind_DebugExplode, + [Btn_T] = BindKind_DebugTeleport, + [Btn_C] = BindKind_DebugClear, + [Btn_1] = BindKind_DebugSpawn1, + [Btn_2] = BindKind_DebugSpawn2, + [Btn_3] = BindKind_DebugSpawn3, + [Btn_4] = BindKind_DebugSpawn4, + [Btn_G] = BindKind_DebugWalls, + [Btn_N] = BindKind_DebugStep, + [Btn_Q] = BindKind_DebugFollow, + [Btn_F1] = BindKind_DebugPause, + [Btn_F2] = BindKind_DebugCamera, + [Btn_F3] = BindKind_DebugDraw, + [Btn_F4] = BindKind_DebugToggleTopmost, + [Btn_GraveAccent] = BindKind_DebugConsole, + [Btn_Alt] = BindKind_FullscreenMod, + [Btn_Enter] = BindKind_Fullscreen, + [Btn_MWheelUp] = BindKind_ZoomIn, + [Btn_MWheelDown] = BindKind_ZoomOut, + [Btn_M3] = BindKind_Pan, #if RtcIsEnabled - [P_Btn_ForwardSlash] = BindKind_ResetDebugSteps, - [P_Btn_Comma] = BindKind_DecrementDebugSteps, - [P_Btn_Period] = BindKind_IncrementDebugSteps + [Btn_ForwardSlash] = BindKind_ResetDebugSteps, + [Btn_Comma] = BindKind_DecrementDebugSteps, + [Btn_Period] = BindKind_IncrementDebugSteps #endif }; @@ -153,8 +153,6 @@ Struct(BindState) Struct(SharedUserState) { Atomic32 shutdown; - P_Window *window; - GPU_Swapchain *swapchain; Fence shutdown_jobs_fence; u64 shutdown_jobs_count; @@ -242,6 +240,10 @@ Struct(SharedUserState) //- Persist start StructRegion(PERSIST_START); + //- Window + + WND_Settings window_settings; + //- Debug camera EntityId debug_following; @@ -319,7 +321,7 @@ MergesortCompareFuncDef(EntitySortCmp, arg_a, arg_b, _); //////////////////////////////////////////////////////////// //~ User update -void UpdateUser(P_Window *window); +void UpdateUser(void); //////////////////////////////////////////////////////////// //~ User update job diff --git a/src/pp/pp.lay b/src/pp/pp.lay index 43a04fc6..15b3e5f3 100644 --- a/src/pp/pp.lay +++ b/src/pp/pp.lay @@ -9,6 +9,8 @@ @Dep mixer @Dep rendertest @Dep playback +@Dep platform +@Dep window @Dep ui //- Api diff --git a/src/window/window.h b/src/window/window.h new file mode 100644 index 00000000..1a8f2d53 --- /dev/null +++ b/src/window/window.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////// +//~ Window types + +Enum(WND_Flag) +{ + WND_Flag_None = (0), + WND_Flag_Fullscreen = (1 << 0), + WND_Flag_ForcedTop = (1 << 1), +}; + +Struct(WND_Settings) +{ + WND_Flag flags; + Vec2I32 p0; + Vec2I32 p1; + i32 vsync; +}; + +Struct(WND_Cmd) +{ + WND_Settings settings; + + GPU_Resource *texture; + Vec2I32 backbuffer_dst; +}; + +Struct(WND_Event) +{ + u64 inputs_count; + Input *inputs; + WND_Settings settings; + + /* The backbuffer src texture is no longer in use once the direct queue fence reaches this target */ + i64 backbuffer_fence_target; +}; + +//////////////////////////////////////////////////////////// +//~ @hookdecl Startup hooks + +void WND_Startup(void); + +//////////////////////////////////////////////////////////// +//~ @hookdecl Window hooks + +WND_Event WND_BeginUpdate(Arena *arena); +void WND_EndUpdate(WND_Cmd cmd); diff --git a/src/window/window.lay b/src/window/window.lay new file mode 100644 index 00000000..50d33b16 --- /dev/null +++ b/src/window/window.lay @@ -0,0 +1,12 @@ +@Layer window + +//- Dependencies +@Dep gpu + +//- Api +@IncludeC window.h + +//- Win32 impl +@DefaultWindowsImpl window_win32 + +@Startup WND_Startup diff --git a/src/window/window_win32/window_win32.c b/src/window/window_win32/window_win32.c new file mode 100644 index 00000000..741beea9 --- /dev/null +++ b/src/window/window_win32/window_win32.c @@ -0,0 +1,480 @@ +WND_W32_SharedState WND_W32_shared_state = ZI; + +//////////////////////////////////////////////////////////// +//~ @hookdef Startup + +void WND_Startup(void) +{ + WND_W32_SharedState *g = &WND_W32_shared_state; + + //- Initialize btn table + { + ZeroArray(g->vk_to_btn); + for (u32 i = 'A', j = Btn_A; i <= 'Z'; ++i, ++j) + { + g->vk_to_btn[i] = (Btn)j; + } + for (u32 i = '0', j = Btn_0; i <= '9'; ++i, ++j) + { + g->vk_to_btn[i] = (Btn)j; + } + for (u32 i = VK_F1, j = Btn_F1; i <= VK_F24; ++i, ++j) + { + g->vk_to_btn[i] = (Btn)j; + } + g->vk_to_btn[VK_ESCAPE] = Btn_Esc; + g->vk_to_btn[VK_OEM_3] = Btn_GraveAccent; + g->vk_to_btn[VK_OEM_MINUS] = Btn_Minus; + g->vk_to_btn[VK_OEM_PLUS] = Btn_Equal; + g->vk_to_btn[VK_BACK] = Btn_Backspace; + g->vk_to_btn[VK_TAB] = Btn_Tab; + g->vk_to_btn[VK_SPACE] = Btn_Space; + g->vk_to_btn[VK_RETURN] = Btn_Enter; + g->vk_to_btn[VK_CONTROL] = Btn_Ctrl; + g->vk_to_btn[VK_SHIFT] = Btn_Shift; + g->vk_to_btn[VK_MENU] = Btn_Alt; + g->vk_to_btn[VK_UP] = Btn_Up; + g->vk_to_btn[VK_LEFT] = Btn_Left; + g->vk_to_btn[VK_DOWN] = Btn_Down; + g->vk_to_btn[VK_RIGHT] = Btn_Right; + g->vk_to_btn[VK_DELETE] = Btn_Delete; + g->vk_to_btn[VK_PRIOR] = Btn_PageUp; + g->vk_to_btn[VK_NEXT] = Btn_PageDown; + g->vk_to_btn[VK_HOME] = Btn_Home; + g->vk_to_btn[VK_END] = Btn_End; + g->vk_to_btn[VK_OEM_2] = Btn_ForwardSlash; + g->vk_to_btn[VK_OEM_PERIOD] = Btn_Period; + g->vk_to_btn[VK_OEM_COMMA] = Btn_Comma; + g->vk_to_btn[VK_OEM_7] = Btn_Quote; + g->vk_to_btn[VK_OEM_4] = Btn_LeftBracket; + g->vk_to_btn[VK_OEM_6] = Btn_RightBracket; + g->vk_to_btn[VK_INSERT] = Btn_Insert; + g->vk_to_btn[VK_OEM_1] = Btn_Semicolon; + } + + //- Create window class + { + HMODULE instance = GetModuleHandle(0); + + /* Register the window class */ + WNDCLASSEXW *wc = &g->window_class; + wc->cbSize = sizeof(WNDCLASSEX); + wc->lpszClassName = WND_W32_WindowClassName; + wc->hCursor = LoadCursor(0, IDC_ARROW); + wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + // wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc->lpfnWndProc = WND_W32_WindowProc; + wc->hInstance = instance; + + /* Use first icon resource as window icon (same as explorer) */ + wchar_t path[4096] = ZI; + GetModuleFileNameW(instance, path, countof(path)); + ExtractIconExW(path, 0, &wc->hIcon, &wc->hIconSm, 1); + + if (!RegisterClassExW(wc)) + { + Panic(Lit("Failed to register window class")); + } + } + + //- Register raw mouse input + { + RAWINPUTDEVICE rid = ZI; + rid.usUsagePage = 0x01; /* HID_USAGE_PAGE_GENERIC */ + rid.usUsage = 0x02; /* HID_USAGE_GENERIC_MOUSE */ + RegisterRawInputDevices(&rid, 1, sizeof(rid)); + } + + //- Start message processing job + RunJob(WND_W32_ProcessMessagesForever, .pool = JobPool_Hyper, .flags = JobFlag_Dedicated); +} + +//////////////////////////////////////////////////////////// +//~ Input helpers + +void WND_W32_PushInput(WND_W32_Window *window, Input input) +{ + LockTicketMutex(&window->inputs_tm); + { + *PushStructNoZero(window->inputs_arena, Input) = input; + } + UnlockTicketMutex(&window->inputs_tm); +} + +//////////////////////////////////////////////////////////// +//~ Initialization + +/* Win32 limitation: Window must be initialized on same thread that processes events */ +JobDef(WND_W32_ProcessMessagesForever, sig, id) +{ + WND_W32_SharedState *g = &WND_W32_shared_state; + WND_W32_Window *window = &g->window; + window->inputs_arena = AcquireArena(Gibi(64)); + + //- Init hwnd + HWND hwnd = 0; + { + /* + * From martins (https://gist.github.com/mmozeiko/5e727f845db182d468a34d524508ad5f#file-win32_d3d11-c-L66-L70): + * WS_EX_NOREDIRECTIONBITMAP flag here is needed to fix ugly bug with Windows 10 + * when window is resized and DXGI swap chain uses FLIP presentation model + * DO NOT use it if you choose to use non-FLIP presentation model + * read about the bug here: https://stackoverflow.com/q/63096226 and here: https://stackoverflow.com/q/53000291 + */ + DWORD exstyle = WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP; + + /* TODO: Check for hwnd success */ + hwnd = CreateWindowExW( + exstyle, + g->window_class.lpszClassName, + L"", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + 0, + 0, + g->window_class.hInstance, + 0 + ); + + /* Dark mode */ + BOOL dark_mode = 1; + DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode)); + + /* Set window as userdata */ + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window); + } + window->hwnd = hwnd; + + //- Begin processing messages + Atomic32Set(&window->is_ready, 1); + for (;;) + { + // SetFocus(window->hwnd); + MSG msg = ZI; + GetMessageW(&msg, 0, 0, 0); + { + __profn("Process window message"); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + /* Destroy window */ + DestroyWindow(window->hwnd); +} + +//////////////////////////////////////////////////////////// +//~ Message processing + +LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + WND_W32_SharedState *g = &WND_W32_shared_state; + WND_W32_Window *window = &g->window; + ++window->proc_depth; + LRESULT result = 0; + + Lock lock = ZI; + if (window->proc_depth == 1) + { + lock = LockE(&window->update_mutex); + } + { + switch(msg) + { + //- Default + default: + { + result = DefWindowProcW(hwnd, msg, wparam, lparam); + } break; + + //- Quit + case WM_QUIT: + case WM_CLOSE: + case WM_DESTROY: + { + WND_W32_PushInput(window, (Input) { .kind = InputKind_Quit }); + } break; + + //- Resizing + case WM_ENTERSIZEMOVE: + case WM_MOVE: + case WM_MOVING: + case WM_SIZE: + case WM_SIZING: + { + /* TODO */ + // P_W32_UpdateWindowFromSystem(window); + result = DefWindowProcW(hwnd, msg, wparam, lparam); + } break; + + //- Keyboard button + case WM_SYSKEYUP: + case WM_SYSKEYDOWN:; + case WM_KEYUP: + case WM_KEYDOWN: + { + WORD vk_code = LOWORD(wparam); + Input input = ZI; + if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) + { + input.kind = InputKind_ButtonDown; + input.is_repeat = (lparam & 0x40000000) != 0; + } + else if (msg == WM_KEYUP || msg == WM_SYSKEYUP) + { + input.kind = InputKind_ButtonUp; + } + if (vk_code < countof(g->vk_to_btn)) + { + input.button = g->vk_to_btn[vk_code]; + } + WND_W32_PushInput(window, input); + if (msg == WM_SYSKEYUP || msg == WM_SYSKEYDOWN) + { + result = DefWindowProcW(hwnd, msg, wparam, lparam); + } + } break; + + //- Text + case WM_SYSCHAR: + case WM_CHAR: + { + u32 codepoint = 0; + { + u16 utf16_char = (u32)wparam; + if (IsUtf16HighSurrogate(utf16_char)) + { + window->previous_utf16_high_surrogate = utf16_char; + } + else if (IsUtf16LowSurrogate(utf16_char)) + { + u16 high = window->previous_utf16_high_surrogate; + u16 low = utf16_char; + if (high) + { + u16 utf16_pair_bytes[2] = { high, low }; + Utf16DecodeResult decoded = DecodeUtf16((String16) { .len = countof(utf16_pair_bytes), .text = utf16_pair_bytes }); + if (decoded.advance16 == 2 && decoded.codepoint < U32Max) + { + codepoint = decoded.codepoint; + } + } + window->previous_utf16_high_surrogate = 0; + } + else + { + window->previous_utf16_high_surrogate = 0; + codepoint = utf16_char; + } + if (codepoint != 0) + { + if (codepoint == '\r') + { + codepoint = '\n'; /* Just treat all \r as newline */ + } + } + } + if ((codepoint >= 32 && codepoint != 127) || codepoint == '\t' || codepoint == '\n') + { + Input input = ZI; + input.kind = InputKind_Text; + input.text_codepoint = codepoint; + WND_W32_PushInput(window, input); + } + } break; + + //- Mouse buttons + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_XBUTTONUP: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_XBUTTONDOWN: + { + Input input = ZI; + b32 is_down = msg == WM_LBUTTONDOWN || + msg == WM_MBUTTONDOWN || + msg == WM_RBUTTONDOWN || + msg == WM_XBUTTONDOWN; + if (is_down) + { + input.kind = InputKind_ButtonDown; + SetCapture(hwnd); + } + else + { + input.kind = InputKind_ButtonUp; + ReleaseCapture(); + } + switch (msg) + { + case WM_LBUTTONUP: case WM_LBUTTONDOWN: input.button = Btn_M1; break; + case WM_RBUTTONUP: case WM_RBUTTONDOWN: input.button = Btn_M2; break; + case WM_MBUTTONUP: case WM_MBUTTONDOWN: input.button = Btn_M3; break; + case WM_XBUTTONUP: case WM_XBUTTONDOWN: + { + u32 wparam_xbutton = GET_XBUTTON_WPARAM(wparam); + if (wparam_xbutton == XBUTTON1) + { + input.button = Btn_M4; + } + else if (wparam_xbutton == XBUTTON2) + { + input.button = Btn_M5; + } + } break; + } + if (input.button) + { + WND_W32_PushInput(window, input); + } + } break; + + //- Mouse wheel + case WM_MOUSEWHEEL: + { + int delta = GET_WHEEL_DELTA_WPARAM(wparam); + i32 dir = delta >= 0 ? 1 : -1; + Btn btn = dir >= 0 ? Btn_MWheelUp : Btn_MWheelDown; + for (i32 i = 0; i < (dir * delta); i += WHEEL_DELTA) + { + /* Send a button down & button up event simultaneously */ + WND_W32_PushInput(window, (Input) { .kind = InputKind_ButtonDown, .button = btn }); + WND_W32_PushInput(window, (Input) { .kind = InputKind_ButtonUp, .button = btn }); + } + } break; + + //- Cursor move + case WM_MOUSEMOVE: + { + i32 x = GET_X_LPARAM(lparam); + i32 y = GET_Y_LPARAM(lparam); + Input input = ZI; + input.kind = InputKind_CursorMove; + input.cursor_pos = VEC2I32(x, y); + WND_W32_PushInput(window, input); + } break; + + //- Raw mouse move + case WM_INPUT: + { + TempArena scratch = BeginScratchNoConflict(); + { + /* Read raw input buffer */ + UINT buff_size; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, 0, &buff_size, sizeof(RAWINPUTHEADER)); + u8 *buff = PushStructs(scratch.arena, u8, buff_size); + if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buff, &buff_size, sizeof(RAWINPUTHEADER)) != buff_size) + { + P_LogErrorF("GetRawInputData did not return correct size"); + break; + } + RAWINPUT raw = ZI; + CopyBytes(&raw, buff, sizeof(RAWINPUT)); + + if (raw.header.dwType == RIM_TYPEMOUSE) + { + i32 x = raw.data.mouse.lLastX; + i32 y = raw.data.mouse.lLastY; + Input input = ZI; + input.kind = InputKind_MouseMove; + input.mouse_delta = VEC2I32(x, y); + WND_W32_PushInput(window, input); + } + } + EndScratch(scratch); + } break; + } + } + if (window->proc_depth == 1) + { + Unlock(&lock); + } + + --window->proc_depth; + return result; +} + +//////////////////////////////////////////////////////////// +//~ @hookdef Update + +WND_Event WND_BeginUpdate(Arena *arena) +{ + WND_W32_SharedState *g = &WND_W32_shared_state; + WND_W32_Window *window = &g->window; + WND_Event result = ZI; + + /* TODO: Yield on swapchain instead */ + while (!Atomic32Fetch(&window->is_ready)) + { + _mm_pause(); + } + + /* FIXME: Don't use TM */ + Lock *lock = &window->user_update_lock; + *lock = LockE(&window->update_mutex); + + /* Pop inputs */ + LockTicketMutex(&window->inputs_tm); + { + Input *src = (Input *)ArenaBase(window->inputs_arena); + result.inputs_count = ArenaCount(window->inputs_arena, Input); + result.inputs = PushStructsNoZero(arena, Input, result.inputs_count); + CopyStructs(result.inputs, src, result.inputs_count); + ResetArena(window->inputs_arena); + } + UnlockTicketMutex(&window->inputs_tm); + + /* FIXME: Remove this */ + result.settings.p0 = window->previous_cmd.settings.p0; + result.settings.p1 = window->previous_cmd.settings.p1; + + return result; +} + +void WND_EndUpdate(WND_Cmd cmd) +{ + WND_W32_SharedState *g = &WND_W32_shared_state; + WND_W32_Window *window = &g->window; + + Unlock(&window->user_update_lock); + + + + WND_Settings old = window->previous_cmd.settings; + WND_Settings new = cmd.settings; + + Vec2I32 new_size = SubVec2I32(new.p1, new.p0); + + /* Acquire swapchain */ + if (!window->swapchain) + { + window->swapchain = GPU_D12_AcquireSwapchain(window->hwnd, GPU_Format_R8G8B8A8_Unorm, new_size); + } + + /* Present */ + /* TODO: Get fence */ + if (cmd.texture != 0) + { + Vec2I32 backbuffer_dst = cmd.backbuffer_dst; + i32 vsync = new.vsync; + GPU_D12_PresentSwapchain(window->swapchain, cmd.texture, new_size, backbuffer_dst, vsync); + } + + /* Show window */ + if (!window->first_shown) + { + i32 show_cmd = SW_SHOWMAXIMIZED; + ShowWindow(window->hwnd, show_cmd); + SetForegroundWindow(window->hwnd); + BringWindowToTop(window->hwnd); + window->first_shown = 1; + } + + window->previous_cmd = cmd; +} diff --git a/src/window/window_win32/window_win32.h b/src/window/window_win32/window_win32.h new file mode 100644 index 00000000..2512cac7 --- /dev/null +++ b/src/window/window_win32/window_win32.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////// +//~ Window types + +Struct(WND_W32_Window) +{ + Atomic32 is_ready; + HWND hwnd; + GPU_D12_Swapchain *swapchain; + + TicketMutex inputs_tm; + Arena *inputs_arena; + + + /* TODO: Remove this */ + // TicketMutex update_tm; + Mutex update_mutex; + Lock user_update_lock; + i32 proc_depth; + + + /* TODO: Remove this */ + b32 first_shown; + + WND_Cmd previous_cmd; + + u16 previous_utf16_high_surrogate; +}; + +//////////////////////////////////////////////////////////// +//~ State types + +#define WND_W32_WindowClassName L"pp_window_class" + +Struct(WND_W32_SharedState) +{ + Btn vk_to_btn[256]; + WNDCLASSEXW window_class; + WND_W32_Window window; /* Only single-window for now */ +} extern WND_W32_shared_state; + +//////////////////////////////////////////////////////////// +//~ Input helpers + +void WND_W32_PushInput(WND_W32_Window *window, Input input); + +//////////////////////////////////////////////////////////// +//~ Initialization + +JobDecl(WND_W32_ProcessMessagesForever, EmptySig); + +//////////////////////////////////////////////////////////// +//~ Message processing + +LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); diff --git a/src/window/window_win32/window_win32.lay b/src/window/window_win32/window_win32.lay new file mode 100644 index 00000000..28c8c5cc --- /dev/null +++ b/src/window/window_win32/window_win32.lay @@ -0,0 +1,10 @@ +@Layer window_win32 + +//- Dependencies +@Dep gpu_dx12 + +//- Api +@IncludeC window_win32.h + +//- Impl +@IncludeC window_win32.c