mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
181 lines
9.6 KiB
Markdown
181 lines
9.6 KiB
Markdown
# Large map performance
|
||
|
||
The September 6, 2026 performance pass preserves the full render geometry and
|
||
collision data. It does not add LODs or simplify physics.
|
||
|
||
Changes:
|
||
|
||
- A bounds hierarchy selects collision clusters instead of scanning all clusters.
|
||
Results retain their original order, including equal-distance hits.
|
||
- Conservative triangle bounds reject impossible line and primitive contacts
|
||
before the expensive narrowphase. Predictive separation and fatness are included.
|
||
- Byte-identical textures and equivalent rendered materials share resources and
|
||
batches. Texture color space and all currently rendered material fields remain
|
||
part of their identity. Every source render triangle is retained.
|
||
- Render meshes and textures release their CPU copies after GPU upload. The
|
||
decoded map package is released after physics and rendering have consumed it.
|
||
- The normal development build now optimizes project code at level 3, matching
|
||
the existing dependency optimization level.
|
||
|
||
## Repeatable measurement
|
||
|
||
From the project directory in PowerShell:
|
||
|
||
```powershell
|
||
./Build.ps1
|
||
$env:SKATE_PERF_REPORT = 'logs/performance.json'
|
||
./bin/skate-game.exe --assets assets --map maps/Skate_2_New_San_Vanelona.skate
|
||
Remove-Item Env:SKATE_PERF_REPORT
|
||
```
|
||
|
||
The benchmark runs continuously regardless of window focus, waits 10 seconds
|
||
for warmup, records roughly 15 seconds, writes JSON, and exits. Leave the player
|
||
stationary for spawn comparisons. The environment variable enables instrumentation;
|
||
normal launches do not collect reports or change their window-focus behavior.
|
||
Frame intervals include renderer synchronization. Main schedule and render timings
|
||
overlap and must not be added together. Detailed physics section timings include
|
||
warmup and nested scopes; these must not be summed either.
|
||
|
||
Local San Vanelona measurements at 1280 x 800, stationary spawn, RTX 5090:
|
||
|
||
| Metric | Before | After |
|
||
| --- | ---: | ---: |
|
||
| Average FPS | 43.6 | 50.7 |
|
||
| Main schedule per frame | 10.56 ms | 2.39 ms |
|
||
| Physics per tick | 6.44 ms | 0.74 ms |
|
||
| Frame interval p95 | 128.1 ms | 94.5 ms |
|
||
|
||
Sources are local ignored reports `logs/perf-baseline-continuous.json` and
|
||
`logs/perf-render-detail.json`. Repeated optimized runs averaged 49–52 FPS.
|
||
Process working set fell from about 10 GB to roughly 1–2.3 GB after loading.
|
||
These are spawn measurements, not a reproduction of the reported 10 FPS camera
|
||
position. Render preparation remains the largest measured cost, and frame spikes
|
||
remain. Low total CPU/GPU utilization does not exclude a CPU render bottleneck.
|
||
|
||
Spatial render splitting was also tested, but increased entity/batch overhead and
|
||
reduced FPS on this map, so it is not included.
|
||
|
||
Validation includes the full skate-core suite and game suite (43 passed, 21 existing
|
||
ignored tests), differential collision queries against the unfiltered implementation
|
||
across 420 varied poses, material/texture identity checks, and exact render triangle
|
||
retention tests. Startup capture checks rendering/integration, not retail parity.
|
||
|
||
## Follow-up: Bevy lightmap binding cache
|
||
|
||
GPU diagnostics confirmed the RTX 5090 Vulkan adapter with binding arrays,
|
||
non-uniform indexing, and indirect drawing enabled. Local source inspection and
|
||
profiling found repeated lightmap bind-group creation in Bevy's
|
||
`prepare_mesh_bind_groups`. The [tracked Bevy patch](../vendor/README.md) caches
|
||
those bindings with resource-identity checks and bounded retention.
|
||
|
||
Same stationary-spawn benchmark, before and after this patch:
|
||
|
||
| Metric | Before cache | After cache |
|
||
| --- | ---: | ---: |
|
||
| Average FPS | 52.3 | 237.1 |
|
||
| Frame interval p95 | 94.5 ms | 5.15 ms |
|
||
| Render preparation per frame | 15.52 ms | 1.19 ms |
|
||
| Mesh binding interval per call, including warmup | 10.39 ms | 0.66 ms |
|
||
|
||
Reports: `logs/perf-bindings.json` and `logs/perf-cache-repeat.json`. The first
|
||
cache trial averaged 158 FPS, but overlapped test compilation and is excluded
|
||
from the clean comparison. The normal window presentation settings are unchanged.
|
||
GPU telemetry during the clean repeat sampled roughly 23–33% usage, compared
|
||
with the earlier single-digit readings. Full utilization is not the goal: this
|
||
GPU can render this view at high FPS without approaching full utilization.
|
||
|
||
An additional render-only camera sweep averaged 91.1 FPS, with a 24.9 ms p95
|
||
frame interval (`logs/perf-cache-sweep.json`). This rotates through different
|
||
views to exercise changing visibility and newly extracted lightmaps; it does not
|
||
move the skater or alter collision behavior. Enable it alongside the usual
|
||
benchmark variable with `$env:SKATE_PERF_CAMERA_SWEEP = '1'`, and remove it
|
||
afterwards with `Remove-Item Env:SKATE_PERF_CAMERA_SWEEP`. It only runs when
|
||
`SKATE_PERF_REPORT` also enables the benchmark plugin.
|
||
|
||
The sweep is a different workload, not an FPS comparison against the stationary
|
||
baseline. Exploration can still encounter heavier views and loading/shader spikes.
|
||
Validation: GPU cache regression test passed, game suite passed (43 passed,
|
||
21 existing ignored), and the staged executable completed a normal city startup
|
||
capture with benchmark settings disabled.
|
||
|
||
## City-facing dips at 1440p / 8x MSAA
|
||
|
||
The next pass uses the user's saved 2560 x 1440, 100% internal resolution,
|
||
8x MSAA, unlimited FPS settings and the same rotating-camera benchmark.
|
||
Do not compare these results directly with the earlier stationary 1280 x 800 run.
|
||
|
||
| Metric | Before | After |
|
||
| --- | ---: | ---: |
|
||
| Average FPS | 84.9 | 119.1 |
|
||
| Frame interval p95 | 28.9 ms | 23.5 ms |
|
||
| Render preparation per frame | 5.42 ms | 1.49 ms |
|
||
|
||
Reports: `logs/city-baseline.json` and `logs/city-double.json`. Temporary direct
|
||
instrumentation measured roughly 3–4 ms in mesh binding preparation before the
|
||
final fix and about 0.05 ms after warmup. GPU telemetry during the optimized run
|
||
sampled roughly 24–39% usage. Heavy views and frame spikes remain.
|
||
The final clean build, with temporary tracing and GPU telemetry polling removed,
|
||
averaged 145.0 FPS with an 18.9 ms p95 frame interval (`logs/city-final.json`).
|
||
The 119–145 FPS range illustrates run/instrumentation variation; it is not a
|
||
guaranteed minimum while exploring the whole map.
|
||
|
||
Bevy's `collect_buffers_for_phase` exchanges two buffer allocations every frame.
|
||
A single-table cache therefore misses every frame even with static resource
|
||
contents. The patch now retains both phase tables, checks resource identities
|
||
and lightmap revisions, and shares unchanged tables without visiting every slab.
|
||
The preceding one-table trial did not materially improve the sweep and is not
|
||
the implementation shipped here.
|
||
|
||
The game also reserves geometrically growing instance-buffer capacity before
|
||
Bevy writes it, bounded by device limits. This reduces reallocations when visible
|
||
counts increase; it does not change logical counts or dispatch extra instances.
|
||
Mesh morph-target lookup is refreshed on GPU asset changes, and identical PBR
|
||
materials can share handles while each mesh retains its own baked lightmap.
|
||
|
||
No triangles, collision detail, lighting, or draw distance were removed. LODs
|
||
remain a possible next step for distant geometry, particularly hierarchical LODs
|
||
that also reduce the number of draws. They do not address the buffer-cache miss
|
||
fixed here. GPU occlusion culling is also relevant to a dense city, but the current
|
||
engine's version is experimental: [Bevy's migration notes](https://bevy.org/learn/migration-guides/0-18-to-0-19/#occlusion-culling-is-no-longer-experimental)
|
||
describe correctness fixes in 0.19. It was not enabled as an unverified workaround.
|
||
|
||
Validation: the explicit GPU regression test and game suite (46 passed,
|
||
21 existing ignored) passed. The staged build completed the normal San Vanelona
|
||
startup capture at the user's saved settings, with benchmark overrides disabled.
|
||
|
||
## Runtime occlusion culling
|
||
|
||
The Escape graphics menu now supports GPU occlusion culling, default On. Existing
|
||
settings files without that field pick up the default. `SKATE_OCCLUSION=0` or `1`
|
||
overrides the saved value at launch for A/B runs. The two-phase GPU algorithm
|
||
uses a depth prepass and dynamically rejects hidden meshes; no map baking, LOD
|
||
generation, distance cutoff, or collision changes are involved.
|
||
|
||
This backports [Bevy #22603](https://github.com/bevyengine/bevy/pull/22603) to
|
||
0.18.1's core pipeline: conservative depth-pyramid sampling at non-power-of-two
|
||
resolutions. It also fixes a reproduced GPU validation failure in 0.18.1's late
|
||
preprocessing pass by making its indirect argument buffer binding read-only.
|
||
Details and provenance are in `vendor/README.md`.
|
||
|
||
Same saved 1440p / 100% / 8x MSAA settings, rotating view, no concurrent builds:
|
||
|
||
| Trial | Average FPS | Frame interval p95 |
|
||
| --- | ---: | ---: |
|
||
| Original material batches, culling Off | 124.1 | 22.1 ms |
|
||
| Original material batches, culling On | 128.0 | 21.3 ms |
|
||
| 128-unit spatial batches, culling On | 72.4 | 51.2 ms |
|
||
|
||
Reports: `logs/cull-off.json`, `logs/cull-on2.json`, `logs/cull-cell128.json`.
|
||
The small On/Off difference is within plausible run variation, not evidence of
|
||
a major FPS win. CPU/render submission overhead remains significant even when
|
||
the GPU skips hidden geometry. These sweeps are not a minimum-FPS guarantee.
|
||
Spatial splitting increased batches from 30,097 to 55,850 and was removed from
|
||
the final implementation. All 8,214,063 render triangles remain available.
|
||
|
||
Validation: city sweep and normal startup capture completed without GPU errors
|
||
at 8x MSAA. The game suite has 47 passing tests and 21 pre-existing ignored tests,
|
||
including changing culling, MSAA and internal resolution together. The explicit
|
||
GPU lightmap cache regression test also passed. The startup
|
||
image was visually inspected. Manual menu testing was interrupted by the user
|
||
stopping Computer Use, so live UI toggling has not been visually verified.
|