Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
work_graph_stats.h
Go to the documentation of this file.
1// work_graph_stats.h — Work Graphs のノードごとのカウンタ(shaders/common/work_graph_stats.hlsli)を CPU 側で持つ(T-0008、16 §1)。
2//
3// データの流れ: ノードが WgCountLaunch などで u1 space1 のバッファに数える → RecordReadbackAndReset() が slot の読み戻しのバッファへ写して
4// 0 に戻す → その slot のリストが終わった後に Read() → Report() がログ(ADR-0006)へ 1 行の要約(Trace)と、上限の Warning を出す。
5// どのノードが何番か・宣言した上限はいくつかは、グラフを作る側が GraphStatsLayout に書く(HLSL の番号と同じ順)。
6//
7// 1 本のコマンドリストの中での使い方:
8// stats.RecordBegin(list); // COMMON → UAV
9// list->SetComputeRootUnorderedAccessView(layout.GraphStatsIndex(), stats.GpuAddress());
10// ... DispatchGraph / Dispatch ...
11// stats.RecordReadbackAndReset(list, slot);
12// (slot のリストが終わったら)auto snapshot = stats.Read(slot); stats.Report(*snapshot);
13#pragma once
14
15#include <cstdint>
16#include <expected>
17#include <string>
18#include <vector>
19
20#include "common/work_graph_stats.hlsli"
21#include "gpu/readback_ring.h"
22
23namespace bicameral::gpu {
24
25 // --- グラフを作る側が書く上限(HLSL の宣言と同じ値)---
26
28 std::string name;
29 uint32_t maxOutputRecords = 0; // 1 回の起動で出せる数(宣言した MaxRecords の合計。0 = 出さない)
30 // 1 回の起動の要求がこれ以上なら「上限に近い」(0 = 見ない。出す数が構造で決まるノード)
31 uint32_t warnOutputRecords = 0;
32 uint32_t maxRecursionDepth = 0; // NodeMaxRecursionDepth(0 = 再帰しない)
33 };
34
36 std::string name;
37 uint32_t capacity = 0;
38 uint32_t warnPercent = 75; // 使った量の最大が容量のこの割合以上なら「近い」(100 なら満杯のときだけ)
39 };
40
42 std::string name; // ログに出すグラフの名前
43 std::vector<GraphNodeLimits> nodes; // WG_STATS_MAX_NODES 個まで。番号 = 並び順
44 std::vector<GraphGaugeLimits> gauges; // WG_STATS_MAX_GAUGES 個まで
45 };
46
47 // --- 読み戻したもの ---
48
50 // --- 数(足す)---
51 uint32_t launches = 0; // 起動
52 uint32_t inputRecords = 0; // 入力のレコード
53 uint32_t outputRecords = 0; // 出力のレコード
54
55 // --- 出力の上限 ---
56 uint32_t refusedOutputs = 0; // 上限を超えるので止めた出力
57 uint32_t peakRequestedOutputs = 0; // 1 回の起動で求めた出力の最大
58
59 // --- 再帰の上限 ---
60 uint32_t deepestRecursion = 0; // 再帰の深さの最大
61 uint32_t refusedRecursions = 0; // 上限の深さで止めた再帰
62
63 friend bool operator==(const GraphNodeCounters&, const GraphNodeCounters&) = default;
64 };
65
67 std::vector<GraphNodeCounters> nodes; // layout.nodes と同じ数・順
68 std::vector<uint32_t> gaugePeaks; // layout.gauges と同じ数・順
69
70 friend bool operator==(const GraphStatsSnapshot&, const GraphStatsSnapshot&) = default;
71 };
72
73 // 何フレームかの分をまとめる(数は足し、最大は最大)。total が空なら frame の形にする。frame が空(読めなかった)なら何もしない
75
76 // 1 行の要約(「グラフ: ノード 起動 n・入力 n・出力 n | … | 計器 最大 n/容量」)
77 [[nodiscard]] std::string FormatGraphStats(const GraphStatsLayout& layout, const GraphStatsSnapshot& snapshot);
78
79 // --- 上限の検出 ---
80
81 enum class GraphFindingKind : uint8_t {
82 OutputsRefused, // 出力の上限を超える要求を止めた(結果が変わっている)
83 OutputsNearLimit, // 1 回の起動の出力の要求が warnOutputRecords 以上
84 RecursionRefused, // 再帰の深さの上限で止めた(結果が変わっている)
85 RecursionNearLimit, // 再帰の深さが宣言の 3/4 以上
86 GaugeOverCapacity, // 容量を超えて使おうとした(溢れた分は落ちている)
87 GaugeNearCapacity, // 容量の warnPercent 以上
89 };
90
91 struct GraphFinding {
93 uint32_t index = 0; // ノードか計器の番号
94 std::string text; // ログに出す 1 行
95 };
96
97 // 上限に当たった・近づいたものを並べる(ノードの順 → 計器の順)。GPU なしで確かめられる
98 [[nodiscard]] std::vector<GraphFinding> EvaluateGraphStats(const GraphStatsLayout& layout,
99 const GraphStatsSnapshot& snapshot);
100
102 public:
103 // 同じ Warning(種類 × 番号)をもう一度ログに出すのは、この回数の Report() の後(毎フレームの Warning でログを埋めない)
104 static constexpr uint64_t WARNING_REPEAT_REPORTS = 600;
105
106 [[nodiscard]] static std::expected<WorkGraphStats, std::string> Create(ID3D12Device* device,
107 GraphStatsLayout layout,
108 uint32_t slotCount = 1);
109
110 // ルートの UAV(u1 space1)に渡すアドレス
111 [[nodiscard]] D3D12_GPU_VIRTUAL_ADDRESS GpuAddress() const { return m_ring.GpuAddress(); }
112 [[nodiscard]] const GraphStatsLayout& Layout() const { return m_layout; }
113
114 // シェーダーが数える前に: COMMON → UNORDERED_ACCESS
115 void RecordBegin(ID3D12GraphicsCommandList* list) const;
116
117 // 数えた後に: slot の読み戻しのバッファへ写し、全部を 0 に戻す。最後は COMMON に戻す
118 void RecordReadbackAndReset(ID3D12GraphicsCommandList* list, uint32_t slot = 0) const;
119
120 // slot のリストを GPU が終えた後に呼ぶ
121 [[nodiscard]] std::expected<GraphStatsSnapshot, std::string> Read(uint32_t slot = 0) const;
122
123 // 要約を Trace で、見つかった上限を Warning でログへ(同じものは WARNING_REPEAT_REPORTS 回に 1 回)。見つかったものを全部返す
124 std::vector<GraphFinding> Report(const GraphStatsSnapshot& snapshot);
125
126 private:
128
131 uint64_t m_reportCount = 0;
132 // (種類 × 番号)ごとに、最後にログに出した Report の番号 + 1(0 = まだ)
133 std::vector<uint64_t> m_lastWarnedReport;
134 };
135
136} // namespace bicameral::gpu
void RecordReadbackAndReset(ID3D12GraphicsCommandList *list, uint32_t slot=0) const
std::expected< GraphStatsSnapshot, std::string > Read(uint32_t slot=0) const
const GraphStatsLayout & Layout() const
WorkGraphStats(ReadbackRing &&ring, GraphStatsLayout &&layout)
D3D12_GPU_VIRTUAL_ADDRESS GpuAddress() const
std::vector< GraphFinding > Report(const GraphStatsSnapshot &snapshot)
static std::expected< WorkGraphStats, std::string > Create(ID3D12Device *device, GraphStatsLayout layout, uint32_t slotCount=1)
static constexpr uint64_t WARNING_REPEAT_REPORTS
std::vector< uint64_t > m_lastWarnedReport
void RecordBegin(ID3D12GraphicsCommandList *list) const
std::string FormatGraphStats(const GraphStatsLayout &layout, const GraphStatsSnapshot &snapshot)
std::vector< GraphFinding > EvaluateGraphStats(const GraphStatsLayout &layout, const GraphStatsSnapshot &snapshot)
void AccumulateGraphStats(GraphStatsSnapshot &total, const GraphStatsSnapshot &frame)
friend bool operator==(const GraphNodeCounters &, const GraphNodeCounters &)=default
std::vector< GraphNodeLimits > nodes
std::vector< GraphGaugeLimits > gauges
std::vector< uint32_t > gaugePeaks
friend bool operator==(const GraphStatsSnapshot &, const GraphStatsSnapshot &)=default
std::vector< GraphNodeCounters > nodes