diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 691d7aec81..7d56bbe5e1 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) @@ -2180,48 +2182,82 @@ std::pair PresetBundle::load_system_pre vendor_name.erase(vendor_name.size() - 5); vendor_names.push_back(vendor_name); } - // Move ORCA_FILAMENT_LIBRARY to the beginning of the list - for (size_t i = 0; i < vendor_names.size(); ++ i) { - if (vendor_names[i] == ORCA_FILAMENT_LIBRARY) { - std::swap(vendor_names[0], vendor_names[i]); - break; - } + // Separate ORCA_FILAMENT_LIBRARY from other vendors. It must be loaded + // first because other vendors' filaments may inherit from it via the + // `base_bundle` lookup in parse_subfile. The remaining vendors are + // independent (no cross-vendor inheritance) and can be loaded in parallel. + std::string orca_lib_vendor; + std::vector other_vendors; + other_vendors.reserve(vendor_names.size()); + for (auto& vn : vendor_names) { + if (vn == ORCA_FILAMENT_LIBRARY) + orca_lib_vendor = vn; + else if (!(validation_mode && !vendor_to_validate.empty() && vn != vendor_to_validate)) + other_vendors.push_back(vn); } - for (auto &vendor_name : vendor_names) - { - if (validation_mode && !vendor_to_validate.empty() && vendor_name != vendor_to_validate && vendor_name != ORCA_FILAMENT_LIBRARY) - continue; - + // Step 1: Load ORCA_FILAMENT_LIBRARY into `this` synchronously. + if (!orca_lib_vendor.empty()) { try { - // Load the config bundle, flatten it. - if (first) { - // Reset this PresetBundle and load the first vendor config. - append(substitutions, this->load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem, compatibility_rule).first); - first = false; - } else { - // Load the other vendor configs, merge them with this PresetBundle. - // Report duplicate profiles. - PresetBundle other; - append(substitutions, other.load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem, compatibility_rule, this).first); - std::vector duplicates = this->merge_presets(std::move(other)); - if (!duplicates.empty()) { - errors_cummulative += "Found duplicated settings in vendor " + vendor_name + "'s json file lists: "; - for (size_t i = 0; i < duplicates.size(); ++i) { - if (i > 0) - errors_cummulative += ", "; - errors_cummulative += duplicates[i]; - ++m_errors; - BOOST_LOG_TRIVIAL(error) << "Found duplicated preset: " + duplicates[i] + " in vendor: " + vendor_name + ": "; - } - } - } + append(substitutions, this->load_vendor_configs_from_json(dir.string(), orca_lib_vendor, PresetBundle::LoadSystem, compatibility_rule).first); + first = false; } catch (const std::runtime_error &err) { if (validation_mode) throw err; - else { - errors_cummulative += err.what(); - errors_cummulative += "\n"; + errors_cummulative += err.what(); + errors_cummulative += "\n"; + } + } + + // Step 2: Load remaining vendors in parallel. Each gets its own + // PresetBundle and uses `this` (which contains ORCA_FILAMENT_LIBRARY) + // as the base_bundle for cross-bundle inheritance lookups. + std::vector> parallel_bundles(other_vendors.size()); + std::vector parallel_substitutions(other_vendors.size()); + std::vector parallel_errors(other_vendors.size()); + + tbb::parallel_for(tbb::blocked_range(0, other_vendors.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + auto bundle = std::make_unique(); + try { + auto result = bundle->load_vendor_configs_from_json( + dir.string(), other_vendors[i], PresetBundle::LoadSystem, + compatibility_rule, this); + parallel_substitutions[i] = std::move(result.first); + parallel_bundles[i] = std::move(bundle); + } catch (const std::runtime_error &err) { + parallel_errors[i] = err.what(); + } + } + }); + + // Step 3: Sequentially merge the parallel-loaded bundles into `this`. + // The merge order is the original vendor order so any duplicate-warning + // output stays stable across runs. + for (size_t i = 0; i < other_vendors.size(); ++i) { + if (!parallel_errors[i].empty()) { + if (validation_mode) + throw std::runtime_error(parallel_errors[i]); + errors_cummulative += parallel_errors[i]; + errors_cummulative += "\n"; + continue; + } + if (!parallel_bundles[i]) + continue; + + const std::string& vendor_name = other_vendors[i]; + append(substitutions, std::move(parallel_substitutions[i])); + std::vector duplicates = this->merge_presets(std::move(*parallel_bundles[i])); + first = false; + if (!duplicates.empty()) { + errors_cummulative += "Found duplicated settings in vendor " + vendor_name + "'s json file lists: "; + for (size_t j = 0; j < duplicates.size(); ++j) { + if (j > 0) + errors_cummulative += ", "; + errors_cummulative += duplicates[j]; + ++m_errors; + BOOST_LOG_TRIVIAL(error) << "Found duplicated preset: " + duplicates[j] + " in vendor: " + vendor_name + ": "; } } } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 38c9199d56..c12fa964f8 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -278,7 +278,11 @@ class SplashScreen : public wxSplashScreen { public: SplashScreen(wxPoint pos = wxDefaultPosition) - : wxSplashScreen(wxBitmap(FromDIP(wxSize(480,480),nullptr)), wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 1500, nullptr, wxID_ANY, wxDefaultPosition, wxDefaultSize, + // No wxSPLASH_TIMEOUT — the splash is closed explicitly once MainFrame + // is shown. The previous 1500 ms auto-timeout closed the splash long + // before init finished, leaving the user staring at a frozen blank + // screen during the slow load_presets / new MainFrame phases. + : wxSplashScreen(wxBitmap(FromDIP(wxSize(480,480),nullptr)), wxSPLASH_CENTRE_ON_SCREEN, 0, nullptr, wxID_ANY, wxDefaultPosition, wxDefaultSize, #ifdef __APPLE__ wxBORDER_NONE | wxFRAME_NO_TASKBAR | wxSTAY_ON_TOP #else @@ -2758,7 +2762,7 @@ bool GUI_App::on_init_inner() //BBS use BBL splashScreen scrn = new SplashScreen(splashscreen_pos); wxYield(); - //scrn->SetText(_L("Loading configuration")+ dots); + scrn->SetText(_L("Loading configuration") + dots); } BOOST_LOG_TRIVIAL(info) << "loading systen presets..."; @@ -2933,6 +2937,7 @@ bool GUI_App::on_init_inner() // Enable all substitutions (in both user and system profiles), but log the substitutions in user profiles only. // If there are substitutions in system profiles, then a "reconfigure" event shall be triggered, which will force // installation of a compatible system preset, thus nullifying the system preset substitutions. + if (scrn) { scrn->SetText(_L("Loading printer & filament profiles") + dots); wxYield(); } init_params->preset_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSystemSilent); } catch (const std::exception& ex) { @@ -2961,6 +2966,7 @@ bool GUI_App::on_init_inner() } #endif + if (scrn) { scrn->SetText(_L("Creating main window") + dots); wxYield(); } BOOST_LOG_TRIVIAL(info) << "create the main window"; mainframe = new MainFrame(); // hide settings tabs after first Layout @@ -2985,8 +2991,10 @@ bool GUI_App::on_init_inner() // ensure the selected technology is ptFFF plater_->set_printer_technology(ptFFF); } - else + else { + if (scrn) { scrn->SetText(_L("Loading current preset") + dots); wxYield(); } load_current_presets(); + } if (plater_ != nullptr) { plater_->reset_project_dirty_initial_presets(); @@ -2998,7 +3006,10 @@ bool GUI_App::on_init_inner() #ifdef __WINDOWS__ mainframe->topbar()->SaveNormalRect(); #endif + if (scrn) { scrn->SetText(_L("Showing main window") + dots); wxYield(); } mainframe->Show(true); + // Close the splash now that the main UI is visible. + if (scrn) { scrn->Destroy(); scrn = nullptr; } BOOST_LOG_TRIVIAL(info) << "main frame firstly shown"; //#if BBL_HAS_FIRST_PAGE