Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
graph_trace.cpp
Go to the documentation of this file.
1// graph_trace.cpp — 連鎖のトレースのバッファ・範囲の書き込み・読み戻し(T-0087・T-0088、16 §1.3)。使い方は graph_trace.h。
2#include "gpu/graph_trace.h"
3
4#include <algorithm>
5#include <cstring>
6#include <format>
7
8#include "core/aliases.h"
9#include "gpu/resources.h"
10
11namespace bicameral::gpu {
12 namespace {
13
14 // 見出しの範囲の部分(語 [4, 16))の中身
15 std::array<uint32_t, GT_FILTER_BYTES / 4> FilterWords(const GraphTraceFilter& filter) {
16 std::array<uint32_t, GT_FILTER_BYTES / 4> words{};
17 const auto at = [&words](uint32_t word) -> uint32_t& {
18 return words[word - GT_FILTER_OFFSET / 4];
19 };
20
21 at(GT_WORD_ENABLED) = filter.enabled ? 1 : 0;
22 at(GT_WORD_CAPACITY) = filter.enabled ? filter.capacity : 0;
23 at(GT_WORD_TICK_BEGIN) = static_cast<uint32_t>(filter.tickBegin);
24 at(GT_WORD_TICK_BEGIN + 1) = static_cast<uint32_t>(filter.tickBegin >> 32);
25 at(GT_WORD_TICK_END) = static_cast<uint32_t>(filter.tickEnd);
26 at(GT_WORD_TICK_END + 1) = static_cast<uint32_t>(filter.tickEnd >> 32);
27 for (uint32_t axis = 0; axis < 3; ++axis) {
28 at(GT_WORD_BOX_MIN + axis) = filter.boxMin[axis];
29 at(GT_WORD_BOX_MAX + axis) = filter.boxMax[axis];
30 }
31
32 return words;
33 }
34
35 GraphTraceRecord DecodeRecord(std::span<const uint32_t, 4> words) {
36 return {.tick = uint64_t{words[0]} | (uint64_t{words[1]} << 32),
37 .kind = words[2] >> GT_SUBJECT_BITS,
38 .subject = words[2] & GT_SUBJECT_MASK,
39 .object = words[3]};
40 }
41
42 } // namespace
43
44 void SortGraphTrace(std::vector<GraphTraceRecord>& records) {
45 rng::sort(records);
46 }
47
48 std::expected<GraphTrace, std::string> GraphTrace::Create(ID3D12Device* device, const GraphTraceFilter& filter,
49 uint32_t slotCount, uint32_t capacity) {
50 capacity = std::max(capacity, filter.enabled ? filter.capacity : 0u);
51 const uint64_t bytes = GT_HEADER_BYTES + uint64_t{capacity} * GT_RECORD_BYTES;
52
53 // 毎フレーム 0 に戻すのは数(先頭の GT_RESET_BYTES)だけ。範囲は残す
54 auto ring = ReadbackRing::Create(device, bytes, GT_RESET_BYTES, slotCount, L"GraphTrace");
55 if (!ring)
56 return std::unexpected("連鎖のトレースを作れない: " + ring.error());
57
58 // --- 範囲の写し元(slot ごと。RecordBegin が範囲の変わったフレームに書いて GPU のバッファへ写す)---
59 ComPtr<ID3D12Resource> upload = CreateBuffer(device, uint64_t{GT_FILTER_BYTES} * slotCount, BufferKind::Upload);
60 if (!upload)
61 return std::unexpected("連鎖のトレースの範囲のバッファを作れない");
62
63 void* mapped = nullptr;
64 const D3D12_RANGE noRead{};
65 if (FAILED(upload->Map(0, &noRead, &mapped)))
66 return std::unexpected("連鎖のトレースの範囲のバッファを Map できない");
67
68 upload->SetName(L"GraphTrace.filter");
69
70 GraphTrace trace(std::move(*ring), std::move(upload), static_cast<std::byte*>(mapped), capacity, slotCount);
71 trace.SetFilter(filter);
72
73 return trace;
74 }
75
77 m_filter = filter;
78 m_filter.capacity = filter.enabled ? std::min(filter.capacity, m_capacity) : 0;
79 m_filterDirty = true;
80 }
81
82 void GraphTrace::RecordBegin(ID3D12GraphicsCommandList* list, uint32_t slot) {
83 if (m_filterDirty) {
84 // slot の写し元は、前にその slot で記録したリストが終わっているので書いてよい(ReadbackRing の slot と同じ約束)
85 const auto words = FilterWords(m_filter);
86 std::memcpy(m_filterMapped + size_t{slot} * GT_FILTER_BYTES, words.data(), sizeof(words));
87
88 // バッファは COMMON(作った時・前のリストの終わり)。範囲を写して COMMON に戻す
89 // (ReadbackRing::RecordBegin が COMMON → UAV で始めるので)
90 ID3D12Resource* buffer = m_ring.Buffer();
91 const D3D12_RESOURCE_BARRIER toCopyDest = Transition(buffer, D3D12_RESOURCE_STATE_COMMON,
92 D3D12_RESOURCE_STATE_COPY_DEST);
93 list->ResourceBarrier(1, &toCopyDest);
94 list->CopyBufferRegion(buffer, GT_FILTER_OFFSET, m_filterUpload.Get(), uint64_t{slot} * GT_FILTER_BYTES,
95 GT_FILTER_BYTES);
96
97 const D3D12_RESOURCE_BARRIER toCommon = Transition(buffer, D3D12_RESOURCE_STATE_COPY_DEST,
98 D3D12_RESOURCE_STATE_COMMON);
99 list->ResourceBarrier(1, &toCommon);
100 m_filterDirty = false;
101 }
102
103 m_slotFilters[slot] = m_filter;
104 m_ring.RecordBegin(list);
105 }
106
107 void GraphTrace::RecordReadbackAndReset(ID3D12GraphicsCommandList* list, uint32_t slot) const {
108 // 範囲が無効のリストは何も書かないので、見出しだけを読み戻す(容量ぶんを毎フレーム写さない)
109 const uint32_t capacity = m_slotFilters[slot].capacity;
110 m_ring.RecordReadbackAndReset(list, slot, GT_HEADER_BYTES + uint64_t{capacity} * GT_RECORD_BYTES);
111 }
112
113 std::expected<GraphTraceFrame, std::string> GraphTrace::Read(uint32_t slot) const {
114 // --- 見出し(書こうとした数)→ 書けた分の記録 ---
115 std::array<uint32_t, GT_HEADER_BYTES / 4> header{};
116 if (!m_ring.Read(slot, std::as_writable_bytes(std::span(header))))
117 return std::unexpected(std::format("連鎖のトレースを読み戻せない(slot {})", slot));
118
119 const uint32_t requested = header[GT_WORD_REQUESTED];
120 const uint32_t written = std::min(requested, m_slotFilters[slot].capacity);
121 GraphTraceFrame frame{.droppedCount = requested - written};
122 if (written == 0)
123 return frame;
124
125 std::vector<uint32_t> words((GT_HEADER_BYTES + size_t{written} * GT_RECORD_BYTES) / 4);
126 if (!m_ring.Read(slot, std::as_writable_bytes(std::span(words))))
127 return std::unexpected(std::format("連鎖のトレースの記録を読み戻せない(slot {})", slot));
128
129 frame.records.reserve(written);
130 for (uint32_t index = 0; index < written; ++index) {
131 const size_t first = (GT_HEADER_BYTES + size_t{index} * GT_RECORD_BYTES) / 4;
132 frame.records.push_back(DecodeRecord(std::span(words).subspan(first).first<4>()));
133 }
134
135 return frame;
136 }
137
138} // namespace bicameral::gpu
GraphTraceFilter m_filter
void RecordReadbackAndReset(ID3D12GraphicsCommandList *list, uint32_t slot=0) const
void RecordBegin(ID3D12GraphicsCommandList *list, uint32_t slot=0)
void SetFilter(const GraphTraceFilter &filter)
static std::expected< GraphTrace, std::string > Create(ID3D12Device *device, const GraphTraceFilter &filter, uint32_t slotCount=1, uint32_t capacity=0)
GraphTrace(ReadbackRing &&ring, ComPtr< ID3D12Resource > filterUpload, std::byte *filterMapped, uint32_t capacity, uint32_t slotCount)
Definition graph_trace.h:94
ComPtr< ID3D12Resource > m_filterUpload
std::vector< GraphTraceFilter > m_slotFilters
std::expected< GraphTraceFrame, std::string > Read(uint32_t slot=0) const
static std::expected< ReadbackRing, std::string > Create(ID3D12Device *device, uint64_t bytes, uint64_t headerBytes, uint32_t slotCount, std::wstring_view name)
GraphTraceRecord DecodeRecord(std::span< const uint32_t, 4 > words)
std::array< uint32_t, GT_FILTER_BYTES/4 > FilterWords(const GraphTraceFilter &filter)
D3D12_RESOURCE_BARRIER Transition(ID3D12Resource *resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
Definition resources.cpp:65
ComPtr< ID3D12Resource > CreateBuffer(ID3D12Device *device, uint64_t sizeBytes, BufferKind kind)
Definition resources.cpp:18
void SortGraphTrace(std::vector< GraphTraceRecord > &records)
std::array< uint32_t, 3 > boxMax
Definition graph_trace.h:43
std::array< uint32_t, 3 > boxMin
Definition graph_trace.h:42