Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
graph_panel.cpp
Go to the documentation of this file.
1// graph_panel.cpp — 「Work Graphs と性能」のパネル(T-0143)。考え方は graph_panel.h。
3
4#include <algorithm>
5#include <format>
6#include <string_view>
7
8#include <imgui.h>
9
10#include "core/aliases.h"
11
12namespace bicameral::editor {
13 namespace {
14
15 constexpr float PANEL_X_PIXELS = 430.0f; // 「時間」「状態」の右
16 constexpr float PANEL_Y_PIXELS = 12.0f;
17 constexpr ImVec4 WARNING_COLOR = {1.0f, 0.55f, 0.35f, 1.0f};
18
19 void Cell(std::string_view text) {
20 ImGui::TableNextColumn();
21 ImGui::TextUnformatted(text.data(), text.data() + text.size());
22 }
23
24 double PerTick(uint64_t count, uint64_t ticks) {
25 return ticks > 0 ? static_cast<double>(count) / static_cast<double>(ticks) : 0.0;
26 }
27
28 // --- 単位ごとの GPU 時間 ---
29 void BuildUnitTable(const GraphPanelStatus& status) {
30 double total = 0.0;
31 for (const UnitTime& unit : status.units)
32 total += unit.millisecondsPerTick;
33
34 if (!ImGui::BeginTable("units", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit))
35 return;
36
37 ImGui::TableSetupColumn("単位");
38 ImGui::TableSetupColumn("ms/刻み");
39 ImGui::TableSetupColumn("割合");
40 ImGui::TableHeadersRow();
41 for (const UnitTime& unit : status.units) {
42 Cell(unit.workGraph ? std::format("{}(Work Graph)", unit.name) : unit.name);
43 Cell(std::format("{:.3f}", unit.millisecondsPerTick));
44 ImGui::TableNextColumn();
45 const float fraction = total > 0.0 ? static_cast<float>(unit.millisecondsPerTick / total) : 0.0f;
46 ImGui::ProgressBar(fraction, {120.0f, 0.0f});
47 }
48
49 ImGui::EndTable();
50 ImGui::TextUnformatted(std::format("合計 {:.3f} ms/刻み", total).c_str());
51 }
52
53 // --- ノードごとの数と上限の余裕(T-0008 のカウンタ)---
54 void BuildNodeTable(const gpu::GraphStatsLayout& layout, const GraphPanelStatus& status) {
55 const ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit;
56 if (!ImGui::BeginTable("nodes", 6, flags))
57 return;
58
59 ImGui::TableSetupColumn("ノード");
60 ImGui::TableSetupColumn("起動/刻み");
61 ImGui::TableSetupColumn("入力/刻み");
62 ImGui::TableSetupColumn("出力/刻み");
63 ImGui::TableSetupColumn("1 回の出力 最大/上限");
64 ImGui::TableSetupColumn("再帰 最大/上限");
65 ImGui::TableHeadersRow();
66 const size_t count = std::min(layout.nodes.size(), status.stats.nodes.size());
67 for (size_t index = 0; index < count; ++index) {
68 const gpu::GraphNodeLimits& limits = layout.nodes[index];
69 const gpu::GraphNodeCounters& counters = status.stats.nodes[index];
70 Cell(limits.name);
71 Cell(std::format("{:.1f}", PerTick(counters.launches, status.ticks)));
72 Cell(std::format("{:.1f}", PerTick(counters.inputRecords, status.ticks)));
73 Cell(std::format("{:.1f}", PerTick(counters.outputRecords, status.ticks)));
74 Cell(limits.maxOutputRecords > 0
75 ? std::format("{} / {}", counters.peakRequestedOutputs, limits.maxOutputRecords)
76 : std::string("-"));
77 Cell(limits.maxRecursionDepth > 0
78 ? std::format("{} / {}", counters.deepestRecursion, limits.maxRecursionDepth)
79 : std::string("-"));
80 }
81
82 ImGui::EndTable();
83 }
84
85 // --- 計器(一覧・キューの使った量の最大と容量)---
86 void BuildGauges(const gpu::GraphStatsLayout& layout, const GraphPanelStatus& status) {
87 const size_t count = std::min(layout.gauges.size(), status.stats.gaugePeaks.size());
88 for (size_t index = 0; index < count; ++index) {
89 const gpu::GraphGaugeLimits& gauge = layout.gauges[index];
90 const uint32_t peak = status.stats.gaugePeaks[index];
91 const float fraction = gauge.capacity > 0
92 ? static_cast<float>(peak) / static_cast<float>(gauge.capacity)
93 : 0.0f;
94 ImGui::ProgressBar(std::min(fraction, 1.0f), {160.0f, 0.0f},
95 std::format("{} / {}", peak, gauge.capacity).c_str());
96 ImGui::SameLine();
97 ImGui::TextUnformatted(gauge.name.c_str());
98 }
99 }
100
101 // --- 上限に当たった・近づいたもの(ログの Warning と同じ判定)---
102 void BuildFindings(const gpu::GraphStatsLayout& layout, const GraphPanelStatus& status) {
103 const std::vector<gpu::GraphFinding> findings = gpu::EvaluateGraphStats(layout, status.stats);
104 if (findings.empty()) {
105 ImGui::TextUnformatted("上限: 余裕あり");
106 return;
107 }
108
109 for (const gpu::GraphFinding& finding : findings)
110 ImGui::TextColored(WARNING_COLOR, "%s", finding.text.c_str());
111 }
112
113 } // namespace
114
115 void BuildGraphPanel(const GraphPanelStatus& status) {
116 ImGui::SetNextWindowPos({PANEL_X_PIXELS, PANEL_Y_PIXELS}, ImGuiCond_FirstUseEver);
117 ImGui::Begin("Work Graphs と性能", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
118 ImGui::TextUnformatted(std::format("直近 1 秒: {} 刻み", status.ticks).c_str());
119
120 ImGui::SeparatorText("刻みの単位の GPU 時間");
121 BuildUnitTable(status);
122
123 if (status.layout != nullptr && !status.stats.nodes.empty()) {
124 ImGui::SeparatorText(std::format("{} のノード", status.layout->name).c_str());
125 BuildNodeTable(*status.layout, status);
126 BuildGauges(*status.layout, status);
127 BuildFindings(*status.layout, status);
128 }
129
130 ImGui::TextDisabled("ノードごとの時間は測れない(タイムスタンプは DispatchGraph の前後だけ)");
131 ImGui::End();
132 }
133
134} // namespace bicameral::editor
void BuildGauges(const gpu::GraphStatsLayout &layout, const GraphPanelStatus &status)
void BuildUnitTable(const GraphPanelStatus &status)
void BuildNodeTable(const gpu::GraphStatsLayout &layout, const GraphPanelStatus &status)
double PerTick(uint64_t count, uint64_t ticks)
void BuildFindings(const gpu::GraphStatsLayout &layout, const GraphPanelStatus &status)
void BuildGraphPanel(const GraphPanelStatus &status)
std::vector< GraphFinding > EvaluateGraphStats(const GraphStatsLayout &layout, const GraphStatsSnapshot &snapshot)
const gpu::GraphStatsLayout * layout
Definition graph_panel.h:25
std::vector< UnitTime > units
Definition graph_panel.h:28
gpu::GraphStatsSnapshot stats
Definition graph_panel.h:26
std::vector< GraphNodeLimits > nodes
std::vector< GraphGaugeLimits > gauges
std::vector< uint32_t > gaugePeaks
std::vector< GraphNodeCounters > nodes