internal void accum_dt_ms(Delta_Time_Accum &accum, f32 dt) { if (accum.count < accum.max) { assert(accum.start == 0); accum.base[accum.count++] = dt; } else { accum.base[accum.start++] = dt; if (accum.start == accum.max) accum.start = 0; } } internal f32 calc_avg_dt_ms(const Delta_Time_Accum &accum) { f32 res = 0; for (u16 idx = 0; idx < accum.count; ++idx) res += accum.base[idx]; if (accum.count) res /= accum.count; return res; } internal String8 to_pretty_size(Arena *arena, u64 bytes) { if (bytes >= GiB(1)) return push_str8f(arena, "%.1f GiB", (f32)bytes / GiB(1)); if (bytes >= MiB(1)) return push_str8f(arena, "%.1f MiB", (f32)bytes / MiB(1)); if (bytes >= KiB(1)) return push_str8f(arena, "%.1f KiB", (f32)bytes / KiB(1)); return push_str8f(arena, "%zu B", bytes); } internal u32 mem_edit_bg_color_fn(const u8 *data, u64 off, const void *user_data) { const App_State *app = reinterpret_cast(user_data); const RNTuple_Info &rinfo = app->rntinfo; u64 rblob_sz = rinfo.rblob_header_size; #define COL(c) (ImColor((c)[0], (c)[1], (c)[2])) if (off <= rinfo.root_file_header_size) return COL(app->vsettings.col_tfile); if (rinfo.rng_anchor_key.start <= off && off <= rinfo.rng_anchor_key.end()) return COL(app->vsettings.col_key); if (rinfo.rng_header.start - rblob_sz <= off && off <= rinfo.rng_header.start) return COL(app->vsettings.col_key); if (rinfo.rng_footer.start - rblob_sz <= off && off <= rinfo.rng_footer.start) return COL(app->vsettings.col_key); if (rinfo.rng_anchor.start <= off && off <= rinfo.rng_anchor.end()) return COL(app->vsettings.col_anchor); if (rinfo.rng_header.start <= off && off <= rinfo.rng_header.end()) return COL(app->vsettings.col_header); if (rinfo.rng_footer.start <= off && off <= rinfo.rng_footer.end()) return COL(app->vsettings.col_footer); #undef COL return IM_COL32(0, 0, 0, 0); } internal MemoryEditor make_memory_editor(const App_State &app) { MemoryEditor mem_edit; // mem_edit.ReadOnly = true; mem_edit.Cols = 32; mem_edit.OptShowDataPreview = true; mem_edit.BgColorFn = mem_edit_bg_color_fn; mem_edit.BgColorFnUserData = &app; return mem_edit; } internal Viewer_Settings make_viewer_settings() { Viewer_Settings settings {}; #define COL(c, r, g, b) settings.c[0] = r/255.0, settings.c[1] = g/255.0, settings.c[2] = b/255.0 COL(col_anchor, 150, 150, 0); COL(col_header, 150, 0, 50); COL(col_footer, 50, 0, 150); COL(col_key, 0, 100, 50); COL(col_tfile, 90, 90, 90); #undef COL return settings; } internal void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms) { accum_dt_ms(app.delta_time_accum, delta_time_ms); ImGui::SetNextWindowPos({ 0, 0 }); ImGui::SetNextWindowSize({ (f32)app.win_data.width, (f32)app.win_data.height }); Temp scratch = temp_begin(arena); defer { temp_end(scratch); }; const u64 inspected_max_size = 2048; u64 text_buf_size = min(app.inspected_file_size * 2, inspected_max_size); char *text_buf = arena_push_array_no_zero(scratch.arena, text_buf_size + 1); // Convert file content to human readable // @Speed: maybe do this only when the file changes for (u64 i = 0; i < text_buf_size / 2; ++i) sprintf(&text_buf[2 * i], "%02X", app.inspected_fmem[i]); text_buf[text_buf_size] = 0; const auto main_win_flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDecoration; if (ImGui::Begin("main", nullptr, main_win_flags)) { String8 ntpl_desc = rntuple_description(scratch.arena, app.rntinfo.anchor); ImGui::Text("RNTuple '%s' (%s) from file \"%s\"", app.ntpl_name, ntpl_desc.c(), app.inspected_fname); // Draw stats { ImGui::SameLine(); u64 mem_used = arena_mem_used(arena); f32 avg_dt = calc_avg_dt_ms(app.delta_time_accum); String8 stat_txt = push_str8f(scratch.arena, "mem used: %s | avg dt: %.1f", to_pretty_size(scratch.arena, mem_used), avg_dt); f32 pos_x = (ImGui::GetCursorPosX() + ImGui::GetColumnWidth() - ImGui::CalcTextSize(stat_txt.c()).x - ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x); if (pos_x > ImGui::GetCursorPosX()) ImGui::SetCursorPosX(pos_x); ImGui::Text("%s", stat_txt.c()); } ImGui::Separator(); // Draw main content { static MemoryEditor mem_edit = make_memory_editor(app); ImGui::BeginTable("Hex View", 2); ImGui::TableNextColumn(); mem_edit.DrawContents(app.inspected_fmem, app.inspected_file_size); ImGui::TableNextColumn(); ImGui::ColorEdit3("TFile", app.vsettings.col_tfile, ImGuiColorEditFlags_NoInputs); ImGui::ColorEdit3("RNTuple Anchor", app.vsettings.col_anchor, ImGuiColorEditFlags_NoInputs); ImGui::ColorEdit3("RNTuple Header", app.vsettings.col_header, ImGuiColorEditFlags_NoInputs); ImGui::ColorEdit3("RNTuple Footer", app.vsettings.col_footer, ImGuiColorEditFlags_NoInputs); ImGui::ColorEdit3("TKey Header", app.vsettings.col_key, ImGuiColorEditFlags_NoInputs); ImGui::EndTable(); } ImGui::End(); } }