16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 01:59:19 +02:00

Stream visual scenery in the driver (play starts after infrastructure pass)

Splits the progressive load across modes: the loader runs only the first
(infrastructure) pass and then hands off to the driver, so play begins as soon
as tracks/traction/events/signals/the player train are ready. The deferred
visual nodes (3d models, terrain shapes/lines) then stream in from the driver,
a small budget per frame, so the world fills in without a long up-front wait.

- state_serializer: the infrastructure pass now returns to the caller (instead of
  chaining the visual pass), restarting the twin for the visual pass; the visual
  pass uses a small ~8 ms/frame budget and marks the load done at the end. A
  text/compile load (no twin) stays single-pass and finishes as before.
- state_manager: retains the load state across the loader->driver hand-off and
  exposes loading_visuals()/continue_loading_visuals().
- driver_mode::update(): advances the deferred visual load each frame.

Verified on td.scn (playable in ~2 s, visuals complete a few seconds later, no
duplicate objects) and on the large tomaszewo scenery (flora/terrain/models
deferred while infrastructure loads). Inserts run on the main thread before the
render step, so progressive loading needs no locking.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
maj00r
2026-06-22 23:23:36 +02:00
parent fd8bfdbcf3
commit 512595234f
5 changed files with 46 additions and 6 deletions

View File

@@ -148,7 +148,10 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
}
auto timenow = std::chrono::steady_clock::now();
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= 200 ) {
// small per-frame budget while streaming visuals in the driver (avoid stutter),
// generous budget while the loading screen is up (infrastructure pass)
auto const budget = ( state->visualphase ? 8 : 200 );
if( std::chrono::duration_cast<std::chrono::milliseconds>( timenow - timelast ).count() >= budget ) {
Application.set_progress( Input.getProgress(), Input.getFullProgress() );
return true;
}
@@ -161,13 +164,15 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
deserialize_firstinit( Input, Scratchpad );
}
// first (infrastructure) pass finished: run a second pass over the same twin to load
// the visual nodes that were skipped. only possible when replaying a binary twin; a
// text/compile load did everything in one pass (restartReplay returns false).
// first (infrastructure) pass finished: the scenario is now playable (tracks, events,
// signals, the player train are all loaded). hand control back so the loader can switch
// to the driver; the visual nodes load progressively from the driver via a second pass
// over the same twin. only possible when replaying a binary twin -- a text/compile load
// did everything in one pass (restartReplay returns false).
if( ( false == state->visualphase )
&& ( true == Input.restartReplay( scene::scenery_load_pass::visual ) ) ) {
state->visualphase = true;
return true; // continue with the visual pass
return false; // infrastructure ready -> go to driver; visuals continue there
}
scene::Groups.close();
@@ -182,6 +187,7 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
// before we report the scenario as loaded
scene::scenerybinary_wait_all();
state->done = true;
return false;
}