From ef5b79e52f22861bbeaa26aec585c9e2bf05853e Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 1 Apr 2026 02:16:09 -0500 Subject: [PATCH] profiler zone chunking --- src/base/base_prof.c | 36 +- src/base/base_prof.h | 4 +- src/gpu/gpu_dx12/gpu_dx12_core.c | 11 +- src/pp/pp_vis/pp_vis_core.c | 721 ++++++++++++++++--------------- src/pp/pp_vis/pp_vis_core.h | 76 ++-- src/ui/ui_core.c | 41 +- 6 files changed, 443 insertions(+), 446 deletions(-) diff --git a/src/base/base_prof.c b/src/base/base_prof.c index e5d22f70..d9be2fcc 100644 --- a/src/base/base_prof.c +++ b/src/base/base_prof.c @@ -1,38 +1,6 @@ //////////////////////////////////////////////////////////// //~ Profile 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; @@ -53,7 +21,7 @@ void BeginProfZone(char *name_cstr_lit) UnlockTicketMutex(&Base.prof.register_track_tm); } u64 sample_seq = *NonAtomic(&track->top_sample_seq); - ProfSample *sample = &track->samples[sample_seq % ProfTrackSamplesCap]; + ProfSample *sample = &track->samples_ring[sample_seq % ProfSamplesRingCap]; { sample->flags = 0; sample->name_cstr_lit = name_cstr_lit; @@ -67,7 +35,7 @@ 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]; + ProfSample *sample = &track->samples_ring[sample_seq % ProfSamplesRingCap]; { sample->flags = ProfSampleFlag_EndZone; sample->tsc = tsc; diff --git a/src/base/base_prof.h b/src/base/base_prof.h index 36f14619..3b709498 100644 --- a/src/base/base_prof.h +++ b/src/base/base_prof.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////// //~ Profile types -#define ProfTrackSamplesCap Kibi(256) +#define ProfSamplesRingCap Kibi(256) #define MaxRegisteredProfTracks Kibi(32) Enum(ProfSampleFlag) @@ -22,7 +22,7 @@ Struct(ProfTrack) { u64 id; Atomic64 top_sample_seq; - ProfSample samples[ProfTrackSamplesCap]; + ProfSample samples_ring[ProfSamplesRingCap]; }; Struct(RegisteredProfTracksArray) diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index ec1f8ae2..8387c717 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -3862,11 +3862,14 @@ G_BackbufferHandle G_PrepareBackbuffer(G_SwapchainHandle swapchain_handle, G_For // Wait for available backbuffer if (swapchain->waitable) { - DWORD wait_result = WaitForSingleObject(swapchain->waitable, 500); - if (wait_result == WAIT_TIMEOUT) + ProfZoneDF("Wait on swapchain") { - ID3D12Fence_SetEventOnCompletion(swapchain->present_fence, swapchain->present_fence_target, swapchain->present_event); - WaitForSingleObject(swapchain->present_event, INFINITE); + DWORD wait_result = WaitForSingleObject(swapchain->waitable, 500); + if (wait_result == WAIT_TIMEOUT) + { + ID3D12Fence_SetEventOnCompletion(swapchain->present_fence, swapchain->present_fence_target, swapchain->present_event); + WaitForSingleObject(swapchain->present_event, INFINITE); + } } } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 32dfb52f..420bf624 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -5377,8 +5377,7 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Collect profiler samples - ProfZoneDF("Collect profiler samples") - if (PROFILING_ENABLED) + if (PROFILING_ENABLED) ProfZoneDF("Collect profiler samples") { RegisteredProfTracksArray prof_tracks = FetchRegisteredProfTracks(frame->arena); for (u64 track_idx = 0; track_idx < prof_tracks.count; ++track_idx) @@ -5386,38 +5385,139 @@ void V_TickForever(WaveLaneCtx *lane) ProfTrack *prof_track = prof_tracks.tracks[track_idx]; //- Fetch collection track - V_ProfilerTrackCollection *col_track = 0; + V_ZoneTrack *zone_track = 0; { - for (col_track = V.first_profiler_track_collection; col_track; col_track = col_track->next) + for (zone_track = V.first_zone_track; zone_track; zone_track = zone_track->next) { - if (col_track->prof_track->id == prof_track->id) + if (zone_track->prof_track->id == prof_track->id) { break; } } - if (!col_track) + if (!zone_track) { - col_track = PushStruct(perm, V_ProfilerTrackCollection); - col_track->samples_arena = AcquireArena(Gibi(64)); - col_track->prof_track = prof_track; - col_track->samples = ArenaFirst(col_track->samples_arena, ProfSample); - SllQueuePush(V.first_profiler_track_collection, V.last_profiler_track_collection, col_track); + zone_track = PushStruct(perm, V_ZoneTrack); + zone_track->prof_track = prof_track; + zone_track->id = V.zone_tracks_count++; + SllQueuePush(V.first_zone_track, V.last_zone_track, zone_track); + + // Init sentinel skip list chunks + for (u64 row_idx = 0; row_idx < countof(zone_track->rows); ++row_idx) + { + V_ZoneRow *row = &zone_track->rows[row_idx]; + row->first_chunk = PushStruct(perm, V_ZoneChunk); + row->first_chunk->start_ns = I64Min; + row->first_chunk->end_ns = I64Min; + row->last_chunk = PushStruct(perm, V_ZoneChunk); + row->last_chunk->start_ns = I64Max; + row->last_chunk->end_ns = I64Max; + for (u64 level = 0; level < V_ZoneChunkLevelsCount; ++level) + { + row->first_chunk->nexts[level] = row->last_chunk; + row->last_chunk->prevs[level] = row->first_chunk; + } + } } } - //- Copy new samples - u64 copy_sample_start_seq = col_track->next_collection_seq; - u64 copy_sample_end_seq = Atomic64Fetch(&prof_track->top_sample_seq); - u64 copy_samples_count = copy_sample_end_seq - copy_sample_start_seq; - ProfSample *copy_samples_dst = PushStructsNoZero(col_track->samples_arena, ProfSample, copy_samples_count); - for (u64 src_sample_seq = copy_sample_start_seq; src_sample_seq < copy_sample_end_seq; ++src_sample_seq) + //- Process new samples { - u64 src_idx = src_sample_seq % ProfTrackSamplesCap; - u64 dst_idx = src_sample_seq - copy_sample_start_seq; - copy_samples_dst[dst_idx] = prof_track->samples[src_idx]; + // TODO: Clamp collection size + u64 sample_start_seq = zone_track->next_collection_sample_seq; + u64 sample_end_seq = Atomic64Fetch(&prof_track->top_sample_seq); + u64 samples_count = sample_end_seq - sample_start_seq; + + f64 ns_per_tsc = GetNsPerTsc(); + i64 startup_tsc = GetStartupTsc(); + + for (u64 sample_seq = sample_start_seq; sample_seq < sample_end_seq; ++sample_seq) + { + u64 sample_idx = sample_seq % ProfSamplesRingCap; + ProfSample *sample = &prof_track->samples_ring[sample_idx]; + + i64 sample_time_ns = (sample->tsc - startup_tsc) * ns_per_tsc; + + b32 is_begin_zone = !(sample->flags & ProfSampleFlag_EndZone); + b32 is_end_zone = !is_begin_zone; + if (is_begin_zone) + { + //- Start zone + if (zone_track->current_sample_depth < V_MaxZoneDepth) + { + u64 zone_id = V.total_zones_count++; + V_ZoneRow *row = &zone_track->rows[zone_track->current_sample_depth]; + + //- Fetch chunk + V_ZoneChunk *chunk = row->last_chunk->prevs[0]; + { + b32 is_first_chunk = chunk->end_ns == I64Min; + if (chunk->zones_count >= V_MaxZonesPerChunk || is_first_chunk) + { + chunk = PushStruct(perm, V_ZoneChunk); + chunk->end_ns = I64Max; + for (u32 level = 0; level < V_ZoneChunkLevelsCount; ++level) + { + // Insert into skip list + V_ZoneChunk *prev_chunk = row->last_chunk->prevs[level]; + DllQueueInsertNP( + row->first_chunk, + row->last_chunk, + prev_chunk, + chunk, + nexts[level], + prevs[level] + ); + if (MixU64(zone_id) % 2) + { + // Exit skip list initialization + break; + } + } + } + } + + //- Push zone + u64 chunk_zone_idx = chunk->zones_count++; + V_Zone *zone = &chunk->zones[chunk_zone_idx]; + { + zone->id = zone_id; + zone->start_ns = sample_time_ns; + zone->end_ns = I64Max; + + // TODO: More efficient name processing + char *name_cstr = sample->name_cstr_lit; + String name = StringFromCstrNoLimit(name_cstr); + + zone->name = name; + + u64 color_seed = HashString(name); + f32 h = (Norm16(color_seed >> 0) * 1) * 360; + f32 s = TweakFloat("Profiler zone saturation", 0.50, 0, 1); + f32 v = TweakFloat("Profiler zone brightness", 1.00, 0, 1); + zone->color = SrgbFromHsv(h, s, v); + } + + zone_track->rows_count = MaxU64(zone_track->rows_count, zone_track->current_sample_depth); + } + zone_track->current_sample_depth += 1; + } + else if (is_end_zone) + { + //- End zone + zone_track->current_sample_depth -= 1; + if (zone_track->current_sample_depth < V_MaxZoneDepth) + { + V_ZoneRow *row = &zone_track->rows[zone_track->current_sample_depth]; + V_ZoneChunk *chunk = row->last_chunk->prevs[0]; + V_Zone *zone = &chunk->zones[chunk->zones_count - 1]; + zone->end_ns = sample_time_ns; + chunk->end_ns = sample_time_ns; + } + } + } + + zone_track->next_collection_sample_seq = sample_end_seq; } - col_track->next_collection_seq = copy_sample_end_seq; - col_track->samples_count += copy_samples_count; } } @@ -5427,8 +5527,6 @@ void V_TickForever(WaveLaneCtx *lane) - - ////////////////////////////// //- Build profiler @@ -5465,47 +5563,6 @@ void V_TickForever(WaveLaneCtx *lane) profiler->graph_dims = DimsFromRng2(UI_Rect(profiler_graph_box)); - //- Init test samples - // // FIXME: Remove this - // Struct(SampleState) - // { - // i64 ready_samples_count; - // Arena *samples_arena; - // }; - // Struct(Sample) - // { - // u64 zone_id; - // u64 thread_id; - // i64 start_ns; - // i64 end_ns; - // }; - // PERSIST i64 sample_states_count = 1; - // PERSIST SampleState *sample_states = 0; - // if (!sample_states) - // { - // sample_states = PushStructs(perm, SampleState, sample_states_count); - // for (i64 sample_state_idx = 0; sample_state_idx < sample_states_count; ++sample_state_idx) - // { - // SampleState *ss = &sample_states[sample_state_idx]; - // ss->samples_arena = AcquireArena(Gibi(64)); - - - - - - - // ss->ready_samples_count = 128; - // for (i64 sample_idx = 0; sample_idx < ss->ready_samples_count; ++sample_idx) - // { - // Sample *sample = PushStruct(ss->samples_arena, Sample); - // sample->zone_id = (u64)"Hello there"; - // sample->thread_id = 0; - // sample->start_ns = NsFromSeconds(sample_idx); - // sample->end_ns = NsFromSeconds(sample_idx + 1); - // } - // } - // } - // FIXME: Remove this { @@ -5533,7 +5590,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_Size graph_height = UI_Fnt(2, 1); UI_Size main_height = UI_Grow(1, 0); UI_Size zone_height = UI_Fnt(1.25, 0); - UI_Size track_height = UI_Fnt(25, 0); + UI_Size track_height = UI_Fnt(10, 0); f32 zone_text_padding_px = UI_Top(FontSize) * 0.2; // Vec2 old_main_dims = DimsFromRng2(UI_Rect(main_box)); @@ -5625,6 +5682,7 @@ void V_TickForever(WaveLaneCtx *lane) } // Drag ruler + // TODO: When measuring stopped, lerp ruler closed b32 is_measuring = 0; if (UI_Downs(main_box, Button_M1)) { @@ -5664,6 +5722,100 @@ void V_TickForever(WaveLaneCtx *lane) + // FIXME: Remove this + f64 view_range_len_px = DimsFromRng2(UI_Rect(main_box)).x; + f64 view_range_len_ns = view_range_len_px * profiler->ns_per_px; + + f64 view_start_ns = profiler->view_ns; + f64 view_end_ns = view_start_ns + view_range_len_ns; + + + + + + + + + + + + + + + + //- Visible zones + Struct(VisRow) + { + VisRow *next; + V_ZoneRow *zone_row; + V_ZoneChunk *closest_zone_chunk; + }; + + Struct(VisTrack) + { + VisTrack *next; + V_ZoneTrack *zone_track; + + VisRow *first_row; + VisRow *last_row; + }; + + VisTrack *first_vis_track = 0; + VisTrack *last_vis_track = 0; + + //- Compute visible zones + ProfZoneDF("Compute visible zones") + { + for (V_ZoneTrack *zone_track = V.first_zone_track; zone_track; zone_track = zone_track->next) + { + // TODO: Ignore non-visible tracks + VisTrack *vis_track = PushStruct(frame->arena, VisTrack); + SllQueuePush(first_vis_track, last_vis_track, vis_track); + vis_track->zone_track = zone_track; + + // TODO: Ignore non-visible rows + for (u64 row_idx = 0; row_idx < zone_track->rows_count; ++row_idx) + { + V_ZoneRow *zone_row = &zone_track->rows[row_idx]; + + V_ZoneChunk *closest_start = zone_row->last_chunk; + { + for (i32 level = countof(closest_start->prevs) - 1; level >= 0; --level) + { + while (closest_start->prevs[level] && closest_start->prevs[level]->end_ns >= view_start_ns) + { + closest_start = closest_start->prevs[level]; + } + } + } + + + + if (closest_start->end_ns >= view_start_ns && closest_start->start_ns <= view_end_ns) + { + VisRow *vis_row = PushStruct(perm, VisRow); + SllQueuePush(vis_track->first_row, vis_track->last_row, vis_row); + vis_row->zone_row = zone_row; + vis_row->closest_zone_chunk = closest_start; + } + } + } + } + + + + + + + + + + + + + + + // f64 ns_per_px = MaxF64((profiler->view_end_ns - profiler->view_start_ns) / old_main_dims.x, 1); @@ -5676,13 +5828,20 @@ void V_TickForever(WaveLaneCtx *lane) UI_Size timeline_cursor_width = UI_Fnt(0.15, 1); UI_Key hovered_zone_box = Zi; - V_ZoneDesc *hovered_zone = 0; + V_Zone *hovered_zone = 0; f64 ruler_len_ns = 0; - Vec4 main_color = profiler_color; - main_color.r *= 0.75; - main_color.g *= 0.75; - main_color.b *= 0.75; + Vec4 main_color_sampled = profiler_color; + main_color_sampled.r *= 0.75; + main_color_sampled.g *= 0.75; + main_color_sampled.b *= 0.75; + + Vec4 main_color_unsampled = main_color_sampled; + main_color_unsampled.r *= 0.85; + main_color_unsampled.g *= 0.85; + main_color_unsampled.b *= 0.85; + + // main_color_sampled.g += 0.05; UI_SetNext(Parent, vis_ui_box); UI_SetNext(BorderColor, theme.col.window_bd); @@ -5699,132 +5858,6 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildSpacer(window_padding, Axis_X); UI_SetDF(Parent, UI_BuildColumn()) { - //- Build zone tracks - i64 highest_sample_ns = 0; - V_ZoneTrack *first_zone_track = 0; - V_ZoneTrack *last_zone_track = 0; - { - for (V_ProfilerTrackCollection *col_track = V.first_profiler_track_collection; col_track; col_track = col_track->next) - { - // FIXME: Remove this - // if (track_idx == 0) continue; - - - V_ZoneTrack *zone_track = PushStruct(frame->arena, V_ZoneTrack); - DllQueuePush(first_zone_track, last_zone_track, zone_track); - - // FIXME: Atomic - u64 samples_count = col_track->samples_count; - ProfSample *samples = col_track->samples; - - - u64 start_sample_idx = 0; - samples_count = MinU64(samples_count, 512); - // samples_count = MinU64(samples_count, Kibi(16)); - - - - // u64 samples_start_seq = track_top_sample_seq - MinU64(track_top_sample_seq, ProfTrackSamplesCap); - // u64 samples_end_seq = track_top_sample_seq; - - - - // for (u64 sample_seq = samples_start_seq; sample_seq < samples_end_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 + Kibi(16)); - - - - // u64 zones_count = samples_end_seq - samples_start_seq; - // zones_count = MinU64(zones_count, 1000); - - // V_ZoneDesc *zones = PushStructs(frame->arena, V_ZoneDesc, zones_count); - - //- Create zones - ProfZoneDF("Create zones") - { - // FIXME: Clamp depth - // u64 zone_idx = 0; - u64 depth = 0; - - for (u64 sample_idx = start_sample_idx; sample_idx < samples_count; ++sample_idx) - { - ProfSample *sample = &samples[sample_idx]; - - - // u64 elapsed_ns = NsFromTsc - i64 time_ns = (sample->tsc - GetStartupTsc()) * GetNsPerTsc(); - - b32 is_end = !!(sample->flags & ProfSampleFlag_EndZone); - if (is_end) - { - depth -= 1; - V_ZoneDesc *zone = zone_track->last_zone; - // FIXME: OOB - for (; zone->depth > depth;) - { - zone = zone->prev; - } - zone->end_ns = time_ns; - // zone->end_sample_seq = sample_seq; - - highest_sample_ns = MaxI64(highest_sample_ns, time_ns); - } - else - { - V_ZoneDesc *zone = PushStruct(frame->arena, V_ZoneDesc); - zone->start_ns = time_ns; - DllQueuePush(zone_track->first_zone, zone_track->last_zone, zone); - - char *name_cstr = sample->name_cstr_lit; - String name = StringFromCstrNoLimit(name_cstr); - - // TODO: Also factor in sample source code location - u64 seed = HashString(name); - // u64 seed = MixU64((u64)name_cstr); - - f32 h = (Norm16(seed >> 0) * 1) * 360; - // f32 s = TweakFloat("Profiler zone saturation", 0.85, 0, 1); - // f32 v = TweakFloat("Profiler zone brightness", 0.50, 0, 1); - f32 s = TweakFloat("Profiler zone saturation", 0.50, 0, 1); - f32 v = TweakFloat("Profiler zone brightness", 1.00, 0, 1); - - zone->color = SrgbFromHsv(h, s, v); - - // zone->start_sample_seq = sample_seq; - zone->name = name; - zone->depth = depth; - - // TODO: Real zone ID - zone->id = MixU64s(sample_idx, col_track->prof_track->id); - - depth += 1; - zone_track->zone_rows_count = MaxU64(depth, zone_track->zone_rows_count); - } - } - } - - //- Push zones to rows - zone_track->zone_rows = PushStructs(frame->arena, V_ZoneRow, zone_track->zone_rows_count); - for (V_ZoneDesc *zone = zone_track->first_zone; zone; zone = zone->next) - { - // FIXME: Large depth - V_ZoneRow *row = &zone_track->zone_rows[zone->depth]; - row->count += 1; - DllQueuePushNP(row->first_zone, row->last_zone, zone, next_in_row, prev_in_row); - } - } - } - //- Header // UI_SetDF(BackgroundColor, Color_Red) // UI_SetNext(BorderColor, Color_Red); @@ -5836,6 +5869,9 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetDF(TextColor, theme.col.hint) UI_BuildLabelF("Profiler"); } + + UI_BuildSpacer(window_padding, Axis_Y); + UI_BuildDivider(UI_Px(1, 1), theme.col.divider, Axis_Y); //- Graph @@ -5846,121 +5882,80 @@ void V_TickForever(WaveLaneCtx *lane) } // UI_BuildDivider(UI_Px(1, 1), theme.col.divider, Axis_Y); + UI_BuildDivider(UI_Px(1, 1), theme.col.divider, Axis_Y); + + UI_BuildSpacer(window_padding, Axis_Y); + //- Main area - UI_SetNext(BackgroundColor, main_color); + UI_SetNext(BackgroundColor, main_color_unsampled); UI_SetNext(Height, main_height); - UI_SetNext(Rounding, UI_Rpx(4)); + UI_SetNext(Rounding, UI_Rpx(10)); // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse | UI_BoxFlag_CaptureThroughChildren | UI_BoxFlag_Scissor); UI_SetNext(Flags, UI_BoxFlag_CaptureMouse | UI_BoxFlag_CaptureThroughChildren); // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); UI_SetDF(Parent, UI_BuildColumnEx(main_box)) { - //- Ruler - UI_SetDF(Opacity, profiler->ruler_opacity) + //- Active area background + UI_SetDF(Opacity, 0.75) { + f64 aabg_left_ns = MaxF64(0, profiler->view_ns); + f64 aabg_right_ns = MinF64(frame->time_ns, view_end_ns); + + f64 aabg_len_ns = aabg_right_ns - aabg_left_ns; + f64 aabg_len_px = aabg_len_ns / profiler->ns_per_px; + + f64 aabg_cursor_offset_px = (aabg_left_ns - profiler->view_ns) / profiler->ns_per_px; + f32 aabg_offset_px = (aabg_left_ns - profiler->view_ns) / profiler->ns_per_px; + Vec2 aabg_cursor_pos = VEC2(aabg_cursor_offset_px, 0); + Vec2 aabg_pos = VEC2(aabg_offset_px, 0); + UI_Size aabg_width = UI_Px(aabg_len_px, 1); + + Vec4 aabg_color = main_color_sampled; + + // Ruler rect { - f64 ruler_left_ns = MinF64(profiler->ruler_start_ns, profiler->cursor_ns); - f64 ruler_right_ns = MaxF64(profiler->ruler_start_ns, profiler->cursor_ns); - - ruler_len_ns = ruler_right_ns - ruler_left_ns; - f64 ruler_len_px = ruler_len_ns / profiler->ns_per_px; - - f64 ruler_cursor_offset_px = (profiler->ruler_start_ns - profiler->view_ns) / profiler->ns_per_px; - f32 ruler_offset_px = (ruler_left_ns - profiler->view_ns) / profiler->ns_per_px; - Vec2 ruler_cursor_pos = VEC2(ruler_cursor_offset_px, 0); - Vec2 ruler_pos = VEC2(ruler_offset_px, 0); - UI_Size ruler_width = UI_Px(ruler_len_px, 1); - - Vec4 ruler_color = VEC4(0.25, 0.25, 0.25, 0.25); - - // Ruler rect - { - // UI_SetNext(BorderSize, 1); - // UI_SetNext(BorderColor, Color_White); - // UI_SetDF(Opacity, profiler->ruler_opacity) - UI_SetNext(Width, ruler_width); - UI_SetNext(FloatingPos, ruler_pos); - UI_SetNext(BackgroundColor, ruler_color); - UI_SetNext(Flags, UI_BoxFlag_Floating); - UI_BuildBoxEx(UI_KeyF("ruler")); - } - - // Ruler cursor - { - // UI_SetNext(BorderSize, 1); - // UI_SetNext(BorderColor, Color_White); - UI_SetNext(BackgroundColor, timeline_cursor_color); - UI_SetNext(Width, timeline_cursor_width); - UI_SetNext(FloatingPos, ruler_cursor_pos); - UI_SetNext(Anchor, UI_Region_Top); - UI_SetNext(Flags, UI_BoxFlag_Floating); - UI_BuildBoxEx(UI_KeyF("ruler cursor")); - } + // UI_SetNext(BorderSize, 1); + // UI_SetNext(BorderColor, Color_White); + UI_SetNext(Width, aabg_width); + UI_SetNext(FloatingPos, aabg_pos); + UI_SetNext(BackgroundColor, aabg_color); + UI_SetNext(Flags, UI_BoxFlag_Floating); + UI_BuildBoxEx(UI_KeyF("active area background")); } } //- Zone tracks { - u64 zone_track_idx = 0; - for (V_ZoneTrack *zone_track = first_zone_track; zone_track; zone_track = zone_track->next) + for (VisTrack *vis_track = first_vis_track; vis_track; vis_track = vis_track->next) { - // FIXME: Remove this - // if (track_idx != 0) continue; + V_ZoneTrack *zone_track = vis_track->zone_track; - UI_Key track_box = UI_KeyF("track %F", FmtUint(zone_track_idx)); UI_SetNext(Height, track_height); - // UI_SetNext(Flags, UI_BoxFlag_CaptureMouse); - UI_SetDF(Tag, track_box.v) - UI_SetDF(Parent, UI_BuildColumnEx(track_box)) + UI_SetDF(Parent, UI_BuildColumn()) { - - // // Binary search to find samples range - // // FIXME: Atomic - // ProfSample *samples = track->samples; - // u64 samples_start_seq = track->top_sample_seq - MinU64(track->top_sample_seq, ProfTrackSamplesCap); - // u64 samples_end_seq = track->new_sample_seq; - // { - // // i64 search_seq = samples_start_seq + (samples_end_seq - samples_start_seq) / 2; - // i64 search_start_seq = samples_start_seq; - // i64 search_end_seq = samples_end_seq; - // while (search_end_seq > search_start_seq) - // { - // if (samples[search_start_seq % ProfTrackSamplesCap]. - // } - // samples_start_seq = search_start_seq; - // } - - - - - - - // FIXME: Remove this - f64 view_range_len_px = DimsFromRng2(UI_Rect(main_box)).x; - f64 view_range_len_ns = view_range_len_px * profiler->ns_per_px; - - - - // Build zone rows + for (VisRow *row = vis_track->first_row; row; row = row->next) { - for (u64 zone_row_idx = 0; zone_row_idx < zone_track->zone_rows_count; ++zone_row_idx) + //- Zone row + // UI_SetNext(Height, zone_height); + UI_SetNext(Height, zone_height); + UI_SetDF(Parent, UI_BuildRow()) { - V_ZoneRow *row = &zone_track->zone_rows[zone_row_idx]; - - //- Zone row - // UI_SetNext(Height, zone_height); - UI_SetNext(Height, zone_height); - UI_SetDF(Parent, UI_BuildRow()) + for (V_ZoneChunk *chunk = row->closest_zone_chunk; chunk && chunk->start_ns <= view_end_ns; chunk = chunk->nexts[0]) { - //- Zones in row - for (V_ZoneDesc *zone = row->first_zone; zone; zone = zone->next_in_row) + b32 reached_end_of_visible_chunk = 0; + for (u64 chunk_zone_idx = 0; chunk_zone_idx < chunk->zones_count && !reached_end_of_visible_chunk; ++chunk_zone_idx) { - // FIXME: Clamp to end of timeline - i64 visual_start_ns = MaxI64(zone->start_ns, profiler->view_ns); - i64 visual_end_ns = MinI64(zone->end_ns, profiler->view_ns + view_range_len_ns); - f64 visual_len_px = (visual_end_ns - visual_start_ns) / profiler->ns_per_px; + V_Zone *zone = &chunk->zones[chunk_zone_idx]; - if (visual_len_px > 1) + i64 visual_zone_start_ns = MaxI64(zone->start_ns, profiler->view_ns); + i64 visual_zone_end_ns = MinI64(zone->end_ns, profiler->view_ns + view_range_len_ns); + f64 visual_zone_len_px = (visual_zone_end_ns - visual_zone_start_ns) / profiler->ns_per_px; + + if (visual_zone_start_ns > view_end_ns || zone->end_ns == I64Max) + { + reached_end_of_visible_chunk = 1; + } + else if (visual_zone_end_ns >= view_start_ns && visual_zone_len_px > 5) { UI_Key zone_box = UI_KeyF("Zone %F", FmtUint(zone->id)); if (UI_HotAbsolute(zone_box)) @@ -5998,8 +5993,8 @@ void V_TickForever(WaveLaneCtx *lane) - f64 zone_offset_px = (visual_start_ns - profiler->view_ns) / profiler->ns_per_px; - f64 zone_len_ns = visual_end_ns - visual_start_ns; + f64 zone_offset_px = (visual_zone_start_ns - profiler->view_ns) / profiler->ns_per_px; + f64 zone_len_ns = visual_zone_end_ns - visual_zone_start_ns; f64 zone_len_px = zone_len_ns / profiler->ns_per_px; @@ -6048,15 +6043,16 @@ void V_TickForever(WaveLaneCtx *lane) // UI_BuildSpacer(UI_Px(zone_text_padding_px, 0), Axis_X); // UI_BuildSpacer(UI_Grow(1, 0), Axis_X); - UI_SetNext(Width, UI_Shrink(0, 1)); + // UI_SetNext(Width, UI_Shrink(0, 1)); + UI_SetNext(Width, UI_Shrink(0, 0)); // UI_SetNext(Width, UI_Grow(1, 1)); UI_SetNext(ChildAlignment, UI_Region_Center); // UI_SetNext(ChildAlignment, UI_Region_Left); UI_SetNext(FontSize, UI_Top(FontSize) * theme.h5); UI_SetNext(TextColor, zone_text_color); UI_SetNext(Text, zone->name); - UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_DontTruncateText); - // UI_SetNext(Flags, UI_BoxFlag_DrawText); + // UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_DontTruncateText); + UI_SetNext(Flags, UI_BoxFlag_DrawText); UI_BuildRow(); // UI_BuildSpacer(UI_Px(zone_text_padding_px, 1), Axis_X); @@ -6068,49 +6064,7 @@ void V_TickForever(WaveLaneCtx *lane) } } } - - - // for (u64 zone_idx = 0; zone_idx < zones_count; ++zone_idx) - // { - // V_ZoneDesc *zone = &zones[zone_idx]; - - // UI_Key zone_box = UI_KeyF("Zone %F", FmtUint(zone_idx)); - - // Vec4 zone_color = zone->color; - // zone_color = LerpSrgb(zone_color, Color_Cyan, UI_Hot(zone_box)); - - // // Vec4 zone_color_bd = Zi; - // // zone_color_bd - - // i64 zone_len_ns = zone->end_ns - zone->start_ns; - // f32 zone_len_px = zone_len_ns / profiler->ns_per_px; - // // i6 - - // f32 zone_offset_px = (zone->start_ns - profiler->view_ns) / profiler->ns_per_px; - // Vec2 zone_pos = VEC2(zone_offset_px, 0); - // UI_Size zone_width = UI_Px(zone_len_px, 1); - - // UI_SetNext(Text, StringF(frame->arena, "ZONE %F", FmtUint(zone_idx))); - // // if (do_break && UI_IsMouseHovered(zone_box)) - // if (do_break && zone_idx == 0) - // { - // UI_SetNext(DebugBreakFlags, UI_DebugBreakFlag_BuildFeedback | UI_DebugBreakFlag_CheckCursorHover); - // } - - - - // UI_SetNext(Width, zone_width); - // UI_SetNext(Height, zone_height); - // UI_SetNext(BackgroundColor, zone_color); - // 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_SetDF(Parent, UI_BuildBoxEx(zone_box)) - // { - // } - // } } - zone_track_idx += 1; } } @@ -6118,9 +6072,49 @@ void V_TickForever(WaveLaneCtx *lane) + //- Ruler + UI_SetDF(Opacity, profiler->ruler_opacity) + { + { + f64 ruler_left_ns = MinF64(profiler->ruler_start_ns, profiler->cursor_ns); + f64 ruler_right_ns = MaxF64(profiler->ruler_start_ns, profiler->cursor_ns); + ruler_len_ns = ruler_right_ns - ruler_left_ns; + f64 ruler_len_px = ruler_len_ns / profiler->ns_per_px; + f64 ruler_cursor_offset_px = (profiler->ruler_start_ns - profiler->view_ns) / profiler->ns_per_px; + f32 ruler_offset_px = (ruler_left_ns - profiler->view_ns) / profiler->ns_per_px; + Vec2 ruler_cursor_pos = VEC2(ruler_cursor_offset_px, 0); + Vec2 ruler_pos = VEC2(ruler_offset_px, 0); + UI_Size ruler_width = UI_Px(ruler_len_px, 1); + Vec4 ruler_color = VEC4(0.25, 0.25, 0.25, 0.25); + + // Ruler rect + { + // UI_SetNext(BorderSize, 1); + // UI_SetNext(BorderColor, Color_White); + // UI_SetDF(Opacity, profiler->ruler_opacity) + UI_SetNext(Width, ruler_width); + UI_SetNext(FloatingPos, ruler_pos); + UI_SetNext(BackgroundColor, ruler_color); + UI_SetNext(Flags, UI_BoxFlag_Floating); + UI_BuildBoxEx(UI_KeyF("ruler")); + } + + // Ruler cursor + { + // UI_SetNext(BorderSize, 1); + // UI_SetNext(BorderColor, Color_White); + UI_SetNext(BackgroundColor, timeline_cursor_color); + UI_SetNext(Width, timeline_cursor_width); + UI_SetNext(FloatingPos, ruler_cursor_pos); + UI_SetNext(Anchor, UI_Region_Top); + UI_SetNext(Flags, UI_BoxFlag_Floating); + UI_BuildBoxEx(UI_KeyF("ruler cursor")); + } + } + } //- Timeline cursor { @@ -6182,24 +6176,24 @@ void V_TickForever(WaveLaneCtx *lane) { // UI_SetNext(TextColor, theme.col.positive); // UI_SetNext(TextColor, theme.col.button_active); - UI_BuildLabelF("%F", FmtTimeNs(ruler_len_ns, .p = 3)); + UI_BuildLabelF("%F", FmtTimeNs(ruler_len_ns, .p = 2)); } if (show_hovered_zone) { - V_ZoneDesc *zone = hovered_zone; + V_Zone *zone = hovered_zone; // UI_BuildLabelF("Hi!!"); i64 zone_elapsed_ns = zone->end_ns - zone->start_ns; - // UI_BuildLabelF("%Fms", FmtFloat(MsFromNs(zone_elapsed_ns), .p = 3)); + // UI_BuildLabelF("%Fms", FmtFloat(MsFromNs(zone_elapsed_ns), .p = 2)); UI_SetDF(Parent, UI_BuildRow()) { UI_SetNext(TextColor, theme.col.hint); UI_BuildLabelF("%F: ", FmtString(zone->name)); - UI_BuildLabelF("%F", FmtTimeNs(zone_elapsed_ns, .p = 3)); + UI_BuildLabelF("%F", FmtTimeNs(zone_elapsed_ns, .p = 2)); } } @@ -6227,8 +6221,8 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetDF(TextColor, theme.col.warn) UI_SetDF(Parent, UI_BuildRow()) { - // UI_BuildIcon(theme.icon_font, UI_Icon_Warning); - UI_BuildIcon(theme.icon_font, UI_Icon_Info); + UI_BuildIcon(theme.icon_font, UI_Icon_Warning); + // UI_BuildIcon(theme.icon_font, UI_Icon_Info); UI_BuildLabelF(" Profiling an unoptimized build"); } } @@ -6245,6 +6239,49 @@ void V_TickForever(WaveLaneCtx *lane) } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ////////////////////////////// //- Build notifications diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 71e81fb8..9ca4922f 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -240,29 +240,37 @@ Struct(V_Palette) //////////////////////////////////////////////////////////// //~ Profiler types -Struct(V_ZoneDesc) +#define V_MaxZonesPerChunk 256 +#define V_ZoneChunkLevelsCount 32 +#define V_MaxZoneDepth 128 + +Struct(V_Zone) { - V_ZoneDesc *prev; - V_ZoneDesc *next; - - V_ZoneDesc *prev_in_row; - V_ZoneDesc *next_in_row; - String name; u64 id; - u32 depth; - Vec4 color; i64 start_ns; i64 end_ns; }; +Struct(V_ZoneChunk) +{ + V_ZoneChunk *nexts[V_ZoneChunkLevelsCount]; + V_ZoneChunk *prevs[V_ZoneChunkLevelsCount]; + + i64 start_ns; + i64 end_ns; + + u64 zones_count; + V_Zone zones[V_MaxZonesPerChunk]; +}; + Struct(V_ZoneRow) { - u64 count; - V_ZoneDesc *first_zone; - V_ZoneDesc *last_zone; + // Skip list sentinels + V_ZoneChunk *first_chunk; + V_ZoneChunk *last_chunk; }; Struct(V_ZoneTrack) @@ -270,13 +278,28 @@ Struct(V_ZoneTrack) V_ZoneTrack *next; V_ZoneTrack *prev; - V_ZoneDesc *first_zone; - V_ZoneDesc *last_zone; + // FIXME: Set this + u64 id; - u64 zone_rows_count; - V_ZoneRow *zone_rows; + u64 rows_count; + V_ZoneRow rows[V_MaxZoneDepth]; + + ProfTrack *prof_track; + u64 next_collection_sample_seq; + u64 current_sample_depth; }; + + + + + + + + + + + Struct(V_Profiler) { Vec2 graph_dims; @@ -386,18 +409,6 @@ Struct(V_ObservationBin) #define V_MaxInterpRatio 4 -Struct(V_ProfilerTrackCollection) -{ - V_ProfilerTrackCollection *next; - - u64 samples_count; - ProfSample *samples; - Arena *samples_arena; - - ProfTrack *prof_track; - u64 next_collection_seq; -}; - Struct(V_Frame) { Arena *arena; @@ -446,11 +457,10 @@ Struct(V_Ctx) Arena *transient_markers_arena; Arena *persistent_markers_arena; - V_ProfilerTrackCollection *first_profiler_track_collection; - V_ProfilerTrackCollection *last_profiler_track_collection; - - u64 profiler_samples_count; - ProfSample *profile_samples; + u64 total_zones_count; + u64 zone_tracks_count; + V_ZoneTrack *first_zone_track; + V_ZoneTrack *last_zone_track; i64 panels_count; i64 windows_count; diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index c9d41e4d..d544bb71 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -199,43 +199,22 @@ UI_BoxIterResult UI_NextBox(Arena *arena, UI_BoxIter *iter) dfs->visited = 1; result.box = box; result.pre = 1; - // Push floating children to dfs stack + + // Push children to dfs stack for (UI_Box *child = box->last; child; child = child->prev) { - if (AnyBit(child->desc.flags, UI_BoxFlag_Floating)) + UI_BoxIterDfsNode *child_dfs = iter->first_free; + if (child_dfs) { - UI_BoxIterDfsNode *child_dfs = iter->first_free; - if (child_dfs) - { - SllQueuePop(iter->first_free, iter->last_free); - ZeroStruct(child_dfs); - } - else - { - child_dfs = PushStruct(arena, UI_BoxIterDfsNode); - } - child_dfs->box = child; - SllStackPush(iter->first, child_dfs); + SllQueuePop(iter->first_free, iter->last_free); + ZeroStruct(child_dfs); } - } - // Push non-floating children to dfs stack - for (UI_Box *child = box->last; child; child = child->prev) - { - if (!AnyBit(child->desc.flags, UI_BoxFlag_Floating)) + else { - UI_BoxIterDfsNode *child_dfs = iter->first_free; - if (child_dfs) - { - SllQueuePop(iter->first_free, iter->last_free); - ZeroStruct(child_dfs); - } - else - { - child_dfs = PushStruct(arena, UI_BoxIterDfsNode); - } - child_dfs->box = child; - SllStackPush(iter->first, child_dfs); + child_dfs = PushStruct(arena, UI_BoxIterDfsNode); } + child_dfs->box = child; + SllStackPush(iter->first, child_dfs); } } else