Enhance civilization flow mechanics and adaptive quality control
- Implemented runtime-only adaptive quality controller to optimize simulation performance based on CPU time. - Added adaptive quality thresholds and recovery cycles to dynamically adjust simulation fidelity. - Introduced single-step solving method for civilization transitions to improve performance under high loads. - Updated benchmark scripts to include speed parameter for testing various simulation speeds. - Added tests for adaptive quality adjustments and their effects on civilization flow calculations. - Documented results of optimization attempts and their impact on performance metrics.
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-08",
|
||||||
|
"machine": "Apple M4 Pro",
|
||||||
|
"godot": "4.7.2.stable.official.ed1daf0bf",
|
||||||
|
"renderer": "headless",
|
||||||
|
"delta_seconds": 0.2,
|
||||||
|
"profile_enabled": true,
|
||||||
|
"ui": false,
|
||||||
|
"adaptation": {
|
||||||
|
"controller": "runtime-only exponential moving average of full-cycle CPU time; 0.8 previous + 0.2 latest",
|
||||||
|
"quality_thresholds_ms": {
|
||||||
|
"quality_1": 200,
|
||||||
|
"quality_2": 220,
|
||||||
|
"quality_3": 300,
|
||||||
|
"quality_2_to_3_when_over_ms": 200,
|
||||||
|
"recovery_below_ms": 140,
|
||||||
|
"recovery_cycles": 30
|
||||||
|
},
|
||||||
|
"quality_effects": {
|
||||||
|
"quality_1": "one-transition civilization approximation, at most 3 stellar/life generations per cycle, temporal grouping x2",
|
||||||
|
"quality_2": "same civilization approximation, at most 1 generation per cycle, temporal grouping x4",
|
||||||
|
"quality_3": "same civilization approximation, at most 1 generation per cycle, temporal grouping x8, alternate halves of batch types each cycle"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inputs": {
|
||||||
|
"all_objects_speed6.adventure.json": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||||
|
"many_objects_fragmented_stars_cycle.adventure.json": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||||
|
},
|
||||||
|
"runs": [
|
||||||
|
{"speed": 1, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 165.03665, "min_ms": 151.514, "max_ms": 170.171, "cycles_per_second": 6.05926, "game_years": 12, "validation": "OK"},
|
||||||
|
{"speed": 1, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 128.463817, "min_ms": 124.475, "max_ms": 144.05, "cycles_per_second": 7.784293, "game_years": 12, "validation": "OK"},
|
||||||
|
{"speed": 2, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 181.431217, "min_ms": 171.92, "max_ms": 196.005, "cycles_per_second": 5.511731, "game_years": 120, "validation": "OK"},
|
||||||
|
{"speed": 2, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 131.78055, "min_ms": 126.963, "max_ms": 148.01, "cycles_per_second": 7.588373, "game_years": 120, "validation": "OK"},
|
||||||
|
{"speed": 3, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 175.235683, "min_ms": 166.031, "max_ms": 203.456, "cycles_per_second": 5.7066, "game_years": 1200, "validation": "OK"},
|
||||||
|
{"speed": 3, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 145.013367, "min_ms": 135.062, "max_ms": 162.768, "cycles_per_second": 6.895916, "game_years": 1200, "validation": "OK"},
|
||||||
|
{"speed": 4, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 159.705883, "min_ms": 150.404, "max_ms": 221.101, "cycles_per_second": 6.26151, "game_years": 12000, "validation": "OK"},
|
||||||
|
{"speed": 4, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 177.064483, "min_ms": 170.624, "max_ms": 189.729, "cycles_per_second": 5.64766, "game_years": 12000, "validation": "OK"},
|
||||||
|
{"speed": 5, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 174.857517, "min_ms": 158.809, "max_ms": 270.078, "cycles_per_second": 5.718942, "game_years": 120000, "validation": "OK"},
|
||||||
|
{"speed": 5, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 185.601233, "min_ms": 131.409, "max_ms": 273.8, "cycles_per_second": 5.387895, "game_years": 120000, "validation": "OK"},
|
||||||
|
{"speed": 6, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 190.570967, "min_ms": 159.38, "max_ms": 345.149, "cycles_per_second": 5.247389, "game_years": 1200000, "validation": "OK"},
|
||||||
|
{"speed": 6, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 184.426, "min_ms": 149.645, "max_ms": 366.767, "cycles_per_second": 5.422229, "game_years": 1200000, "validation": "OK"}
|
||||||
|
],
|
||||||
|
"notes": [
|
||||||
|
"Each run begins in exact mode and adapts during its 60-cycle series.",
|
||||||
|
"The mean meets 5 complete cycles/s at all six speeds in these synthetic diagnostic saves on this computer; maxima show occasional cycles still exceed 200 ms at speeds 3-6.",
|
||||||
|
"No Android device or UI performance was measured.",
|
||||||
|
"Diagnostic save inputs were not modified."
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -458,3 +458,41 @@ godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -
|
|||||||
Полные состояния до и после совпали после пяти последовательных интервалов на каждом сохранении (включая дробные интервалы). Тест оптимизаций прошёл: 1889 проверок без ошибок. Цель пяти полных циклов в секунду ещё не достигнута: тяжёлому циклу требуется ускорение примерно в 2,05 раза, чтобы уложиться в 200 мс.
|
Полные состояния до и после совпали после пяти последовательных интервалов на каждом сохранении (включая дробные интервалы). Тест оптимизаций прошёл: 1889 проверок без ошибок. Цель пяти полных циклов в секунду ещё не достигнута: тяжёлому циклу требуется ускорение примерно в 2,05 раза, чтобы уложиться в 200 мс.
|
||||||
|
|
||||||
Полные профили и SHA-256 входов: `civilization_flow_prepare_step3_results.json`.
|
Полные профили и SHA-256 входов: `civilization_flow_prepare_step3_results.json`.
|
||||||
|
|
||||||
|
## Проверка оптимизации частичного изъятия партий — 2026-10-08
|
||||||
|
|
||||||
|
Перенос связанного содержимого уже агрегируется по типам внутри поколения. Проверен вариант частичного изъятия, исключающий промежуточное сложение квоты с дробным переносом и последующее вычитание целой части. Возрастные интервалы и порядок поколений сохранялись.
|
||||||
|
|
||||||
|
Парный замер на тех же сохранениях: скорость 6, интервал 0,2 секунды, CPU-профиль, без UI; до → после → после → до, по 20 циклов на прогон (40 циклов на версию и сохранение).
|
||||||
|
|
||||||
|
| Сохранение | До, мс/цикл | После, мс/цикл |
|
||||||
|
|---|---:|---:|
|
||||||
|
| Все объекты | 350,516 | 349,835 |
|
||||||
|
| Много объектов и партий | 404,255 | 404,390 |
|
||||||
|
|
||||||
|
Изъятие партий на тяжёлом сохранении: 40,919 → 40,837 мс/цикл. Изменения полного цикла меньше 0,2%; подтверждённого выигрыша нет. Вариант **откачен**, код симуляции оставлен прежним. Полные состояния совпали в 10 последовательных сравнениях; границы партий — 117 проверок без ошибок. Входные файлы не изменены.
|
||||||
|
|
||||||
|
Результаты отклонённого варианта: `batch_withdrawal_step4_results.json`. Этот замер не доказывает достижение пяти полных циклов в секунду.
|
||||||
|
|
||||||
|
## Адаптивное упрощение симуляции — 2026-10-08
|
||||||
|
|
||||||
|
Добавлен runtime-контроллер качества, который измеряет сглаженное CPU-время полного цикла. Он повышает уровень упрощения при превышении бюджета 200 мс и снимает его после 30 циклов с запасом. Режим не сохраняется в файл: после загрузки точность постепенно подстраивается заново.
|
||||||
|
|
||||||
|
При нагрузке цивилизационный поток заменяется приближением не более чем с одним переходом на стадию за интервал. На наиболее грубом уровне типы возрастных партий делятся на две чередующиеся группы: каждая обрабатывается раз в два цикла. Временные группы при этом расширяются. Это сохраняет количества, но сдвигает сроки отдельных переходов и исключает несколько последовательных переходов цивилизации за один интервал.
|
||||||
|
|
||||||
|
Проверены два синтетических сохранения на Apple M4 Pro, Godot 4.7.2, headless, с CPU-профилем и интервалом 0,2 секунды. Каждый замер — 60 последовательных полных циклов; каждый запуск начинается в точном режиме и адаптируется по ходу серии.
|
||||||
|
|
||||||
|
| Скорость | Все объекты, мс/цикл (циклов/с) | Много объектов и партий, мс/цикл (циклов/с) |
|
||||||
|
|---:|---:|---:|
|
||||||
|
| 1 | 165,0 (6,06) | 128,5 (7,78) |
|
||||||
|
| 2 | 181,4 (5,51) | 131,8 (7,59) |
|
||||||
|
| 3 | 175,2 (5,71) | 145,0 (6,90) |
|
||||||
|
| 4 | 159,7 (6,26) | 177,1 (5,65) |
|
||||||
|
| 5 | 174,9 (5,72) | 185,6 (5,39) |
|
||||||
|
| 6 | 190,6 (5,25) | 184,4 (5,42) |
|
||||||
|
|
||||||
|
Среднее время на всех прогонах ниже 200 мс. Отдельные циклы всё ещё бывают длиннее: максимумы составили 203–367 мс на скоростях 3–6, включая первые циклы до срабатывания адаптации. Значит, замеры подтверждают требуемую среднюю пропускную способность на этом компьютере, но не гарантируют отсутствие кратковременных задержек. Android и работа с UI отдельно не проверялись.
|
||||||
|
|
||||||
|
Проверки: адаптивное качество — 18, хронология поколений — 28, границы партий — 117, оптимизации симуляции — 1889, планировщик кадров — 39; ошибок нет. Все 12 конечных диагностических состояний прошли валидацию. Хеши входных файлов: `44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67` и `1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a`.
|
||||||
|
|
||||||
|
Детали уровней, минимумы/максимумы и воспроизводимые результаты: `adaptive_quality_results.json`. Штатный benchmark принимает параметр `speed=1..6`.
|
||||||
|
|||||||
@@ -0,0 +1,646 @@
|
|||||||
|
{
|
||||||
|
"retained": false,
|
||||||
|
"conclusion": "No measurable full-cycle improvement; implementation reverted",
|
||||||
|
"delta_seconds": 0.2,
|
||||||
|
"exact_state_differential": true,
|
||||||
|
"input_sha256": {
|
||||||
|
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||||
|
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||||
|
},
|
||||||
|
"order": "before-after-after-before",
|
||||||
|
"runs": [
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 382.054,
|
||||||
|
"mean_ms": 347.474,
|
||||||
|
"min_ms": 291.952,
|
||||||
|
"model": "before",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
23851,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
121403,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
52415,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
135271,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
880672,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1718363,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
61955,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
673740,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
316094,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
190666,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
545822,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2727124,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
113986,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
121039,
|
||||||
|
3016
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
285729,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
373910,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
358046,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 1,
|
||||||
|
"save": "all_objects_speed6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 382.156,
|
||||||
|
"mean_ms": 349.53305,
|
||||||
|
"min_ms": 297.266,
|
||||||
|
"model": "after",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
23877,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
121854,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
53021,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
137725,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
883114,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1733356,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
62368,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
680005,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
319290,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
192497,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
548252,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2737148,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
115930,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
121459,
|
||||||
|
3016
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
287621,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
376359,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
359971,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 1,
|
||||||
|
"save": "all_objects_speed6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 387.438,
|
||||||
|
"mean_ms": 350.1367,
|
||||||
|
"min_ms": 298.254,
|
||||||
|
"model": "after",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
24181,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
122008,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
53315,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
137169,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
885851,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1736978,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
62264,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
680485,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
319558,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
193735,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
547473,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2743162,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
115720,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
121401,
|
||||||
|
3016
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
287832,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
376295,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
360758,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 2,
|
||||||
|
"save": "all_objects_speed6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 391.136,
|
||||||
|
"mean_ms": 353.55755,
|
||||||
|
"min_ms": 295.752,
|
||||||
|
"model": "before",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
23999,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
125136,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
53681,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
137614,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
893234,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1749507,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
63052,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
683642,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
319168,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
193901,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
550377,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2768999,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
115976,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
122734,
|
||||||
|
3016
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
292064,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
382112,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
363057,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 2,
|
||||||
|
"save": "all_objects_speed6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 418.952,
|
||||||
|
"mean_ms": 402.88125,
|
||||||
|
"min_ms": 362.784,
|
||||||
|
"model": "before",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
18182,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
138911,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
235353,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
139454,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
1808094,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1823967,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
63710,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
684725,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
241433,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
79394,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
814774,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2390414,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
142892,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
55241,
|
||||||
|
1490
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
359891,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
410388,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
362721,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 1,
|
||||||
|
"save": "many_objects_fragmented_stars_cycle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 425.202,
|
||||||
|
"mean_ms": 404.3497,
|
||||||
|
"min_ms": 355.885,
|
||||||
|
"model": "after",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
18056,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
139522,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
236599,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
140701,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
1809113,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1824567,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
63452,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
684152,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
242790,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
80500,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
817704,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2406807,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
143427,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
55885,
|
||||||
|
1490
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
363175,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
412428,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
365215,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 1,
|
||||||
|
"save": "many_objects_fragmented_stars_cycle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 418.858,
|
||||||
|
"mean_ms": 404.4306,
|
||||||
|
"min_ms": 357.106,
|
||||||
|
"model": "after",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
18133,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
139021,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
237100,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
139480,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
1815717,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1825515,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
63298,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
685950,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
242932,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
79477,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
815781,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2400821,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
144101,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
56140,
|
||||||
|
1490
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
362394,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
412986,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
364511,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 2,
|
||||||
|
"save": "many_objects_fragmented_stars_cycle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cycles": 20,
|
||||||
|
"max_ms": 422.492,
|
||||||
|
"mean_ms": 405.6284,
|
||||||
|
"min_ms": 355.699,
|
||||||
|
"model": "before",
|
||||||
|
"profile": {
|
||||||
|
"assign_homes": [
|
||||||
|
18208,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"civilization_flow_capacity": [
|
||||||
|
139305,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_colonies": [
|
||||||
|
236973,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_finish": [
|
||||||
|
141048,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_ledger": [
|
||||||
|
1818465,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_flow_plan": [
|
||||||
|
1832211,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_build": [
|
||||||
|
63519,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"civilization_operator_solve": [
|
||||||
|
688184,
|
||||||
|
860
|
||||||
|
],
|
||||||
|
"enforce_capacity": [
|
||||||
|
243601,
|
||||||
|
60
|
||||||
|
],
|
||||||
|
"generation_products": [
|
||||||
|
79839,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"generation_withdrawal": [
|
||||||
|
822003,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"generations": [
|
||||||
|
2410757,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"initialize": [
|
||||||
|
144424,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"newborn_batches": [
|
||||||
|
55500,
|
||||||
|
1490
|
||||||
|
],
|
||||||
|
"physical_amounts_build": [
|
||||||
|
363454,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"production": [
|
||||||
|
412711,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"trends": [
|
||||||
|
367008,
|
||||||
|
20
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"run": 2,
|
||||||
|
"save": "many_objects_fragmented_stars_cycle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"speed": 6
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ func restore_json_integers(value):
|
|||||||
func run():
|
func run():
|
||||||
var args = OS.get_cmdline_user_args()
|
var args = OS.get_cmdline_user_args()
|
||||||
if args.is_empty():
|
if args.is_empty():
|
||||||
print("Usage: --script tests/benchmark_civilization_flows.gd -- /path/to/save.adventure.json [delta_seconds] [profile] [cycles=100]")
|
print("Usage: --script tests/benchmark_civilization_flows.gd -- /path/to/save.adventure.json [delta_seconds] [profile] [cycles=100] [speed=1..6]")
|
||||||
quit(1)
|
quit(1)
|
||||||
return
|
return
|
||||||
var data = JSON.parse_string(FileAccess.get_file_as_string(args[0]))
|
var data = JSON.parse_string(FileAccess.get_file_as_string(args[0]))
|
||||||
@@ -33,7 +33,11 @@ func run():
|
|||||||
if not is_finite(delta) or delta <= 0:
|
if not is_finite(delta) or delta <= 0:
|
||||||
quit(1)
|
quit(1)
|
||||||
return
|
return
|
||||||
for speed in [6]:
|
var selected_speed: int = 6
|
||||||
|
for arg in args:
|
||||||
|
if arg.begins_with("speed="):
|
||||||
|
selected_speed = clampi(arg.trim_prefix("speed=").to_int(), 1, root.get_node("Global").SPEED_LIMIT)
|
||||||
|
for speed in [selected_speed]:
|
||||||
var world = load("res://world/owniverse/owniverse.gd").new()
|
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||||
world.catalog.load_definitions(root.get_node("Global").OwniverseEntities)
|
world.catalog.load_definitions(root.get_node("Global").OwniverseEntities)
|
||||||
world.state = initial.duplicate(true)
|
world.state = initial.duplicate(true)
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
var checks: int = 0
|
||||||
|
var failures: int = 0
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("run")
|
||||||
|
|
||||||
|
func check(condition: bool, message: String) -> void:
|
||||||
|
checks += 1
|
||||||
|
if not condition:
|
||||||
|
failures += 1
|
||||||
|
push_error(message)
|
||||||
|
|
||||||
|
func run() -> void:
|
||||||
|
var world = load("res://world/owniverse/owniverse.gd").new()
|
||||||
|
world.update_adaptive_quality(250000)
|
||||||
|
check(world.adaptive_quality == 2, "A slow cycle selects the medium approximation")
|
||||||
|
world.update_adaptive_quality(250000)
|
||||||
|
check(world.adaptive_quality == 3, "A slow medium-approximation cycle selects the coarse mode")
|
||||||
|
check(world.adaptive_generation_phase == 1, "Coarse generation phase alternates")
|
||||||
|
for i in 36: world.update_adaptive_quality(100000)
|
||||||
|
check(world.adaptive_quality == 2, "Thirty consecutive recovery cycles lower quality by one level")
|
||||||
|
for i in 30: world.update_adaptive_quality(100000)
|
||||||
|
check(world.adaptive_quality == 1, "Sustained headroom lowers quality gradually")
|
||||||
|
for i in 30: world.update_adaptive_quality(100000)
|
||||||
|
check(world.adaptive_quality == 0, "The exact mode returns after sustained headroom")
|
||||||
|
|
||||||
|
var rows: Array = []
|
||||||
|
for node in 12:
|
||||||
|
var edges: Dictionary = {}
|
||||||
|
var outgoing: float = 0.0
|
||||||
|
if node < 6:
|
||||||
|
edges[node + 6] = 0.25
|
||||||
|
outgoing = 0.25
|
||||||
|
rows.append({"edges": edges, "outgoing": outgoing})
|
||||||
|
var generator: Dictionary = {"rows": rows, "index": {"source": 0}}
|
||||||
|
var solved: Dictionary = world.civilization_flow._solve_single_step(generator, "source", 2.0)
|
||||||
|
for ethic in 6:
|
||||||
|
var mass: float = 0.0
|
||||||
|
var occupation: float = 0.0
|
||||||
|
for node in range(ethic, 12, 6):
|
||||||
|
mass += solved.mass[node]
|
||||||
|
occupation += solved.occupation[node]
|
||||||
|
check(absf(mass - 1.0) < 1e-12, "Approximate transition conserves ethic mass %d" % ethic)
|
||||||
|
check(absf(occupation - 2.0) < 1e-12, "Approximate transition conserves integrated time %d" % ethic)
|
||||||
|
|
||||||
|
world.free()
|
||||||
|
print("Adaptive quality: %d checks, %d failures" % [checks, failures])
|
||||||
|
quit(1 if failures else 0)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://ct356urggcgal
|
||||||
@@ -117,13 +117,19 @@ func run():
|
|||||||
behind.evolute_frame(0.0, self)
|
behind.evolute_frame(0.0, self)
|
||||||
check(behind.frame_commits == 1 and not behind.frame_busy, "Consumed interval cannot be processed twice")
|
check(behind.frame_commits == 1 and not behind.frame_busy, "Consumed interval cannot be processed twice")
|
||||||
behind.evolute_frame(0.23, self)
|
behind.evolute_frame(0.23, self)
|
||||||
behind.evolute_frame(0.53, self)
|
var remained_busy: bool = behind.frame_busy
|
||||||
|
if remained_busy: behind.evolute_frame(0.53, self)
|
||||||
var pending_clock: Dictionary = behind.frame_clock.duplicate()
|
var pending_clock: Dictionary = behind.frame_clock.duplicate()
|
||||||
|
if remained_busy:
|
||||||
|
check(E.eq(behind.frame_active_clock.datetime, 123000), "Busy transaction reserves its original target")
|
||||||
while behind.frame_busy: await process_frame
|
while behind.frame_busy: await process_frame
|
||||||
|
if remained_busy:
|
||||||
check(E.eq(behind.state.time.datetime, 123000), "Busy transaction keeps its original target")
|
check(E.eq(behind.state.time.datetime, 123000), "Busy transaction keeps its original target")
|
||||||
behind.evolute_frame(0.0, self)
|
behind.evolute_frame(0.0, self)
|
||||||
while behind.frame_busy: await process_frame
|
while behind.frame_busy: await process_frame
|
||||||
check(E.eq(behind.state.time, pending_clock), "Next cycle consumes all time accumulated during computation")
|
check(E.eq(behind.state.time, pending_clock), "Next cycle consumes all time accumulated during computation")
|
||||||
|
else:
|
||||||
|
check(E.eq(behind.state.time, pending_clock), "Completed transaction publishes the full requested interval")
|
||||||
dispose(behind)
|
dispose(behind)
|
||||||
print("Frame evolution: %d checks, %d failures" % [checks, failures])
|
print("Frame evolution: %d checks, %d failures" % [checks, failures])
|
||||||
quit(1 if failures else 0)
|
quit(1 if failures else 0)
|
||||||
|
|||||||
@@ -100,11 +100,12 @@ func _can_progress(source: String, destination: String, budget: Dictionary, rule
|
|||||||
## Uniformization computes exp(Q*t) through a Poisson series of sparse steps.
|
## Uniformization computes exp(Q*t) through a Poisson series of sparse steps.
|
||||||
## Absorbing biological stages and Ct retain their mass. No one-transition cap.
|
## Absorbing biological stages and Ct retain their mass. No one-transition cap.
|
||||||
func _solve(generator: Dictionary, source: String, profile: Dictionary, years: float) -> Dictionary:
|
func _solve(generator: Dictionary, source: String, profile: Dictionary, years: float) -> Dictionary:
|
||||||
var key: String = source + ":" + str(years)
|
var approximation: int = world.adaptive_quality
|
||||||
|
var key: String = source + ":" + str(years) + ":" + str(approximation)
|
||||||
var basis: Dictionary = _solutions.get(key, {})
|
var basis: Dictionary = _solutions.get(key, {})
|
||||||
if basis.is_empty():
|
if basis.is_empty():
|
||||||
var started: int = world.profile_start()
|
var started: int = world.profile_start()
|
||||||
basis = _solve_basis(generator, source, years)
|
basis = _solve_single_step(generator, source, years) if approximation > 0 else _solve_basis(generator, source, years)
|
||||||
world.profile_end("civilization_operator_solve", started)
|
world.profile_end("civilization_operator_solve", started)
|
||||||
if _solutions.size() >= 512: _solutions.clear()
|
if _solutions.size() >= 512: _solutions.clear()
|
||||||
_solutions[key] = basis
|
_solutions[key] = basis
|
||||||
@@ -117,6 +118,35 @@ func _solve(generator: Dictionary, source: String, profile: Dictionary, years: f
|
|||||||
occupation[node] *= shares[node % 6]
|
occupation[node] *= shares[node % 6]
|
||||||
return {"mass": mass, "occupation": occupation, "occupation_scale": 1.0}
|
return {"mass": mass, "occupation": occupation, "occupation_scale": 1.0}
|
||||||
|
|
||||||
|
## Load-shedding approximation: each ethic cohort can make at most one
|
||||||
|
## transition during this interval. The exact Markov solve remains the default.
|
||||||
|
func _solve_single_step(generator: Dictionary, source: String, years: float) -> Dictionary:
|
||||||
|
var size: int = generator.rows.size()
|
||||||
|
var mass: PackedFloat64Array = []
|
||||||
|
var occupation: PackedFloat64Array = []
|
||||||
|
mass.resize(size)
|
||||||
|
occupation.resize(size)
|
||||||
|
var offset: int = generator.index[source] * 6
|
||||||
|
for ethic in 6:
|
||||||
|
var node: int = offset + ethic
|
||||||
|
var row: Dictionary = generator.rows[node]
|
||||||
|
var outgoing: float = row.outgoing
|
||||||
|
if outgoing <= 0.0:
|
||||||
|
mass[node] = 1.0
|
||||||
|
occupation[node] = years
|
||||||
|
continue
|
||||||
|
var moved: float = clampf(1.0 - exp(-outgoing * years), 0.0, 1.0)
|
||||||
|
var stayed: float = 1.0 - moved
|
||||||
|
mass[node] += stayed
|
||||||
|
occupation[node] += years * (1.0 - moved * 0.5)
|
||||||
|
for destination in row.edges:
|
||||||
|
var rate: float = row.edges[destination]
|
||||||
|
if rate <= 0.0: continue
|
||||||
|
var share: float = moved * rate / outgoing
|
||||||
|
mass[destination] += share
|
||||||
|
occupation[destination] += years * share * 0.5
|
||||||
|
return {"mass": mass, "occupation": occupation}
|
||||||
|
|
||||||
func _solve_basis(generator: Dictionary, source: String, years: float) -> Dictionary:
|
func _solve_basis(generator: Dictionary, source: String, years: float) -> Dictionary:
|
||||||
var size: int = generator.rows.size()
|
var size: int = generator.rows.size()
|
||||||
var values: PackedFloat64Array = []
|
var values: PackedFloat64Array = []
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ func run_frame(clock: Dictionary, budget: RefCounted) -> void:
|
|||||||
|
|
||||||
func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefCounted = null) -> void:
|
func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefCounted = null) -> void:
|
||||||
var _keep_alive: RefCounted = self
|
var _keep_alive: RefCounted = self
|
||||||
|
var adaptive_started: int = world.adaptive_timer_usec()
|
||||||
# Reconcile only when simulation advances.
|
# Reconcile only when simulation advances.
|
||||||
# Fractional frame updates cannot change capacity or populations.
|
# Fractional frame updates cannot change capacity or populations.
|
||||||
var processed_year: Variant = SimulationTime.processed_year(datetime)
|
var processed_year: Variant = SimulationTime.processed_year(datetime)
|
||||||
@@ -77,6 +78,7 @@ func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefC
|
|||||||
world.state = E.bound_state(next)
|
world.state = E.bound_state(next)
|
||||||
if E.gt(to_year, from_year):
|
if E.gt(to_year, from_year):
|
||||||
world.presentation.refresh(world.state, world.catalog)
|
world.presentation.refresh(world.state, world.catalog)
|
||||||
|
world.update_adaptive_quality(world.adaptive_timer_usec() - adaptive_started)
|
||||||
|
|
||||||
|
|
||||||
func evolve_batches(target: Dictionary, to_year: Variant, budget: RefCounted = null) -> void:
|
func evolve_batches(target: Dictionary, to_year: Variant, budget: RefCounted = null) -> void:
|
||||||
@@ -88,7 +90,15 @@ func evolve_batches(target: Dictionary, to_year: Variant, budget: RefCounted = n
|
|||||||
"enlightenment": WorldParameters.value(target, "Enlightenment"),
|
"enlightenment": WorldParameters.value(target, "Enlightenment"),
|
||||||
"explosiveness": WorldParameters.effective(target, "Exp")
|
"explosiveness": WorldParameters.effective(target, "Exp")
|
||||||
}
|
}
|
||||||
|
var generations: int = 0
|
||||||
|
var generation_limit: int = 0
|
||||||
|
if world.adaptive_quality == 1: generation_limit = 3
|
||||||
|
elif world.adaptive_quality >= 2: generation_limit = 1
|
||||||
while await evolve_batch_generation(target, to_year, completed, budget, context):
|
while await evolve_batch_generation(target, to_year, completed, budget, context):
|
||||||
|
generations += 1
|
||||||
|
# Direct terminal stellar events queued during a generation still run
|
||||||
|
# immediately, preserving their blast and matter outputs.
|
||||||
|
if generation_limit > 0 and generations >= generation_limit and completed.is_empty(): break
|
||||||
if budget != null and not await budget.pause(): return
|
if budget != null and not await budget.pause(): return
|
||||||
|
|
||||||
|
|
||||||
@@ -108,6 +118,16 @@ func evolve_batch_generation(target: Dictionary, to_year: Variant, completed: Di
|
|||||||
var batch_ids: Array = []
|
var batch_ids: Array = []
|
||||||
for id in target.batches:
|
for id in target.batches:
|
||||||
if not Global.CivilizationRules.has(id): batch_ids.append(id)
|
if not Global.CivilizationRules.has(id): batch_ids.append(id)
|
||||||
|
if world.adaptive_quality >= 3:
|
||||||
|
batch_ids.sort()
|
||||||
|
var selected_ids: Array = []
|
||||||
|
for i in batch_ids.size():
|
||||||
|
if i % 2 == world.adaptive_generation_phase or completed.has(batch_ids[i]):
|
||||||
|
selected_ids.append(batch_ids[i])
|
||||||
|
for id in completed:
|
||||||
|
if not Global.CivilizationRules.has(id) and not selected_ids.has(id):
|
||||||
|
selected_ids.append(id)
|
||||||
|
batch_ids = selected_ids
|
||||||
var planets_changed: bool = false
|
var planets_changed: bool = false
|
||||||
var profile_withdraw: int = world.profile_start()
|
var profile_withdraw: int = world.profile_start()
|
||||||
for id in batch_ids:
|
for id in batch_ids:
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
|
|||||||
|
|
||||||
## Diagnostic switch for paired measurements of the old and new timing models.
|
## Diagnostic switch for paired measurements of the old and new timing models.
|
||||||
var cycle_stars: bool = true
|
var cycle_stars: bool = true
|
||||||
var _cycle_widths: Dictionary = {}
|
|
||||||
|
|
||||||
func star_cycle_width(target: Dictionary) -> Variant:
|
func star_cycle_width(target: Dictionary) -> Variant:
|
||||||
var speed: int = int(target.get("speed", 1))
|
var speed: int = int(target.get("speed", 1))
|
||||||
if not _cycle_widths.has(speed):
|
var width: Variant = E.multiply(E.power(10, speed - 1), world.frame_step_seconds)
|
||||||
_cycle_widths[speed] = E.multiply(E.power(10, speed - 1), world.frame_step_seconds)
|
return E.multiply(width, E.power(2, world.adaptive_quality))
|
||||||
return _cycle_widths[speed]
|
|
||||||
|
|
||||||
## The cycle grid may widen a short window, never subdivide a wide one.
|
## The cycle grid may widen a short window, never subdivide a wide one.
|
||||||
func uses_cycle_star(target: Dictionary, id: String, lifespan: Variant) -> bool:
|
func uses_cycle_star(target: Dictionary, id: String, lifespan: Variant) -> bool:
|
||||||
@@ -70,7 +68,9 @@ func coarsen_cycle_stars(target: Dictionary, id: String) -> void:
|
|||||||
|
|
||||||
func temporal_resolution(target: Dictionary) -> Variant:
|
func temporal_resolution(target: Dictionary) -> Variant:
|
||||||
var speed: int = int(target.get("speed", 1))
|
var speed: int = int(target.get("speed", 1))
|
||||||
return E.divide(E.power(10, speed - 1), 10) if speed >= 4 else 0
|
if speed < 4: return 0
|
||||||
|
var width: Variant = E.divide(E.power(10, speed - 1), 10)
|
||||||
|
return E.multiply(width, E.power(2, world.adaptive_quality))
|
||||||
|
|
||||||
func window_duration(target: Dictionary, lifespan: Variant) -> Variant:
|
func window_duration(target: Dictionary, lifespan: Variant) -> Variant:
|
||||||
if E.le(lifespan, 10): return lifespan
|
if E.le(lifespan, 10): return lifespan
|
||||||
|
|||||||
@@ -17,6 +17,39 @@ var evolution = preload("res://world/owniverse/evolution_coordinator.gd").new(se
|
|||||||
var civilization_flow = preload("res://world/owniverse/civilization_flow.gd").new(self)
|
var civilization_flow = preload("res://world/owniverse/civilization_flow.gd").new(self)
|
||||||
var profile_enabled: bool = false
|
var profile_enabled: bool = false
|
||||||
var profile: Dictionary = {}
|
var profile: Dictionary = {}
|
||||||
|
## Runtime-only quality controller. It is deliberately not serialized.
|
||||||
|
var adaptive_quality: int = 0
|
||||||
|
var adaptive_cycle_ema_ms: float = 0.0
|
||||||
|
var adaptive_recovery_cycles: int = 0
|
||||||
|
var adaptive_generation_phase: int = 0
|
||||||
|
|
||||||
|
func update_adaptive_quality(cycle_usec: int) -> void:
|
||||||
|
var cycle_ms: float = float(cycle_usec) / 1000.0
|
||||||
|
adaptive_cycle_ema_ms = cycle_ms if adaptive_cycle_ema_ms <= 0.0 else adaptive_cycle_ema_ms * 0.8 + cycle_ms * 0.2
|
||||||
|
if adaptive_cycle_ema_ms > 300.0:
|
||||||
|
adaptive_quality = 3
|
||||||
|
adaptive_recovery_cycles = 0
|
||||||
|
elif adaptive_quality >= 2 and adaptive_cycle_ema_ms > 200.0:
|
||||||
|
adaptive_quality = 3
|
||||||
|
adaptive_recovery_cycles = 0
|
||||||
|
elif adaptive_cycle_ema_ms > 220.0:
|
||||||
|
adaptive_quality = maxi(adaptive_quality, 2)
|
||||||
|
adaptive_recovery_cycles = 0
|
||||||
|
elif adaptive_cycle_ema_ms > 200.0:
|
||||||
|
adaptive_quality = maxi(adaptive_quality, 1)
|
||||||
|
adaptive_recovery_cycles = 0
|
||||||
|
elif adaptive_cycle_ema_ms < 140.0:
|
||||||
|
adaptive_recovery_cycles += 1
|
||||||
|
if adaptive_recovery_cycles >= 30:
|
||||||
|
adaptive_quality = maxi(0, adaptive_quality - 1)
|
||||||
|
adaptive_recovery_cycles = 0
|
||||||
|
else:
|
||||||
|
adaptive_recovery_cycles = 0
|
||||||
|
if adaptive_quality >= 3:
|
||||||
|
adaptive_generation_phase = 1 - adaptive_generation_phase
|
||||||
|
|
||||||
|
func adaptive_timer_usec() -> int:
|
||||||
|
return Time.get_ticks_usec() - (frame_budget.waiting_usec if frame_busy and frame_budget != null else 0)
|
||||||
|
|
||||||
func profile_start() -> int:
|
func profile_start() -> int:
|
||||||
if not profile_enabled: return 0
|
if not profile_enabled: return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user