17 const std::span<const uint32_t> entry = words.subspan(
size_t{node} * WG_NODE_WORDS, WG_NODE_WORDS);
18 return {.launches = entry[WG_NODE_LAUNCHES],
19 .inputRecords = entry[WG_NODE_INPUT_RECORDS],
20 .outputRecords = entry[WG_NODE_OUTPUT_RECORDS],
21 .refusedOutputs = entry[WG_NODE_REFUSED_OUTPUTS],
22 .peakRequestedOutputs = entry[WG_NODE_PEAK_REQUESTED],
23 .deepestRecursion = entry[WG_NODE_DEEPEST_RECURSION],
24 .refusedRecursions = entry[WG_NODE_REFUSED_RECURSIONS]};
30 std::vector<GraphFinding>& findings) {
34 .text = std::format(
"ノード {}: 出力の上限 {} を超える要求を {} 件止めた"
42 .text = std::format(
"ノード {}: 1 回の起動の出力の要求 {} が上限 {} に近い", limits.
name,
49 .text = std::format(
"ノード {}: 再帰の深さの上限 {} で {} 回止めた", limits.
name,
52 uint64_t{counters.deepestRecursion} * 4 >= uint64_t{limits.maxRecursionDepth} * 3) {
55 .text = std::format(
"ノード {}: 再帰の深さ {} が上限 {} に近い", limits.
name,
61 std::vector<GraphFinding>& findings) {
68 .text = std::format(
"{}: 容量 {} を超えて {} まで使おうとした(溢れた分は落ちた)",
73 .text = std::format(
"{}: 使った量の最大 {} が容量 {} に近い", limits.
name, peak,
84 if (
frame.nodes.empty() &&
frame.gaugePeaks.empty())
92 for (
size_t index = 0; index <
frame.nodes.size(); ++index) {
104 for (
size_t index = 0; index <
frame.gaugePeaks.size(); ++index)
109 std::string text = layout.
name +
":";
110 const char* separator =
" ";
111 for (
size_t index = 0; index < layout.
nodes.size() && index < snapshot.
nodes.size(); ++index) {
113 text += std::format(
"{}{} 起動 {}・入力 {}・出力 {}", separator, layout.
nodes[index].name, node.
launches,
118 if (layout.
nodes[index].maxRecursionDepth > 0)
127 for (
size_t index = 0; index < layout.
gauges.size() && index < snapshot.
gaugePeaks.size(); ++index) {
128 text += std::format(
"{}{} 最大 {}/{}", separator, layout.
gauges[index].name, snapshot.
gaugePeaks[index],
129 layout.
gauges[index].capacity);
137 std::vector<GraphFinding> findings;
138 for (
size_t index = 0; index < layout.
nodes.size() && index < snapshot.
nodes.size(); ++index)
139 EvaluateNode(layout.
nodes[index], snapshot.
nodes[index],
static_cast<uint32_t
>(index), findings);
141 for (
size_t index = 0; index < layout.
gauges.size() && index < snapshot.
gaugePeaks.size(); ++index)
142 EvaluateGauge(layout.
gauges[index], snapshot.
gaugePeaks[index],
static_cast<uint32_t
>(index), findings);
150 :
m_ring(std::move(ring)),
155 uint32_t slotCount) {
156 if (layout.
nodes.size() > WG_STATS_MAX_NODES || layout.
gauges.size() > WG_STATS_MAX_GAUGES) {
157 return std::unexpected(std::format(
"{}: ノード {} 個・計器 {} 個は多すぎる(最大 {}・{})", layout.
name,
158 layout.
nodes.size(), layout.
gauges.size(), WG_STATS_MAX_NODES,
159 WG_STATS_MAX_GAUGES));
163 auto ring =
ReadbackRing::Create(device, WG_STATS_BYTES, WG_STATS_BYTES, slotCount, L
"WorkGraphStats");
165 return std::unexpected(
"Work Graphs のカウンタを作れない: " + ring.error());
175 m_ring.RecordReadbackAndReset(list, slot);
179 std::vector<uint32_t> words(WG_STATS_WORDS);
180 if (!
m_ring.Read(slot, std::as_writable_bytes(std::span(words))))
181 return std::unexpected(std::format(
"{}: カウンタを読み戻せない(slot {})",
m_layout.name, slot));
185 for (uint32_t node = 0; node <
m_layout.nodes.size(); ++node)
186 snapshot.
nodes.push_back(ReadNode(words, node));
188 const std::span<const uint32_t> gauges = std::span(words).subspan(
size_t{WG_STATS_MAX_NODES} * WG_NODE_WORDS,
189 WG_STATS_MAX_GAUGES);
190 snapshot.
gaugePeaks.assign(gauges.begin(), gauges.begin() +
static_cast<ptrdiff_t
>(
m_layout.gauges.size()));
202 uint64_t& last =
m_lastWarnedReport[
size_t{
static_cast<uint32_t
>(finding.kind)} * FINDING_SLOTS_PER_KIND +
static std::expected< ReadbackRing, std::string > Create(ID3D12Device *device, uint64_t bytes, uint64_t headerBytes, uint32_t slotCount, std::wstring_view name)
void RecordReadbackAndReset(ID3D12GraphicsCommandList *list, uint32_t slot=0) const
std::expected< GraphStatsSnapshot, std::string > Read(uint32_t slot=0) const
GraphStatsLayout m_layout
WorkGraphStats(ReadbackRing &&ring, GraphStatsLayout &&layout)
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
constexpr uint32_t FINDING_SLOTS_PER_KIND
constexpr uint32_t KIND_COUNT
void EvaluateGauge(const GraphGaugeLimits &limits, uint32_t peak, uint32_t index, std::vector< GraphFinding > &findings)
GraphNodeCounters ReadNode(std::span< const uint32_t > words, uint32_t node)
void EvaluateNode(const GraphNodeLimits &limits, const GraphNodeCounters &counters, uint32_t index, std::vector< GraphFinding > &findings)
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)
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
uint32_t refusedRecursions
uint32_t deepestRecursion
uint32_t peakRequestedOutputs
uint32_t maxRecursionDepth
uint32_t maxOutputRecords
uint32_t warnOutputRecords
std::vector< GraphNodeLimits > nodes
std::vector< GraphGaugeLimits > gauges
std::vector< uint32_t > gaugePeaks
std::vector< GraphNodeCounters > nodes