use rdtsc for profiler timestamps
This commit is contained in:
parent
0706662050
commit
db3f0e6b2d
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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; \
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user