rntviewer/src/render.cpp

323 lines
11 KiB
C++
Raw Normal View History

2024-07-11 12:59:59 +00:00
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;
}
2024-07-25 15:01:08 +00:00
internal
ImColor imcol(const f32 col[3])
{
return ImColor(col[0], col[1], col[2]);
}
2024-07-25 10:25:05 +00:00
internal
u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data)
{
App_State *app = reinterpret_cast<App_State *>(user_data);
2024-07-26 14:05:04 +00:00
off += app->base_display_addr;
2024-07-25 10:25:05 +00:00
2024-07-29 09:59:56 +00:00
if (app->viewer.hovered_range.start <= off && off < app->viewer.hovered_range.end())
return imcol(app->viewer.col_highlight);
2024-07-25 16:23:58 +00:00
i64 hilite_cluster = app->viewer.highlight_cluster ? app->viewer.highlighted_cluster : -1;
Section section = find_section(*app, off, hilite_cluster);
2024-07-25 10:25:05 +00:00
2024-07-25 15:01:08 +00:00
if (section.highlighted) return imcol(app->viewer.col_highlight);
if (section.id == Sec_Page && off == section.range.start) return imcol(app->viewer.col_page_start);
if (off < section.range.start) return imcol(app->viewer.col_key);
if (section.range.end() - section.post_size <= off && off < section.range.end()) return imcol(app->viewer.col_checksum);
return imcol(app->viewer.col_section[section.id]);
2024-07-11 12:00:43 +00:00
}
2024-07-25 08:44:50 +00:00
internal
void mem_edit_interact_fn(const u8 *, u64 off, void *user_data)
{
2024-07-29 09:59:56 +00:00
App_State *app = reinterpret_cast<App_State *>(user_data);
off += app->base_display_addr;
app->viewer.hovered_off = off;
2024-07-25 08:44:50 +00:00
}
2024-07-11 12:00:43 +00:00
internal
2024-07-25 15:01:08 +00:00
MemoryEditor make_memory_editor(App_State &app, i32 n_cols)
{
MemoryEditor mem_edit;
2024-07-25 15:01:08 +00:00
mem_edit.Cols = n_cols ? n_cols : 32;
2024-07-11 12:27:19 +00:00
mem_edit.OptShowDataPreview = true;
2024-07-19 14:31:48 +00:00
// Do nothing on write.
// Note that we don't use ReadOnly = true because that disables selecting bytes
mem_edit.WriteFn = [] (ImU8*, size_t, ImU8) {};
2024-07-11 12:00:43 +00:00
mem_edit.BgColorFn = mem_edit_bg_color_fn;
2024-07-11 12:27:19 +00:00
mem_edit.BgColorFnUserData = &app;
2024-07-25 08:44:50 +00:00
mem_edit.InteractFn = mem_edit_interact_fn;
mem_edit.InteractFnUserData = &app;
return mem_edit;
}
2024-07-11 12:59:59 +00:00
internal
2024-07-25 15:01:08 +00:00
void make_viewer(App_State &app, u16 n_cols)
2024-07-11 12:27:19 +00:00
{
2024-07-16 12:34:51 +00:00
Viewer viewer {};
2024-07-25 15:01:08 +00:00
viewer.mem_edit = make_memory_editor(app, (i32)n_cols);
2024-07-16 12:34:51 +00:00
#define COL(c, r, g, b) viewer.c[0] = r/255.0, viewer.c[1] = g/255.0, viewer.c[2] = b/255.0
2024-07-11 14:29:44 +00:00
COL(col_key, 0, 100, 50);
COL(col_page_start, 200, 0, 200);
2024-07-16 10:04:38 +00:00
COL(col_checksum, 134, 65, 25);
2024-07-19 19:02:27 +00:00
COL(col_highlight, 190, 190, 190);
2024-07-25 10:25:05 +00:00
#define COL_S(c, r, g, b) viewer.col_section[c][0] = r/255.0, viewer.col_section[c][1] = g/255.0, viewer.col_section[c][2] = b/255.0
COL_S(Sec_RNTuple_Anchor, 150, 150, 0);
COL_S(Sec_RNTuple_Header, 150, 0, 50);
COL_S(Sec_RNTuple_Footer, 50, 0, 150);
COL_S(Sec_TFile_Header, 90, 90, 90);
COL_S(Sec_TFile_Object, 120, 120, 120);
COL_S(Sec_TFile_Info, 95, 76, 76);
COL_S(Sec_TFile_FreeList, 60, 60, 90);
COL_S(Sec_Page, 125, 0, 125);
COL_S(Sec_Page_List, 60, 110, 120);
COL_S(Sec_TKey_List, 100, 140, 100);
2024-07-11 12:27:19 +00:00
#undef COL
2024-07-25 10:25:05 +00:00
#undef COL_S
2024-07-16 12:34:51 +00:00
app.viewer = viewer;
}
internal
2024-07-25 16:23:58 +00:00
void viewer_jump_to(App_State &app, u64 addr)
2024-07-16 12:34:51 +00:00
{
2024-07-25 16:23:58 +00:00
app.base_display_addr = addr;
app.viewer.mem_edit.GotoAddr = 0;
2024-07-11 12:27:19 +00:00
}
2024-07-15 16:02:55 +00:00
internal
2024-07-16 12:34:51 +00:00
void viewer_jump_to_page(App_State &app, u64 page_idx)
2024-07-15 16:02:55 +00:00
{
assert(app.rndata.n_pages > 0);
page_idx = (page_idx + app.rndata.n_pages) % app.rndata.n_pages;
// @Speed
Page_Info_Node *page = app.rndata.pages;
for (u64 i = 0; i < page_idx; ++i) {
page = page->next;
assert(page);
}
2024-07-16 12:34:51 +00:00
app.viewer.latest_page_gone_to = page_idx;
2024-07-25 16:23:58 +00:00
viewer_jump_to(app, page->range.start);
2024-07-15 16:02:55 +00:00
}
2024-07-18 15:43:44 +00:00
internal
void viewer_jump_to_page_list(App_State &app, u64 page_list_idx)
{
assert(app.rndata.n_cluster_groups > 0);
page_list_idx = (page_list_idx + app.rndata.n_cluster_groups) % app.rndata.n_cluster_groups;
Cluster_Group_Info &cg_info = app.rndata.cluster_groups[page_list_idx];
app.viewer.latest_page_list_gone_to = page_list_idx;
2024-07-25 16:23:58 +00:00
viewer_jump_to(app, cg_info.rng_page_list.start);
2024-07-18 15:43:44 +00:00
}
2024-07-19 14:31:48 +00:00
internal
void viewer_jump_to_cluster(App_State &app, u64 cluster_idx)
{
assert(app.rndata.n_clusters > 0);
cluster_idx = (cluster_idx + app.rndata.n_clusters) % app.rndata.n_clusters;
2024-07-23 08:43:28 +00:00
// @Speed: this is slow! Consider an acceleration structure, or maybe we can reuse
// Page_Info_Groups + binary search? (depends on whether cluster_idx are sorted)
2024-07-19 14:31:48 +00:00
Page_Info_Node *page = app.rndata.pages;
for (u64 i = 0; i < app.rndata.n_pages; ++i) {
if (page->cluster_id == cluster_idx)
break;
page = page->next;
assert(page);
}
app.viewer.highlighted_cluster = cluster_idx;
2024-07-25 16:23:58 +00:00
viewer_jump_to(app, page->range.start);
2024-07-19 14:31:48 +00:00
}
2024-07-10 19:47:37 +00:00
internal
2024-07-11 12:59:59 +00:00
void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms)
2024-07-10 17:38:16 +00:00
{
2024-07-11 14:29:44 +00:00
accum_dt_ms(app.delta_time_accum, delta_time_ms);
2024-07-11 12:00:43 +00:00
2024-07-10 17:38:16 +00:00
ImGui::SetNextWindowPos({ 0, 0 });
ImGui::SetNextWindowSize({ (f32)app.win_data.width, (f32)app.win_data.height });
2024-07-10 18:11:42 +00:00
Temp scratch = scratch_begin(&arena, 1);
defer { scratch_end(scratch); };
2024-07-10 18:11:42 +00:00
2024-07-11 12:27:19 +00:00
const auto main_win_flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDecoration;
if (ImGui::Begin("main", nullptr, main_win_flags)) {
2024-07-11 12:59:59 +00:00
2024-07-12 09:58:55 +00:00
String8 ntpl_desc = rntuple_description(scratch.arena, app.rndata);
2024-07-18 13:32:32 +00:00
ImGui::Text("RNTuple '%s' (%s) from file \"%s\"", app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c());
2024-07-11 12:59:59 +00:00
2024-07-11 13:27:38 +00:00
// Draw stats
2024-07-11 12:59:59 +00:00
{
ImGui::SameLine();
f32 avg_dt = calc_avg_dt_ms(app.delta_time_accum);
2024-07-12 09:58:55 +00:00
String8 mem_used = to_pretty_size(scratch.arena, arena->mem_used);
String8 mem_peak = to_pretty_size(scratch.arena, arena->mem_peak_used);
String8 stat_txt = push_str8f(scratch.arena, "mem used: %s (peak: %s) | avg dt: %.1f",
mem_used.c(), mem_peak.c(), avg_dt);
2024-07-11 13:27:38 +00:00
f32 pos_x = (ImGui::GetCursorPosX() + ImGui::GetColumnWidth() - ImGui::CalcTextSize(stat_txt.c()).x
2024-07-11 12:59:59 +00:00
- ImGui::GetScrollX() - 2 * ImGui::GetStyle().ItemSpacing.x);
if (pos_x > ImGui::GetCursorPosX())
ImGui::SetCursorPosX(pos_x);
2024-07-11 13:27:38 +00:00
ImGui::Text("%s", stat_txt.c());
2024-07-11 12:59:59 +00:00
}
2024-07-11 12:27:19 +00:00
ImGui::Separator();
2024-07-11 12:59:59 +00:00
// Draw main content
2024-07-11 12:27:19 +00:00
{
2024-07-19 14:31:48 +00:00
const i64 step_i64 = 1;
2024-07-18 15:43:44 +00:00
ImGui::BeginTable("Hex View", 2, ImGuiTableFlags_Resizable);
2024-07-11 12:27:19 +00:00
ImGui::TableNextColumn();
2024-07-12 08:07:27 +00:00
2024-07-26 14:05:04 +00:00
assert(app.base_display_addr < app.inspected_file.size);
void *content = app.inspected_file.mem + app.base_display_addr;
u64 content_size = app.inspected_file.size - app.base_display_addr;
2024-07-15 09:54:45 +00:00
app.last_pinfo = &invalid_pinfo;
2024-07-26 14:05:04 +00:00
app.viewer.mem_edit.DrawContents(content, content_size, app.base_display_addr);
2024-07-11 12:27:19 +00:00
ImGui::TableNextColumn();
2024-07-12 08:07:27 +00:00
ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoLabel;
2024-07-25 10:25:05 +00:00
// Unique sections: just display a button that jumps to the start of it and show their size
for (u32 i = 0; i < Sec_COUNT; ++i) {
2024-07-25 15:01:08 +00:00
Byte_Range range = get_section_range(app, static_cast<Section_Id>(i));
if (!range.len) continue;
2024-07-25 10:25:05 +00:00
String8 sec_name = section_names[i];
String8 col_label = push_str8f(scratch.arena, "_%s", sec_name.c());
ImGui::ColorEdit3(col_label.c(), app.viewer.col_section[i], flags);
ImGui::SameLine();
if (ImGui::Button(sec_name.c()))
2024-07-25 16:23:58 +00:00
viewer_jump_to(app, range.start);
2024-07-25 10:25:05 +00:00
ImGui::SameLine();
2024-07-25 15:01:08 +00:00
ImGui::Text("%s", to_pretty_size(scratch.arena, range.len).c());
2024-07-25 10:25:05 +00:00
}
2024-07-15 13:54:22 +00:00
2024-07-25 10:25:05 +00:00
// Repeated sections: allow jumping to the N-th
2024-07-29 09:59:56 +00:00
ImGui::ColorEdit3("_TKey Header", app.viewer.col_key, flags);
ImGui::SameLine();
if (ImGui::Button("TKey Header")) {} // TODO: jump to next key
2024-07-16 12:34:51 +00:00
ImGui::ColorEdit3("_Page Start", app.viewer.col_page_start, flags);
2024-07-15 13:54:22 +00:00
ImGui::SameLine();
2024-07-25 10:25:05 +00:00
if (ImGui::Button("Page Start"))
viewer_jump_to_page(app, 0);
2024-07-15 13:54:22 +00:00
2024-07-25 10:25:05 +00:00
ImGui::ColorEdit3("_Page", app.viewer.col_section[Sec_Page], flags);
2024-07-16 12:34:51 +00:00
ImGui::SameLine();
2024-07-25 10:25:05 +00:00
if (ImGui::Button("Page"))
viewer_jump_to_page(app, app.viewer.latest_page_gone_to);
2024-07-15 16:02:55 +00:00
ImGui::SameLine();
{
2024-07-19 14:31:48 +00:00
const i64 step_fast_i64 = app.rndata.n_pages / 100;
i64 page_to_go_to = app.viewer.latest_page_gone_to;
2024-07-15 16:02:55 +00:00
ImGui::PushItemWidth(100.f);
2024-07-19 14:31:48 +00:00
if (ImGui::InputScalar("##page_viewed", ImGuiDataType_S64, &page_to_go_to, &step_i64, &step_fast_i64, "%u"))
2024-07-16 12:34:51 +00:00
viewer_jump_to_page(app, page_to_go_to);
2024-07-15 16:02:55 +00:00
ImGui::PopItemWidth();
}
2024-07-16 12:34:51 +00:00
ImGui::SameLine();
ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.tot_page_size).c());
2024-07-11 12:27:19 +00:00
2024-07-16 12:34:51 +00:00
ImGui::ColorEdit3("_Checksum", app.viewer.col_checksum, flags);
2024-07-16 10:04:38 +00:00
ImGui::SameLine();
if (ImGui::Button("Checksum")) {} // TODO jump to next checksum
2024-07-25 10:25:05 +00:00
ImGui::ColorEdit3("_Page List", app.viewer.col_section[Sec_Page_List], flags);
2024-07-16 12:34:51 +00:00
ImGui::SameLine();
2024-07-30 08:47:13 +00:00
if (ImGui::Button("Page List"))
viewer_jump_to_page_list(app, app.viewer.latest_page_list_gone_to);
2024-07-18 15:43:44 +00:00
ImGui::SameLine();
{
2024-07-19 14:31:48 +00:00
i64 page_list_to_go_to = app.viewer.latest_page_list_gone_to;
2024-07-18 15:43:44 +00:00
ImGui::PushItemWidth(80.f);
2024-07-19 14:31:48 +00:00
if (ImGui::InputScalar("##page_list_viewed", ImGuiDataType_S64, &page_list_to_go_to, &step_i64, nullptr, "%u"))
2024-07-18 15:43:44 +00:00
viewer_jump_to_page_list(app, page_list_to_go_to);
ImGui::PopItemWidth();
}
2024-07-16 12:34:51 +00:00
ImGui::SameLine();
ImGui::Text("%s", to_pretty_size(scratch.arena, app.rndata.tot_page_list_size).c());
2024-07-23 08:41:55 +00:00
// -------------------------------
2024-07-12 16:29:35 +00:00
ImGui::Separator();
2024-07-18 13:32:32 +00:00
String8 root_version_str = push_str8f(scratch.arena, "%u.%u.%u",
app.tfile_data.root_version_major,
app.tfile_data.root_version_minor,
app.tfile_data.root_version_patch);
ImGui::Text("ROOT version: %s", root_version_str.c());
2024-07-18 13:56:55 +00:00
ImGui::Text("TFile compression: %u", app.tfile_data.compression);
2024-07-12 16:29:35 +00:00
ImGui::Text("Num pages: %lu", app.rndata.n_pages);
ImGui::Text("Num elements: %lu", app.rndata.n_elems);
2024-07-19 14:31:48 +00:00
{
const i64 step_fast_i64 = app.rndata.n_clusters / 100;
i64 cluster_to_highlight = app.viewer.highlighted_cluster;
ImGui::PushItemWidth(100.f);
if (ImGui::InputScalar("##highlighted_cluster", ImGuiDataType_S64, &cluster_to_highlight, &step_i64, &step_fast_i64, "%u")) {
2024-07-25 10:25:05 +00:00
app.viewer.highlight_cluster = true;
2024-07-19 14:31:48 +00:00
viewer_jump_to_cluster(app, cluster_to_highlight);
}
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::Checkbox("Highlight cluster", &app.viewer.highlight_cluster);
}
2024-07-29 09:59:56 +00:00
// ---------------------------------
ImGui::Separator();
{
Section hovered_section = find_section(app, app.viewer.hovered_off);
Sec_Hover_Info hover_info = get_section_hover_info(scratch.arena, hovered_section, app.viewer.hovered_off, app.inspected_file.mem);
u8 indent = 0;
for (String8_Node *node = hover_info.desc; node; node = node->next) {
String8 fmt = str8("%s");
if (indent) {
// create indent
fmt = push_str8f(scratch.arena, "%%%ds", indent + node->str.size);
}
ImGui::Text(fmt.c(), node->str.c());
indent += 2;
}
app.viewer.hovered_range = hover_info.rng;
}
2024-07-11 12:27:19 +00:00
ImGui::EndTable();
}
2024-07-11 12:00:43 +00:00
2024-07-10 17:38:16 +00:00
ImGui::End();
}
}