From db3f0e6b2d52639aa6252cfc5d958699b5f99464 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 31 Mar 2026 17:37:22 -0500 Subject: [PATCH] use rdtsc for profiler timestamps --- src/base/base.cgh | 21 +- src/base/base_prof.c | 58 +- src/base/base_prof.h | 18 +- src/base/base_string.c | 9 +- src/base/base_win32/base_win32.c | 62 +- src/base/base_win32/base_win32.h | 6 +- src/gpu/gpu_dx12/gpu_dx12_core.c | 20 - src/platform/platform_win32/platform_win32.c | 2 +- src/pp/pp_sim/pp_sim_core.c | 4 +- src/pp/pp_vis/pp_vis_core.c | 83 +-- src/ui/ui_core.c | 85 +-- src/ui/ui_core.h | 2 +- src/window/window_win32/window_win32.c | 629 ++++++++++--------- 13 files changed, 557 insertions(+), 442 deletions(-) diff --git a/src/base/base.cgh b/src/base/base.cgh index dfb47414..c3b22313 100644 --- a/src/base/base.cgh +++ b/src/base/base.cgh @@ -226,7 +226,7 @@ #define ThreadLocal __thread #endif -//- Compiler memory barriers +//- Compiler reordering barriers #if IsCompilerMsvc #define CompilerBarrier() _ReadWriteBarrier() #elif IsArchX64 @@ -263,12 +263,6 @@ #define UsFromNs(ns) ((f64)((ns) / 1000.0)) #define SecondsFromNs(ns) ((f64)(ns) / 1000000000.0) -//- Busy-wait -#if IsArchX64 - #define YieldCpu() -#else -#endif - //////////////////////////////////////////////////////////// //~ Linked list helper macros @@ -556,7 +550,7 @@ Inline b32 MatchU128(u128 a, u128 b) { return a.lo == b.lo && a.hi == b.hi; } AlignedStruct(IsolatedAtomic32, IsolationSize) { Atomic32 v; }; AlignedStruct(IsolatedAtomic64, IsolationSize) { Atomic64 v; }; - #define Unatomic(atomic) (&(atomic)->_atom) + #define NonAtomic(atomic) (&(atomic)->_atom) #if IsPlatformWindows && IsArchX64 //- 8 bit atomic ops @@ -822,11 +816,14 @@ Inline u64 MixU64s(u64 seed_a, u64 seed_b) //~ @hookdecl Core api #if IsCpu + //- Application context StringList GetRawCommandline(void); String GetAppName(void); String GetEngineDirectory(void); String GetLibsDirectory(void); String GetAppDirectory(void); + + //- Program context void LoadApi(ApiDesc api); void Echo(String msg); b32 Panic(String msg); @@ -834,7 +831,15 @@ Inline u64 MixU64s(u64 seed_a, u64 seed_b) Callstack CaptureCallstack(u64 skip_frames); b32 IsRunningInDebugger(void); b32 IsRunningInWine(void); + + //- Time i64 TimeNs(void); + u64 BeginTsc(void); + u64 EndTsc(void); + u64 GetStartupTsc(void); + f64 GetNsPerTsc(void); + + //- Misc void TrueRand(String buffer); CpuTopologyInfo GetCpuTopologyInfo(void); void SleepSeconds(f64 seconds); diff --git a/src/base/base_prof.c b/src/base/base_prof.c index 174c1bd8..6f479bb9 100644 --- a/src/base/base_prof.c +++ b/src/base/base_prof.c @@ -1,7 +1,39 @@ //////////////////////////////////////////////////////////// //~ Profile zone -void PushProfZoneEx(char *name_cstr_lit, b32 end_zone) +// void PushProfZoneEx(char *name_cstr_lit, b32 end_zone) +// { +// ProfTrack *track = Base_tl.prof.thread_track; +// if (!track) +// { +// Arena *perm = PermArena(); +// { +// track = PushStruct(perm, ProfTrack); +// Base_tl.prof.thread_track = track; +// } +// LockTicketMutex(&Base.prof.register_track_tm); +// { +// Base_tl.prof.track_idx = Atomic32Fetch(&Base.prof.tracks_count); +// Base.prof.tracks[Base_tl.prof.track_idx] = track; +// Atomic32FetchAdd(&Base.prof.tracks_count, 1); +// } +// UnlockTicketMutex(&Base.prof.register_track_tm); +// } + +// u64 sample_seq = track->top_sample_seq; +// ProfSample *sample = &track->samples[sample_seq % ProfTrackSamplesCap]; +// { +// // TODO: Wrap rdtsc for cpu-relative <-> program-relative timestamp conversion +// sample->stamp = __rdtsc() | ((u64)(!!end_zone) << 63); +// // sample->stamp = TimeNs() | ((u64)(!!end_zone) << 63); +// sample->name_cstr_lit = name_cstr_lit; +// } +// track->top_sample_seq = sample_seq + 1; +// } + + + +void BeginProfZone(char *name_cstr_lit) { ProfTrack *track = Base_tl.prof.thread_track; if (!track) @@ -19,16 +51,28 @@ void PushProfZoneEx(char *name_cstr_lit, b32 end_zone) } UnlockTicketMutex(&Base.prof.register_track_tm); } - - u64 sample_seq = track->top_sample_seq; + u64 sample_seq = *NonAtomic(&track->top_sample_seq); ProfSample *sample = &track->samples[sample_seq % ProfTrackSamplesCap]; { - // TODO: Wrap rdtsc for cpu-relative <-> program-relative timestamp conversion - // sample->stamp = __rdtsc() | ((u64)end_zone << 63); - sample->stamp = TimeNs() | ((u64)end_zone << 63); + sample->flags = 0; sample->name_cstr_lit = name_cstr_lit; + sample->tsc = BeginTsc(); } - track->top_sample_seq = sample_seq + 1; + Atomic64Set(&track->top_sample_seq, sample_seq + 1); +} + +void EndProfZone(void) +{ + i64 tsc = EndTsc(); + ProfTrack *track = Base_tl.prof.thread_track; + u64 sample_seq = *NonAtomic(&track->top_sample_seq); + ProfSample *sample = &track->samples[sample_seq % ProfTrackSamplesCap]; + { + sample->flags = ProfSampleFlag_EndZone; + sample->tsc = tsc; + sample->name_cstr_lit = 0; + } + Atomic64Set(&track->top_sample_seq, sample_seq + 1); } //////////////////////////////////////////////////////////// diff --git a/src/base/base_prof.h b/src/base/base_prof.h index adc9c8fa..efa8d354 100644 --- a/src/base/base_prof.h +++ b/src/base/base_prof.h @@ -4,18 +4,23 @@ #define ProfTrackSamplesCap Kibi(256) #define MaxRegisteredProfTracks Kibi(32) -#define NsFromProfStamp(stamp) (stamp & ~((u64)1 << 63)) +Enum(ProfSampleFlag) +{ + ProfSampleFlag_None = 0, + ProfSampleFlag_EndZone = (1 << 0) +}; Struct(ProfSample) { - // Bit 63: zone-end, Bits 62-0: timestamp - u64 stamp; + ProfSampleFlag flags; + u8 _pad[4]; + u64 tsc; char *name_cstr_lit; }; Struct(ProfTrack) { - u64 top_sample_seq; + Atomic64 top_sample_seq; ProfSample samples[ProfTrackSamplesCap]; }; @@ -45,10 +50,11 @@ Struct(ThreadLocalProfCtx) //////////////////////////////////////////////////////////// //~ Profile zone -void PushProfZoneEx(char *name_cstr_lit, b32 end_zone); +void BeginProfZone(char *name_cstr_lit); +void EndProfZone(void); #if PROFILING_ENABLED - #define ProfZoneDF(name_cstr_lit) DeferFor(PushProfZoneEx(name_cstr_lit"\0", 0), PushProfZoneEx(name_cstr_lit"\0", 1)) + #define ProfZoneDF(name_cstr_lit) DeferFor(BeginProfZone(name_cstr_lit"\0"), EndProfZone()) #else #define ProfZoneDF(...) #endif diff --git a/src/base/base_string.c b/src/base/base_string.c index f6ab76ff..0749b2a6 100644 --- a/src/base/base_string.c +++ b/src/base/base_string.c @@ -217,12 +217,17 @@ String StringFromTimeNs(Arena *arena, i64 time_ns, u32 precision) { String result = Zi; u64 abs_time_ns = AbsI64(time_ns); - if (abs_time_ns <= 999 * 1000) + if (abs_time_ns < 1000) + { + // Nanoseconds + result = StringF(arena, "%F ns", FmtSint(time_ns)); + } + else if (abs_time_ns <= 1000 * 1000) { // Microseconds result = StringF(arena, "%F µs", FmtFloat(UsFromNs(time_ns), .p = precision)); } - else if (abs_time_ns <= 999 * 1000000) + else if (abs_time_ns <= 1000 * 1000 * 1000) { // Milliseconds result = StringF(arena, "%F ms", FmtFloat(MsFromNs(time_ns), .p = precision)); diff --git a/src/base/base_win32/base_win32.c b/src/base/base_win32/base_win32.c index c90f89c1..33d1b12a 100644 --- a/src/base/base_win32/base_win32.c +++ b/src/base/base_win32/base_win32.c @@ -199,9 +199,36 @@ b32 IsRunningInWine(void) i64 TimeNs(void) { LARGE_INTEGER qpc; - QueryPerformanceCounter(&qpc); - i64 result = (qpc.QuadPart - W32.timer_start_qpc) * W32.ns_per_qpc; - return result; + CompilerBarrier(); + { + QueryPerformanceCounter(&qpc); + } + CompilerBarrier(); + return (qpc.QuadPart - W32.startup_qpc) * W32.ns_per_qpc; +} + +u64 BeginTsc(void) +{ + _mm_lfence(); + return __rdtsc(); +} + +u64 EndTsc(void) +{ + u32 aux; + u64 tsc = __rdtscp(&aux); + _mm_lfence(); + return tsc; +} + +u64 GetStartupTsc(void) +{ + return W32.startup_tsc; +} + +f64 GetNsPerTsc(void) +{ + return W32.ns_per_tsc; } void TrueRand(String buffer) @@ -557,16 +584,39 @@ i32 W32_Main(void) } #endif - // Init time + // Calibrate QPC { LARGE_INTEGER qpf = Zi; QueryPerformanceFrequency(&qpf); - W32.ns_per_qpc = 1000000000 / qpf.QuadPart; + W32.ns_per_qpc = 1000000000.0 / qpf.QuadPart; } { LARGE_INTEGER qpc = Zi; QueryPerformanceCounter(&qpc); - W32.timer_start_qpc = qpc.QuadPart; + W32.startup_qpc = qpc.QuadPart; + } + + // Calibrate TSC + { + LARGE_INTEGER start_qpc; + LARGE_INTEGER end_qpc; + i64 start_tsc; + i64 end_tsc; + // Collect samples + u32 tsc_sleep_calibration_ms = 20; + { + start_tsc = __rdtsc(); + QueryPerformanceCounter(&start_qpc); + { + Sleep(tsc_sleep_calibration_ms); + } + QueryPerformanceCounter(&end_qpc); + end_tsc = __rdtsc(); + } + f64 elapsed_ns = (end_qpc.QuadPart - start_qpc.QuadPart) * W32.ns_per_qpc; + f64 elapsed_tsc = end_tsc - start_tsc; + W32.ns_per_tsc = elapsed_ns / elapsed_tsc; + W32.startup_tsc = end_tsc; } // Load module funcs diff --git a/src/base/base_win32/base_win32.h b/src/base/base_win32/base_win32.h index 7a018965..39a1ea31 100644 --- a/src/base/base_win32/base_win32.h +++ b/src/base/base_win32/base_win32.h @@ -88,8 +88,10 @@ Struct(W32_Ctx) Atomic32 shutdown; Atomic32 exit_code; - i64 timer_start_qpc; - i64 ns_per_qpc; + u64 startup_qpc; + u64 startup_tsc; + u64 ns_per_qpc; + f64 ns_per_tsc; StringList raw_command_line; String app_name; diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index 6ce60779..ec1f8ae2 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -1412,26 +1412,6 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle } } - - - - - - - - // FIXME: Remove this - if (memory_desc.buffer.stride == 52) - { - DEBUGBREAKABLE; - } - - - - - - - - // TODO: Less stringent reuse constraints. We could even create textures as placed resources and reset their underlying heaps. can_reuse = can_reuse && MatchStruct(&compare_d3d_desc, &d3d_desc); b32 can_reuse_descriptor = can_reuse && (!is_buffer || (MaxU64(memory_desc.buffer.stride, 1) == old_buffer_stride)); diff --git a/src/platform/platform_win32/platform_win32.c b/src/platform/platform_win32/platform_win32.c index 3a1e4689..2b1e68f5 100644 --- a/src/platform/platform_win32/platform_win32.c +++ b/src/platform/platform_win32/platform_win32.c @@ -67,7 +67,7 @@ void PLT_W32_SyncTimerForever(WaveLaneCtx *lane) for (;;) { { - // TODO: Minimum timer frequency in case timers ever become ultra precise in the future + // TODO: Clamp timer frequency in case timers ever become ultra precise in the future LARGE_INTEGER due = Zi; due.QuadPart = -1; //due.QuadPart = -10000; diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 0c927f64..3bc6ec62 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -85,8 +85,7 @@ void S_TickForever(WaveLaneCtx *lane) //- Sim loop b32 shutdown = 0; - while (!shutdown) - ProfZoneDF("Sim tick") + while (!shutdown) ProfZoneDF("Sim tick") { shutdown = Atomic32Fetch(&S.shutdown); P_tl.debug_draw_enabled = TweakBool("Simulation debug draw", 0); @@ -982,6 +981,7 @@ void S_TickForever(WaveLaneCtx *lane) //- Sleep if (!shutdown) + ProfZoneDF("Sleep sim") { i64 step_dt_ns = SIM_TICK_INTERVAL_NS; PLT_SleepFrame(begin_time_ns, step_dt_ns); diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 2fa1a067..5d6ade6f 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -871,8 +871,7 @@ void V_TickForever(WaveLaneCtx *lane) //- Begin vis loop b32 shutdown = 0; - while (!shutdown) - ProfZoneDF("Vis tick") + while (!shutdown) ProfZoneDF("Vis tick") { shutdown = Atomic32Fetch(&V.shutdown); P_tl.debug_draw_enabled = TweakBool("Vis debug draw", 0); @@ -3632,7 +3631,7 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Build panels - UI_PushDF(Parent, vis_game_box) + UI_SetDF(Parent, vis_game_box) // if (show_editor_ui && frame->is_editing) { Struct(PanelDfsNode) { PanelDfsNode *next; b32 visited; V_Panel *panel; UI_Checkpoint cp; }; @@ -4322,7 +4321,7 @@ void V_TickForever(WaveLaneCtx *lane) //- Build command palette V_Palette *palette = &frame->palette; - UI_PushDF(Parent, UI_RootKey) + UI_SetDF(Parent, UI_RootKey) { UI_Push(Tag, HashF("developer command palette")); @@ -5393,7 +5392,7 @@ void V_TickForever(WaveLaneCtx *lane) profiler->ns_per_px = ClampF64(profiler->ns_per_px, min_profiler_ns_per_px, max_profiler_ns_per_px); UI_Key profiler_graph_box = UI_KeyF("graph"); - UI_PushDF(OmitFlags, UI_BoxFlag_CaptureMouse * !!(frame->is_looking)) + UI_SetDF(OmitFlags, UI_BoxFlag_CaptureMouse * !!(frame->is_looking)) if (TweakBool("Show profiler", 1)) { profiler->is_showing = 1; @@ -5472,8 +5471,8 @@ void V_TickForever(WaveLaneCtx *lane) //- Build profiler UI_Key profiler_box = UI_KeyF("profiler"); - UI_PushDF(Tag, profiler_box.v) - UI_PushDF(Parent, profiler_box) + UI_SetDF(Tag, profiler_box.v) + UI_SetDF(Parent, profiler_box) { UI_Key main_box = UI_KeyF("main"); @@ -5628,22 +5627,22 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(Height, profiler_height); UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); UI_SetNext(Opacity, profiler_opacity); - UI_PushDF(Width, UI_Grow(1, 0)) - UI_PushDF(Parent, UI_BuildColumnEx(profiler_box)) - UI_PushDF(Parent, UI_BuildRow()) + UI_SetDF(Width, UI_Grow(1, 0)) + UI_SetDF(Parent, UI_BuildColumnEx(profiler_box)) + UI_SetDF(Parent, UI_BuildRow()) { UI_BuildSpacer(window_padding, Axis_X); - UI_PushDF(Parent, UI_BuildColumn()) + UI_SetDF(Parent, UI_BuildColumn()) { //- Header - // UI_PushDF(BackgroundColor, Color_Red) + // UI_SetDF(BackgroundColor, Color_Red) // UI_SetNext(BorderColor, Color_Red); // UI_SetNext(BorderSize, 1); UI_SetNext(ChildAlignment, UI_Region_Center); - UI_PushDF(Height, header_height) - UI_PushDF(Parent, UI_BuildBoxEx(UI_KeyF("profiler header"))) + UI_SetDF(Height, header_height) + UI_SetDF(Parent, UI_BuildBoxEx(UI_KeyF("profiler header"))) { - UI_PushDF(TextColor, theme.col.hint) + UI_SetDF(TextColor, theme.col.hint) UI_BuildLabelF("Profiler"); } UI_BuildDivider(UI_Px(1, 1), theme.col.divider, Axis_Y); @@ -5651,7 +5650,7 @@ void V_TickForever(WaveLaneCtx *lane) //- Graph // UI_SetNext(BackgroundColor, Color_Cyan); UI_SetNext(Height, graph_height); - UI_PushDF(Parent, UI_BuildBoxEx(profiler_graph_box)) + UI_SetDF(Parent, UI_BuildBoxEx(profiler_graph_box)) { } // UI_BuildDivider(UI_Px(1, 1), theme.col.divider, Axis_Y); @@ -5667,16 +5666,19 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(Rounding, UI_Rpx(4)); UI_SetNext(Flags, UI_BoxFlag_CaptureMouse | UI_BoxFlag_CaptureThroughChildren | UI_BoxFlag_Scissor); // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); - UI_PushDF(Parent, UI_BuildColumnEx(main_box)) + UI_SetDF(Parent, UI_BuildColumnEx(main_box)) { RegisteredProfTracksArray tracks = FetchRegisteredProfTracks(frame->arena); for (u64 track_idx = 0; track_idx < tracks.count; ++track_idx) { + // FIXME: Remove this + // if (track_idx != 0) continue; + UI_Key track_box = UI_KeyF("track %F", FmtUint(track_idx)); UI_SetNext(Height, track_height); // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); - UI_PushDF(Tag, track_box.v) - UI_PushDF(Parent, UI_BuildColumnEx(track_box)) + UI_SetDF(Tag, track_box.v) + UI_SetDF(Parent, UI_BuildColumnEx(track_box)) { ProfTrack *track = tracks.tracks[track_idx]; @@ -5728,14 +5730,15 @@ void V_TickForever(WaveLaneCtx *lane) // FIXME: Atomic + u64 track_top_sample_seq = Atomic64Fetch(&track->top_sample_seq); ProfSample *samples = track->samples; - u64 samples_start_seq = track->top_sample_seq - MinU64(track->top_sample_seq, ProfTrackSamplesCap); - u64 samples_end_seq = track->top_sample_seq; + u64 samples_start_seq = track_top_sample_seq - MinU64(track_top_sample_seq, ProfTrackSamplesCap); + u64 samples_end_seq = track_top_sample_seq; - // samples_end_seq = MinU64(samples_end_seq, samples_start_seq + 512); - samples_end_seq = MinU64(samples_end_seq, samples_start_seq + 64); + samples_end_seq = MinU64(samples_end_seq, samples_start_seq + 512); + // samples_end_seq = MinU64(samples_end_seq, samples_start_seq + 64); // samples_end_seq = MinU64(samples_end_seq, samples_start_seq + Kibi(16)); @@ -5758,9 +5761,11 @@ void V_TickForever(WaveLaneCtx *lane) { ProfSample *sample = &samples[sample_seq % ProfTrackSamplesCap]; - i64 time_ns = NsFromProfStamp(sample->stamp); - b32 is_end = !!(sample->stamp & ((u64)1 << 63)); + // u64 elapsed_ns = NsFromTsc + i64 time_ns = (sample->tsc - GetStartupTsc()) * GetNsPerTsc(); + + b32 is_end = !!(sample->flags & ProfSampleFlag_EndZone); if (is_end) { depth -= 1; @@ -5832,7 +5837,7 @@ void V_TickForever(WaveLaneCtx *lane) //- Zone row // UI_SetNext(Height, zone_height); UI_SetNext(Height, zone_height); - UI_PushDF(Parent, UI_BuildRow()) + UI_SetDF(Parent, UI_BuildRow()) { //- Zones in row u64 zone_idx = 0; @@ -5848,14 +5853,18 @@ void V_TickForever(WaveLaneCtx *lane) } // TODO: Dim in HSV space - Vec4 zone_color_dim = VEC4(0.15, 0.15, 0.15, 0); Vec4 zone_color = zone->color; // Vec4 zone_color_bd = Zi; Vec4 zone_color_bd = zone_color; - zone_color_bd.r -= zone_color_dim.r; - zone_color_bd.g -= zone_color_dim.g; - zone_color_bd.b -= zone_color_dim.b; + + // Vec4 zone_color_dim = VEC4(0.15, 0.15, 0.15, 0); + // zone_color_bd.r -= zone_color_dim.r; + // zone_color_bd.g -= zone_color_dim.g; + // zone_color_bd.b -= zone_color_dim.b; + + zone_color_bd = MulVec4(zone_color_bd, 0.65); + zone_color_bd.a = 1; Vec4 zone_text_color = UI_Top(TextColor); zone_text_color.a *= 0.75; @@ -5890,7 +5899,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(ChildAlignment, UI_Region_Center); UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_DontClampFloatingX | UI_BoxFlag_DontClampFloatingY | UI_BoxFlag_CaptureMouse | UI_BoxFlag_Scissor); // UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_DontClampFloatingX | UI_BoxFlag_DontClampFloatingY); - UI_PushDF(Parent, UI_BuildRowEx(zone_box)) + UI_SetDF(Parent, UI_BuildRowEx(zone_box)) { UI_BuildSpacer(UI_Px(zone_text_padding_px, 0.25), Axis_X); @@ -5949,7 +5958,7 @@ void V_TickForever(WaveLaneCtx *lane) // UI_SetNext(FloatingPos, zone_pos); // UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_DontClampFloatingX | UI_BoxFlag_DontClampFloatingY | UI_BoxFlag_CaptureMouse); // // UI_SetNext(Flags, UI_BoxFlag_Floating | UI_BoxFlag_DontClampFloatingX | UI_BoxFlag_DontClampFloatingY); - // UI_PushDF(Parent, UI_BuildBoxEx(zone_box)) + // UI_SetDF(Parent, UI_BuildBoxEx(zone_box)) // { // } // } @@ -6008,13 +6017,13 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(Opacity, tooltip_opacity); UI_SetNext(FloatingPos, tooltip_pos); UI_SetNext(Flags, UI_BoxFlag_Floating); - UI_PushDF(Parent, UI_BuildRowEx(tooltip_box)) + UI_SetDF(Parent, UI_BuildRowEx(tooltip_box)) { UI_BuildSpacer(window_padding, Axis_X); UI_SetNext(Width, UI_Shrink(0, 1)); UI_SetNext(Height, UI_Shrink(0, 1)); - UI_PushDF(Parent, UI_BuildColumn()) + UI_SetDF(Parent, UI_BuildColumn()) { if (hovered_zone) { @@ -6040,9 +6049,9 @@ void V_TickForever(WaveLaneCtx *lane) // UI_BuildSpacer(window_padding, Axis_Y); //- Footer - UI_PushDF(Height, footer_height) - UI_PushDF(ChildAlignment, UI_Region_Center) - UI_PushDF(Parent, UI_BuildRow()) + UI_SetDF(Height, footer_height) + UI_SetDF(ChildAlignment, UI_Region_Center) + UI_SetDF(Parent, UI_BuildRow()) { UI_BuildSpacer(UI_Grow(1, 0), Axis_X); diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index f8685e64..abe345b9 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -535,50 +535,51 @@ void UI_SetRawTexture(UI_Key key, G_TextureRef tex, Rng2 uv) UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags) { + ////////////////////////////// + //- Init persistent state + + if (!UI.box_arena) + { + UI.box_arena = AcquireArena(Gibi(64)); + UI.gpu_frame_arena = G_AcquireArena(); + // Init frames + for (u64 i = 0; i < countof(UI.frames); ++i) + { + UI_Frame *frame = &UI.frames[i]; + frame->arena = AcquireArena(Gibi(64)); + frame->rects_arena = AcquireArena(Gibi(64)); + } + // Init root box + { + UI_Box *box = PushStruct(UI.box_arena, UI_Box); + box->key = UI_RootKey; + box->gen = 1; + UI.boxes_count += 1; + UI_BoxBin *bin = &UI.box_bins[box->key.v % countof(UI.box_bins)]; + bin->first = box; + bin->last = box; + UI.root_box = box; + } + } + + ////////////////////////////// + //- Begin frame + + UI.cur_frame_tick += 1; + UI_Frame *prev_frame = UI_PrevFrame(); + UI_Frame *frame = UI_CurrentFrame(); + + { + Arena *old_arena = frame->arena; + Arena *old_rects_arena = frame->rects_arena; + ZeroStruct(frame); + frame->arena = old_arena; + frame->rects_arena = old_rects_arena; + } + frame->window_frame = WND_BeginFrame(G_Format_R16G16B16A16_Float, WND_BackbufferSizeMode_MatchMonitor); + ProfZoneDF("Begin UI") { - ////////////////////////////// - //- Init persistent state - - if (!UI.box_arena) - { - UI.box_arena = AcquireArena(Gibi(64)); - UI.gpu_frame_arena = G_AcquireArena(); - // Init frames - for (u64 i = 0; i < countof(UI.frames); ++i) - { - UI_Frame *frame = &UI.frames[i]; - frame->arena = AcquireArena(Gibi(64)); - frame->rects_arena = AcquireArena(Gibi(64)); - } - // Init root box - { - UI_Box *box = PushStruct(UI.box_arena, UI_Box); - box->key = UI_RootKey; - box->gen = 1; - UI.boxes_count += 1; - UI_BoxBin *bin = &UI.box_bins[box->key.v % countof(UI.box_bins)]; - bin->first = box; - bin->last = box; - UI.root_box = box; - } - } - - ////////////////////////////// - //- Begin frame - - UI.cur_frame_tick += 1; - UI_Frame *prev_frame = UI_PrevFrame(); - UI_Frame *frame = UI_CurrentFrame(); - - { - Arena *old_arena = frame->arena; - Arena *old_rects_arena = frame->rects_arena; - ZeroStruct(frame); - frame->arena = old_arena; - frame->rects_arena = old_rects_arena; - } - frame->window_frame = WND_BeginFrame(G_Format_R16G16B16A16_Float, WND_BackbufferSizeMode_MatchMonitor); UI.cl = G_PrepareCommandList(G_QueueKind_Direct); ResetArena(frame->arena); ResetArena(frame->rects_arena); diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 3cfe48c7..348f842c 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -510,7 +510,7 @@ UI_Style UI_PopStyle(UI_StyleDesc desc); #define UI_PeekTop(name, ...) UI_PopStyle(UI_STYLEDESC(name, __VA_ARGS__)).name #define UI_Top(name, ...) UI_PopStyle(UI_STYLEDESC(name, .use = 1, __VA_ARGS__)).name -#define UI_PushDF(name, ...) DeferFor(UI_Push(name, __VA_ARGS__), UI_Pop(name)) +#define UI_SetDF(name, ...) DeferFor(UI_Push(name, __VA_ARGS__), UI_Pop(name)) #define UI_PushCopy(name, src, ...) do { \ UI_StyleDesc _new = src; \ diff --git a/src/window/window_win32/window_win32.c b/src/window/window_win32/window_win32.c index 61cba921..db15b43d 100644 --- a/src/window/window_win32/window_win32.c +++ b/src/window/window_win32/window_win32.c @@ -472,371 +472,384 @@ WND_Frame WND_BeginFrame(G_Format backbuffer_format, WND_BackbufferSizeMode back { WND_W32_Window *window = &WND_W32.window; WND_Frame result = Zi; - - while (!Atomic32Fetch(&window->is_ready)) + ProfZoneDF("Begin window") { - _mm_pause(); - } - HWND hwnd = window->hwnd; - result.window.v = (u64)window; - - // Grab monitor info - RECT monitor_rect = Zi; - { - MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; - GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); - monitor_rect = monitor_info.rcMonitor; - } - result.monitor_size = VEC2I32(monitor_rect.right - monitor_rect.left, monitor_rect.bottom - monitor_rect.top); - result.monitor_size.x = MaxI32(result.monitor_size.x, 1); - result.monitor_size.y = MaxI32(result.monitor_size.y, 1); - - // 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); - result.draw_size.x = MaxI32(result.draw_size.x, 1); - result.draw_size.y = MaxI32(result.draw_size.y, 1); - - // Prepare backbuffer - Vec2I32 backbuffer_size = Zi; - if (backbuffer_size_mode == WND_BackbufferSizeMode_MatchWindow) - { - backbuffer_size = result.draw_size; - } - else if (backbuffer_size_mode == WND_BackbufferSizeMode_MatchMonitor) - { - backbuffer_size = result.monitor_size; - } - result.backbuffer = G_PrepareBackbuffer(window->swapchain, backbuffer_format, backbuffer_size); - - // Reset per-frame data - if (!window->frame_arena) - { - window->frame_arena = AcquireArena(Gibi(64)); - } - ResetArena(window->frame_arena); - window->first_cmd = 0; - window->last_cmd = 0; - - // Pop user input - { - LockTicketMutex(&window->w2u_tm); + while (!Atomic32Fetch(&window->is_ready)) { - ControllerEvent *src = ArenaFirst(window->w2u_events_arena, ControllerEvent); - result.controller_events.count = ArenaCount(window->w2u_events_arena, ControllerEvent); - result.controller_events.events = PushStructsNoZero(window->frame_arena, ControllerEvent, result.controller_events.count); - CopyStructs(result.controller_events.events, src, result.controller_events.count); - ResetArena(window->w2u_events_arena); + _mm_pause(); } - UnlockTicketMutex(&window->w2u_tm); - } + HWND hwnd = window->hwnd; + result.window.v = (u64)window; - // 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); - { - if (placement.showCmd == SW_MAXIMIZE) + // Grab monitor info + RECT monitor_rect = Zi; { - result.maximized = 1; + MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; + GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); + monitor_rect = monitor_info.rcMonitor; } - if (placement.showCmd == SW_MINIMIZE) - { - result.minimized = 1; - } - 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 - ) - { - result.fullscreen = 1; - } - } + result.monitor_size = VEC2I32(monitor_rect.right - monitor_rect.left, monitor_rect.bottom - monitor_rect.top); + result.monitor_size.x = MaxI32(result.monitor_size.x, 1); + result.monitor_size.y = MaxI32(result.monitor_size.y, 1); - result.forced_top = window->is_forced_top; - result.fullscreen = window->is_fullscreen; - result.has_focus = GetForegroundWindow() == hwnd; + // Client rect + RECT client_rect = Zi; + GetClientRect(hwnd, (LPRECT)&client_rect); - // Generate restore data - { - WND_W32_RestorableData restore = Zi; + // 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); + result.draw_size.x = MaxI32(result.draw_size.x, 1); + result.draw_size.y = MaxI32(result.draw_size.y, 1); + + // Prepare backbuffer + Vec2I32 backbuffer_size = Zi; + if (backbuffer_size_mode == WND_BackbufferSizeMode_MatchWindow) { - 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; - restore.has_focus = result.has_focus; - if (W32.api.func.IsWindowArranged && W32.api.func.IsWindowArranged(hwnd)) + backbuffer_size = result.draw_size; + } + else if (backbuffer_size_mode == WND_BackbufferSizeMode_MatchMonitor) + { + backbuffer_size = result.monitor_size; + } + + ProfZoneDF("Prepare backbuffer") + { + result.backbuffer = G_PrepareBackbuffer(window->swapchain, backbuffer_format, backbuffer_size); + } + + // Reset per-frame data + if (!window->frame_arena) + { + window->frame_arena = AcquireArena(Gibi(64)); + } + ResetArena(window->frame_arena); + window->first_cmd = 0; + window->last_cmd = 0; + + // Pop user input + { + LockTicketMutex(&window->w2u_tm); { - restore.is_snapped = 1; - restore.snapped_screen_rect = screen_rect; + ControllerEvent *src = ArenaFirst(window->w2u_events_arena, ControllerEvent); + result.controller_events.count = ArenaCount(window->w2u_events_arena, ControllerEvent); + result.controller_events.events = PushStructsNoZero(window->frame_arena, ControllerEvent, result.controller_events.count); + CopyStructs(result.controller_events.events, src, result.controller_events.count); + ResetArena(window->w2u_events_arena); + } + UnlockTicketMutex(&window->w2u_tm); + } + + // 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); + { + if (placement.showCmd == SW_MAXIMIZE) + { + result.maximized = 1; + } + if (placement.showCmd == SW_MINIMIZE) + { + result.minimized = 1; + } + 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 + ) + { + result.fullscreen = 1; } } - result.restore = PushString(window->frame_arena, StringFromStruct(&restore)); - } + result.forced_top = window->is_forced_top; + result.fullscreen = window->is_fullscreen; + result.has_focus = GetForegroundWindow() == hwnd; + + // 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; + restore.has_focus = result.has_focus; + if (W32.api.func.IsWindowArranged && W32.api.func.IsWindowArranged(hwnd)) + { + restore.is_snapped = 1; + restore.snapped_screen_rect = screen_rect; + } + } + result.restore = PushString(window->frame_arena, StringFromStruct(&restore)); + } + + } return result; } + void WND_EndFrame(WND_Frame frame, Vec2I32 backbuffer_pos, G_TextureRef src, Rng2I32 src_range, i32 vsync) { TempArena scratch = BeginScratchNoConflict(); WND_W32_Window *window = WND_W32_WindowFromHandle(frame.window); - HWND hwnd = window->hwnd; - - // Process cmds - b32 was_restored = 0; - b32 desires_locked_cursor = 0; - b32 desires_hidden_cursor = 0; - HCURSOR desired_cursor = (HCURSOR)Atomic64Fetch(&window->desired_cursor); - for (WND_W32_CmdNode *n = window->first_cmd; n; n = n->next) + ProfZoneDF("End window") { - WND_Cmd cmd = n->cmd; - switch(cmd.kind) + HWND hwnd = window->hwnd; + + // Process cmds + b32 was_restored = 0; + b32 desires_locked_cursor = 0; + b32 desires_hidden_cursor = 0; + HCURSOR desired_cursor = (HCURSOR)Atomic64Fetch(&window->desired_cursor); + for (WND_W32_CmdNode *n = window->first_cmd; n; n = n->next) { - default: break; - - //- Minimize - case WND_CmdKind_SetMinimized: + WND_Cmd cmd = n->cmd; + switch(cmd.kind) { - ShowWindow(hwnd, SW_MINIMIZE); - } break; + default: break; - //- Maximize - case WND_CmdKind_SetMaximized: - { - ShowWindow(hwnd, SW_MAXIMIZE); - } break; - - //- Fullscreen - case WND_CmdKind_SetFullscreen: - { - if (cmd.v != window->is_fullscreen) + //- Minimize + case WND_CmdKind_SetMinimized: { - DWORD old_style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); - RECT old_rect = Zi; + 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) { - 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 + DWORD old_style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); + RECT old_rect = Zi; { - MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; - GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); - new_rect = monitor_info.rcMonitor; + GetClientRect(hwnd, (LPRECT)&old_rect); + ClientToScreen(hwnd, (LPPOINT)&old_rect.left); + ClientToScreen(hwnd, (LPPOINT)&old_rect.right); + AdjustWindowRect(&old_rect, old_style, 0); } - new_style &= ~WS_OVERLAPPEDWINDOW; - new_style |= WS_POPUP; - window->fullscreen_restore_rect = old_rect; - new_hwnd = HWND_TOP; + 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; + + //- Set cursor + case WND_CmdKind_SetCursor: + { + if (cmd.cursor == WND_CursorKind_Hidden) + { + desires_hidden_cursor = 1; } 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; + desired_cursor = WND_W32.cursors[cmd.cursor]; } - 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; + } 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; - - //- Set cursor - case WND_CmdKind_SetCursor: - { - if (cmd.cursor == WND_CursorKind_Hidden) + //- Set cursor pos + case WND_CmdKind_SetCursorPos: { - desires_hidden_cursor = 1; - } - else + POINT screen_pt = { cmd.pos.x, cmd.pos.y }; + ClientToScreen(hwnd, (LPPOINT)&screen_pt); + SetCursorPos(screen_pt.x, screen_pt.y); + } break; + + //- Set locked cursor + case WND_CmdKind_SetLockedCursor: { - desired_cursor = WND_W32.cursors[cmd.cursor]; - } - } break; + desires_locked_cursor = !!cmd.v; + } break; - //- Set cursor pos - case WND_CmdKind_SetCursorPos: - { - POINT screen_pt = { cmd.pos.x, cmd.pos.y }; - ClientToScreen(hwnd, (LPPOINT)&screen_pt); - SetCursorPos(screen_pt.x, screen_pt.y); - } break; - - //- Set locked cursor - case WND_CmdKind_SetLockedCursor: - { - desires_locked_cursor = !!cmd.v; - } break; - - //- Restore - case WND_CmdKind_Restore: - { - // FIXME: Cap bounds - String restore_str = cmd.restore; - if (restore_str.len == sizeof(WND_W32_RestorableData)) + //- Restore + case WND_CmdKind_Restore: { - WND_W32_RestorableData *restore = PushStructNoZero(scratch.arena, WND_W32_RestorableData); - CopyBytes(restore, restore_str.text, restore_str.len); - if (restore->magic == WND_W32_RestoreMagic) + // FIXME: Cap bounds + String restore_str = cmd.restore; + if (restore_str.len == sizeof(WND_W32_RestorableData)) { - WINDOWPLACEMENT placement = restore->placement; - was_restored = 1; - SetWindowPlacement(hwnd, &placement); - SetWindowLongPtr(hwnd, GWL_STYLE, restore->style); - SetWindowLongPtr(hwnd, GWL_EXSTYLE, restore->ex_style); - - if (restore->is_fullscreen) + WND_W32_RestorableData *restore = PushStructNoZero(scratch.arena, WND_W32_RestorableData); + CopyBytes(restore, restore_str.text, restore_str.len); + if (restore->magic == WND_W32_RestoreMagic) { - 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); - } + WINDOWPLACEMENT placement = restore->placement; + was_restored = 1; + SetWindowPlacement(hwnd, &placement); + SetWindowLongPtr(hwnd, GWL_STYLE, restore->style); + SetWindowLongPtr(hwnd, GWL_EXSTYLE, restore->ex_style); - 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_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_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); - } + 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->has_focus) - { - ShowWindow(hwnd, SW_SHOW); - SetForegroundWindow(hwnd); - BringWindowToTop(hwnd); + 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); + } + + if (restore->has_focus) + { + ShowWindow(hwnd, SW_SHOW); + SetForegroundWindow(hwnd); + BringWindowToTop(hwnd); + } } } - } - } break; - } - } - - // Bring window to front on first frame - if (!was_restored && window->frame_gen == 0) - { - ShowWindow(hwnd, SW_SHOW); - SetForegroundWindow(hwnd); - BringWindowToTop(hwnd); - } - - // Set / Clip / Hide cursor - { - RECT virtual_screen_rect = Zi; - { - virtual_screen_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); - virtual_screen_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); - virtual_screen_rect.right = virtual_screen_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN); - virtual_screen_rect.bottom = virtual_screen_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN); - } - RECT client_screen_rect = Zi; - { - GetClientRect(hwnd, (LPRECT)&client_screen_rect); - ClientToScreen(hwnd, (LPPOINT)&client_screen_rect.left); - ClientToScreen(hwnd, (LPPOINT)&client_screen_rect.right); - } - b32 is_cursor_in_client_rect = 0; - { - POINT pos = Zi; - GetCursorPos(&pos); - is_cursor_in_client_rect = ( - pos.x >= client_screen_rect.left && - pos.x < client_screen_rect.right && - pos.y >= client_screen_rect.top && - pos.y < client_screen_rect.bottom - ); - } - RECT clipped_screen_rect = Zi; - { - GetClipCursor(&clipped_screen_rect); + } break; + } } - b32 is_cursor_clipped_any = !MatchStruct(&clipped_screen_rect, &virtual_screen_rect); - b32 is_cursor_clipped_client = MatchStruct(&clipped_screen_rect, &client_screen_rect); - - b32 final_desires_locked_cursor = desires_locked_cursor && frame.has_focus && is_cursor_in_client_rect; - - // Clip cursor + // Bring window to front on first frame + if (!was_restored && window->frame_gen == 0) { - if (final_desires_locked_cursor && !is_cursor_clipped_client) + ShowWindow(hwnd, SW_SHOW); + SetForegroundWindow(hwnd); + BringWindowToTop(hwnd); + } + + // Set / Clip / Hide cursor + { + RECT virtual_screen_rect = Zi; { - RECT rect = Zi; + virtual_screen_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); + virtual_screen_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); + virtual_screen_rect.right = virtual_screen_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN); + virtual_screen_rect.bottom = virtual_screen_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN); + } + RECT client_screen_rect = Zi; + { + GetClientRect(hwnd, (LPRECT)&client_screen_rect); + ClientToScreen(hwnd, (LPPOINT)&client_screen_rect.left); + ClientToScreen(hwnd, (LPPOINT)&client_screen_rect.right); + } + b32 is_cursor_in_client_rect = 0; + { + POINT pos = Zi; + GetCursorPos(&pos); + is_cursor_in_client_rect = ( + pos.x >= client_screen_rect.left && + pos.x < client_screen_rect.right && + pos.y >= client_screen_rect.top && + pos.y < client_screen_rect.bottom + ); + } + RECT clipped_screen_rect = Zi; + { + GetClipCursor(&clipped_screen_rect); + } + + b32 is_cursor_clipped_any = !MatchStruct(&clipped_screen_rect, &virtual_screen_rect); + b32 is_cursor_clipped_client = MatchStruct(&clipped_screen_rect, &client_screen_rect); + + b32 final_desires_locked_cursor = desires_locked_cursor && frame.has_focus && is_cursor_in_client_rect; + + // Clip cursor + { + if (final_desires_locked_cursor && !is_cursor_clipped_client) { - GetClientRect(hwnd, (LPRECT)&rect); - ClientToScreen(hwnd, (LPPOINT)&rect.left); - ClientToScreen(hwnd, (LPPOINT)&rect.right); + RECT rect = Zi; + { + GetClientRect(hwnd, (LPRECT)&rect); + ClientToScreen(hwnd, (LPPOINT)&rect.left); + ClientToScreen(hwnd, (LPPOINT)&rect.right); + } + ClipCursor(&rect); + } + else if (!final_desires_locked_cursor && is_cursor_clipped_any) + { + ClipCursor(0); } - ClipCursor(&rect); } - else if (!final_desires_locked_cursor && is_cursor_clipped_any) + + if (frame.has_focus) { - ClipCursor(0); + // Set cursor + { + HCURSOR old_desired_cursor = (HCURSOR)Atomic64FetchSet(&window->desired_cursor, (i64)desired_cursor); + if (old_desired_cursor != desired_cursor) + { + PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); + } + } + + // Hide cursor + { + b32 final_desires_hidden_cursor = desires_hidden_cursor && is_cursor_in_client_rect; + b32 old_desired_cursor_hidden = !!Atomic64FetchSet(&window->desired_cursor_hidden, final_desires_hidden_cursor); + if (old_desired_cursor_hidden != final_desires_hidden_cursor) + { + PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); + } + } } } - if (frame.has_focus) + // Commit backbuffer + ProfZoneDF("Commit backbuffer") { - // Set cursor - { - HCURSOR old_desired_cursor = (HCURSOR)Atomic64FetchSet(&window->desired_cursor, (i64)desired_cursor); - if (old_desired_cursor != desired_cursor) - { - PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); - } - } - - // Hide cursor - { - b32 final_desires_hidden_cursor = desires_hidden_cursor && is_cursor_in_client_rect; - b32 old_desired_cursor_hidden = !!Atomic64FetchSet(&window->desired_cursor_hidden, final_desires_hidden_cursor); - if (old_desired_cursor_hidden != final_desires_hidden_cursor) - { - PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); - } - } + G_CommitBackbuffer(frame.backbuffer, backbuffer_pos, src, src_range, vsync); } + + ++window->frame_gen; } - - // Commit backbuffer - G_CommitBackbuffer(frame.backbuffer, backbuffer_pos, src, src_range, vsync); - - ++window->frame_gen; EndScratch(scratch); }