Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
lab_session.cpp
Go to the documentation of this file.
1// lab_session.cpp — 実験室の 1 回の実験(lab_session.h)。
2#include "sim/lab_session.h"
3
4#include <algorithm>
5#include <format>
6
7namespace bicameral::sim {
8
9 std::expected<LabSession, std::string> LabSession::Create(ID3D12Device5* device, D3D12_COMMAND_LIST_TYPE queueType,
10 const BakedReactionTable& table) {
11 auto queue = gpu::ImmediateQueue::Create(device, queueType);
12 if (!queue)
13 return std::unexpected(queue.error());
14
15 auto ring = gpu::DebugRing::Create(device);
16 if (!ring)
17 return std::unexpected(ring.error());
18
19 auto box = GpuLabBox::Create(device, table);
20 if (!box)
21 return std::unexpected(box.error());
22
23 return LabSession(table, std::move(*queue), std::move(*ring), std::move(*box));
24 }
25
27 : m_table(std::move(table)),
28 m_queue(std::move(queue)),
29 m_ring(std::move(ring)),
30 m_box(std::move(box)),
33
34 // --- 置く ---
35
36 bool LabSession::Queue(const Command& command) {
38 return false;
39
40 m_pending.push_back(command);
41 m_sequence += 1;
42
43 return true;
44 }
45
46 bool LabSession::Place(LabCellPosition cell, std::span<const SpeciesAmount> contents,
47 uint32_t temperatureMilliKelvin) {
48 return Queue(MakeLabFillCommand(m_tick, m_sequence, cell, contents, temperatureMilliKelvin));
49 }
50
51 bool LabSession::SetTemperature(LabCellPosition cell, uint32_t temperatureMilliKelvin) {
52 return Queue(MakeLabTemperatureCommand(m_tick, m_sequence, cell, temperatureMilliKelvin));
53 }
54
55 // --- 刻む ---
56
57 // 刻み tick のコマンド(置いたもの + 再生中の記録)を (targetTick, sequence) の順に
58 std::vector<Command> LabSession::TakeCommands(uint64_t tick) {
59 std::vector<Command> commands;
60 for (Command command : m_pending) {
61 command.targetTick = tick; // 置いた後に刻みが進んでいなければ同じ値
62 commands.push_back(command);
63 }
64
65 m_pending.clear();
66 while (!m_scheduled.empty() && m_scheduled.front().targetTick == tick) {
67 commands.push_back(m_scheduled.front());
68 m_scheduled.erase(m_scheduled.begin());
69 }
70
71 std::ranges::sort(commands, CommandPrecedes);
72 if (commands.size() > LAB_MAX_COMMANDS_PER_TICK)
73 commands.resize(LAB_MAX_COMMANDS_PER_TICK);
74
75 return commands;
76 }
77
78 std::expected<void, std::string> LabSession::StepOne() {
79 const uint64_t tick = m_tick;
80 const std::vector<Command> commands = TakeCommands(tick);
81
82 // --- GPU: 1 本のリスト(最初だけ初めの箱を写す)---
83 ID3D12GraphicsCommandList10* list = m_queue.Begin();
84 if (list == nullptr)
85 return std::unexpected("コマンドリストを始められない");
86
87 m_ring.RecordBegin(list);
88 if (!m_uploaded && !m_box.RecordUpload(list, MakeLabBoxNest(m_table)))
89 return std::unexpected("初めの箱を写せない");
90
91 if (!m_box.RecordTick(list, m_ring.GpuAddress(), tick, commands))
92 return std::unexpected("刻みを記録できない");
93
94 m_box.RecordReadback(list);
95 m_ring.RecordReadbackAndReset(list);
96 if (!m_queue.ExecuteAndWait())
97 return std::unexpected("GPU での実行に失敗");
98
99 m_uploaded = true;
100 if (const gpu::DebugRingContents debug = m_ring.Drain(); debug.assertCount > 0)
101 return std::unexpected(std::format("GPU の FX_ASSERT が {} 件(刻み {})", debug.assertCount, tick));
102
103 if (!m_box.Read(m_read))
104 return std::unexpected("箱を読み戻せない");
105
106 // --- CPU リファレンス → 比べる ---
107 StepLabBox(m_cpu, m_table, tick, commands);
109
110 m_history.insert(m_history.end(), commands.begin(), commands.end());
111 m_hashes.push_back(HashWholeNest(m_cpu));
112 if (tick < m_replayHashes.size() && m_replayHashes[tick] != m_hashes.back() && !m_replayDivergence)
113 m_replayDivergence = tick;
114
115 m_tick += 1;
116
117 return {};
118 }
119
120 std::expected<void, std::string> LabSession::Step(uint32_t tickCount) {
121 for (uint32_t i = 0; i < tickCount && !m_mismatch; ++i) {
122 if (auto stepped = StepOne(); !stepped)
123 return stepped;
124 }
125
126 return {};
127 }
128
129 // --- 戻す・再生 ---
130
131 std::expected<void, std::string> LabSession::Reset() {
134 m_uploaded = false; // 次の刻みのリストの先頭で、初めの箱を写し直す
135 m_tick = 0;
136 m_sequence = 0;
137 m_pending.clear();
138 m_scheduled.clear();
139 m_history.clear();
140 m_hashes.clear();
141 m_mismatch.reset();
142 m_replayHashes.clear();
143 m_replayEnd = 0;
144 m_replayDivergence.reset();
145
146 return {};
147 }
148
149 std::expected<void, std::string> LabSession::Replay(const LabRecording& recording) {
150 if (auto reset = Reset(); !reset)
151 return reset;
152
153 m_scheduled = recording.commands;
154 m_replayHashes = recording.hashes;
155 m_replayEnd = recording.tickCount;
156 for (const Command& command : recording.commands)
157 m_sequence = std::max(m_sequence, command.sequence + 1); // 再生中に置いたコマンドは記録の後ろに並ぶ
158
159 return Step(static_cast<uint32_t>(recording.tickCount));
160 }
161
163 return {.tickCount = m_tick, .commands = m_history, .hashes = m_hashes};
164 }
165
166} // namespace bicameral::sim
static std::expected< DebugRing, std::string > Create(ID3D12Device *device, uint32_t slotCount=1)
static std::expected< ImmediateQueue, std::string > Create(ID3D12Device5 *device, D3D12_COMMAND_LIST_TYPE type)
static std::expected< GpuLabBox, std::string > Create(ID3D12Device5 *device, const BakedReactionTable &table)
std::optional< LabMismatch > m_mismatch
Definition lab_session.h:82
std::vector< uint64_t > m_hashes
Definition lab_session.h:81
bool Queue(const Command &command)
std::expected< void, std::string > Step(uint32_t tickCount)
std::vector< Command > m_history
Definition lab_session.h:78
bool SetTemperature(LabCellPosition cell, uint32_t temperatureMilliKelvin)
BakedReactionTable m_table
Definition lab_session.h:63
std::optional< uint64_t > m_replayDivergence
Definition lab_session.h:85
std::expected< void, std::string > Replay(const LabRecording &recording)
std::expected< void, std::string > StepOne()
LabRecording Recording() const
std::expected< void, std::string > Reset()
std::vector< Command > m_scheduled
Definition lab_session.h:77
std::vector< uint64_t > m_replayHashes
Definition lab_session.h:83
static std::expected< LabSession, std::string > Create(ID3D12Device5 *device, D3D12_COMMAND_LIST_TYPE queueType, const BakedReactionTable &table)
std::vector< Command > m_pending
Definition lab_session.h:76
bool Place(LabCellPosition cell, std::span< const SpeciesAmount > contents, uint32_t temperatureMilliKelvin)
gpu::ImmediateQueue m_queue
Definition lab_session.h:64
std::vector< Command > TakeCommands(uint64_t tick)
LabSession(BakedReactionTable table, gpu::ImmediateQueue queue, gpu::DebugRing ring, GpuLabBox box)
constexpr bool CommandPrecedes(const Command &a, const Command &b)
Definition command.h:32
constexpr uint32_t LAB_MAX_COMMANDS_PER_TICK
Definition lab_box.h:31
Command MakeLabFillCommand(uint64_t targetTick, uint32_t sequence, LabCellPosition cell, std::span< const SpeciesAmount > contents, uint32_t temperatureMilliKelvin)
Definition lab_box.cpp:85
uint64_t HashWholeNest(const MultiresNest &nest)
void StepLabBox(MultiresNest &nest, const BakedReactionTable &table, uint64_t tick, std::span< const Command > commands)
Definition lab_box.cpp:208
Command MakeLabTemperatureCommand(uint64_t targetTick, uint32_t sequence, LabCellPosition cell, uint32_t temperatureMilliKelvin)
Definition lab_box.cpp:107
MultiresNest MakeLabBoxNest(const BakedReactionTable &table)
Definition lab_box.cpp:162
std::optional< LabMismatch > FindLabMismatch(const MultiresNest &cpu, const MultiresNest &gpu, uint64_t tick)
Definition lab_box.cpp:216
std::vector< uint64_t > hashes
Definition lab_box.h:107
std::vector< Command > commands
Definition lab_box.h:106