Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
probe_peek.cpp
Go to the documentation of this file.
1// probe_peek.cpp — 覗き窓(probe_peek.h)。GPU は多重解像度の入れ子(sim/gpu_multires)に世界の写しと抽出の Compute
2// (shaders/sim/multires_peek.hlsl)を足して記録する。CPU リファレンスは同じ順で sim/multires_nest の関数を呼ぶ。
3#include "sim/probe_peek.h"
4
5#include "common/probe_world.hlsli"
6#include "gpu/resources.h"
7
8using namespace bicameral::multires;
9using namespace bicameral::reaction;
10
11namespace bicameral::sim {
12
13 namespace {
14
15 constexpr uint32_t PEEK_GROUP_THREADS = 64; // multires_peek.hlsl の numthreads
16 constexpr int32_t PEEK_POINT_LEVEL = static_cast<int32_t>(PEEK_LEVEL_COUNT);
17
18 // 覗き窓の入れ子は観察の枠だけ(世界の木を持たない。写しと影の鎖。17 §5)
19 constexpr MultiresCapacity PEEK_CAPACITY = {.worldBlocks = 0,
20 .observerBlocks = PEEK_BLOCK_CAPACITY,
21 .fractions = 1,
22 .indexEntries = 1,
23 .ledgerColumns = 1};
24
25 // 点を含む世界の 8³ のブロックの原点(1 軸ぶん。レベル 0 のセル)
26 uint32_t BlockOrigin(uint32_t cell) {
27 return cell & ~(MR_BLOCK_EDGE - 1);
28 }
29
30 uint32_t Generation(uint64_t tick) {
31 return static_cast<uint32_t>(tick & 1u);
32 }
33
34 } // namespace
35
37 const int64_t halfCell = int64_t{1} << (PEEK_POINT_LEVEL - 1);
38 const auto center = [halfCell](uint32_t value) {
39 return (int64_t{value} << PEEK_POINT_LEVEL) + halfCell;
40 };
41
42 return {.x = center(cell.x), .y = center(cell.y), .z = center(cell.z), .level = PEEK_POINT_LEVEL};
43 }
44
45 // --- 覗く場所の状態 ---
46
48 Plan plan;
49
50 // --- やめた・移った: 前の影の鎖を捨てる ---
52 plan.removeShadow = true;
53 m_hasActive = false;
54 }
55
56 if (!m_hasRequest)
57 return plan;
58
59 plan.mirror = true;
60 plan.cell = m_requested;
61
62 // --- 初めて: 今の世界の写しから細かくする(影は世界と同じ数で始まる)---
63 if (!m_hasActive) {
64 plan.refine = true;
66 m_hasActive = true;
67 m_lastTick = tick;
68
69 return plan;
70 }
71
72 // --- 続き: 世界の刻みが進んでいれば影も 1 刻み。どちらでも親(今の世界)へ引き戻す ---
73 plan.step = tick > m_lastTick;
74 plan.pullBack = true;
75 m_lastTick = tick;
76
77 return plan;
78 }
79
80 // --- GPU ---
81
82 std::expected<ProbePeek, std::string> ProbePeek::Create(ID3D12Device5* device, const BakedReactionTable& table) {
83 auto nest = GpuMultires::Create(device, table, PEEK_CAPACITY);
84 if (!nest)
85 return std::unexpected(nest.error());
86
87 ProbePeek result(std::move(*nest), ProbeViewSpecies(table));
88 ID3D12RootSignature* rootSignature = result.m_nest.RootSignature();
89
90 const auto mirror = gpu::LoadShader("sim/multires_peek_mirror.cso");
91 const auto extract = gpu::LoadShader("sim/multires_peek_extract.cso");
92 if (!mirror)
93 return std::unexpected(mirror.error());
94
95 if (!extract)
96 return std::unexpected(extract.error());
97
98 result.m_mirrorPipeline = gpu::CreateComputePipeline(device, rootSignature, *mirror);
99 result.m_extractPipeline = gpu::CreateComputePipeline(device, rootSignature, *extract);
100 if (!result.m_mirrorPipeline || !result.m_extractPipeline)
101 return std::unexpected("覗き窓のパイプラインを作れない");
102
103 return result;
104 }
105
106 void ProbePeek::RecordAfterExtract(ID3D12GraphicsCommandList10* list, const ProbeExtractContext& context) {
107 // --- 最初だけ: 空の入れ子(全部の枠が空き)を写す ---
108 if (!m_uploaded) {
109 if (!m_nest.RecordUpload(list, MakeMultiresNest(PEEK_CAPACITY)))
110 return;
111
112 m_uploaded = true;
113 }
114
115 m_nest.SetExternalViews(context.cells->GetGPUVirtualAddress(), context.extraction->GetGPUVirtualAddress());
116
117 const PeekState::Plan plan = m_state.NextPlan(context.tick);
118 if (plan.removeShadow)
119 m_nest.RecordRemoveShadow(list, context.debugRing, PEEK_FIRST_SHADOW_SLOT, PEEK_LEVEL_COUNT);
120
121 // --- 世界の写し → 細かくする、または刻んで引き戻す ---
122 if (plan.mirror) {
123 const std::array<uint32_t, 4> mirrorConstants = {Generation(context.tick), BlockOrigin(plan.cell.x),
124 BlockOrigin(plan.cell.y), BlockOrigin(plan.cell.z)};
125 m_nest.RecordExternalDispatch(list, context.debugRing, m_mirrorPipeline.Get(),
126 MR_BLOCK_CELLS / PEEK_GROUP_THREADS, mirrorConstants);
127 }
128
129 if (plan.refine) {
130 m_nest.RecordRefineShadow(list, context.debugRing, PEEK_MIRROR_SLOT, PEEK_FIRST_SHADOW_SLOT,
132 }
133
134 if (plan.step)
135 m_nest.RecordStep(list, context.debugRing, ProbeWorldSeed(), context.tick);
136
137 if (plan.pullBack)
138 m_nest.RecordPullBack(list, context.debugRing, PEEK_FIRST_SHADOW_SLOT, PEEK_LEVEL_COUNT);
139
140 // --- 抽出の覗きの欄(覗いていなければ段の数 0)---
141 const std::array<uint32_t, 4> extractConstants = {m_viewSpecies[0], m_viewSpecies[1], m_viewSpecies[2],
143 m_nest.RecordExternalDispatch(list, context.debugRing, m_extractPipeline.Get(),
144 PEEK_LEVEL_COUNT * MR_BLOCK_CELLS / PEEK_GROUP_THREADS, extractConstants);
145 }
146
147 // --- CPU リファレンス ---
148
151
152 void ProbePeekReference::Advance(std::span<const RxCell> world, uint64_t tick) {
153 FX_ASSERT(world.size() == PROBE_CELL_COUNT);
154 const PeekState::Plan plan = m_state.NextPlan(tick);
155 if (plan.removeShadow)
157
158 // --- 世界の写し(multires_peek.hlsl の MirrorWorld と同じ)---
159 if (plan.mirror) {
160 const uint32_t originX = BlockOrigin(plan.cell.x);
161 const uint32_t originY = BlockOrigin(plan.cell.y);
162 const uint32_t originZ = BlockOrigin(plan.cell.z);
163 std::vector<RxCell> cells(MR_BLOCK_CELLS);
164 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
165 cells[index] = world[ProbeCellIndex(originX + MrCellX(index), originY + MrCellY(index),
166 originZ + MrCellZ(index))];
167 }
168
169 PlaceMirrorBlock(m_nest, PEEK_MIRROR_SLOT, 0, originX, originY, originZ, cells);
170 }
171
172 if (plan.refine) {
174 PeekPointOfCell(plan.cell));
175 }
176
177 // 反応は待ちの丸め(ADR-0018。仮の世界と同じ。T-0122)
178 if (plan.step)
179 StepNest(m_nest, *m_table, ProbeWorldSeed(), tick);
180
181 if (plan.pullBack)
183 }
184
185 std::vector<uint32_t> ProbePeekReference::Extraction() const {
186 std::vector<uint32_t> words(PROBE_EXTRACTION_PEEK_WORDS, 0);
187 const ReactionTableView table = m_table->View();
188
189 uint32_t levelCount = 0;
190 while (levelCount < PEEK_LEVEL_COUNT &&
191 m_nest.blocks[PEEK_FIRST_SHADOW_SLOT + levelCount].kind == MR_BLOCK_SHADOW)
192 ++levelCount;
193
194 words[0] = levelCount;
195 for (uint32_t level = 0; level < levelCount; ++level) {
196 const uint32_t slot = PEEK_FIRST_SHADOW_SLOT + level;
197 const MrBlock& block = m_nest.blocks[slot];
198 const uint32_t header = PROBE_PEEK_LEVEL_WORDS + (level * PROBE_PEEK_LEVEL_WORDS);
199 words[header] = static_cast<uint32_t>(block.level);
200 words[header + 1] = static_cast<uint32_t>(static_cast<int32_t>(block.originX));
201 words[header + 2] = static_cast<uint32_t>(static_cast<int32_t>(block.originY));
202 words[header + 3] = static_cast<uint32_t>(static_cast<int32_t>(block.originZ));
203
204 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
205 const RxCell cell = LoadNestCell(m_nest, slot, index);
206 const uint32_t base = PROBE_PEEK_HEADER_WORDS +
207 (((level * PROBE_PEEK_BLOCK_CELLS) + index) * PROBE_EXTRACTION_CELL_WORDS);
208 words[base] = ProbeMakeCache(table, cell).temperature;
209 for (uint32_t view = 0; view < PROBE_VIEW_SPECIES_COUNT; ++view)
210 words[base + 1 + view] = ProbeViewAmount(cell, m_viewSpecies[view]);
211 }
212 }
213
214 return words;
215 }
216
217} // namespace bicameral::sim
ID3D12RootSignature * RootSignature() const
static std::expected< GpuMultires, std::string > Create(ID3D12Device5 *device, const BakedReactionTable &table, const MultiresCapacity &capacity, const GpuMultiresOptions &options={})
Plan NextPlan(uint64_t tick)
std::vector< uint32_t > Extraction() const
const BakedReactionTable * m_table
Definition probe_peek.h:133
std::array< uint32_t, PROBE_VIEW_SPECIES_COUNT > m_viewSpecies
Definition probe_peek.h:134
void Advance(std::span< const reaction::RxCell > world, uint64_t tick)
ProbePeekReference(const BakedReactionTable &table)
static std::expected< ProbePeek, std::string > Create(ID3D12Device5 *device, const BakedReactionTable &table)
ComPtr< ID3D12PipelineState > m_extractPipeline
Definition probe_peek.h:110
ProbePeek(GpuMultires &&nest, const std::array< uint32_t, PROBE_VIEW_SPECIES_COUNT > &viewSpecies)
Definition probe_peek.h:105
ComPtr< ID3D12PipelineState > m_mirrorPipeline
Definition probe_peek.h:109
void RecordAfterExtract(ID3D12GraphicsCommandList10 *list, const ProbeExtractContext &context)
std::array< uint32_t, PROBE_VIEW_SPECIES_COUNT > m_viewSpecies
Definition probe_peek.h:111
std::expected< std::vector< std::byte >, std::string > LoadShader(std::string_view relativePath)
ComPtr< ID3D12PipelineState > CreateComputePipeline(ID3D12Device *device, ID3D12RootSignature *rootSignature, std::span< const std::byte > bytecode)
void PlaceMirrorBlock(MultiresNest &nest, uint32_t slot, int32_t level, int64_t originX, int64_t originY, int64_t originZ, std::span< const RxCell > cells)
void PullBackShadowChain(MultiresNest &nest, const BakedReactionTable &table, uint32_t firstShadowSlot, uint32_t levelCount)
void RemoveShadowChain(MultiresNest &nest, uint32_t firstSlot, uint32_t levelCount)
MultiresPoint PeekPointOfCell(PeekCell cell)
std::array< uint32_t, PROBE_VIEW_SPECIES_COUNT > ProbeViewSpecies(const BakedReactionTable &table)
constexpr uint32_t PEEK_LEVEL_COUNT
Definition probe_peek.h:34
MultiresNest MakeMultiresNest(const MultiresCapacity &capacity)
constexpr uint32_t PEEK_FIRST_SHADOW_SLOT
Definition probe_peek.h:33
constexpr uint32_t PEEK_MIRROR_SLOT
Definition probe_peek.h:32
void RefineShadowChain(MultiresNest &nest, uint32_t parentSlot, uint32_t firstChildSlot, uint32_t levelCount, const MultiresPoint &point)
RxCell LoadNestCell(const MultiresNest &nest, uint32_t slot, uint32_t index)
constexpr uint32_t PEEK_BLOCK_CAPACITY
Definition probe_peek.h:35
void StepNest(MultiresNest &nest, const BakedReactionTable &table, uint64_t worldSeed, uint64_t tick, const MultiresStepOptions &options)
D3D12_GPU_VIRTUAL_ADDRESS debugRing
Definition probe_sim.h:129