Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
probe_save_point.cpp
Go to the documentation of this file.
1// probe_save_point.cpp — 仮の世界の保存点(刻みの境界の状態の写し)と、そこへ戻す(T-0143・ADR-0036)。
2//
3// 巻き戻し = 保存点 + 再生(docs/design/14 §2): 刻み t の境界の状態 S(t) を GPU → GPU のコピーで VRAM の保存点に写しておき、
4// 戻すときは写しを世界へ書き戻してから、刻み t 以降のコマンドを足し直して進め直す。決定性(ADR-0008)があるので、
5// 同じコマンドなら最初から流した時と同じハッシュ列になる(tests/gpu_probe_rewind_test.cpp)。CPU への読み戻しは無い。
6//
7// どちらもフレームのリストの先頭(単位とコマンドを足す前)で記録する。リストの間はどのバッファも COMMON(バッファは
8// ExecuteCommandLists の後で COMMON に戻る)なので、COMMON → コピー → COMMON の明示の遷移で囲めば、後ろの単位の状態の扱いは変わらない。
9// 写すもの: セルと熱のキャッシュ(S(t) の世代だけ)・予定の印・活性の一覧 2 組・ハッシュの表・刻みのイベントの一時置き場・物理の u0〜u13。
10// 写さないもの: コマンドキュー(戻すと空にし、呼ぶ側が足し直す)・抽出・イベントのリング・カウンタ・トレース(どれも出力。世界の結果に入らない)。
11#include <algorithm>
12
13#include "core/aliases.h"
14#include "core/log.h"
15#include "gpu/com_ptr.h"
16#include "gpu/resources.h"
17#include "sim/probe_sim.h"
18
19namespace bicameral::sim {
20 namespace {
21
22 constexpr uint64_t CELL_BYTES = uint64_t{PROBE_CELL_COUNT} * sizeof(reaction::RxCell); // 1 世代
23 constexpr uint64_t THERMAL_BYTES = uint64_t{PROBE_CELL_COUNT} * sizeof(reaction::HcThermalCache); // 1 世代
24
25 void AppendTransitions(std::vector<D3D12_RESOURCE_BARRIER>& barriers,
26 std::span<ID3D12Resource* const> resources, D3D12_RESOURCE_STATES before,
27 D3D12_RESOURCE_STATES after) {
28 for (ID3D12Resource* resource : resources)
29 barriers.push_back(gpu::Transition(resource, before, after));
30 }
31
32 // COMMON → state(コピーの前)と state → COMMON(コピーの後)
33 void RecordAround(ID3D12GraphicsCommandList10* list, std::span<ID3D12Resource* const> sources,
34 std::span<ID3D12Resource* const> destinations, bool after) {
35 constexpr D3D12_RESOURCE_STATES COMMON = D3D12_RESOURCE_STATE_COMMON;
36 constexpr D3D12_RESOURCE_STATES SOURCE = D3D12_RESOURCE_STATE_COPY_SOURCE;
37 constexpr D3D12_RESOURCE_STATES DESTINATION = D3D12_RESOURCE_STATE_COPY_DEST;
38
39 std::vector<D3D12_RESOURCE_BARRIER> barriers;
40 barriers.reserve(sources.size() + destinations.size());
41 AppendTransitions(barriers, sources, after ? SOURCE : COMMON, after ? COMMON : SOURCE);
42 AppendTransitions(barriers, destinations, after ? DESTINATION : COMMON, after ? COMMON : DESTINATION);
43 list->ResourceBarrier(static_cast<UINT>(barriers.size()), barriers.data());
44 }
45
46 } // namespace
47
48 // --- 作る ---
49
50 bool ProbeSim::CreateSavePoints(ID3D12Device5* device, uint32_t count) {
51 m_savePoints.clear();
52 if (!m_zeroQueueHeader) {
53 // 既定のヒープは 0 で初期化される(D3D12_HEAP_FLAG_CREATE_NOT_ZEROED を付けない)
54 m_zeroQueueHeader = gpu::CreateBuffer(device, PROBE_COMMAND_QUEUE_HEADER_BYTES,
57 return false;
58
59 m_zeroQueueHeader->SetName(L"ProbeSim.zeroQueueHeader");
60 }
61
62 const std::vector<StateRegion> regions = StateRegions(0);
63 m_savePoints.resize(count);
64 for (uint32_t index = 0; index < count; ++index) {
65 SavePoint& savePoint = m_savePoints[index];
66 for (const StateRegion& region : regions) {
67 ComPtr<ID3D12Resource> buffer = gpu::CreateBuffer(device, region.bytes,
69 if (!buffer) {
70 Log(Channel::Sim, Level::Error, "保存点 {} を作れない({} バイト)", index, region.bytes);
71 m_savePoints.clear();
72 return false;
73 }
74
75 buffer->SetName(std::format(L"ProbeSim.savePoint{}.{}", index, savePoint.buffers.size()).c_str());
76 savePoint.buffers.push_back(std::move(buffer));
77 }
78 }
79
80 return true;
81 }
82
83 uint64_t ProbeSim::SavePointBytes() const {
84 uint64_t bytes = 0;
85 for (const StateRegion& region : StateRegions(0))
86 bytes += region.bytes;
87
88 return bytes;
89 }
90
91 // 刻み tick の境界で写す部分(並びは保存点のバッファの並び。tick で変わるのはセルと熱の世代の位置だけ)
92 std::vector<ProbeSim::StateRegion> ProbeSim::StateRegions(uint64_t tick) const {
93 const uint64_t generation = tick & 1;
94 std::vector<StateRegion> regions = {
95 {.resource = m_cells.Get(),
96 .offset = generation * CELL_BYTES,
97 .bytes = CELL_BYTES,
98 .generationBytes = CELL_BYTES},
99 {.resource = m_thermal.Get(),
100 .offset = generation * THERMAL_BYTES,
101 .bytes = THERMAL_BYTES,
102 .generationBytes = THERMAL_BYTES},
103 {.resource = m_blockSchedule.Get(), .bytes = PROBE_SCHEDULE_BYTES},
104 {.resource = m_activeLists[0].Get(), .bytes = PROBE_ACTIVE_LIST_BYTES},
105 {.resource = m_activeLists[1].Get(), .bytes = PROBE_ACTIVE_LIST_BYTES},
106 {.resource = m_hashes.Get(), .bytes = PROBE_HASH_BYTES},
107 {.resource = m_tickEvents.Get(), .bytes = PROBE_TICK_EVENT_BYTES},
108 };
109
110 if (m_physics) {
111 for (const ComPtr<ID3D12Resource>& buffer : m_physics->StateBuffers())
112 regions.push_back({.resource = buffer.Get(), .bytes = buffer->GetDesc().Width});
113 }
114
115 return regions;
116 }
117
118 // --- 入力の約束 ---
119
121 const bool saving = input.saveTo != NO_SAVE_POINT;
122 const bool restoring = input.restoreFrom != NO_SAVE_POINT;
123 if (!saving && !restoring)
124 return true;
125
126 const uint32_t index = restoring ? input.restoreFrom : input.saveTo;
127 const bool valid = !(saving && restoring) && m_initialized && input.firstUnit == 0 &&
128 index < m_savePoints.size() && (!restoring || m_savePoints[index].tick == input.firstTick);
129 if (!valid) {
131 "保存点の使い方が不正: 写す {} 戻す {} 刻み {} 単位 {}(保存点の数 {}、初期化 {})",
132 saving ? static_cast<int64_t>(input.saveTo) : -1,
133 restoring ? static_cast<int64_t>(input.restoreFrom) : -1, input.firstTick, input.firstUnit,
135 }
136
137 return valid;
138 }
139
140 // --- 写す・戻す ---
141
142 void ProbeSim::RecordSave(ID3D12GraphicsCommandList10* list, uint32_t index, uint64_t tick) {
143 SavePoint& savePoint = m_savePoints[index];
144 const std::vector<StateRegion> regions = StateRegions(tick);
145 std::vector<ID3D12Resource*> sources;
146 std::vector<ID3D12Resource*> destinations;
147 for (size_t region = 0; region < regions.size(); ++region) {
148 sources.push_back(regions[region].resource);
149 destinations.push_back(savePoint.buffers[region].Get());
150 }
151
152 RecordAround(list, sources, destinations, false);
153 for (size_t region = 0; region < regions.size(); ++region) {
154 list->CopyBufferRegion(destinations[region], 0, regions[region].resource, regions[region].offset,
155 regions[region].bytes);
156 }
157
158 RecordAround(list, sources, destinations, true);
159
160 savePoint.tick = tick;
161 savePoint.physics = m_physics ? m_physics->Cursor() : GpuPhysicsCursor{};
162 }
163
164 void ProbeSim::RecordRestore(ID3D12GraphicsCommandList10* list, uint32_t index) {
165 const SavePoint& savePoint = m_savePoints[index];
166 const std::vector<StateRegion> regions = StateRegions(savePoint.tick);
167 std::vector<ID3D12Resource*> sources = {m_zeroQueueHeader.Get()};
168 std::vector<ID3D12Resource*> destinations = {m_commandQueue.Get()};
169 for (size_t region = 0; region < regions.size(); ++region) {
170 sources.push_back(savePoint.buffers[region].Get());
171 destinations.push_back(regions[region].resource);
172 }
173
174 RecordAround(list, sources, destinations, false);
175 for (size_t region = 0; region < regions.size(); ++region) {
176 const StateRegion& state = regions[region];
177 ID3D12Resource* saved = savePoint.buffers[region].Get();
178 if (state.generationBytes == 0) {
179 list->CopyBufferRegion(state.resource, 0, saved, 0, state.bytes);
180 continue;
181 }
182
183 // 2 世代とも S(t) にする(予定していないブロックは 2 世代が同じ値、という約束を保つ)
184 for (uint64_t generation = 0; generation < 2; ++generation)
185 list->CopyBufferRegion(state.resource, generation * state.generationBytes, saved, 0, state.bytes);
186 }
187
188 // コマンドキューを空に(末尾 = 先頭 = 0。CPU の控えは RecordFrame が入力を書く前に空にしている)
189 list->CopyBufferRegion(m_commandQueue.Get(), 0, m_zeroQueueHeader.Get(), 0, PROBE_COMMAND_QUEUE_HEADER_BYTES);
190 RecordAround(list, sources, destinations, true);
191
192 if (m_physics)
193 m_physics->SetCursor(savePoint.physics);
194 }
195
197 m_commandTail = 0;
199 m_queuedTicks.clear();
200 m_hasEnqueued = false;
201 }
202
203} // namespace bicameral::sim
std::array< ComPtr< ID3D12Resource >, 2 > m_activeLists
Definition probe_sim.h:410
std::vector< StateRegion > StateRegions(uint64_t tick) const
uint64_t SavePointBytes() const
std::vector< SavePoint > m_savePoints
Definition probe_sim.h:422
ComPtr< ID3D12Resource > m_zeroQueueHeader
Definition probe_sim.h:423
std::unique_ptr< GpuPhysics > m_physics
Definition probe_sim.h:400
void RecordSave(ID3D12GraphicsCommandList10 *list, uint32_t index, uint64_t tick)
std::vector< QueuedTick > m_queuedTicks
Definition probe_sim.h:433
ComPtr< ID3D12Resource > m_hashes
Definition probe_sim.h:405
bool ValidateSavePoints(const ProbeFrameInput &input) const
ComPtr< ID3D12Resource > m_blockSchedule
Definition probe_sim.h:411
ComPtr< ID3D12Resource > m_commandQueue
Definition probe_sim.h:406
ComPtr< ID3D12Resource > m_tickEvents
Definition probe_sim.h:407
void RecordRestore(ID3D12GraphicsCommandList10 *list, uint32_t index)
ComPtr< ID3D12Resource > m_cells
Definition probe_sim.h:388
ComPtr< ID3D12Resource > m_thermal
Definition probe_sim.h:389
bool CreateSavePoints(ID3D12Device5 *device, uint32_t count)
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 RecordAround(ID3D12GraphicsCommandList10 *list, std::span< ID3D12Resource *const > sources, std::span< ID3D12Resource *const > destinations, bool after)
void AppendTransitions(std::vector< D3D12_RESOURCE_BARRIER > &barriers, std::span< ID3D12Resource *const > resources, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
constexpr uint32_t NO_SAVE_POINT
Definition probe_sim.h:133
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
Definition log.h:140
std::vector< ComPtr< ID3D12Resource > > buffers
Definition probe_sim.h:357