mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 20:59:19 +02:00
Tune EU7 PACK streaming for fast camera flight and reduce frame hitches
This commit is contained in:
@@ -404,8 +404,6 @@ bool driver_mode::update()
|
|||||||
|
|
||||||
Timer::subsystem.sim_total.stop();
|
Timer::subsystem.sim_total.stop();
|
||||||
|
|
||||||
simulation::State.drain_deferred_eu7_trainsets( 12.0 );
|
|
||||||
|
|
||||||
if( scene::eu7::section_stream_active() ) {
|
if( scene::eu7::section_stream_active() ) {
|
||||||
if( scene::eu7::section_stream_needs_bootstrap() ) {
|
if( scene::eu7::section_stream_needs_bootstrap() ) {
|
||||||
scene::eu7::kick_section_stream_bootstrap();
|
scene::eu7::kick_section_stream_bootstrap();
|
||||||
@@ -424,6 +422,12 @@ bool driver_mode::update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
simulation::State.drain_deferred_eu7_trainsets(
|
||||||
|
scene::eu7::section_stream_active()
|
||||||
|
&& scene::eu7::section_stream_frame_budget_pressure() ?
|
||||||
|
4.0 :
|
||||||
|
10.0 );
|
||||||
|
|
||||||
simulation::Region->update_sounds();
|
simulation::Region->update_sounds();
|
||||||
audio::renderer.update(Global.iPause ? 0.0 : deltarealtime);
|
audio::renderer.update(Global.iPause ? 0.0 : deltarealtime);
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ warm_one_pack_texture( std::string texture_file, std::unordered_set<std::string>
|
|||||||
if( false == seen.insert( texture_file ).second ) {
|
if( false == seen.insert( texture_file ).second ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if( false == g_session_warmed_textures.insert( texture_file ).second ) {
|
if( g_session_warmed_textures.contains( texture_file ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +73,8 @@ warm_one_pack_texture( std::string texture_file, std::unordered_set<std::string>
|
|||||||
log_pack_texture_fail( texture_file );
|
log_pack_texture_fail( texture_file );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_session_warmed_textures.insert( texture_file );
|
||||||
GfxRenderer->Fetch_Material( resolved );
|
GfxRenderer->Fetch_Material( resolved );
|
||||||
|
|
||||||
for( int skinindex { 1 }; skinindex <= 4; ++skinindex ) {
|
for( int skinindex { 1 }; skinindex <= 4; ++skinindex ) {
|
||||||
|
|||||||
@@ -58,20 +58,30 @@ constexpr std::size_t kLoaderSliceColdMeshes { 32 };
|
|||||||
constexpr double kGameplayColdBudgetMs { 3.0 };
|
constexpr double kGameplayColdBudgetMs { 3.0 };
|
||||||
constexpr double kCatchupColdBudgetMs { 5.0 };
|
constexpr double kCatchupColdBudgetMs { 5.0 };
|
||||||
|
|
||||||
constexpr int kInitialBootstrapRadius { 4 };
|
constexpr int kInitialBootstrapRadius { 5 };
|
||||||
constexpr int kStreamRadius { 11 };
|
constexpr int kStreamRadius { 14 };
|
||||||
constexpr int kMovementLookahead { 7 };
|
constexpr int kMovementLookahead { 10 };
|
||||||
constexpr std::size_t kMaxPackStreamWorkers { 4 };
|
constexpr std::size_t kMaxPackStreamWorkers { 8 };
|
||||||
constexpr std::size_t kMaxInFlightSections { 8 };
|
constexpr std::size_t kMaxInFlightSections { 12 };
|
||||||
constexpr std::size_t kMaxReadySections { 3 };
|
constexpr std::size_t kMaxReadySections { 6 };
|
||||||
constexpr double kStreamStatusLogIntervalSec { 5.0 };
|
constexpr double kStreamStatusLogIntervalSec { 5.0 };
|
||||||
constexpr double kReenqueueDistanceM { 500.0 };
|
constexpr double kReenqueueDistanceM { 500.0 };
|
||||||
constexpr double kCatchupReenqueueDistanceM { 40.0 };
|
constexpr double kCatchupReenqueueDistanceM { 40.0 };
|
||||||
|
constexpr std::size_t kHeavySectionModelThreshold { 800 };
|
||||||
|
constexpr double kUrgentApplyBudgetMs { 32.0 };
|
||||||
|
constexpr std::size_t kUrgentSliceInstances { 512 };
|
||||||
|
constexpr std::size_t kUrgentSliceColdMeshes { 20 };
|
||||||
|
constexpr double kUrgentColdBudgetMs { 24.0 };
|
||||||
|
constexpr std::size_t kUrgentMaxChunksPerDrain { 8 };
|
||||||
|
constexpr double kReadyTexturePrefetchBudgetMs { 14.0 };
|
||||||
|
constexpr std::size_t kReadyTexturePrefetchSlice { 256 };
|
||||||
|
constexpr std::size_t kCatchupMaxInFlightSections { 28 };
|
||||||
|
constexpr std::size_t kCatchupMaxReadySections { 14 };
|
||||||
|
constexpr int kUrgentSectionDistanceKm { 1 };
|
||||||
|
constexpr int kPreemptPendingDistanceKm { 2 };
|
||||||
constexpr double kTeleportReenqueueDistanceM { 2000.0 };
|
constexpr double kTeleportReenqueueDistanceM { 2000.0 };
|
||||||
constexpr float kStationaryCatchupRingThreshold { 0.70f };
|
constexpr float kStationaryCatchupRingThreshold { 0.70f };
|
||||||
constexpr double kStationaryCatchupSpeedMps { 5.0 };
|
constexpr double kStationaryCatchupSpeedMps { 5.0 };
|
||||||
constexpr std::size_t kCatchupMaxInFlightSections { 20 };
|
|
||||||
constexpr std::size_t kCatchupMaxReadySections { 8 };
|
|
||||||
constexpr std::size_t kBootstrapDrainMs { 32 };
|
constexpr std::size_t kBootstrapDrainMs { 32 };
|
||||||
constexpr std::size_t kBootstrapTimeoutMs { 120000 };
|
constexpr std::size_t kBootstrapTimeoutMs { 120000 };
|
||||||
constexpr std::chrono::milliseconds kPresentableHoldMs { 200 };
|
constexpr std::chrono::milliseconds kPresentableHoldMs { 200 };
|
||||||
@@ -98,6 +108,7 @@ struct PackSectionReady {
|
|||||||
std::unique_ptr<std::vector<Eu7Model>> models;
|
std::unique_ptr<std::vector<Eu7Model>> models;
|
||||||
bool failed { false };
|
bool failed { false };
|
||||||
std::size_t apply_offset { 0 };
|
std::size_t apply_offset { 0 };
|
||||||
|
std::size_t texture_warm_offset { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SectionStreamState {
|
struct SectionStreamState {
|
||||||
@@ -169,6 +180,12 @@ enqueue_failed_section( PackSectionJob const &job );
|
|||||||
void
|
void
|
||||||
release_pending_buffer();
|
release_pending_buffer();
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
section_has_pack_models( int const row, int const column );
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t
|
||||||
|
pending_section_total();
|
||||||
|
|
||||||
[[nodiscard]] double
|
[[nodiscard]] double
|
||||||
camera_stream_speed_mps();
|
camera_stream_speed_mps();
|
||||||
|
|
||||||
@@ -306,8 +323,40 @@ gameplay_cold_budget_ms() {
|
|||||||
return g_stream_catchup ? kCatchupColdBudgetMs : kGameplayColdBudgetMs;
|
return g_stream_catchup ? kCatchupColdBudgetMs : kGameplayColdBudgetMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
pending_section_near_camera( int const max_distance_km = kUrgentSectionDistanceKm ) {
|
||||||
|
if( false == g_stream.pending_apply.has_value() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto const &batch { *g_stream.pending_apply };
|
||||||
|
return section_manhattan_sections(
|
||||||
|
batch.row,
|
||||||
|
batch.column,
|
||||||
|
g_stream.center_row,
|
||||||
|
g_stream.center_column ) <= max_distance_km;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
pending_apply_is_urgent() {
|
||||||
|
if( false == pending_section_near_camera() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return g_stream_catchup || camera_stream_speed_mps() > 80.0;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
adaptive_slice_instances( std::size_t const section_total ) {
|
adaptive_slice_instances( std::size_t const section_total, bool const urgent = false ) {
|
||||||
|
if( urgent ) {
|
||||||
|
auto limit { kUrgentSliceInstances };
|
||||||
|
if( section_total > 4000 ) {
|
||||||
|
limit = std::min( limit, std::size_t { 384 } );
|
||||||
|
}
|
||||||
|
else if( section_total > 2000 ) {
|
||||||
|
limit = std::min( limit, std::size_t { 448 } );
|
||||||
|
}
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
auto limit { gameplay_slice_instances() };
|
auto limit { gameplay_slice_instances() };
|
||||||
if( g_stream_catchup ) {
|
if( g_stream_catchup ) {
|
||||||
auto const speed { camera_stream_speed_mps() };
|
auto const speed { camera_stream_speed_mps() };
|
||||||
@@ -344,7 +393,10 @@ adaptive_slice_instances( std::size_t const section_total ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
adaptive_cold_meshes() {
|
adaptive_cold_meshes( bool const urgent = false ) {
|
||||||
|
if( urgent ) {
|
||||||
|
return kUrgentSliceColdMeshes;
|
||||||
|
}
|
||||||
auto limit { gameplay_slice_cold_meshes() };
|
auto limit { gameplay_slice_cold_meshes() };
|
||||||
if( pack_bench_stream().last_chunk_ms >= 12.0 ) {
|
if( pack_bench_stream().last_chunk_ms >= 12.0 ) {
|
||||||
limit = 1;
|
limit = 1;
|
||||||
@@ -492,7 +544,82 @@ maybe_skip_stuck_apply_offset() {
|
|||||||
|
|
||||||
void
|
void
|
||||||
maybe_preempt_distant_pending() {
|
maybe_preempt_distant_pending() {
|
||||||
// Wyłączone — najpierw stabilność: dokończ bieżącą sekcję zamiast preemptować.
|
if(
|
||||||
|
false == g_loading_screen_dismissed ||
|
||||||
|
g_stream.bootstrap_active ||
|
||||||
|
g_stream.bootstrap_pending ||
|
||||||
|
false == g_stream.pending_apply.has_value() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &batch { *g_stream.pending_apply };
|
||||||
|
if( batch.models == nullptr || batch.models->empty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( g_stream.pending_apply_offset >= batch.models->size() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const pending_dist {
|
||||||
|
section_manhattan_sections(
|
||||||
|
batch.row,
|
||||||
|
batch.column,
|
||||||
|
g_stream.center_row,
|
||||||
|
g_stream.center_column ) };
|
||||||
|
if( pending_dist <= kUrgentSectionDistanceKm ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nearer_ready { false };
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock { g_stream.ready.mutex };
|
||||||
|
for( auto const &ready : g_stream.ready.data ) {
|
||||||
|
if( ready.failed || ready.models == nullptr || ready.models->empty() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto const ready_dist {
|
||||||
|
section_manhattan_sections(
|
||||||
|
ready.row,
|
||||||
|
ready.column,
|
||||||
|
g_stream.center_row,
|
||||||
|
g_stream.center_column ) };
|
||||||
|
if(
|
||||||
|
ready_dist < pending_dist &&
|
||||||
|
ready_dist <= kUrgentSectionDistanceKm ) {
|
||||||
|
nearer_ready = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( false == nearer_ready ) {
|
||||||
|
if(
|
||||||
|
pending_dist <= kPreemptPendingDistanceKm ||
|
||||||
|
camera_stream_speed_mps() <= 80.0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const [cam_row, cam_col] {
|
||||||
|
section_row_column( resolve_section_stream_position( Global.pCamera.Pos ) ) };
|
||||||
|
if( section_has_pack_models( cam_row, cam_col ) ) {
|
||||||
|
nearer_ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( false == nearer_ready ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.apply_offset = g_stream.pending_apply_offset;
|
||||||
|
auto suspended { std::move( batch ) };
|
||||||
|
g_stream.pending_apply.reset();
|
||||||
|
g_stream.pending_apply_offset = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock { g_stream.ready.mutex };
|
||||||
|
g_stream.ready.data.push_front( std::move( suspended ) );
|
||||||
|
}
|
||||||
|
pack_bench_inc( &Eu7PackBench::stream_reenqueue );
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
@@ -502,9 +629,6 @@ apply_pending_chunk(
|
|||||||
std::size_t const max_cold_meshes,
|
std::size_t const max_cold_meshes,
|
||||||
double const cold_budget_ms,
|
double const cold_budget_ms,
|
||||||
std::size_t const max_chunks ) {
|
std::size_t const max_chunks ) {
|
||||||
(void)max_instances;
|
|
||||||
(void)max_cold_meshes;
|
|
||||||
(void)cold_budget_ms;
|
|
||||||
if( g_stream.serializer == nullptr ) {
|
if( g_stream.serializer == nullptr ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -550,9 +674,30 @@ apply_pending_chunk(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sekcja 1 km (komorka TCP) — apply w calosci, bez dzielenia na plasterki.
|
|
||||||
auto const remaining { total - offset };
|
auto const remaining { total - offset };
|
||||||
|
auto const is_urgent { pending_apply_is_urgent() };
|
||||||
|
auto const is_heavy {
|
||||||
|
total > kHeavySectionModelThreshold || remaining > kHeavySectionModelThreshold };
|
||||||
|
|
||||||
std::size_t chunk_count { remaining };
|
std::size_t chunk_count { remaining };
|
||||||
|
std::size_t cold_mesh_limit { remaining };
|
||||||
|
double effective_cold_budget_ms { 0.0 };
|
||||||
|
|
||||||
|
if( is_heavy ) {
|
||||||
|
chunk_count = std::min( remaining, adaptive_slice_instances( total, is_urgent ) );
|
||||||
|
cold_mesh_limit = adaptive_cold_meshes( is_urgent );
|
||||||
|
effective_cold_budget_ms =
|
||||||
|
is_urgent ? kUrgentColdBudgetMs : gameplay_cold_budget_ms();
|
||||||
|
if( max_instances > 0 ) {
|
||||||
|
chunk_count = std::min( chunk_count, max_instances );
|
||||||
|
}
|
||||||
|
if( max_cold_meshes > 0 ) {
|
||||||
|
cold_mesh_limit = std::min( cold_mesh_limit, max_cold_meshes );
|
||||||
|
}
|
||||||
|
if( cold_budget_ms > 0.0 ) {
|
||||||
|
effective_cold_budget_ms = cold_budget_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto const chunk_started { std::chrono::steady_clock::now() };
|
auto const chunk_started { std::chrono::steady_clock::now() };
|
||||||
double cold_ms { 0.0 };
|
double cold_ms { 0.0 };
|
||||||
@@ -565,9 +710,9 @@ apply_pending_chunk(
|
|||||||
auto const phase_started { std::chrono::steady_clock::now() };
|
auto const phase_started { std::chrono::steady_clock::now() };
|
||||||
cold_loads = preload_slice_cold_meshes(
|
cold_loads = preload_slice_cold_meshes(
|
||||||
models_ptr + offset,
|
models_ptr + offset,
|
||||||
remaining,
|
chunk_count,
|
||||||
remaining,
|
cold_mesh_limit,
|
||||||
0.0,
|
effective_cold_budget_ms,
|
||||||
chunk_count );
|
chunk_count );
|
||||||
cold_ms = std::chrono::duration<double, std::milli>(
|
cold_ms = std::chrono::duration<double, std::milli>(
|
||||||
std::chrono::steady_clock::now() - phase_started ).count();
|
std::chrono::steady_clock::now() - phase_started ).count();
|
||||||
@@ -687,7 +832,9 @@ maybe_log_stream_status( glm::dvec3 const &world_position ) {
|
|||||||
pending_offset,
|
pending_offset,
|
||||||
pending_total,
|
pending_total,
|
||||||
gameplay_apply_budget_ms(),
|
gameplay_apply_budget_ms(),
|
||||||
adaptive_slice_instances( pending_total ) );
|
g_stream.pending_apply.has_value() && g_stream.pending_apply->models != nullptr ?
|
||||||
|
g_stream.pending_apply->models->size() :
|
||||||
|
std::size_t { 0 } );
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
@@ -776,6 +923,10 @@ worker_loop( std::stop_token const stop_token ) {
|
|||||||
result.failed = result.models == nullptr;
|
result.failed = result.models == nullptr;
|
||||||
|
|
||||||
if( result.failed ) {
|
if( result.failed ) {
|
||||||
|
ErrorLog(
|
||||||
|
"EU7 PACK: pusty odczyt sekcji " + std::to_string( job.row ) + "," +
|
||||||
|
std::to_string( job.column ) + " (PIDX model_count=" +
|
||||||
|
std::to_string( entry->model_count ) + ")" );
|
||||||
enqueue_failed_section( job );
|
enqueue_failed_section( job );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -783,6 +934,8 @@ worker_loop( std::stop_token const stop_token ) {
|
|||||||
pack_bench_inc(
|
pack_bench_inc(
|
||||||
&Eu7PackBench::worker_models_decoded, result.models->size() );
|
&Eu7PackBench::worker_models_decoded, result.models->size() );
|
||||||
|
|
||||||
|
preload_pack_models( *result.models );
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock { g_stream.ready.mutex };
|
std::lock_guard<std::mutex> lock { g_stream.ready.mutex };
|
||||||
g_stream.ready.data.push_back( std::move( result ) );
|
g_stream.ready.data.push_back( std::move( result ) );
|
||||||
@@ -954,6 +1107,13 @@ enqueue_section_if_needed(
|
|||||||
if( g_stream.loaded_sections.contains( section_idx ) ) {
|
if( g_stream.loaded_sections.contains( section_idx ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pusta komorka siatki (brak wpisu PACK) — nie kolejkuj, oznacz jako zaladowana.
|
||||||
|
if( false == section_has_pack_models( row, column ) ) {
|
||||||
|
g_stream.loaded_sections.insert( section_idx );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if( g_stream.in_flight_sections.contains( section_idx ) ) {
|
if( g_stream.in_flight_sections.contains( section_idx ) ) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock { g_stream.jobs.mutex };
|
std::lock_guard<std::mutex> lock { g_stream.jobs.mutex };
|
||||||
@@ -1128,13 +1288,13 @@ enqueue_movement_lookahead(
|
|||||||
lookahead_steps = std::min( max_steps, 6 );
|
lookahead_steps = std::min( max_steps, 6 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( max_steps > kMovementLookahead ) {
|
{
|
||||||
auto const cam_speed { camera_stream_speed_mps() };
|
auto const cam_speed { camera_stream_speed_mps() };
|
||||||
if( cam_speed > 200.0 ) {
|
if( cam_speed > 200.0 ) {
|
||||||
lookahead_steps = max_steps;
|
lookahead_steps = max_steps;
|
||||||
}
|
}
|
||||||
else if( cam_speed > 80.0 ) {
|
else if( cam_speed > 80.0 ) {
|
||||||
lookahead_steps = std::max( lookahead_steps, std::min( max_steps, 8 ) );
|
lookahead_steps = std::max( lookahead_steps, std::min( max_steps, 10 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,9 +1436,86 @@ try_dequeue_ready_batch() {
|
|||||||
g_stream.pending_apply = std::move( batch );
|
g_stream.pending_apply = std::move( batch );
|
||||||
g_stream.pending_apply_offset = g_stream.pending_apply->apply_offset;
|
g_stream.pending_apply_offset = g_stream.pending_apply->apply_offset;
|
||||||
g_stream.pending_apply->apply_offset = 0;
|
g_stream.pending_apply->apply_offset = 0;
|
||||||
|
|
||||||
|
if(
|
||||||
|
g_stream.pending_apply->models != nullptr &&
|
||||||
|
g_stream.pending_apply->texture_warm_offset < g_stream.pending_apply->models->size() ) {
|
||||||
|
(void)warm_pack_textures_main(
|
||||||
|
g_stream.pending_apply->models->data() + g_stream.pending_apply->texture_warm_offset,
|
||||||
|
g_stream.pending_apply->models->size() - g_stream.pending_apply->texture_warm_offset );
|
||||||
|
g_stream.pending_apply->texture_warm_offset = g_stream.pending_apply->models->size();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
prefetch_ready_queue_textures( double const budget_ms ) {
|
||||||
|
if( budget_ms <= 0.0 || GfxRenderer == nullptr ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const started { std::chrono::steady_clock::now() };
|
||||||
|
std::vector<std::deque<PackSectionReady>::iterator> candidates;
|
||||||
|
candidates.reserve( ready_queue_size() );
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock { g_stream.ready.mutex };
|
||||||
|
for( auto it { g_stream.ready.data.begin() }; it != g_stream.ready.data.end(); ++it ) {
|
||||||
|
if( it->failed || it->models == nullptr || it->models->empty() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if( it->texture_warm_offset >= it->models->size() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
candidates.push_back( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( candidates.empty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(
|
||||||
|
candidates.begin(),
|
||||||
|
candidates.end(),
|
||||||
|
[]( std::deque<PackSectionReady>::iterator const &lhs,
|
||||||
|
std::deque<PackSectionReady>::iterator const &rhs ) {
|
||||||
|
auto const lhs_dist {
|
||||||
|
section_manhattan_sections(
|
||||||
|
lhs->row,
|
||||||
|
lhs->column,
|
||||||
|
g_stream.center_row,
|
||||||
|
g_stream.center_column ) };
|
||||||
|
auto const rhs_dist {
|
||||||
|
section_manhattan_sections(
|
||||||
|
rhs->row,
|
||||||
|
rhs->column,
|
||||||
|
g_stream.center_row,
|
||||||
|
g_stream.center_column ) };
|
||||||
|
return lhs_dist < rhs_dist;
|
||||||
|
} );
|
||||||
|
|
||||||
|
for( auto const it : candidates ) {
|
||||||
|
auto &batch { *it };
|
||||||
|
while( batch.texture_warm_offset < batch.models->size() ) {
|
||||||
|
auto const remaining { batch.models->size() - batch.texture_warm_offset };
|
||||||
|
auto const slice { std::min( remaining, kReadyTexturePrefetchSlice ) };
|
||||||
|
(void)warm_pack_textures_main(
|
||||||
|
batch.models->data() + batch.texture_warm_offset,
|
||||||
|
slice );
|
||||||
|
batch.texture_warm_offset += slice;
|
||||||
|
|
||||||
|
auto const elapsed_ms {
|
||||||
|
std::chrono::duration<double, std::milli>(
|
||||||
|
std::chrono::steady_clock::now() - started ).count() };
|
||||||
|
if( elapsed_ms >= budget_ms ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
gameplay_stream_mode() {
|
gameplay_stream_mode() {
|
||||||
return g_loading_screen_dismissed
|
return g_loading_screen_dismissed
|
||||||
@@ -1387,17 +1624,31 @@ drain_until_budget( double const budget_ms ) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sync_stream_limits( glm::dvec3 const &world_position ) {
|
sync_stream_limits( glm::dvec3 const &world_position ) {
|
||||||
(void)world_position;
|
|
||||||
g_stream_catchup = false;
|
|
||||||
if( false == g_loading_screen_dismissed ) {
|
if( false == g_loading_screen_dismissed ) {
|
||||||
|
g_stream_catchup = false;
|
||||||
g_stream_max_in_flight = 6;
|
g_stream_max_in_flight = 6;
|
||||||
g_stream_max_ready = 2;
|
g_stream_max_ready = 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto const stream_position { resolve_section_stream_position( world_position ) };
|
||||||
|
auto const cam_speed { camera_stream_speed_mps() };
|
||||||
|
auto const outer_ring {
|
||||||
|
section_stream_ring_progress( stream_position, g_stream.radius ) };
|
||||||
|
|
||||||
|
g_stream_catchup = (
|
||||||
|
cam_speed > 80.0 ||
|
||||||
|
outer_ring < 0.98f );
|
||||||
|
|
||||||
|
if( g_stream_catchup ) {
|
||||||
|
g_stream_max_in_flight = kCatchupMaxInFlightSections;
|
||||||
|
g_stream_max_ready = kCatchupMaxReadySections;
|
||||||
|
}
|
||||||
|
else {
|
||||||
g_stream_max_in_flight = kMaxInFlightSections;
|
g_stream_max_in_flight = kMaxInFlightSections;
|
||||||
g_stream_max_ready = kMaxReadySections;
|
g_stream_max_ready = kMaxReadySections;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -1578,8 +1829,19 @@ update_section_stream( glm::dvec3 const &world_position ) {
|
|||||||
section_stream_ready_around( stream_position, kSectionStreamGameplayRadiusKm ) };
|
section_stream_ready_around( stream_position, kSectionStreamGameplayRadiusKm ) };
|
||||||
auto const ring_radius {
|
auto const ring_radius {
|
||||||
inner_ring_ready ? g_stream.radius : kSectionStreamGameplayRadiusKm };
|
inner_ring_ready ? g_stream.radius : kSectionStreamGameplayRadiusKm };
|
||||||
auto const reenqueue_distance { kReenqueueDistanceM };
|
|
||||||
auto max_lookahead { inner_ring_ready ? 5 : 0 };
|
auto const cam_speed { camera_stream_speed_mps() };
|
||||||
|
auto const reenqueue_distance {
|
||||||
|
( g_stream_catchup || cam_speed > 80.0 ) ?
|
||||||
|
std::clamp( cam_speed * 0.25, kCatchupReenqueueDistanceM, kReenqueueDistanceM ) :
|
||||||
|
kReenqueueDistanceM };
|
||||||
|
auto max_lookahead { 0 };
|
||||||
|
if( inner_ring_ready ) {
|
||||||
|
max_lookahead = ( g_stream_catchup || cam_speed > 200.0 ) ?
|
||||||
|
kMovementLookahead :
|
||||||
|
( cam_speed > 80.0 ? 10 : 7 );
|
||||||
|
}
|
||||||
|
|
||||||
auto const travel_forward { guess_travel_forward() };
|
auto const travel_forward { guess_travel_forward() };
|
||||||
|
|
||||||
auto const current_section_unloaded {
|
auto const current_section_unloaded {
|
||||||
@@ -1598,7 +1860,9 @@ update_section_stream( glm::dvec3 const &world_position ) {
|
|||||||
if( should_reenqueue || inner_ring_incomplete ) {
|
if( should_reenqueue || inner_ring_incomplete ) {
|
||||||
pack_bench_inc( &Eu7PackBench::stream_reenqueue );
|
pack_bench_inc( &Eu7PackBench::stream_reenqueue );
|
||||||
}
|
}
|
||||||
if( max_lookahead > 0 ) {
|
}
|
||||||
|
|
||||||
|
if( inner_ring_ready && max_lookahead > 0 && cam_speed > 50.0 ) {
|
||||||
enqueue_movement_lookahead(
|
enqueue_movement_lookahead(
|
||||||
center_row,
|
center_row,
|
||||||
center_column,
|
center_column,
|
||||||
@@ -1607,7 +1871,6 @@ update_section_stream( glm::dvec3 const &world_position ) {
|
|||||||
max_lookahead );
|
max_lookahead );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
section_stream_drain_idle( glm::dvec3 const &world_position ) {
|
section_stream_drain_idle( glm::dvec3 const &world_position ) {
|
||||||
@@ -1640,7 +1903,23 @@ drain_section_stream(
|
|||||||
kSectionStreamGameplayRadiusKm ) };
|
kSectionStreamGameplayRadiusKm ) };
|
||||||
|
|
||||||
if( gameplay_stream_mode() && inner_ring_ready ) {
|
if( gameplay_stream_mode() && inner_ring_ready ) {
|
||||||
|
prefetch_ready_queue_textures(
|
||||||
|
g_stream_catchup || camera_stream_speed_mps() > 80.0 ?
|
||||||
|
kReadyTexturePrefetchBudgetMs :
|
||||||
|
kReadyTexturePrefetchBudgetMs * 0.5 );
|
||||||
|
|
||||||
|
if( g_stream_catchup ) {
|
||||||
|
auto const urgent { pending_apply_is_urgent() };
|
||||||
|
drain_apply_budget(
|
||||||
|
urgent ? kUrgentApplyBudgetMs : gameplay_apply_budget_ms(),
|
||||||
|
adaptive_slice_instances( pending_section_total(), urgent ),
|
||||||
|
adaptive_cold_meshes( urgent ),
|
||||||
|
urgent ? kUrgentColdBudgetMs : gameplay_cold_budget_ms(),
|
||||||
|
urgent ? kUrgentMaxChunksPerDrain : 1 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
drain_until_budget( kDrainBudgetMs );
|
drain_until_budget( kDrainBudgetMs );
|
||||||
|
}
|
||||||
maybe_log_stream_status( stream_position );
|
maybe_log_stream_status( stream_position );
|
||||||
}
|
}
|
||||||
else if( gameplay_stream_mode() ) {
|
else if( gameplay_stream_mode() ) {
|
||||||
@@ -2105,4 +2384,9 @@ reset_section_stream() {
|
|||||||
reset_pack_bench();
|
reset_pack_bench();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
section_stream_frame_budget_pressure() {
|
||||||
|
return g_stream_catchup || pending_apply_is_urgent();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace scene::eu7
|
} // namespace scene::eu7
|
||||||
|
|||||||
@@ -106,4 +106,8 @@ section_stream_ring_progress(
|
|||||||
void
|
void
|
||||||
reset_section_stream();
|
reset_section_stream();
|
||||||
|
|
||||||
|
// PACK catchup / pilny apply — ogranicz rownolegle ladowanie skladow.
|
||||||
|
[[nodiscard]] bool
|
||||||
|
section_stream_frame_budget_pressure();
|
||||||
|
|
||||||
} // namespace scene::eu7
|
} // namespace scene::eu7
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ namespace {
|
|||||||
constexpr double kDeferTrainsetHorizDistM { 4000.0 };
|
constexpr double kDeferTrainsetHorizDistM { 4000.0 };
|
||||||
constexpr double kDeferTrainsetHorizDistSq {
|
constexpr double kDeferTrainsetHorizDistSq {
|
||||||
kDeferTrainsetHorizDistM * kDeferTrainsetHorizDistM };
|
kDeferTrainsetHorizDistM * kDeferTrainsetHorizDistM };
|
||||||
|
constexpr double kTrainsetDrainMaxDistM { 12000.0 };
|
||||||
|
constexpr double kTrainsetDrainMaxDistSq {
|
||||||
|
kTrainsetDrainMaxDistM * kTrainsetDrainMaxDistM };
|
||||||
|
|
||||||
struct DeferredEu7Trainset {
|
struct DeferredEu7Trainset {
|
||||||
scene::eu7::Eu7Trainset trainset;
|
scene::eu7::Eu7Trainset trainset;
|
||||||
@@ -111,24 +114,39 @@ eu7_trainset_is_player(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] double
|
[[nodiscard]] glm::dvec3
|
||||||
eu7_trainset_horiz_dist_sq( scene::eu7::Eu7Trainset const &trainset ) {
|
eu7_observer_position() {
|
||||||
auto *path { simulation::Paths.find( trainset.track ) };
|
if( FreeFlyModeFlag || Global.pCamera.m_owner == nullptr ) {
|
||||||
if( path == nullptr ) {
|
return Global.pCamera.Pos;
|
||||||
return 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const spawn { eu7_trainset_spawn_origin() };
|
return eu7_trainset_spawn_origin();
|
||||||
if( spawn.x == 0.0 && spawn.y == 0.0 && spawn.z == 0.0 ) {
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double
|
||||||
|
eu7_trainset_horiz_dist_sq_to(
|
||||||
|
scene::eu7::Eu7Trainset const &trainset,
|
||||||
|
glm::dvec3 const &observer ) {
|
||||||
|
auto *path { simulation::Paths.find( trainset.track ) };
|
||||||
|
if( path == nullptr ) {
|
||||||
|
return std::numeric_limits<double>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( observer.x == 0.0 && observer.y == 0.0 && observer.z == 0.0 ) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const track_pos { glm::dvec3 { path->location() } };
|
auto const track_pos { glm::dvec3 { path->location() } };
|
||||||
auto const dx { track_pos.x - spawn.x };
|
auto const dx { track_pos.x - observer.x };
|
||||||
auto const dz { track_pos.z - spawn.z };
|
auto const dz { track_pos.z - observer.z };
|
||||||
return dx * dx + dz * dz;
|
return dx * dx + dz * dz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double
|
||||||
|
eu7_trainset_horiz_dist_sq( scene::eu7::Eu7Trainset const &trainset ) {
|
||||||
|
return eu7_trainset_horiz_dist_sq_to( trainset, eu7_observer_position() );
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
eu7_should_load_trainset_now(
|
eu7_should_load_trainset_now(
|
||||||
scene::eu7::Eu7Trainset const &trainset,
|
scene::eu7::Eu7Trainset const &trainset,
|
||||||
@@ -136,7 +154,7 @@ eu7_should_load_trainset_now(
|
|||||||
if( eu7_trainset_is_player( trainset, scene ) ) {
|
if( eu7_trainset_is_player( trainset, scene ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if( eu7_trainset_horiz_dist_sq( trainset ) <= kDeferTrainsetHorizDistSq ) {
|
if( eu7_trainset_horiz_dist_sq_to( trainset, eu7_observer_position() ) <= kDeferTrainsetHorizDistSq ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -1412,24 +1430,33 @@ state_serializer::apply_eu7_scene(
|
|||||||
|
|
||||||
void
|
void
|
||||||
state_serializer::drain_deferred_eu7_trainsets( double const max_ms ) {
|
state_serializer::drain_deferred_eu7_trainsets( double const max_ms ) {
|
||||||
if( g_deferred_eu7_trainsets.empty() ) {
|
if( g_deferred_eu7_trainsets.empty() || max_ms <= 0.0 ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const deadline {
|
auto const observer { eu7_observer_position() };
|
||||||
std::chrono::steady_clock::now() +
|
auto best_it { g_deferred_eu7_trainsets.end() };
|
||||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
|
auto best_dist_sq { std::numeric_limits<double>::max() };
|
||||||
std::chrono::duration<double, std::milli>( max_ms ) ) };
|
for( auto it { g_deferred_eu7_trainsets.begin() }; it != g_deferred_eu7_trainsets.end(); ++it ) {
|
||||||
|
auto const dist_sq { eu7_trainset_horiz_dist_sq_to( it->trainset, observer ) };
|
||||||
|
if( dist_sq < best_dist_sq ) {
|
||||||
|
best_dist_sq = dist_sq;
|
||||||
|
best_it = it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
best_it == g_deferred_eu7_trainsets.end() ||
|
||||||
|
best_dist_sq > kTrainsetDrainMaxDistSq ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto job { std::move( *best_it ) };
|
||||||
|
g_deferred_eu7_trainsets.erase( best_it );
|
||||||
|
|
||||||
cParser dummy_parser( "", cParser::buffer_TEXT, "", false );
|
cParser dummy_parser( "", cParser::buffer_TEXT, "", false );
|
||||||
scene::scratch_data scratchpad;
|
scene::scratch_data scratchpad;
|
||||||
|
|
||||||
while(
|
|
||||||
false == g_deferred_eu7_trainsets.empty() &&
|
|
||||||
std::chrono::steady_clock::now() < deadline ) {
|
|
||||||
auto job { std::move( g_deferred_eu7_trainsets.front() ) };
|
|
||||||
g_deferred_eu7_trainsets.pop_front();
|
|
||||||
|
|
||||||
scene::eu7::ScopedTimer const timer { scene::eu7::load_stats().trainset_ms };
|
scene::eu7::ScopedTimer const timer { scene::eu7::load_stats().trainset_ms };
|
||||||
++scene::eu7::load_stats().trainsets;
|
++scene::eu7::load_stats().trainsets;
|
||||||
|
|
||||||
@@ -1558,7 +1585,7 @@ state_serializer::drain_deferred_eu7_trainsets( double const max_ms ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deserialize_endtrainset( dummy_parser, scratchpad );
|
deserialize_endtrainset( dummy_parser, scratchpad );
|
||||||
}
|
(void)max_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ struct global_settings {
|
|||||||
int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan
|
int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan
|
||||||
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
|
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
|
||||||
bool file_binary_terrain_state{true};
|
bool file_binary_terrain_state{true};
|
||||||
int eu7_pack_stream_workers_percent{ 40 }; // EU7 PACK async loader thread pool size (% of CPU threads)
|
int eu7_pack_stream_workers_percent{ 75 }; // EU7 PACK async loader thread pool size (% of CPU threads)
|
||||||
bool eu7_auto_bake{ true }; // przy ladowaniu mapy: bake SCM→EU7 gdy brak lub nieaktualny .eu7
|
bool eu7_auto_bake{ true }; // przy ladowaniu mapy: bake SCM→EU7 gdy brak lub nieaktualny .eu7
|
||||||
int eu7_bake_threads{ 0 }; // watki bake (0 = auto = hardware_concurrency)
|
int eu7_bake_threads{ 0 }; // watki bake (0 = auto = hardware_concurrency)
|
||||||
// logs
|
// logs
|
||||||
|
|||||||
Reference in New Issue
Block a user