From ada6553917cfd68009a73534d4c9577186689c6e Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 14 Jun 2026 04:18:23 +0200 Subject: [PATCH] fix --- application/application.cpp | 59 -- application/application.h | 11 - application/applicationmode.h | 2 +- application/drivermode.cpp | 46 +- application/scenarioloadermode.cpp | 104 +--- application/scenarioloadermode.h | 14 - application/scenarioloaderuilayer.cpp | 13 +- rendering/opengl33renderer.cpp | 5 +- rendering/openglrenderer.cpp | 8 +- scene/eu7/eu7_bake.cpp | 42 +- scene/eu7/eu7_loader.cpp | 22 +- scene/eu7/eu7_loader.h | 8 +- scene/eu7/eu7_model_prefetch.h | 66 +-- scene/eu7/eu7_reader.cpp | 85 +-- scene/eu7/eu7_reader.h | 27 - scene/eu7/eu7_section_stream.cpp | 650 +++++++++++++---------- scene/eu7/eu7_section_stream.h | 6 +- scene/eu7/eu7_types.h | 2 +- simulation/simulationstateserializer.cpp | 32 +- tools/parse_minidump.py | 2 +- utilities/parser.cpp | 4 +- widgets/map.cpp | 39 +- 22 files changed, 518 insertions(+), 729 deletions(-) diff --git a/application/application.cpp b/application/application.cpp index 4ed3e8e8..ba09bde9 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -12,9 +12,6 @@ http://mozilla.org/MPL/2.0/. #include "application/drivermode.h" #include "application/editormode.h" #include "application/scenarioloadermode.h" -#include "application/scenarioloaderuilayer.h" -#include "scene/eu7/eu7_load_stats.h" -#include "scene/eu7/eu7_section_stream.h" #include "launcher/launchermode.h" #include "utilities/Globals.h" @@ -710,59 +707,9 @@ void eu07_application::exit() #endif } -void -eu07_application::extend_loading_overlay( std::chrono::milliseconds const duration ) { - if( duration.count() <= 0 ) { - return; - } - auto const until { std::chrono::steady_clock::now() + duration }; - if( until > m_loading_overlay_until ) { - m_loading_overlay_until = until; - } - if( m_loading_overlay == nullptr ) { - m_loading_overlay = std::make_shared(); - } -} - -bool -eu07_application::loading_overlay_active() const { - if( m_modestack.empty() || m_modestack.top() != mode::driver ) { - return false; - } - - if( false == simulation::is_ready ) { - return true; - } - - return false; -} - -void -eu07_application::sync_loading_overlay_state() { - if( m_loading_overlay == nullptr ) { - m_loading_overlay = std::make_shared(); - } - - m_loading_overlay->set_progress( 100.f, 0.f ); -} - -void -eu07_application::render_loading_overlay() { - sync_loading_overlay_state(); - if( m_loading_overlay == nullptr ) { - m_loading_overlay = std::make_shared(); - } - m_loading_overlay->render(); -} - void eu07_application::render_ui() { - if( loading_overlay_active() ) { - render_loading_overlay(); - return; - } - if (m_modestack.empty()) { return; @@ -774,12 +721,6 @@ void eu07_application::render_ui() void eu07_application::begin_ui_frame() { - if( loading_overlay_active() ) { - sync_loading_overlay_state(); - m_loading_overlay->begin_ui_frame(); - return; - } - if (m_modestack.empty()) { return; diff --git a/application/application.h b/application/application.h index c678822d..11415603 100644 --- a/application/application.h +++ b/application/application.h @@ -9,8 +9,6 @@ http://mozilla.org/MPL/2.0/. #pragma once -#include - #include "application/applicationmode.h" #include "scripting/PyInt.h" #include "network/manager.h" @@ -57,12 +55,6 @@ public: render_ui(); void begin_ui_frame(); - void - extend_loading_overlay( std::chrono::milliseconds const Duration ); - [[nodiscard]] bool - loading_overlay_active() const; - void - render_loading_overlay(); // switches application to specified mode bool pop_mode(); @@ -123,12 +115,9 @@ private: int init_modes(); bool init_network(); int run_crashgui(); - void sync_loading_overlay_state(); // members bool m_screenshot_queued = false; - std::chrono::steady_clock::time_point m_loading_overlay_until {}; - std::shared_ptr m_loading_overlay; modeptr_array m_modes { nullptr }; // collection of available application behaviour modes mode_stack m_modestack; // current behaviour mode diff --git a/application/applicationmode.h b/application/applicationmode.h index 75f70362..feea3bdb 100644 --- a/application/applicationmode.h +++ b/application/applicationmode.h @@ -32,7 +32,7 @@ public: void begin_ui_frame() { if( m_userinterface != nullptr ) { m_userinterface->begin_ui_frame(); } } - virtual + inline void set_progress( float const Progress = 0.f, float const Subtaskprogress = 0.f ) { if( m_userinterface != nullptr ) { m_userinterface->set_progress( Progress, Subtaskprogress ); } } diff --git a/application/drivermode.cpp b/application/drivermode.cpp index d691446d..6bd7c885 100644 --- a/application/drivermode.cpp +++ b/application/drivermode.cpp @@ -404,24 +404,14 @@ bool driver_mode::update() Timer::subsystem.sim_total.stop(); - auto const world_presentable { - false == scene::eu7::section_stream_active() - || scene::eu7::loading_screen_dismissed() }; - auto const gameplay_stream { - scene::eu7::section_stream_active() && scene::eu7::loading_screen_dismissed() }; - - simulation::State.drain_deferred_eu7_trainsets( gameplay_stream ? 3.0 : 12.0 ); + simulation::State.drain_deferred_eu7_trainsets( 12.0 ); if( scene::eu7::section_stream_active() ) { - auto const stream_active { - simulation::is_ready || Application.loading_overlay_active() }; - if( stream_active && scene::eu7::section_stream_needs_bootstrap() ) { + if( scene::eu7::section_stream_needs_bootstrap() ) { scene::eu7::kick_section_stream_bootstrap(); } - if( stream_active ) { - scene::eu7::update_section_stream( Global.pCamera.Pos ); - scene::eu7::drain_section_stream(); - } + scene::eu7::update_section_stream( Global.pCamera.Pos ); + scene::eu7::drain_section_stream(); if( scene::eu7::pack_bench_stream_phase_active() ) { static double s_last_stream_bench_log { 0.0 }; @@ -442,9 +432,7 @@ bool driver_mode::update() GfxRenderer->Update(deltarealtime); - simulation::is_ready = simulation::is_ready - || ( ( simulation::Train != nullptr ) && ( simulation::Train->is_cab_initialized ) ) - || ( Global.local_start_vehicle == "ghostview" && world_presentable ); + simulation::is_ready = simulation::is_ready || ((simulation::Train != nullptr) && (simulation::Train->is_cab_initialized)) || (Global.local_start_vehicle == "ghostview"); return true; } @@ -455,6 +443,10 @@ void driver_mode::enter() TDynamicObject *nPlayerTrain{((Global.local_start_vehicle != "ghostview") ? simulation::Vehicles.find(Global.local_start_vehicle) : nullptr)}; + if( scene::eu7::section_stream_active() ) { + scene::eu7::dismiss_loading_screen(); + } + Camera.Init(Global.FreeCameraInit[0], Global.FreeCameraInitAngle[0], nullptr); Global.pCamera = Camera; Global.pDebugCamera = DebugCamera; @@ -462,6 +454,26 @@ void driver_mode::enter() FreeFlyModeFlag = true; DebugCamera = Camera; + if( scene::eu7::section_stream_active() ) { + if( scene::eu7::section_stream_needs_bootstrap() ) { + scene::eu7::kick_section_stream_bootstrap(); + } + auto stream_pos { scene::eu7::resolve_section_stream_position( Global.pCamera.Pos ) }; + if( stream_pos.x != 0.0 || stream_pos.y != 0.0 || stream_pos.z != 0.0 ) { + Global.pCamera.Pos = stream_pos; + Camera = Global.pCamera; + } + scene::eu7::update_section_stream( Global.pCamera.Pos ); + for( int i { 0 }; i < 64; ++i ) { + scene::eu7::drain_section_stream(); + if( scene::eu7::section_stream_ready_around( + stream_pos, + scene::eu7::kSectionStreamBootstrapRadiusKm ) ) { + break; + } + } + } + if (nPlayerTrain) { WriteLog("Trying to enter player train, \"" + Global.local_start_vehicle + "\""); diff --git a/application/scenarioloadermode.cpp b/application/scenarioloadermode.cpp index 67e808a2..c9a9cf7c 100644 --- a/application/scenarioloadermode.cpp +++ b/application/scenarioloadermode.cpp @@ -19,16 +19,8 @@ http://mozilla.org/MPL/2.0/. #include "rendering/renderer.h" #include "utilities/Logs.h" #include "utilities/translation.h" -#include "scene/eu7/eu7_load_stats.h" -#include "scene/eu7/eu7_pack_bench.h" #include "scene/eu7/eu7_section_stream.h" -namespace { -constexpr std::chrono::seconds kEu7LoadTimeout { 90 }; -constexpr float kDeserializeBarShare { 70.f }; -constexpr float kPackBarShare { 30.f }; -} // namespace - scenarioloader_mode::scenarioloader_mode() { m_userinterface = std::make_shared(); } @@ -39,13 +31,6 @@ bool scenarioloader_mode::init() { return true; } -void scenarioloader_mode::update_bar_progress( float const progress ) { - m_bar_progress = std::max( m_bar_progress, std::clamp( progress, 0.f, 100.f ) ); - if( m_userinterface != nullptr ) { - m_userinterface->set_progress( m_bar_progress, 0.f ); - } -} - // mode-specific update of simulation data. returns: false on error, true otherwise bool scenarioloader_mode::update() { if (!Global.ready_to_load) @@ -64,80 +49,22 @@ bool scenarioloader_mode::update() { } try { - if( m_phase == load_phase::deserialize ) { - if( simulation::State.deserialize_continue( state ) ) { - return true; - } - m_phase = load_phase::eu7_load; - m_eu7_load_started = std::chrono::steady_clock::now(); - m_eu7_stream_primed = false; - } + if (simulation::State.deserialize_continue(state)) + return true; } catch (invalid_scenery_exception &e) { ErrorLog( "Bad init: scenario loading failed" ); Application.pop_mode(); - return true; - } - - if( m_phase == load_phase::eu7_load ) { - if( scene::eu7::section_stream_active() ) { - simulation::State.drain_deferred_eu7_trainsets( 16.0 ); - - auto const position { scene::eu7::stream_loading_position() }; - - if( false == m_eu7_stream_primed ) { - if( scene::eu7::section_stream_needs_bootstrap() ) { - scene::eu7::kick_section_stream_bootstrap(); - } - m_eu7_stream_primed = true; - } - - scene::eu7::drain_section_stream(); - - auto const ring_progress { - scene::eu7::section_stream_ring_progress( - position, - scene::eu7::kSectionStreamBootstrapRadiusKm ) }; - update_bar_progress( - kDeserializeBarShare + ring_progress * kPackBarShare ); - - auto const timed_out { - std::chrono::steady_clock::now() - m_eu7_load_started >= kEu7LoadTimeout }; - auto const bootstrap_ready { - scene::eu7::section_stream_ready_around( - position, - scene::eu7::kSectionStreamBootstrapRadiusKm ) }; - if( - bootstrap_ready - || scene::eu7::section_stream_presentable_around( - position, - scene::eu7::kSectionStreamBootstrapRadiusKm ) ) { - WriteLog( "EU7 PACK: pierścień wokół pozycji startowej gotowy do pokazania" ); - scene::eu7::dismiss_loading_screen(); - m_phase = load_phase::finished; - } - else if( timed_out ) { - ErrorLog( - "EU7 PACK: timeout ładowania pierścienia wokół kamery — wchodzę w jazdę (ring=" + - std::to_string( static_cast( ring_progress * 100.f ) ) + "%)" ); - scene::eu7::dismiss_loading_screen(); - m_phase = load_phase::finished; - } - else { - return true; - } - } - else { - scene::eu7::dismiss_loading_screen(); - m_phase = load_phase::finished; - } } WriteLog( "Scenario loading time: " + std::to_string( std::chrono::duration_cast( ( std::chrono::system_clock::now() - timestart ) ).count() ) + " seconds" ); - scene::eu7::log_load_stats(); - scene::eu7::log_pack_bench(); - update_bar_progress( 100.f ); + if( scene::eu7::section_stream_active() ) { + scene::eu7::preload_section_stream( 3000.0 ); + } + + // TODO: implement and use next mode cue + Application.pop_mode(); Application.push_mode( eu07_application::mode::driver ); @@ -153,9 +80,6 @@ void scenarioloader_mode::enter() { // TBD: hide cursor in fullscreen mode? Application.set_cursor( GLFW_CURSOR_NORMAL ); - m_phase = load_phase::deserialize; - m_eu7_stream_primed = false; - m_bar_progress = 0.f; simulation::is_ready = false; Application.set_title( Global.AppName + " (" + Global.SceneryFile + ")" ); @@ -167,15 +91,3 @@ void scenarioloader_mode::exit() { simulation::Time.init( Global.starting_timestamp ); simulation::Environment.init(); } - -void scenarioloader_mode::set_progress( - float const Progress, - float const Subtaskprogress ) { - if( m_phase != load_phase::deserialize ) { - return; - } - - auto const parser_progress { - std::max( Progress, Subtaskprogress ) }; - update_bar_progress( parser_progress * kDeserializeBarShare / 100.f ); -} diff --git a/application/scenarioloadermode.h b/application/scenarioloadermode.h index 06c5ceca..db23458d 100644 --- a/application/scenarioloadermode.h +++ b/application/scenarioloadermode.h @@ -13,21 +13,8 @@ http://mozilla.org/MPL/2.0/. #include "simulation/simulation.h" class scenarioloader_mode : public application_mode { - - enum class load_phase { - deserialize, - eu7_load, - finished - }; - std::shared_ptr state; std::chrono::system_clock::time_point timestart; - load_phase m_phase { load_phase::deserialize }; - std::chrono::steady_clock::time_point m_eu7_load_started {}; - bool m_eu7_stream_primed { false }; - float m_bar_progress { 0.f }; - - void update_bar_progress( float progress ); public: // constructors @@ -41,7 +28,6 @@ public: void enter() override; // maintenance method, called when the mode is deactivated void exit() override; - void set_progress( float Progress = 0.f, float Subtaskprogress = 0.f ) override; // input handlers void on_key( int const Key, int const Scancode, int const Action, int const Mods ) override { ; } void on_cursor_pos( double const Horizontal, double const Vertical ) override { ; } diff --git a/application/scenarioloaderuilayer.cpp b/application/scenarioloaderuilayer.cpp index 3670ee4f..7b53e543 100644 --- a/application/scenarioloaderuilayer.cpp +++ b/application/scenarioloaderuilayer.cpp @@ -195,10 +195,6 @@ void scenarioloader_ui::render_() ImGui::SetNextWindowSize(ImVec2(Global.window_size.x + padding * 2, Global.window_size.y + padding * 2)); ImGui::Begin("Neo Loading Screen", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddRectFilled( - ImVec2( 0.f, 0.f ), - ImVec2( screen_size.x, screen_size.y ), - IM_COL32( 0, 0, 0, 255 ) ); ImGui::PushFont(font_loading); ImGui::SetWindowFontScale(1); const float font_scale_mult = 48 / ImGui::GetFontSize(); @@ -232,14 +228,7 @@ void scenarioloader_ui::render_() icon_center_y - text_size.y * 0.5f); // Draw - if( loading_tex != static_cast( -1 ) ) { - draw_list->AddImage( - reinterpret_cast( loading_tex ), - icon_pos, - ImVec2( icon_pos.x + loading_size.x, icon_pos.y + loading_size.y ), - ImVec2( 0, 0 ), - ImVec2( 1, 1 ) ); - } + //draw_list->AddImage(reinterpret_cast(loading_tex), icon_pos, ImVec2(icon_pos.x + loading_size.x, icon_pos.y + loading_size.y), ImVec2(0, 0), ImVec2(1, 1)); draw_list->AddText(text_pos, IM_COL32_WHITE, m_progresstext.c_str()); // Trivia diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index ee829f2d..8b112d6b 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -14,7 +14,6 @@ http://mozilla.org/MPL/2.0/. #include "utilities/Timer.h" #include "vehicle/Train.h" #include "vehicle/Camera.h" -#include "scene/eu7/eu7_section_stream.h" #include "simulation/simulation.h" #include "utilities/Logs.h" #include "simulation/simulationtime.h" @@ -734,9 +733,7 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode) m_current_viewport = &vp; } - if( ( false == simulation::is_ready ) - || scene::eu7::loading_screen_blocks_world( scene::eu7::stream_loading_position() ) - || Global.gfx_skiprendering ) + if ((!simulation::is_ready) || (Global.gfx_skiprendering)) { gl::framebuffer::unbind(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index fd7be708..868e5122 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -13,7 +13,6 @@ http://mozilla.org/MPL/2.0/. #include "utilities/color.h" #include "utilities/Globals.h" #include "vehicle/Camera.h" -#include "scene/eu7/eu7_section_stream.h" #include "utilities/Timer.h" #include "simulation/simulation.h" #include "simulation/simulationtime.h" @@ -415,12 +414,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { m_colorpass = m_renderpass; - if( ( false == simulation::is_ready ) - || scene::eu7::loading_screen_blocks_world( scene::eu7::stream_loading_position() ) - || Global.gfx_skiprendering ) { - ::glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + if( ( !simulation::is_ready ) || ( Global.gfx_skiprendering ) ) { + ::glClearColor( 51.0f / 255.f, 102.0f / 255.f, 85.0f / 255.f, 1.f ); // initial background Color ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - Application.render_ui(); break; } diff --git a/scene/eu7/eu7_bake.cpp b/scene/eu7/eu7_bake.cpp index 41334ea3..18f086a7 100644 --- a/scene/eu7/eu7_bake.cpp +++ b/scene/eu7/eu7_bake.cpp @@ -164,7 +164,7 @@ scenario_needs_eu7_regen( std::string const &scenario_file ) { if( probe_file( resolved ) ) { - return text_module_is_newer_than_binary( scenario_file ); + return false; } @@ -176,7 +176,7 @@ scenario_needs_eu7_regen( std::string const &scenario_file ) { } - return false == should_use_binary_module( scenario_file ); + return false == probe_baked_scenario( scenario_file ); #else @@ -204,28 +204,6 @@ ensure_scenario_eu7( std::string const &scenario_file ) { if( probe_file( resolved ) ) { -#ifdef WITH_EU7_PARSER - - if( Global.eu7_auto_bake && text_module_is_newer_than_binary( scenario_file ) ) { - - auto const text_root { text_source_path( scenario_file ) }; - - if( false == text_root.empty() ) { - - WriteLog( - - "EU7: .eu7 nieaktualny wzgledem " + text_root + ", rebake..." ); - - run_scenario_tree_bake( text_root, eu7_path, outcome ); - - return outcome; - - } - - } - -#endif - outcome.ok = true; outcome.message = "uzywam .eu7: " + resolved; @@ -242,7 +220,7 @@ ensure_scenario_eu7( std::string const &scenario_file ) { if( false == Global.eu7_auto_bake ) { - if( should_use_binary_module( scenario_file ) ) { + if( probe_baked_scenario( scenario_file ) ) { outcome.ok = true; @@ -282,7 +260,7 @@ ensure_scenario_eu7( std::string const &scenario_file ) { - if( should_use_binary_module( scenario_file ) ) { + if( probe_baked_scenario( scenario_file ) ) { outcome.ok = true; @@ -296,23 +274,13 @@ ensure_scenario_eu7( std::string const &scenario_file ) { - if( probe_file( eu7_path ) ) { - - WriteLog( - - "EU7: .eu7 nieaktualny wzgledem " + resolved + ", rebake..." ); - - } - - - run_scenario_tree_bake( resolved, eu7_path, outcome ); return outcome; #else - if( should_use_binary_module( scenario_file ) ) { + if( probe_baked_scenario( scenario_file ) ) { outcome.ok = true; diff --git a/scene/eu7/eu7_loader.cpp b/scene/eu7/eu7_loader.cpp index 045f68ca..63f98439 100644 --- a/scene/eu7/eu7_loader.cpp +++ b/scene/eu7/eu7_loader.cpp @@ -257,11 +257,7 @@ load_module_recursive( if( child_loaded ) { continue; } - if( !child.empty() && probe_file( child ) && false == should_use_binary_module( ref ) ) { - WriteLog( - "EU7: przestarzaly .eu7, fallback SCM: " + include_text_path( include ) ); - } - else if( !child.empty() ) { + if( !child.empty() ) { WriteLog( "EU7 include niedostepny, fallback SCM: " + include_text_path( include ) ); } @@ -458,23 +454,9 @@ text_source_path( std::string const &reference ) { return {}; } -bool -text_module_is_newer_than_binary( std::string const &reference ) { - auto const text { text_source_path( reference ) }; - if( text.empty() || false == FileExists( text ) ) { - return false; - } - auto const eu7 { binary_path( reference ) }; - if( false == FileExists( eu7 ) ) { - return true; - } - return last_modified( text ) > last_modified( eu7 ); -} - bool should_use_binary_module( std::string const &reference ) { - auto const eu7 { binary_path( reference ) }; - return probe_file( eu7 ) && false == text_module_is_newer_than_binary( reference ); + return probe_file( binary_path( reference ) ); } bool diff --git a/scene/eu7/eu7_loader.h b/scene/eu7/eu7_loader.h index 02394f2d..2fe04de3 100644 --- a/scene/eu7/eu7_loader.h +++ b/scene/eu7/eu7_loader.h @@ -75,13 +75,9 @@ namespace eu7 { [[nodiscard]] std::string text_source_path( std::string const &Reference ); -// Tekst istnieje i jest nowszy niz .eu7 (lub brak .eu7). +// Istniejacy, poprawny plik .eu7 obok referencji tekstowej. [[nodiscard]] bool - text_module_is_newer_than_binary( std::string const &Reference ); - -// Istniejacy, poprawny .eu7 nie starszy niz odpowiednik tekstowy. -[[nodiscard]] bool - should_use_binary_module( std::string const &Reference ); +should_use_binary_module( std::string const &Reference ); // Czy plik zawiera chunk TERR. [[nodiscard]] bool diff --git a/scene/eu7/eu7_model_prefetch.h b/scene/eu7/eu7_model_prefetch.h index 52358b29..e7113982 100644 --- a/scene/eu7/eu7_model_prefetch.h +++ b/scene/eu7/eu7_model_prefetch.h @@ -1,33 +1,33 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -#include "scene/eu7/eu7_types.h" - -#include -#include -#include -#include - -namespace scene::eu7 { - -// Tylko warm_instanceable_cache dla juz zcache'owanych meshy (bez cold GetModel na workerze). -void -preload_pack_models( std::vector const &Models ); - -// Main thread: Fetch_Material dla unikalnych texture_file z chunka przed apply. -// Zwraca liczbe unikalnych Fetch_Material w tym slice. -std::size_t -warm_pack_textures_main( Eu7Model const *models, std::size_t count ); - -void -reset_pack_texture_warm_cache(); - -} // namespace scene::eu7 +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "scene/eu7/eu7_types.h" + +#include +#include +#include +#include + +namespace scene::eu7 { + +// Tylko warm_instanceable_cache dla juz zcache'owanych meshy (bez cold GetModel na workerze). +void +preload_pack_models( std::vector const &Models ); + +// Main thread: Fetch_Material dla unikalnych texture_file z chunka przed apply. +// Zwraca liczbe unikalnych Fetch_Material w tym slice. +std::size_t +warm_pack_textures_main( Eu7Model const *models, std::size_t count ); + +void +reset_pack_texture_warm_cache(); + +} // namespace scene::eu7 diff --git a/scene/eu7/eu7_reader.cpp b/scene/eu7/eu7_reader.cpp index 35d93320..46a8a77b 100644 --- a/scene/eu7/eu7_reader.cpp +++ b/scene/eu7/eu7_reader.cpp @@ -497,26 +497,39 @@ parse_pack_section_header( Eu7PackIndexEntry const &entry, Eu7PackSectionCursor &cursor ) { cursor = {}; - input.seekg( - static_cast( module.pack_payload_offset + entry.pack_offset ) ); + auto const section_start { + static_cast( module.pack_payload_offset + entry.pack_offset ) }; + input.seekg( section_start ); const auto peek { input.peek() }; const std::uint8_t first_byte { peek == EOF ? std::uint8_t { 0 } : static_cast( peek ) }; - if( first_byte == kPackSectionFormatV8 ) { - cursor.section_format = sn_utils::d_uint8( input ); - const std::uint32_t solo_total { sn_utils::ld_uint32( input ) }; - const std::uint32_t inst_total { sn_utils::ld_uint32( input ) }; - cursor.model_total = solo_total + inst_total; - cursor.solo_remaining = solo_total; - cursor.inst_remaining = inst_total; - } - else if( first_byte == 2 ) { + if( first_byte == 2 ) { throw std::runtime_error( "EU7 PACK: format v9 nieobslugiwany — przebake plik .eu7" ); } - else { + + // v8 header starts with byte 1 — same as kNodeFlagHasName on flat v7 models. + // Accept v8 only when PROT exists and solo+inst counts match PIDX model_count. + bool use_v8_header { false }; + if( + first_byte == kPackSectionFormatV8 && + false == module.model_prototypes.empty() ) { + cursor.section_format = sn_utils::d_uint8( input ); + const std::uint32_t solo_total { sn_utils::ld_uint32( input ) }; + const std::uint32_t inst_total { sn_utils::ld_uint32( input ) }; + if( solo_total + inst_total == entry.model_count ) { + use_v8_header = true; + cursor.model_total = solo_total + inst_total; + cursor.solo_remaining = solo_total; + cursor.inst_remaining = inst_total; + } + } + + if( false == use_v8_header ) { + input.seekg( section_start ); + cursor.section_format = 0; cursor.model_total = entry.model_count; cursor.solo_remaining = entry.model_count; cursor.inst_remaining = 0; @@ -539,8 +552,10 @@ read_pack_models_chunk_impl( std::vector models; auto const remaining { - static_cast( cursor.solo_remaining ) + - static_cast( cursor.inst_remaining ) }; + std::min( + static_cast( cursor.solo_remaining ) + + static_cast( cursor.inst_remaining ), + static_cast( cursor.model_total ) ) }; models.reserve( std::min( max_count, remaining ) ); while( @@ -1104,46 +1119,4 @@ read_pack_section( Eu7Module const &module, int const row, int const column ) { return read_pack_section_impl( module, row, column ); } -void -seek_pack_section( - Eu7Module const &module, - std::istream &input, - Eu7PackIndexEntry const &entry, - Eu7PackSectionCursor &cursor ) { - if( false == module.has_pack_chunk ) { - cursor = {}; - return; - } - parse_pack_section_header( module, input, entry, cursor ); -} - -std::vector -read_pack_models_chunk( - Eu7Module const &module, - std::istream &input, - Eu7PackSectionCursor &cursor, - std::size_t const max_count ) { - StringTable const strings { module.strings }; - return read_pack_models_chunk_impl( module, input, cursor, max_count, strings ); -} - -void -resume_pack_section( - Eu7Module const &module, - std::istream &input, - Eu7PackIndexEntry const &entry, - std::uint64_t const resume_byte_offset, - Eu7PackSectionCursor const resume_cursor, - Eu7PackSectionCursor &cursor ) { - if( false == module.has_pack_chunk ) { - cursor = {}; - return; - } - - input.seekg( - static_cast( - module.pack_payload_offset + entry.pack_offset + resume_byte_offset ) ); - cursor = resume_cursor; -} - } // namespace scene::eu7 diff --git a/scene/eu7/eu7_reader.h b/scene/eu7/eu7_reader.h index 0cdc4286..40a984aa 100644 --- a/scene/eu7/eu7_reader.h +++ b/scene/eu7/eu7_reader.h @@ -11,7 +11,6 @@ http://mozilla.org/MPL/2.0/. #include "scene/eu7/eu7_types.h" -#include #include #include #include @@ -32,30 +31,4 @@ find_pack_entry( Eu7Module const &Module, int Row, int Column ); [[nodiscard]] std::vector read_pack_section( Eu7Module const &Module, int Row, int Column ); -// Seek do pack_offset z PIDX i parsuj naglowek sekcji (v7/v8). -void -seek_pack_section( - Eu7Module const &Module, - std::istream &Input, - Eu7PackIndexEntry const &Entry, - Eu7PackSectionCursor &Cursor ); - -// Odczyt do max_count modeli z biezacej pozycji strumienia (po seek_pack_section). -[[nodiscard]] std::vector -read_pack_models_chunk( - Eu7Module const &Module, - std::istream &Input, - Eu7PackSectionCursor &Cursor, - std::size_t MaxCount ); - -// Wznowienie odczytu sub-chunka (offset wzgledem pack_offset sekcji z PIDX). -void -resume_pack_section( - Eu7Module const &Module, - std::istream &Input, - Eu7PackIndexEntry const &Entry, - std::uint64_t ResumeByteOffset, - Eu7PackSectionCursor ResumeCursor, - Eu7PackSectionCursor &Cursor ); - } // namespace scene::eu7 diff --git a/scene/eu7/eu7_section_stream.cpp b/scene/eu7/eu7_section_stream.cpp index 2eb5dd47..827e4a9a 100644 --- a/scene/eu7/eu7_section_stream.cpp +++ b/scene/eu7/eu7_section_stream.cpp @@ -44,7 +44,7 @@ namespace scene::eu7 { namespace { constexpr double kDrainBudgetMs { 12.0 }; -constexpr double kLoaderDrainBudgetMs { 48.0 }; +constexpr double kLoaderDrainBudgetMs { 96.0 }; constexpr std::size_t kLoaderSectionsPerDrain { 8 }; constexpr double kGameplayApplyBudgetMs { 4.0 }; @@ -72,7 +72,6 @@ constexpr float kStationaryCatchupRingThreshold { 0.70f }; constexpr double kStationaryCatchupSpeedMps { 5.0 }; constexpr std::size_t kCatchupMaxInFlightSections { 20 }; constexpr std::size_t kCatchupMaxReadySections { 8 }; -constexpr std::size_t kPackWorkerSubChunkModels { 512 }; constexpr std::size_t kBootstrapDrainMs { 32 }; constexpr std::size_t kBootstrapTimeoutMs { 120000 }; constexpr std::chrono::milliseconds kPresentableHoldMs { 200 }; @@ -90,10 +89,6 @@ struct PackSectionJob { int column { 0 }; std::size_t section_idx { 0 }; int priority { 0 }; - std::uint64_t resume_byte_offset { 0 }; - std::uint32_t subchunk_index { 0 }; - Eu7PackSectionCursor header_cursor {}; - Eu7PackSectionCursor resume_cursor {}; }; struct PackSectionReady { @@ -102,8 +97,7 @@ struct PackSectionReady { std::size_t section_idx { 0 }; std::unique_ptr> models; bool failed { false }; - bool section_final { true }; - std::uint32_t subchunk_index { 0 }; + std::size_t apply_offset { 0 }; }; struct SectionStreamState { @@ -123,6 +117,8 @@ struct SectionStreamState { std::size_t pending_apply_offset { 0 }; glm::dvec3 last_enqueue_position {}; bool has_last_enqueue_position { false }; + glm::dvec3 anchor_position {}; + bool has_anchor_position { false }; std::unordered_map mesh_cache; std::unordered_map nodedata_cache; @@ -165,7 +161,10 @@ void finalize_section( PackSectionReady const &batch ); void -fail_section( std::size_t const section_idx ); +fail_section( std::size_t const section_idx, int const row = -1, int const column = -1 ); + +void +enqueue_failed_section( PackSectionJob const &job ); void release_pending_buffer(); @@ -194,6 +193,12 @@ stream_worker_count() { return std::min( requested, kMaxPackStreamWorkers ); } +void +reset_stream_fields(); + +void +note_apply_progress(); + void reset_stream_fields() { g_stream.module = nullptr; @@ -210,9 +215,12 @@ reset_stream_fields() { g_stream.pending_apply.reset(); g_stream.pending_apply_offset = 0; g_stream.has_last_enqueue_position = false; + g_stream.anchor_position = {}; + g_stream.has_anchor_position = false; g_stream.mesh_cache.clear(); g_stream.nodedata_cache.clear(); reset_pack_texture_warm_cache(); + note_apply_progress(); } [[nodiscard]] Eu7Module const & @@ -223,14 +231,6 @@ stream_module() { return *g_stream.module; } -[[nodiscard]] std::string const & -pack_stream_file_path() { - if( false == g_stream.path.empty() ) { - return g_stream.path; - } - return stream_module().source_path; -} - void release_pending_buffer() { if( false == g_stream.pending_apply.has_value() ) { @@ -315,10 +315,7 @@ adaptive_slice_instances( std::size_t const section_total ) { limit = std::min( limit, kCatchupSliceInstances ); } else if( speed > 600.0 ) { - limit = std::min( limit, std::size_t { 96 } ); - } - else if( speed > 300.0 ) { - limit = std::min( limit, std::size_t { 80 } ); + limit = std::min( limit, std::size_t { 128 } ); } else { limit = std::min( limit, std::size_t { 96 } ); @@ -334,19 +331,13 @@ adaptive_slice_instances( std::size_t const section_total ) { else if( section_total > 800 ) { limit = std::min( limit, std::size_t { 80 } ); } - else if( section_total <= kPackWorkerSubChunkModels ) { - limit = std::min( limit, std::size_t { 64 } ); - if( g_stream_catchup && camera_stream_speed_mps() > 300.0 ) { - limit = std::min( limit, std::size_t { 48 } ); - } - } auto const last_ms { pack_bench_stream().last_chunk_ms }; if( last_ms >= 24.0 ) { limit = std::min( limit, std::size_t { 32 } ); } else if( last_ms >= 12.0 ) { - limit = std::min( limit, std::size_t { 48 } ); + limit = std::min( limit, std::size_t { 64 } ); } return std::max( limit, std::size_t { 16 } ); @@ -354,12 +345,8 @@ adaptive_slice_instances( std::size_t const section_total ) { [[nodiscard]] std::size_t adaptive_cold_meshes() { - auto const &stream { pack_bench_stream() }; - if( stream.last_chunk_ms >= 40.0 || stream.peak_chunk_ms >= 80.0 ) { - return 0; - } auto limit { gameplay_slice_cold_meshes() }; - if( stream.last_chunk_ms >= 12.0 ) { + if( pack_bench_stream().last_chunk_ms >= 12.0 ) { limit = 1; } return limit; @@ -432,29 +419,82 @@ preload_slice_cold_meshes( return 0; } replace_slashes( model_file ); - if( g_stream.mesh_cache.contains( model_file ) ) { - slice_count = 1; - return 0; + if( false == g_stream.mesh_cache.contains( model_file ) ) { + TModel3d *mesh { nullptr }; + { + PackBenchTimer const load_timer { &Eu7PackBench::main_cold_getmodel_ms }; + mesh = TModelsManager::GetModel( model_file, false, false ); + pack_bench_inc( &Eu7PackBench::main_cold_getmodel_calls ); + } + if( mesh != nullptr ) { + TAnimModel::warm_instanceable_cache( mesh ); + } + g_stream.mesh_cache.emplace( model_file, mesh ); + cold_loaded = 1; } - if( max_cold_meshes == 0 ) { - return 0; - } - TModel3d *mesh { nullptr }; - { - PackBenchTimer const load_timer { &Eu7PackBench::main_cold_getmodel_ms }; - mesh = TModelsManager::GetModel( model_file, false, false ); - pack_bench_inc( &Eu7PackBench::main_cold_getmodel_calls ); - } - if( mesh != nullptr ) { - TAnimModel::warm_instanceable_cache( mesh ); - } - g_stream.mesh_cache.emplace( model_file, mesh ); slice_count = 1; - cold_loaded = 1; } return cold_loaded; } +constexpr int kApplyStuckSkipFrames { 20 }; + +std::size_t g_apply_stuck_offset { std::numeric_limits::max() }; +int g_apply_stuck_frames { 0 }; + +void +note_apply_progress() { + g_apply_stuck_offset = std::numeric_limits::max(); + g_apply_stuck_frames = 0; +} + +[[nodiscard]] bool +maybe_skip_stuck_apply_offset() { + if( false == g_stream.pending_apply.has_value() ) { + return false; + } + + auto const offset { g_stream.pending_apply_offset }; + if( offset == g_apply_stuck_offset ) { + ++g_apply_stuck_frames; + } + else { + g_apply_stuck_offset = offset; + g_apply_stuck_frames = 0; + } + + if( g_apply_stuck_frames < kApplyStuckSkipFrames ) { + return false; + } + + auto const total { + g_stream.pending_apply->models != nullptr ? + g_stream.pending_apply->models->size() : + 0 }; + if( offset >= total ) { + return false; + } + + auto model_file { ( *g_stream.pending_apply->models )[ offset ].model_file }; + WriteLog( + "EU7 PACK: skip stuck model offset=" + std::to_string( offset ) + "/" + + std::to_string( total ) + " sec=" + std::to_string( g_stream.pending_apply->row ) + "," + + std::to_string( g_stream.pending_apply->column ) + + ( model_file.empty() ? "" : " file=\"" + model_file + "\"" ) ); + if( false == model_file.empty() && model_file != "notload" ) { + replace_slashes( model_file ); + g_stream.mesh_cache.emplace( std::move( model_file ), nullptr ); + } + g_stream.pending_apply_offset = offset + 1; + note_apply_progress(); + return true; +} + +void +maybe_preempt_distant_pending() { + // Wyłączone — najpierw stabilność: dokończ bieżącą sekcję zamiast preemptować. +} + [[nodiscard]] bool apply_pending_chunk( double const budget_ms, @@ -462,9 +502,16 @@ apply_pending_chunk( std::size_t const max_cold_meshes, double const cold_budget_ms, std::size_t const max_chunks ) { + (void)max_instances; + (void)max_cold_meshes; + (void)cold_budget_ms; if( g_stream.serializer == nullptr ) { return false; } + if( maybe_skip_stuck_apply_offset() ) { + return true; + } + maybe_preempt_distant_pending(); if( false == g_stream.pending_apply.has_value() && false == try_dequeue_ready_batch() ) { return false; } @@ -483,7 +530,7 @@ apply_pending_chunk( auto &batch { *g_stream.pending_apply }; if( batch.failed || batch.models == nullptr || batch.models->empty() ) { if( batch.failed ) { - fail_section( batch.section_idx ); + fail_section( batch.section_idx, batch.row, batch.column ); } else { finalize_section( batch ); @@ -497,21 +544,15 @@ apply_pending_chunk( auto const total { batch.models->size() }; auto const offset { g_stream.pending_apply_offset }; if( offset >= total ) { - if( batch.section_final ) { - finalize_section( batch ); - } + finalize_section( batch ); release_pending_buffer(); applied_work = true; continue; } - auto const effective_instances { - std::min( max_instances, adaptive_slice_instances( total ) ) }; - auto const effective_cold { - std::min( max_cold_meshes, adaptive_cold_meshes() ) }; + // Sekcja 1 km (komorka TCP) — apply w calosci, bez dzielenia na plasterki. auto const remaining { total - offset }; - auto const chunk_cap { std::min( effective_instances, remaining ) }; - std::size_t chunk_count { chunk_cap }; + std::size_t chunk_count { remaining }; auto const chunk_started { std::chrono::steady_clock::now() }; double cold_ms { 0.0 }; @@ -524,15 +565,18 @@ apply_pending_chunk( auto const phase_started { std::chrono::steady_clock::now() }; cold_loads = preload_slice_cold_meshes( models_ptr + offset, - chunk_cap, - effective_cold, - cold_budget_ms, + remaining, + remaining, + 0.0, chunk_count ); cold_ms = std::chrono::duration( std::chrono::steady_clock::now() - phase_started ).count(); } if( chunk_count == 0 ) { - return applied_work; + g_stream.pending_apply_offset = offset + 1; + note_apply_progress(); + applied_work = true; + continue; } { @@ -582,13 +626,12 @@ apply_pending_chunk( } load_stats().pack_models += chunk_count; g_stream.pending_apply_offset = offset + chunk_count; + note_apply_progress(); applied_work = true; ++chunks_done; if( g_stream.pending_apply_offset >= total ) { - if( batch.section_final ) { - finalize_section( batch ); - } + finalize_section( batch ); release_pending_buffer(); continue; } @@ -712,134 +755,46 @@ worker_loop( std::stop_token const stop_token ) { auto const &module { stream_module() }; auto const entry { find_pack_entry( module, job.row, job.column ) }; if( false == entry.has_value() || entry->model_count == 0 ) { - PackSectionReady result; - result.row = job.row; - result.column = job.column; - result.section_idx = job.section_idx; - result.failed = true; - result.section_final = true; - { - std::lock_guard lock { g_stream.ready.mutex }; - g_stream.ready.data.push_back( std::move( result ) ); - } - pack_bench_inc( &Eu7PackBench::worker_failures ); + enqueue_failed_section( job ); continue; } - auto const &pack_path { pack_stream_file_path() }; - if( pack_path.empty() ) { - throw std::runtime_error( "EU7 PACK: brak sciezki pliku PACK" ); - } - - std::ifstream input { pack_path, std::ios::binary }; - if( !input ) { - throw std::runtime_error( - "EU7 PACK: nie mozna otworzyc \"" + pack_path + "\"" ); - } - - Eu7PackSectionCursor header_cursor {}; - Eu7PackSectionCursor cursor {}; - if( job.resume_byte_offset == 0 ) { - seek_pack_section( module, input, *entry, header_cursor ); - cursor = header_cursor; - } - else { - header_cursor = job.header_cursor; - resume_pack_section( - module, - input, - *entry, - job.resume_byte_offset, - job.resume_cursor, - cursor ); - } - - auto batch_index { job.subchunk_index }; - bool section_completed { false }; - for( ;; ) { - if( cursor.solo_remaining == 0 && cursor.inst_remaining == 0 ) { - break; - } - - if( ready_queue_size() >= g_stream_max_ready ) { - PackSectionJob resume_job { job }; - resume_job.header_cursor = header_cursor; - resume_job.resume_byte_offset = static_cast( - input.tellg() - static_cast( - module.pack_payload_offset + entry->pack_offset ) ); - resume_job.subchunk_index = batch_index; - resume_job.resume_cursor = cursor; - { - std::lock_guard lock { g_stream.jobs.mutex }; - g_stream.jobs.data.push_front( std::move( resume_job ) ); - } - g_stream.work_cv.notify_all(); - break; - } - - std::unique_ptr> models; - { - PackBenchTimer const read_timer { &Eu7PackBench::worker_read_pack_ms }; - auto chunk { - read_pack_models_chunk( - module, - input, - cursor, - kPackWorkerSubChunkModels ) }; - if( chunk.empty() ) { - break; - } - models = std::make_unique>( std::move( chunk ) ); - } - - section_completed = - cursor.solo_remaining == 0 && cursor.inst_remaining == 0; - - PackSectionReady result; - result.row = job.row; - result.column = job.column; - result.section_idx = job.section_idx; - result.models = std::move( models ); - result.subchunk_index = batch_index; - result.section_final = section_completed; - - if( result.models != nullptr ) { - pack_bench_inc( - &Eu7PackBench::worker_models_decoded, result.models->size() ); - } - - { - std::lock_guard lock { g_stream.ready.mutex }; - g_stream.ready.data.push_back( std::move( result ) ); - } - - ++batch_index; - - if( section_completed ) { - break; + std::unique_ptr> models; + { + PackBenchTimer const read_timer { &Eu7PackBench::worker_read_pack_ms }; + auto section { read_pack_section( module, job.row, job.column ) }; + if( false == section.empty() ) { + models = std::make_unique>( std::move( section ) ); } } - if( section_completed ) { - pack_bench_inc( &Eu7PackBench::worker_sections_done ); + PackSectionReady result; + result.row = job.row; + result.column = job.column; + result.section_idx = job.section_idx; + result.models = std::move( models ); + result.failed = result.models == nullptr; + + if( result.failed ) { + enqueue_failed_section( job ); + continue; } + + pack_bench_inc( + &Eu7PackBench::worker_models_decoded, result.models->size() ); + + { + std::lock_guard lock { g_stream.ready.mutex }; + g_stream.ready.data.push_back( std::move( result ) ); + } + + pack_bench_inc( &Eu7PackBench::worker_sections_done ); } catch( std::exception const &ex ) { ErrorLog( std::string{ "EU7 PACK: sekcja " + std::to_string( job.row ) + "," + std::to_string( job.column ) + ": " } + ex.what() ); - fail_section( job.section_idx ); - PackSectionReady result; - result.row = job.row; - result.column = job.column; - result.section_idx = job.section_idx; - result.failed = true; - result.section_final = true; - { - std::lock_guard lock { g_stream.ready.mutex }; - g_stream.ready.data.push_back( std::move( result ) ); - } - pack_bench_inc( &Eu7PackBench::worker_failures ); + enqueue_failed_section( job ); } } } @@ -894,9 +849,6 @@ start_workers() { void finalize_section( PackSectionReady const &batch ) { - if( false == batch.section_final ) { - return; - } g_stream.loaded_sections.insert( batch.section_idx ); g_stream.in_flight_sections.erase( batch.section_idx ); ++load_stats().pack_sections_loaded; @@ -904,11 +856,43 @@ finalize_section( PackSectionReady const &batch ) { } void -fail_section( std::size_t const section_idx ) { +fail_section( + std::size_t const section_idx, + int const row, + int const column ) { + if( g_stream.loaded_sections.contains( section_idx ) ) { + return; + } + + if( row >= 0 && column >= 0 ) { + WriteLog( + "EU7 PACK: failed section " + std::to_string( row ) + "," + + std::to_string( column ) ); + } + else { + WriteLog( "EU7 PACK: failed section idx=" + std::to_string( section_idx ) ); + } + g_stream.loaded_sections.insert( section_idx ); g_stream.in_flight_sections.erase( section_idx ); } +void +enqueue_failed_section( PackSectionJob const &job ) { + PackSectionReady result; + result.row = job.row; + result.column = job.column; + result.section_idx = job.section_idx; + result.failed = true; + + { + std::lock_guard lock { g_stream.ready.mutex }; + g_stream.ready.data.push_back( std::move( result ) ); + } + + pack_bench_inc( &Eu7PackBench::worker_failures ); +} + [[nodiscard]] int section_manhattan_sections( int const row, @@ -1212,35 +1196,66 @@ try_dequeue_ready_batch() { bool has_batch { false }; { std::lock_guard lock { g_stream.ready.mutex }; + for( auto it { g_stream.ready.data.begin() }; it != g_stream.ready.data.end(); ) { + if( it->failed || it->models == nullptr || it->models->empty() ) { + if( it->failed ) { + fail_section( it->section_idx, it->row, it->column ); + } + else { + finalize_section( *it ); + } + it = g_stream.ready.data.erase( it ); + continue; + } + ++it; + } + if( false == g_stream.ready.data.empty() ) { - auto const best_it { - std::min_element( - g_stream.ready.data.begin(), - g_stream.ready.data.end(), - []( PackSectionReady const &lhs, PackSectionReady const &rhs ) { - auto const lhs_dist { + auto const pick_best { + [&]( int const max_distance ) -> std::deque::iterator { + auto best_it { g_stream.ready.data.end() }; + auto best_dist { std::numeric_limits::max() }; + 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; + } + auto const dist { section_manhattan_sections( - lhs.row, - lhs.column, + it->row, + it->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 ) }; - if( lhs_dist != rhs_dist ) { - return lhs_dist < rhs_dist; + if( max_distance >= 0 && dist > max_distance ) { + continue; } - if( lhs.section_idx != rhs.section_idx ) { - return lhs.section_idx < rhs.section_idx; + if( dist < best_dist || ( dist == best_dist && best_it == g_stream.ready.data.end() ) ) { + best_dist = dist; + best_it = it; } - return lhs.subchunk_index < rhs.subchunk_index; - } ) }; - batch = std::move( *best_it ); - g_stream.ready.data.erase( best_it ); - has_batch = true; + else if( + dist == best_dist && best_it != g_stream.ready.data.end() && + it->section_idx < best_it->section_idx ) { + best_it = it; + } + } + return best_it; + } }; + + auto best_it { + pick_best( kSectionStreamGameplayRadiusKm ) }; + if( best_it == g_stream.ready.data.end() ) { + best_it = pick_best( -1 ); + } + if( + best_it == g_stream.ready.data.end() + && false == g_stream.ready.data.empty() ) { + best_it = g_stream.ready.data.begin(); + } + if( best_it != g_stream.ready.data.end() ) { + batch = std::move( *best_it ); + g_stream.ready.data.erase( best_it ); + has_batch = true; + } } } @@ -1250,7 +1265,7 @@ try_dequeue_ready_batch() { if( batch.failed || batch.models == nullptr || batch.models->empty() ) { if( batch.failed ) { - fail_section( batch.section_idx ); + fail_section( batch.section_idx, batch.row, batch.column ); } else { finalize_section( batch ); @@ -1259,7 +1274,8 @@ try_dequeue_ready_batch() { } g_stream.pending_apply = std::move( batch ); - g_stream.pending_apply_offset = 0; + g_stream.pending_apply_offset = g_stream.pending_apply->apply_offset; + g_stream.pending_apply->apply_offset = 0; return true; } @@ -1371,30 +1387,29 @@ drain_until_budget( double const budget_ms ) { void sync_stream_limits( glm::dvec3 const &world_position ) { + (void)world_position; + g_stream_catchup = false; if( false == g_loading_screen_dismissed ) { - g_stream_catchup = false; g_stream_max_in_flight = 6; g_stream_max_ready = 2; return; } - auto const inner_progress { - section_stream_ring_progress( world_position, kSectionStreamGameplayRadiusKm ) }; - g_stream_catchup = - inner_progress < 0.99f - || false == section_stream_ready_around( - world_position, - kSectionStreamGameplayRadiusKm ); - g_stream_max_in_flight = g_stream_catchup ? - kCatchupMaxInFlightSections : - kMaxInFlightSections; - g_stream_max_ready = g_stream_catchup ? - kCatchupMaxReadySections : - kMaxReadySections; + g_stream_max_in_flight = kMaxInFlightSections; + g_stream_max_ready = kMaxReadySections; } } // namespace +void +remember_stream_anchor( glm::dvec3 const &position ); + +[[nodiscard]] bool +camera_far_from_stream_anchor( glm::dvec3 const &camera_position ); + +[[nodiscard]] glm::dvec3 +resolve_section_stream_position( glm::dvec3 const &hint ); + void init_section_stream( Eu7Module const &root_module, @@ -1434,6 +1449,8 @@ prime_section_stream( Eu7Module const &root_module ) { return; } + remember_stream_anchor( initial ); + auto const [row, column] { section_row_column( initial ) }; g_stream.center_row = row; g_stream.center_column = column; @@ -1534,12 +1551,14 @@ update_section_stream( glm::dvec3 const &world_position ) { return; } - auto const [center_row, center_column] { section_row_column( world_position ) }; + auto const stream_position { resolve_section_stream_position( world_position ) }; + + auto const [center_row, center_column] { section_row_column( stream_position ) }; auto const center_moved { center_row != g_stream.center_row || center_column != g_stream.center_column }; auto const moved_since_enqueue { g_stream.has_last_enqueue_position ? - glm::length( world_position - g_stream.last_enqueue_position ) : + glm::length( stream_position - g_stream.last_enqueue_position ) : std::numeric_limits::infinity() }; if( moved_since_enqueue > kTeleportReenqueueDistanceM ) { @@ -1553,65 +1572,47 @@ update_section_stream( glm::dvec3 const &world_position ) { reprioritize_job_queue(); } - sync_stream_limits( world_position ); + sync_stream_limits( stream_position ); - auto const ring_radius { g_stream.radius }; - auto const reenqueue_distance { - g_stream_catchup ? kCatchupReenqueueDistanceM : kReenqueueDistanceM }; - auto max_lookahead { g_stream_catchup ? 15 : 5 }; - if( g_stream_catchup ) { - auto const speed { camera_stream_speed_mps() }; - if( speed > 1200.0 ) { - max_lookahead = 25; - } - else if( speed > 600.0 ) { - max_lookahead = 20; - } - } + auto const inner_ring_ready { + section_stream_ready_around( stream_position, kSectionStreamGameplayRadiusKm ) }; + auto const ring_radius { + inner_ring_ready ? g_stream.radius : kSectionStreamGameplayRadiusKm }; + auto const reenqueue_distance { kReenqueueDistanceM }; + auto max_lookahead { inner_ring_ready ? 5 : 0 }; auto const travel_forward { guess_travel_forward() }; auto const current_section_unloaded { section_has_pack_models( center_row, center_column ) && false == g_stream.loaded_sections.contains( section_index( center_row, center_column ) ) }; - auto const inner_ring_incomplete { - current_section_unloaded - || false == section_stream_ready_around( world_position, kSectionStreamGameplayRadiusKm ) }; + auto const inner_ring_incomplete { current_section_unloaded || false == inner_ring_ready }; auto const should_reenqueue { center_moved || false == g_stream.has_last_enqueue_position || moved_since_enqueue >= reenqueue_distance }; - if( should_reenqueue || inner_ring_incomplete || g_stream_catchup ) { - enqueue_sections_around( center_row, center_column, ring_radius, world_position ); - g_stream.last_enqueue_position = world_position; + if( should_reenqueue || inner_ring_incomplete ) { + enqueue_sections_around( center_row, center_column, ring_radius, stream_position ); + g_stream.last_enqueue_position = stream_position; g_stream.has_last_enqueue_position = true; if( should_reenqueue || inner_ring_incomplete ) { pack_bench_inc( &Eu7PackBench::stream_reenqueue ); } - enqueue_movement_lookahead( - center_row, - center_column, - ring_radius, - travel_forward, - max_lookahead ); + if( max_lookahead > 0 ) { + enqueue_movement_lookahead( + center_row, + center_column, + ring_radius, + travel_forward, + max_lookahead ); + } } } [[nodiscard]] bool section_stream_drain_idle( glm::dvec3 const &world_position ) { - if( g_stream.bootstrap_pending || g_stream.bootstrap_active ) { - return false; - } - if( g_stream.pending_apply.has_value() || ready_queue_size() > 0 ) { - return false; - } - if( false == g_stream.in_flight_sections.empty() || g_stream_catchup ) { - return false; - } - if( false == section_stream_ready_around( world_position, kSectionStreamGameplayRadiusKm ) ) { - return false; - } - return load_stats().pack_models >= 500; + (void)world_position; + return false; } void @@ -1624,33 +1625,27 @@ drain_section_stream( if( false == g_stream.active ) { return; } - if( section_stream_drain_idle( Global.pCamera.Pos ) ) { + auto const stream_position { + resolve_section_stream_position( + g_loading_screen_dismissed ? + Global.pCamera.Pos : + stream_loading_position() ) }; + if( gameplay_stream_mode() && section_stream_drain_idle( stream_position ) ) { return; } - if( gameplay_stream_mode() ) { - auto apply_budget { gameplay_apply_budget_ms() }; - std::size_t max_chunks { 1 }; - if( g_stream_catchup ) { - auto const inner_ring { - section_stream_ring_progress( - Global.pCamera.Pos, - kSectionStreamGameplayRadiusKm ) }; - auto const speed { camera_stream_speed_mps() }; - if( - inner_ring < kStationaryCatchupRingThreshold - && speed < kStationaryCatchupSpeedMps ) { - max_chunks = 2; - apply_budget = std::max( apply_budget, kCatchupApplyBudgetMs * 1.25 ); - } - } - drain_apply_budget( - apply_budget, - gameplay_slice_instances(), - gameplay_slice_cold_meshes(), - gameplay_cold_budget_ms(), - max_chunks ); - maybe_log_stream_status( Global.pCamera.Pos ); + auto const inner_ring_ready { + section_stream_ready_around( + stream_position, + kSectionStreamGameplayRadiusKm ) }; + + if( gameplay_stream_mode() && inner_ring_ready ) { + drain_until_budget( kDrainBudgetMs ); + maybe_log_stream_status( stream_position ); + } + else if( gameplay_stream_mode() ) { + drain_until_budget( kLoaderDrainBudgetMs ); + maybe_log_stream_status( stream_position ); } else if( false == g_loading_screen_dismissed ) { auto position { stream_loading_position() }; @@ -1693,6 +1688,8 @@ kick_section_stream_bootstrap() { return; } + remember_stream_anchor( position ); + auto const [row, column] { section_row_column( position ) }; g_stream.center_row = row; g_stream.center_column = column; @@ -1734,6 +1731,7 @@ preload_section_stream( double const max_drain_ms ) { if( position.x == 0.0 && position.y == 0.0 && position.z == 0.0 ) { position = guess_initial_stream_position( stream_module() ); } + position = resolve_section_stream_position( position ); if( section_stream_needs_bootstrap() ) { if( position.x != 0.0 || position.y != 0.0 || position.z != 0.0 ) { @@ -1932,6 +1930,23 @@ dismiss_loading_screen() { pack_bench_begin_stream_phase(); g_loading_screen_dismissed = true; g_ring_ready_since.reset(); + + if( g_stream.has_anchor_position ) { + auto const &saved_camera { Global.FreeCameraInit[ 0 ] }; + auto const camera_unset { + saved_camera.x == 0.0 && saved_camera.y == 0.0 && saved_camera.z == 0.0 }; + auto const camera_off_scenery { + false == camera_unset && camera_far_from_stream_anchor( saved_camera ) }; + if( camera_unset || camera_off_scenery ) { + Global.FreeCameraInit[ 0 ] = g_stream.anchor_position; + Global.pCamera.Pos = g_stream.anchor_position; + WriteLog( + "EU7 PACK: ustawiam kamere startowa na " + + std::to_string( g_stream.anchor_position.x ) + "," + + std::to_string( g_stream.anchor_position.y ) + "," + + std::to_string( g_stream.anchor_position.z ) ); + } + } } bool @@ -1978,15 +1993,24 @@ loading_screen_blocks_world( glm::dvec3 stream_loading_position() { - if( Global.pCamera.Pos.x != 0.0 || Global.pCamera.Pos.y != 0.0 || Global.pCamera.Pos.z != 0.0 ) { + if( g_loading_screen_dismissed ) { return Global.pCamera.Pos; } + auto const &camera { Global.pCamera.Pos }; + if( camera.x != 0.0 || camera.y != 0.0 || camera.z != 0.0 ) { + return camera; + } + auto position { resolve_stream_position() }; if( position.x != 0.0 || position.y != 0.0 || position.z != 0.0 ) { return position; } + if( g_stream.has_anchor_position ) { + return g_stream.anchor_position; + } + return Global.FreeCameraInit[ 0 ]; } @@ -2025,6 +2049,48 @@ section_stream_ring_progress( return std::clamp( accumulated / static_cast( pack_sections ), 0.f, 1.f ); } +void +remember_stream_anchor( glm::dvec3 const &position ) { + if( position.x == 0.0 && position.y == 0.0 && position.z == 0.0 ) { + return; + } + g_stream.anchor_position = position; + g_stream.has_anchor_position = true; +} + +[[nodiscard]] glm::dvec3 +resolve_section_stream_position( glm::dvec3 const &hint ) { + if( + hint.x == 0.0 && hint.y == 0.0 && hint.z == 0.0 + && g_stream.has_anchor_position ) { + return g_stream.anchor_position; + } + if( camera_far_from_stream_anchor( hint ) && g_stream.has_anchor_position ) { + return g_stream.anchor_position; + } + return hint; +} + +[[nodiscard]] bool +camera_far_from_stream_anchor( glm::dvec3 const &camera_position ) { + if( false == g_stream.has_anchor_position ) { + return false; + } + auto const anchor_dist { + glm::length( + glm::dvec2 { + camera_position.x - g_stream.anchor_position.x, + camera_position.z - g_stream.anchor_position.z } ) }; + auto const anchor_magnitude { + glm::length( + glm::dvec2 { g_stream.anchor_position.x, g_stream.anchor_position.z } ) }; + auto const camera_magnitude { + glm::length( glm::dvec2 { camera_position.x, camera_position.z } ) }; + return anchor_dist > 30000.0 + && camera_magnitude < 30000.0 + && anchor_magnitude > 50000.0; +} + void reset_section_stream() { flush_pack_stream_bench(); diff --git a/scene/eu7/eu7_section_stream.h b/scene/eu7/eu7_section_stream.h index a1e2cd92..49e24b8f 100644 --- a/scene/eu7/eu7_section_stream.h +++ b/scene/eu7/eu7_section_stream.h @@ -19,7 +19,7 @@ class state_serializer; namespace scene::eu7 { -constexpr int kSectionStreamBootstrapRadiusKm { 3 }; +constexpr int kSectionStreamBootstrapRadiusKm { 4 }; constexpr int kSectionStreamGameplayRadiusKm { 4 }; void @@ -34,6 +34,10 @@ prime_section_stream( Eu7Module const &RootModule ); [[nodiscard]] glm::dvec3 resolve_stream_position(); +// Kamera / pojazd moga byc w innej przestrzeni wspolrzednych niz PACK — uzyj kotwicy scenerii. +[[nodiscard]] glm::dvec3 +resolve_section_stream_position( glm::dvec3 const &Hint ); + // Opcjonalnie blokuje do zaladowania pierścienia bootstrap wokol pozycji (debug/narzedzia). void bootstrap_section_stream( glm::dvec3 const &WorldPosition ); diff --git a/scene/eu7/eu7_types.h b/scene/eu7/eu7_types.h index cbd50790..eb4d67f9 100644 --- a/scene/eu7/eu7_types.h +++ b/scene/eu7/eu7_types.h @@ -360,7 +360,7 @@ struct Eu7PackIndexEntry { std::uint64_t pack_offset = 0; }; -// Stan inkrementalnego odczytu sekcji PACK z istream (po seek_pack_section). +// Stan odczytu sekcji PACK z istream (uzywany wewnetrznie przez reader). struct Eu7PackSectionCursor { std::uint32_t solo_remaining { 0 }; std::uint32_t inst_remaining { 0 }; diff --git a/simulation/simulationstateserializer.cpp b/simulation/simulationstateserializer.cpp index e86ba3ae..9e807446 100644 --- a/simulation/simulationstateserializer.cpp +++ b/simulation/simulationstateserializer.cpp @@ -617,15 +617,16 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) { is_pure_eu7_scenario = scene::eu7::probe_file( resolved_scenario ); } - bool const has_baked_eu7_root { - false == is_pure_eu7_scenario && - scene::eu7::should_use_binary_module( Scenariofile ) }; - auto const baked_root_path { - scene::eu7::resolve_scenery_path( scene::eu7::binary_path( Scenariofile ) ) }; + bool const use_eu7_scenario { + is_pure_eu7_scenario || scene::eu7::probe_baked_scenario( Scenariofile ) }; + auto const eu7_load_path { + is_pure_eu7_scenario ? + resolved_scenario : + scene::eu7::resolve_scenery_path( scene::eu7::binary_path( Scenariofile ) ) }; // Scenariusz EU7B: nie parsujemy binarki jako tekstu SCM. std::shared_ptr state; - if( is_pure_eu7_scenario ) { + if( use_eu7_scenario ) { state = std::make_shared( std::string{}, cParser::buffer_TEXT, Global.asCurrentSceneryPath, Global.bLoadTraction ); state->scenariofile = Scenariofile; @@ -637,25 +638,12 @@ state_serializer::deserialize_begin( std::string const &Scenariofile ) { state->scratchpad.name = Scenariofile; - if( is_pure_eu7_scenario ) { + if( use_eu7_scenario ) { Global.file_binary_terrain_state = true; state->scratchpad.binary.terrain = true; state->scratchpad.binary.terrain_included = true; - WriteLog( "EU7 scenario: " + resolved_scenario ); - if( false == scene::eu7::load_module( resolved_scenario, *this ) ) { - throw invalid_scenery_exception(); - } - } - else if( has_baked_eu7_root ) { - Global.file_binary_terrain_state = true; - state->scratchpad.binary.terrain = true; - if( scene::eu7::is_scenario_terrain( Scenariofile ) ) { - state->scratchpad.binary.terrain_included = true; - } - WriteLog( - "EU7 hybrid: metadata z \"" + Scenariofile + "\", sceneria z \"" + - baked_root_path + "\"" ); - if( false == scene::eu7::load_module( baked_root_path, *this ) ) { + WriteLog( "EU7 scenario: " + eu7_load_path ); + if( false == scene::eu7::load_module( eu7_load_path, *this ) ) { throw invalid_scenery_exception(); } } diff --git a/tools/parse_minidump.py b/tools/parse_minidump.py index 43e3ec80..29f52b66 100644 --- a/tools/parse_minidump.py +++ b/tools/parse_minidump.py @@ -43,7 +43,7 @@ def read_cstring(f, rva: int) -> str: def parse(path: Path) -> None: with path.open("rb") as f: sig, ver, num_streams, dir_rva, checksum, ts, flags = struct.unpack( - "(sizeof(gl::scene_ubs), 0); + if( simulation::Train != nullptr && simulation::Train->Dynamic() != nullptr ) { + mode = MODE_VEHICLE; + } + else { + auto const &start { Global.FreeCameraInit[ 0 ] }; + if( start.x != 0.0 || start.z != 0.0 ) { + mode = MODE_CAMERA; + translate = glm::vec2( static_cast( start.x ), static_cast( start.z ) ) * -zoom; + } + } + init_done = true; } @@ -88,9 +99,6 @@ float ui::map_panel::get_vehicle_rotation() void ui::map_panel::render_map_texture(glm::mat4 transform, glm::vec2 surface_size) { - cFrustum frustum; - frustum.calculate(transform, glm::mat4()); - m_colored_paths.switches.clear(); m_colored_paths.occupied.clear(); m_colored_paths.future.clear(); @@ -102,14 +110,14 @@ void ui::map_panel::render_map_texture(glm::mat4 transform, glm::vec2 surface_si for (int column = 0; column < scene::EU07_REGIONSIDESECTIONCOUNT; column++) { scene::basic_section *section = simulation::Region->get_section(row * scene::EU07_REGIONSIDESECTIONCOUNT + column); - if (section && frustum.sphere_inside(section->area().center, section->area().radius) > 0.f) + if( section == nullptr ) { + continue; + } + const gfx::geometrybank_handle handle = section->get_map_geometry(); + if( handle != null_handle ) { - const gfx::geometrybank_handle handle = section->get_map_geometry(); - if (handle != null_handle) - { - m_section_handles.push_back(handle); - section->get_map_active_paths(m_colored_paths); - } + m_section_handles.push_back(handle); + section->get_map_active_paths(m_colored_paths); } } } @@ -230,6 +238,17 @@ void ui::map_panel::render_contents() return; } + if( simulation::Train != nullptr && simulation::Train->Dynamic() != nullptr ) { + if( mode == MODE_MANUAL ) { + mode = MODE_VEHICLE; + } + } + else if( + mode == MODE_MANUAL + && ( Global.pCamera.Pos.x != 0.0 || Global.pCamera.Pos.z != 0.0 ) ) { + mode = MODE_CAMERA; + } + { float prev_zoom = zoom;