52 using Clock = chr::steady_clock;
84 return {.togglePause =
true};
87 return {.stepTicks = 1};
90 return {.speedSteps = 1};
93 return {.speedSteps = -2};
96 return {.resetSpeed =
true};
105 return chr::duration<double, std::milli>(duration).count();
111 return static_cast<double>(ticks) * 1000.0 /
static_cast<double>(frequency);
121 ComPtr<ID3D12GraphicsCommandList10>
list;
208 m_sim(std::move(parts.simulation)),
209 m_peek(std::move(parts.peek)),
210 m_view(std::move(parts.view)),
214 options.editor && options.replayPath.empty()),
215 m_scheduler(m_sim.UnitsPerTick(), {.targetFps =
static_cast<double>(options.targetFps),
217 m_computeFrequency(m_compute.TimestampFrequency()),
218 m_directFrequency(m_direct.TimestampFrequency()) {
220 if (options.screenshotTick)
221 m_scheduler.SetStopTick(*options.screenshotTick);
224 if (!options.tracePath.empty())
225 m_traceCapture.emplace_back(options.tracePath, options.trace);
229 m_viewController.Peek(options.peekCell, options.peekDepth);
232 if (options.physics && options.checkPhysics) {
234 physics::PxDefaultParameters());
238 [[nodiscard]]
bool CreateFrameSlots();
239 [[nodiscard]]
bool CreateEditor();
240 [[nodiscard]]
bool CreateSavePoints();
241 [[nodiscard]]
int Run();
245 bool RecordRenderLists();
248 [[nodiscard]]
bool WantsScreenshot()
const;
249 [[nodiscard]]
bool SubmitScreenshot();
250 [[nodiscard]]
bool WriteScreenshot();
251 [[nodiscard]]
bool NeedsStopExtraction()
const;
254 void CollectSimSubmissions();
255 void ReportSimReadback(
const sim::ProbeFrameReadback& readback);
256 void ReportEvents(
const sim::ProbeFrameReadback& readback);
257 void CheckPhysics(
const sim::ProbeTickHash& tickHash);
258 void CollectFrameSlot(uint32_t slotIndex);
262 void ApplyPeekChange();
264 [[nodiscard]] std::vector<sim::ProbeCommand> TakeCommands(SimCursor start);
265 [[nodiscard]] std::vector<sim::ProbeCommand> TakeClickCommands(uint64_t applyTick, uint32_t limit);
266 void TakeTableSwaps(SimCursor start, uint32_t limit, std::vector<sim::ProbeCommand>& commands);
267 [[nodiscard]]
bool FinishReplay();
272 void StartRequestedTrace(SimCursor start);
273 void CollectTrace(
const sim::ProbeFrameReadback& readback);
274 [[nodiscard]]
bool FinishTrace();
277 bool RunFrame(Clock::time_point frameStart);
278 int Finish(
bool failed, Clock::time_point start);
279 [[nodiscard]]
bool IsDeviceLost()
const;
283 [[nodiscard]]
bool SubmitEditor();
284 void ApplyTimeRequest(
const editor::TimeRequest& request);
285 void CheckAutoTime();
286 void FilterEditorInput(std::vector<InputEvent>& events)
const;
287 [[nodiscard]] editor::EditorStatus MakeEditorStatus()
const;
288 [[nodiscard]] editor::GraphPanelStatus MakeGraphPanelStatus()
const;
291 [[nodiscard]] std::string RewindUnavailableReason()
const;
292 [[nodiscard]] std::vector<uint64_t> SavePointTicks()
const;
293 void RequestRewind(uint64_t tick);
294 [[nodiscard]] uint32_t PrepareRewind();
295 [[nodiscard]] uint32_t SaveSlotFor(SimCursor start, uint32_t unitCount, uint32_t restoreFrom)
const;
296 [[nodiscard]] std::vector<sim::ProbeCommand> TakeLiveCommands(uint64_t applyTick, uint64_t endApplyTick,
298 void CheckAutoRewind();
301 void LogStats(
const Stats& stats, Clock::duration elapsed, std::string_view label)
const;
302 void AddStats(
const Stats& frame);
332 .y = sim::PROBE_GRID_SIZE / 2,
333 .z = sim::PROBE_GRID_SIZE / 2};
392 return std::unexpected(window.error());
394 (*window)->PumpMessages();
395 (void)(*window)->TakeResized();
409 return deviceOptions;
419 {.busyIterations = options.
simLoad,
421 .trace = options.
trace,
423 .physicsScene = options.
physics ? &physicsScene :
nullptr,
432 const auto start = chr::steady_clock::now();
435 return std::unexpected(loaded.error());
437 const auto elapsedMs = chr::duration_cast<chr::milliseconds>(chr::steady_clock::now() - start).count();
439 std::string packages;
440 for (
const std::string& name : loaded->loadOrder)
441 packages += (packages.empty() ?
"" :
"・") + name;
444 packages, loaded->table.species.size() - 1, loaded->table.rules.size(), loaded->tableVersion,
445 loaded->modifiedWorld ?
"・改造された世界" :
"", elapsedMs);
451 for (
const std::string& warning : loaded->table.warnings)
454 return std::make_shared<const script::LoadedReactionTable>(std::move(*loaded));
460 return std::unexpected(reactionTable.error());
464 return std::unexpected(window.error());
467 deviceOptions.
presentMonitor = MonitorFromWindow((*window)->Handle(), MONITOR_DEFAULTTONEAREST);
470 return std::unexpected(device.error());
472 ID3D12Device5* native = device->Get();
476 native, D3D12_COMMAND_LIST_TYPE_DIRECT, L
"Render",
477 options.
renderHighPriority ? D3D12_COMMAND_QUEUE_PRIORITY_HIGH : D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
480 if (!direct || !compute)
481 return std::unexpected(
"キューを作れない");
486 return std::unexpected(swapChain.error());
488 auto simulation =
CreateSimulation(native, options, (*reactionTable)->table);
490 return std::unexpected(simulation.error());
494 return std::unexpected(peek.error());
498 return std::unexpected(view.error());
500 std::vector<save::ReplayPlayer> replay;
504 return std::unexpected(player.error());
506 replay.push_back(std::move(*player));
510 .device = std::move(*device),
511 .direct = std::move(*direct),
512 .compute = std::move(*compute),
513 .swapChain = std::move(*swapChain),
514 .simulation = std::move(*simulation),
515 .peek = std::move(*peek),
516 .view = std::move(*view),
517 .replay = std::move(replay),
518 .reactionTable = *reactionTable};
522 ID3D12Device5* device =
m_device.Get();
524 if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
525 IID_PPV_ARGS(&slot.allocator))) ||
526 FAILED(device->CreateCommandList1(0, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_FLAG_NONE,
527 IID_PPV_ARGS(&slot.list)))) {
535 void* mapped =
nullptr;
536 const D3D12_RANGE noRead{};
537 if (FAILED(slot.constants->Map(0, &noRead, &mapped)))
543 const D3D12_QUERY_HEAP_DESC queryDesc{.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP,
561 ID3D12GraphicsCommandList10* list = slot.
list.Get();
567 .renderTargetView =
m_swapChain.RenderTargetView(index),
570 {.extraction0 =
m_sim.Extraction(0)->GetGPUVirtualAddress(),
571 .extraction1 =
m_sim.Extraction(1)->GetGPUVirtualAddress(),
572 .extraction2 =
m_sim.Extraction(2)->GetGPUVirtualAddress(),
573 .constants = slot.
constants->GetGPUVirtualAddress()});
576 list->ResolveQueryData(
m_renderTimestamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, firstQuery,
578 if (FAILED(list->Close()))
616 submission.
read =
true;
627 const uint32_t unitsPerTick =
m_sim.UnitsPerTick();
628 m_interval.unitGpuMilliseconds.resize(unitsPerTick);
629 for (
size_t index = 0; index < readback.
unitGpuTicks.size(); ++index) {
630 const auto unit =
static_cast<uint32_t
>((readback.
firstUnit + index) % unitsPerTick);
633 m_interval.unitGpuMilliseconds[unit] += milliseconds;
641 if (!readback.
hashes.empty())
662 const auto word64 = [&](
size_t index) {
663 return static_cast<int64_t
>(uint64_t{command.
payload[index]} |
664 (uint64_t{command.
payload[index + 1]} << 32));
666 const auto direction = [&](
size_t index) {
667 return static_cast<int64_t
>(
static_cast<int32_t
>(command.
payload[index]));
670 (void)world.
Push(physics::PxMakeVec3(word64(0), word64(2), word64(4)),
671 physics::PxMakeVec3(direction(6), direction(7), direction(8)), command.
payload[9]);
679 if (command.targetTick == tick)
697 const auto now = Clock::now();
700 if (event.
type == sim::PROBE_EVENT_COMMAND_LATE) {
706 if (event.
type == sim::PROBE_EVENT_BODY_PUSHED) {
707 if (event.
place == sim::PROBE_PUSH_NOTHING)
717 return pending.
tick ==
event.tick && pending.
cell == cell;
723 "つつき ({}, {}, {}) を刻み {} で適用(クリックから CPU に戻るまで {:.1f} ms)", cell.
x, cell.
y,
724 cell.
z, event.
tick, latency);
753 m_interval.renderGpuMilliseconds += milliseconds;
764 std::vector<InputEvent> events =
m_window->TakeInputEvents();
784 if (autoClick || autoIgnite) {
788 const uint32_t u = step == 0 ? 28 : (step * 37 % 16 + 1) * sim::PROBE_GRID_SIZE / 18;
789 const uint32_t v = step == 0 ? 32 : (step * 11 % 16 + 1) * sim::PROBE_GRID_SIZE / 18;
795 const auto now = Clock::now();
798 m_clicks.push_back({.cell = cell, .time = now});
809 rays.push_back({.origin = {20.0f, 53.0f, 0.0f}, .direction = {0.0f, 0.0f, 1.0f}});
813 const float length = std::sqrt((ray.direction.x * ray.direction.x) +
814 (ray.direction.y * ray.direction.y) +
815 (ray.direction.z * ray.direction.z));
819 constexpr auto UNITS_PER_CELL =
static_cast<double>(1u << sim::PROBE_PHYSICS_CELL_SHIFT);
820 constexpr auto DIRECTION_ONE =
static_cast<double>(1u << 30);
821 const std::array<int64_t, 3> origin = {
822 std::llround(ray.origin.x * UNITS_PER_CELL),
823 std::llround((
static_cast<double>(sim::PROBE_GRID_SIZE) - ray.origin.y) * UNITS_PER_CELL),
824 std::llround(ray.origin.z * UNITS_PER_CELL)};
825 const std::array<int32_t, 3> direction = {
826 static_cast<int32_t
>(std::lround(ray.direction.x / length * DIRECTION_ONE)),
827 static_cast<int32_t
>(std::lround(-ray.direction.y / length * DIRECTION_ONE)),
828 static_cast<int32_t
>(std::lround(ray.direction.z / length * DIRECTION_ONE))};
853 const uint32_t limit = std::min(
m_sim.FreeCommandSlots(), sim::PROBE_MAX_COMMANDS);
858 std::vector<sim::ProbeCommand> commands =
m_replay.empty()
860 :
m_replay.front().TakeCommands(applyTick, limit);
868 return command.
type == sim::PROBE_COMMAND_TYPE_PUSH;
878 const uint32_t unitsPerTick =
m_sim.UnitsPerTick();
879 const uint64_t firstUnit = (start.
tick * unitsPerTick) + start.
unit;
881 auto swaps =
m_tableReload.TakeSwaps(firstUnit,
static_cast<uint32_t
>(endUnit - firstUnit), unitsPerTick,
901 command.targetTick = applyTick;
902 if (command.type != sim::PROBE_COMMAND_TYPE_POKE)
906 .x = command.payload[0], .y = command.payload[1], .z = command.payload[2]};
908 return pending.
tick == UINT64_MAX && pending.
cell == cell;
912 click->tick = applyTick;
924 if (!submission.
read) {
935 const uint32_t unitCount =
m_scheduler.TakeUnits();
939 if (unitCount == 0 && !extractOnly && !restoring)
944 if (extractOnly && !extract && !restoring)
950 const std::vector<sim::ProbeCommand> commands =
TakeCommands(start);
956 const uint64_t extractionTick =
m_scheduler.Cursor().tick;
958 const auto extractionTarget =
static_cast<uint32_t
>(extraction % sim::PROBE_EXTRACTION_COUNT);
960 ID3D12CommandList* list =
m_sim.RecordFrame(
961 slot, {.firstTick = start.
tick,
962 .firstUnit = start.
unit,
963 .unitCount = unitCount,
965 .extractionTarget = extractionTarget,
966 .commands = commands,
967 .afterExtract = [
this](
auto* simList,
968 const auto& context) {
m_peek.RecordAfterExtract(simList, context); },
969 .saveTo =
SaveSlotFor(start, unitCount, restoreFrom),
970 .restoreFrom = restoreFrom,
978 m_sim.DiscardSavePointsAfter(0);
990 submission = {.fence =
m_compute.Submit(list),
991 .extraction = extract ? extraction : 0,
992 .extractionTick = extractionTick,
1002 const uint32_t index =
m_swapChain.CurrentIndex();
1007 const auto extraction =
static_cast<uint32_t
>(
m_completedExtraction.number % sim::PROBE_EXTRACTION_COUNT);
1046 ID3D12CommandList* list =
m_screenshot.front().Record(backBuffer);
1047 if (list ==
nullptr)
1074 FAILED(
m_device.Get()->GetDeviceRemovedReason());
1102 const bool pointer =
m_editor->WantsPointer();
1103 const bool keyboard =
m_editor->WantsKeyboard();
1104 std::erase_if(events, [&](
const InputEvent& event) {
1121 if (automatic.
Any())
1122 request = automatic;
1136 const uint32_t index =
m_swapChain.CurrentIndex();
1162 if (!holdBroken && !stepBroken)
1173 const auto perSecond = [&](uint64_t count) {
1174 return seconds > 0.0 ?
static_cast<double>(count) / seconds : 0.0;
1177 const auto average = [](
double sum, uint64_t count) {
1178 return count > 0 ? sum /
static_cast<double>(count) : 0.0;
1183 return {.tick = cursor.
tick,
1184 .unit = cursor.
unit,
1192 .framesPerSecond = perSecond(last.
frames),
1193 .ticksPerSecond = perSecond(last.
ticks),
1197 .budgetMilliseconds =
m_scheduler.BudgetMilliseconds(),
1200 .recording = !
m_options.recordPath.empty(),
1203 .saveIntervalTicks =
m_options.saveIntervalTicks,
1206 .reactionTable = {.version =
m_tableReload.AppliedVersion(),
1221 const uint32_t unitsPerTick =
m_sim.UnitsPerTick();
1225 const bool physics =
m_sim.Physics() !=
nullptr && unit == sim::PROBE_UNIT_PHYSICS;
1226 const char* name = unit == sim::PROBE_UNIT_APPLY ?
"コマンドの適用"
1227 : unit == sim::PROBE_UNIT_CONDUCT ?
"伝導と反応"
1229 : unit == unitsPerTick - 1 ?
"ハッシュとイベント"
1231 status.
units.push_back({.name = name,
1232 .workGraph = unit == sim::PROBE_UNIT_CONDUCT || physics,
1233 .millisecondsPerTick = perTick});
1261 static_cast<double>(
m_sim.SavePointBytes()) / (1024.0 * 1024.0),
m_options.saveIntervalTicks);
1269 return "保存点なし(--save-points 0)";
1272 return "記録中は巻き戻さない";
1275 return "--check-physics の間は巻き戻さない";
1278 return "トレースを集めている間は巻き戻さない";
1284 std::vector<uint64_t> ticks;
1286 const uint64_t tick =
m_sim.SavePointTick(index);
1287 if (tick != UINT64_MAX)
1288 ticks.push_back(tick);
1300 reason.empty() ?
"その保存点が無い" : reason);
1316 if (
m_sim.SavePointTick(candidate) == tick)
1325 m_sim.DiscardSavePointsAfter(tick);
1330 const auto kept = rng::find_if(
1348 const uint64_t interval =
m_options.saveIntervalTicks;
1350 start.
unit == 0 && start.
tick > 0 && start.
tick % interval == 0 &&
1363 const uint64_t feedEnd = commands.empty() ? endApplyTick : std::max(endApplyTick, applyTick + 1);
1373 commands.push_back(command);
1390 if (ticks.size() >= 2)
1399 m_total.cpuMaxMilliseconds = std::max(
m_total.cpuMaxMilliseconds,
frame.cpuMaxMilliseconds);
1400 m_total.presentMilliseconds +=
frame.presentMilliseconds;
1404 m_total.simGpuMilliseconds +=
frame.simGpuMilliseconds;
1405 m_total.simSubmissionsMeasured +=
frame.simSubmissionsMeasured;
1406 m_total.renderGpuMilliseconds +=
frame.renderGpuMilliseconds;
1407 m_total.renderFramesMeasured +=
frame.renderFramesMeasured;
1409 m_total.skippedSubmissions +=
frame.skippedSubmissions;
1410 m_total.skippedExtractions +=
frame.skippedExtractions;
1416 const double seconds = chr::duration<double>(elapsed).count();
1417 if (seconds <= 0.0 || stats.
frames == 0)
1420 const auto average = [](
double sum, uint64_t count) {
1421 return count > 0 ? sum /
static_cast<double>(count) : 0.0;
1424 const auto perSecond = [&](uint64_t count) {
1425 return static_cast<double>(count) / seconds;
1429 "{}: {:.1f} fps CPU {:.3f} ms/フレーム(うち Present {:.3f}、最大 {:.3f}) 世界 {:.1f} 刻み/秒 "
1430 "シミュ {:.1f} 単位/投入・GPU {:.3f} ms/投入(予算 {:.2f}) 描画 GPU {:.3f} ms/フレーム "
1431 "捨てた刻み {} 見送り {}(抽出 {}) CPU の待ち {} イベント {} 状態 S({}) = {:016x}"
1432 "(エネルギー {} mJ・計算したブロック {})",
1462 const auto presentStart = Clock::now();
1468 const double cpuMilliseconds =
Milliseconds(Clock::now() - frameStart);
1469 m_interval.cpuMilliseconds += cpuMilliseconds;
1479 "フレームのループを始める(vsync {} 先行 {} 目標 {} fps 重さ {}(分けて {} 個。1 刻み {} 単位) "
1480 "描画の優先度 {} 自動クリック {})",
1497 const auto start = Clock::now();
1498 auto lastFrame = start;
1499 auto intervalStart = start;
1514 return Finish(
true, start);
1518 lastFrame = Clock::now();
1523 const auto frameStart = Clock::now();
1525 lastFrame = frameStart;
1527 return Finish(
true, start);
1537 intervalStart = frameStart;
1540 return Finish(
false, start);
1565 "--screenshot-tick: 刻み {} の画面を写せなかった(--frames {} のうちに届かなかった)",
1578 if (
m_device.ValidationErrorCount() > 0) {
1596 return replayPassed ? 0 : 1;
1621 const auto below = [](uint32_t cell) {
1624 const std::array<uint32_t, 3> cellMin = {below(center.
x), below(center.
y), below(center.
z)};
1625 const std::array<uint32_t, 3> cellMax = {
1631 const fs::path path =
m_options.traceDirectory /
1632 std::format(L
"trace-t{}-cell{}_{}_{}.txt", tickBegin, center.
x, center.
y, center.
z);
1634 m_sim.SetTraceFilter(filter);
1651 const auto written = capture.
Write();
1658 m_sim.SetTraceFilter({});
1696 "再生: {}(ハッシュ 一致 {} / 不一致 {} / 全部 {}、間に合わなかったコマンド {}{})",
1700 return passed && player.
Passed();
1706 auto parts = CreateParts(options);
1712 FrameLoop loop(options, std::move(*parts));
1713 if (!loop.CreateFrameSlots()) {
1718 if (!loop.CreateEditor() || !loop.CreateSavePoints())
static std::expected< std::unique_ptr< Window >, std::string > Create(std::wstring_view title, uint32_t clientWidth, uint32_t clientHeight)
static std::expected< std::unique_ptr< EditorOverlay >, std::string > Create(Window &window, ID3D12Device *device, gpu::Queue &direct, DXGI_FORMAT format, uint32_t frameCount)
void Add(std::span< const gpu::GraphTraceRecord > records, uint32_t droppedCount)
std::expected< void, std::string > Write()
bool IsComplete(uint64_t latestStateTick) const
uint32_t m_savePointCount
editor::TimeControl m_timeControl
std::vector< sim::ProbeCommand > m_pendingCommands
bool NeedsStopExtraction() const
std::vector< sim::ProbeTableSwap > m_frameTableSwaps
FrameLoop(const FrameLoopOptions &options, FrameLoopParts &&parts)
std::vector< TraceCapture > m_traceCapture
void LogStats(const Stats &stats, Clock::duration elapsed, std::string_view label) const
std::unique_ptr< editor::EditorOverlay > m_editor
void RequestRewind(uint64_t tick)
std::vector< render::ScreenshotCapture > m_screenshot
std::vector< sim::ProbeCommand > TakeClickCommands(uint64_t applyTick, uint32_t limit)
uint32_t SaveSlotFor(SimCursor start, uint32_t unitCount, uint32_t restoreFrom) const
void CollectTrace(const sim::ProbeFrameReadback &readback)
std::unique_ptr< sim::PhysicsWorld > m_physicsCheck
std::unique_ptr< Window > m_window
std::array< uint64_t, sim::PROBE_EXTRACTION_COUNT > m_lastRenderReading
TableHotReload m_tableReload
uint64_t m_lastExtractionTick
sim::ProbeTickHash m_latestHash
editor::EditorStatus MakeEditorStatus() const
std::vector< sim::ProbeCommand > TakeCommands(SimCursor start)
std::vector< sim::ProbeCommand > TakeLiveCommands(uint64_t applyTick, uint64_t endApplyTick, uint32_t limit)
editor::GraphPanelStatus MakeGraphPanelStatus() const
double m_lastIntervalSeconds
std::vector< PendingClick > m_clicks
void CollectSimSubmissions()
std::deque< sim::ProbeCommand > m_rewindFeed
bool RunFrame(Clock::time_point frameStart)
std::vector< sim::ProbeCommand > m_commandHistory
void ReportEvents(const sim::ProbeFrameReadback &readback)
void TakeTableSwaps(SimCursor start, uint32_t limit, std::vector< sim::ProbeCommand > &commands)
std::vector< uint64_t > SavePointTicks() const
bool IsDeviceLost() const
save::ReplayRecorder m_recorder
uint64_t m_directFrequency
void StartRequestedTrace(SimCursor start)
uint64_t m_lastExtraction
std::array< FrameSlot, FRAME_SLOT_COUNT > m_frames
std::vector< save::ReplayPlayer > m_replay
render::DebugViewController m_viewController
std::vector< sim::ProbeCommand > m_physicsCheckPushes
ComPtr< ID3D12QueryHeap > m_renderTimestamps
render::CellCoordinate m_lastPokedCell
void ApplyTimeRequest(const editor::TimeRequest &request)
int Finish(bool failed, Clock::time_point start)
void CollectFrameSlot(uint32_t slotIndex)
gpu::SwapChain m_swapChain
void ReportSimReadback(const sim::ProbeFrameReadback &readback)
std::string RewindUnavailableReason() const
void CheckPhysics(const sim::ProbeTickHash &tickHash)
uint64_t m_physicsCheckMismatches
void AddStats(const Stats &frame)
std::array< SimSubmission, SIM_SLOT_COUNT > m_simSubmissions
FrameLoopOptions m_options
bool m_extractAfterRewind
uint64_t m_simSubmissionCount
bool WantsScreenshot() const
void FilterEditorInput(std::vector< InputEvent > &events) const
CompletedExtraction m_completedExtraction
ComPtr< ID3D12Resource > m_renderTimestampReadback
uint64_t m_computeFrequency
static std::expected< Device, std::string > Create(AdapterKind kind, const DeviceOptions &options=DefaultDeviceOptions())
static std::expected< Queue, std::string > Create(ID3D12Device *device, D3D12_COMMAND_LIST_TYPE type, std::wstring_view name, D3D12_COMMAND_QUEUE_PRIORITY priority=D3D12_COMMAND_QUEUE_PRIORITY_NORMAL)
static constexpr DXGI_FORMAT FORMAT
static constexpr uint32_t BUFFER_COUNT
static std::expected< SwapChain, std::string > Create(ID3D12Device *device, IDXGIFactory6 *factory, ID3D12CommandQueue *directQueue, HWND window, uint32_t maxFrameLatency)
static std::expected< ProbeView, std::string > Create(ID3D12Device *device, DXGI_FORMAT renderTargetFormat)
static std::expected< ScreenshotCapture, std::string > Create(ID3D12Device *device, ID3D12Resource *backBuffer)
uint64_t Mismatches() const
uint64_t LateCommands() const
static std::expected< ReplayPlayer, std::string > Load(const fs::path &path)
uint32_t Push(const physics::PxVec3 &origin, const physics::PxVec3 &direction, uint32_t impulseMillinewtonSeconds)
static std::expected< ProbePeek, std::string > Create(ID3D12Device5 *device, const BakedReactionTable &table)
static constexpr uint32_t MAX_UNITS_PER_FRAME
static std::expected< ProbeSim, std::string > Create(ID3D12Device5 *device, D3D12_COMMAND_LIST_TYPE listType, const BakedReactionTable &table, const ProbeSimOptions &options={})
static uint64_t NextApplyTick(uint64_t tick, uint32_t unit)
static constexpr uint32_t FRAME_SLOT_COUNT
std::expected< FrameLoopParts, std::string > CreateParts(const FrameLoopOptions &options)
constexpr std::array< uint64_t, 3 > AUTO_TIME_STEP_FRAMES
std::expected< std::shared_ptr< const script::LoadedReactionTable >, std::string > LoadWorldReactionTable(const FrameLoopOptions &options)
constexpr uint32_t PUSH_IMPULSE_MILLINEWTON_SECONDS
constexpr uint64_t AUTO_TIME_STEP_CHECK_FRAME
constexpr uint64_t AUTO_TRACE_FRAME
constexpr uint32_t SIM_SLOT_COUNT
editor::TimeRequest AutoTimeRequest(uint64_t frame)
std::expected< sim::ProbeSim, std::string > CreateSimulation(ID3D12Device5 *device, const FrameLoopOptions &options, const sim::BakedReactionTable &reactionTable)
std::expected< std::unique_ptr< Window >, std::string > CreateWindowForLoop()
constexpr uint64_t AUTO_TIME_FASTER_FRAME
constexpr uint32_t TIMESTAMPS_PER_FRAME
constexpr uint64_t AUTO_TIME_SLOWER_FRAME
double Milliseconds(Clock::duration duration)
constexpr uint64_t AUTO_TIME_NORMAL_FRAME
constexpr uint32_t TRACE_KEY_RADIUS_CELLS
constexpr uint64_t AUTO_TIME_HOLD_FRAME
constexpr auto MINIMIZED_SLEEP
constexpr uint32_t CONSTANTS_BYTES
constexpr uint64_t AUTO_PUSH_FRAME
constexpr uint64_t TRACE_KEY_TICKS
constexpr uint32_t FRAME_SLOT_COUNT
constexpr uint64_t AUTO_TIME_HOLD_CHECK_FRAME
constexpr auto STATS_INTERVAL
constexpr uint32_t FRAME_WAIT_TIMEOUT_MS
constexpr uint32_t INITIAL_CLIENT_WIDTH
double TimestampMilliseconds(uint64_t ticks, uint64_t frequency)
void PushFromCommand(sim::PhysicsWorld &world, const sim::ProbeCommand &command)
constexpr uint64_t AUTO_TIME_RESUME_FRAME
gpu::DeviceOptions LoopDeviceOptions(const FrameLoopOptions &options)
constexpr uint32_t AUTO_CLICK_INTERVAL_FRAMES
constexpr uint32_t INITIAL_CLIENT_HEIGHT
constexpr uint64_t AUTO_TIME_PAUSE_FRAME
constexpr uint32_t DEFAULT_EDITOR_SAVE_POINTS
constexpr uint32_t MAX_SAVE_POINTS
constexpr uint32_t TRACE_CAPACITY_PER_FRAME
constexpr uint32_t AUTO_SAVE_POINTS
int RunFrameLoop(const FrameLoopOptions &options)
DeviceRemovedReport LogDeviceRemoved(ID3D12Device *device)
std::string FormatGraphStats(const GraphStatsLayout &layout, const GraphStatsSnapshot &snapshot)
bool ReadBuffer(ID3D12Resource *readback, std::span< std::byte > destination)
void AccumulateGraphStats(GraphStatsSnapshot &total, const GraphStatsSnapshot &frame)
ComPtr< ID3D12Resource > CreateBuffer(ID3D12Device *device, uint64_t sizeBytes, BufferKind kind)
DeviceOptions DefaultDeviceOptions()
CellCoordinate CellOnSlice(uint32_t axis, uint32_t plane, uint32_t u, uint32_t v)
constexpr uint32_t PEEK_MAX_DEPTH
std::expected< void, std::string > WriteReplayFile(const fs::path &path, const ReplayFile &replay)
std::expected< LoadedReactionTable, std::string > LoadReactionTable(const ReactionTableSource &source)
constexpr uint32_t NO_SAVE_POINT
PhysicsScene MakeProbeStackScene()
constexpr uint32_t PEEK_LEVEL_COUNT
ProbeCommand MakePokeCommand(uint64_t targetTick, uint32_t sequence, uint32_t x, uint32_t y, uint32_t z)
ProbeCommand MakePushCommand(uint64_t targetTick, uint32_t sequence, const std::array< int64_t, 3 > &origin, const std::array< int32_t, 3 > &direction, uint32_t impulseMillinewtonSeconds)
gpu::GraphTraceFilter ProbeTraceFilterForCells(uint64_t tickBegin, uint64_t tickEnd, std::array< uint32_t, 3 > cellMin, std::array< uint32_t, 3 > cellMax, uint32_t capacity)
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
std::string ToUtf8(std::wstring_view text)
std::vector< UnitTime > units
bool physicsComputeBroadphase
gpu::GraphTraceFilter trace
std::vector< save::ReplayPlayer > replay
std::shared_ptr< const script::LoadedReactionTable > reactionTable
std::unique_ptr< Window > window
ComPtr< ID3D12GraphicsCommandList10 > list
ComPtr< ID3D12CommandAllocator > allocator
render::ProbeViewConstants * mappedConstants
ComPtr< ID3D12Resource > constants
render::CellCoordinate cell
double simGpuMilliseconds
uint64_t skippedExtractions
std::vector< double > unitGpuMilliseconds
double presentMilliseconds
uint64_t renderFramesMeasured
uint64_t simSubmissionsMeasured
double cpuMaxMilliseconds
uint64_t skippedSubmissions
gpu::GraphStatsSnapshot conductGraph
double renderGpuMilliseconds
std::vector< GraphNodeCounters > nodes
std::vector< sim::Command > commands
std::vector< ReplayTickHash > tickHashes
std::array< uint32_t, COMMAND_PAYLOAD_WORDS > payload
std::vector< ProbeTickHash > hashes
gpu::GraphStatsSnapshot graphStats
std::vector< gpu::GraphTraceRecord > trace
std::vector< uint64_t > unitGpuTicks
uint32_t droppedEventCount
uint64_t gpuBeginTimestamp
uint32_t droppedTraceCount
std::vector< ProbeEvent > events
uint64_t WorldHash() const