Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
sim_scheduler.cpp
Go to the documentation of this file.
1// sim_scheduler.cpp — 1 フレームに投げるシミュの単位の数(T-0012、ADR-0011)。考え方は sim_scheduler.h。
3
4#include <algorithm>
5#include <cmath>
6
7namespace bicameral::frame {
8 namespace {
9
10 double Blend(double average, double sample, double weight) {
11 return average <= 0.0 ? sample : average + (sample - average) * weight;
12 }
13
14 } // namespace
15
16 SimScheduler::SimScheduler(uint32_t unitsPerTick, const SimSchedulerSettings& settings)
17 : m_settings(settings), m_unitsPerTick(std::max(unitsPerTick, 1u)), m_unitMilliseconds(m_unitsPerTick, 0.0) {}
18
19 void SimScheduler::AddRealTime(double seconds, double speedScale) {
20 if (seconds <= 0.0)
21 return;
22
23 m_pendingTicks += seconds * m_settings.ticksPerSecond * std::max(speedScale, 0.0);
24 // 未処理が上限を超えたら、超えた分は捨てる(追いつこうとして重くなり続けるのを防ぐ。世界が遅くなる。D-202)
25 if (m_pendingTicks > m_settings.maxBacklogTicks) {
26 const double dropped = std::floor(m_pendingTicks - m_settings.maxBacklogTicks);
27 m_droppedTicks += static_cast<uint64_t>(dropped);
28 m_pendingTicks -= dropped;
29 }
30 }
31
32 void SimScheduler::ReportUnitTime(uint32_t unit, double gpuMilliseconds) {
33 if (unit >= m_unitsPerTick || gpuMilliseconds <= 0.0)
34 return;
35
36 m_unitMilliseconds[unit] = Blend(m_unitMilliseconds[unit], gpuMilliseconds, m_settings.smoothing);
37 }
38
39 void SimScheduler::ReportRenderTime(double gpuMilliseconds) {
40 if (gpuMilliseconds <= 0.0)
41 return;
42
43 m_renderMilliseconds = Blend(m_renderMilliseconds, gpuMilliseconds, m_settings.smoothing);
44 }
45
47 const double frameMilliseconds = 1000.0 / m_settings.targetFps;
48 return std::max(frameMilliseconds - m_renderMilliseconds - m_settings.marginMilliseconds,
49 m_settings.minBudgetMilliseconds);
50 }
51
52 // まだ測っていない単位は予算いっぱいと見なす(1 フレームに 1 つだけ投げて、測ってから増やす)
53 double SimScheduler::EstimateMilliseconds(uint32_t unit, double budgetMilliseconds) const {
54 const double measured = m_unitMilliseconds[unit];
55 return measured > 0.0 ? measured : budgetMilliseconds;
56 }
57
59 const double budget = BudgetMilliseconds();
60 double used = 0.0;
61 uint32_t count = 0;
62
63 while (count < m_settings.maxUnitsPerFrame) {
64 const bool startsTick = m_cursor.unit == 0;
65 // 現実の時間より先へは進めない
66 if (startsTick && m_pendingTicks < 1.0)
67 break;
68
69 // 止まる刻みに着いた(--screenshot-tick)
70 if (startsTick && m_cursor.tick >= m_stopTick)
71 break;
72
73 // 保存点を写す境界(巻き戻し。次のフレームの先頭で写す)
74 if (startsTick && count > 0 && m_breakInterval > 0 && m_cursor.tick % m_breakInterval == 0)
75 break;
76
77 const double estimate = EstimateMilliseconds(m_cursor.unit, budget);
78 if (count > 0 && used + estimate > budget)
79 break;
80
81 if (startsTick)
82 m_pendingTicks -= 1.0;
83
84 used += estimate;
85 ++count;
86 if (++m_cursor.unit == m_unitsPerTick) {
87 m_cursor.unit = 0;
88 ++m_cursor.tick;
89 }
90 }
91
92 return count;
93 }
94
95} // namespace bicameral::frame
void ReportUnitTime(uint32_t unit, double gpuMilliseconds)
void ReportRenderTime(double gpuMilliseconds)
SimScheduler(uint32_t unitsPerTick, const SimSchedulerSettings &settings={})
std::vector< double > m_unitMilliseconds
SimSchedulerSettings m_settings
void AddRealTime(double seconds, double speedScale=1.0)
double EstimateMilliseconds(uint32_t unit, double budgetMilliseconds) const
double Blend(double average, double sample, double weight)