Add partial cache generation when only one of the vendros is changed to speed up recalculation time

This commit is contained in:
ExPikaPaka
2026-06-15 09:28:59 +02:00
parent 5d0c640f7b
commit 88901a969f
3 changed files with 89 additions and 45 deletions

View File

@@ -2196,45 +2196,59 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
dir = (boost::filesystem::path(data_dir())).make_preferred();
// Try loading from binary cache first (skips JSON parsing on cache hit).
// partial_dirty_vendors is non-empty when some vendors changed but others are still cached.
std::set<std::string> partial_dirty_vendors;
if (!validation_mode) {
const auto t0 = std::chrono::steady_clock::now();
PresetBundleCache::SystemPresetsCache cache;
const std::string cache_file = PresetBundleCache::SystemPresetsCache::cache_path();
if (cache.load(cache_file) && cache.is_valid(dir.string())) {
cache.apply(*this);
update_system_maps();
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - t0).count();
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""};
}
// Try bundled cache shipped with the installer (first launch, before user cache exists).
// Validates against resources/profiles/ — the same directory it was generated from in CI.
{
const std::string bundled_dir =
(boost::filesystem::path(resources_dir()) / "profiles").make_preferred().string();
PresetBundleCache::SystemPresetsCache bundled;
if (bundled.load(PresetBundleCache::SystemPresetsCache::bundled_cache_path()) &&
bundled.is_valid(bundled_dir)) {
bundled.apply(*this);
update_system_maps();
// Promote to user cache so subsequent launches skip this check.
bundled.save(cache_file);
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - t0).count();
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from bundled cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""};
if (cache.load(cache_file)) {
std::set<std::string> dirty;
if (cache.get_dirty_vendors(dir.string(), dirty)) {
if (dirty.empty()) {
// Full hit — every vendor is unchanged.
cache.apply(*this);
update_system_maps();
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - t0).count();
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""};
}
// Partial hit — restore clean vendors from cache, re-parse only dirty ones.
BOOST_LOG_TRIVIAL(info) << "PresetBundle: partial cache hit, " << dirty.size()
<< " vendor(s) changed — re-parsing those only";
cache.apply_partial(*this, dirty);
partial_dirty_vendors = std::move(dirty);
}
// else: structural mismatch (format version or option count changed) → full re-parse.
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cache miss, falling back to JSON load";
if (partial_dirty_vendors.empty()) {
// No partial hit — try bundled cache shipped with the installer (first launch).
{
const std::string bundled_dir =
(boost::filesystem::path(resources_dir()) / "profiles").make_preferred().string();
PresetBundleCache::SystemPresetsCache bundled;
if (bundled.load(PresetBundleCache::SystemPresetsCache::bundled_cache_path()) &&
bundled.is_valid(bundled_dir)) {
bundled.apply(*this);
update_system_maps();
// Promote to user cache so subsequent launches skip this check.
bundled.save(cache_file);
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - t0).count();
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from bundled cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""};
}
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cache miss, falling back to JSON load";
}
}
const auto json_load_t0 = std::chrono::steady_clock::now();
PresetsConfigSubstitutions substitutions;
std::string errors_cummulative;
bool first = true;
bool first = partial_dirty_vendors.empty(); // false in partial mode: clean vendors already applied
std::vector<std::string> vendor_names;
// store all vendor names in vendor_names
for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) {
@@ -2256,6 +2270,9 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
std::vector<std::string> other_vendors;
other_vendors.reserve(vendor_names.size());
for (auto& vn : vendor_names) {
// In partial mode, skip vendors already loaded from cache.
if (!partial_dirty_vendors.empty() && !partial_dirty_vendors.count(vn))
continue;
if (vn == ORCA_FILAMENT_LIBRARY)
orca_lib_vendor = vn;
else if (!(validation_mode && !vendor_to_validate.empty() && vn != vendor_to_validate))

View File

@@ -124,9 +124,14 @@ std::string SystemPresetsCache::bundled_cache_path()
bool SystemPresetsCache::is_valid(const std::string& system_dir) const
{
if (format_version != FORMAT_VERSION)
return false;
if (config_options_count != print_config_def.options.size())
std::set<std::string> dummy;
return get_dirty_vendors(system_dir, dummy) && dummy.empty();
}
bool SystemPresetsCache::get_dirty_vendors(const std::string& system_dir, std::set<std::string>& out_dirty) const
{
out_dirty.clear();
if (format_version != FORMAT_VERSION || config_options_count != print_config_def.options.size())
return false;
std::map<std::string, std::string> current;
try {
@@ -143,12 +148,15 @@ bool SystemPresetsCache::is_valid(const std::string& system_dir) const
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: directory scan failed: " << e.what();
return false;
}
if (current.size() != vendor_versions.size())
return false;
// A vendor was removed from disk — safest to force a full re-parse.
for (const auto& [name, ver] : vendor_versions)
if (current.find(name) == current.end())
return false;
// Collect vendors with changed or new version strings.
for (const auto& [name, ver] : current) {
auto it = vendor_versions.find(name);
if (it == vendor_versions.end() || it->second != ver)
return false;
out_dirty.insert(name);
}
return true;
}
@@ -234,9 +242,16 @@ void SystemPresetsCache::capture(const PresetBundle& bundle, const std::string&
}
void SystemPresetsCache::apply(PresetBundle& bundle) const
{
apply_partial(bundle, {});
}
void SystemPresetsCache::apply_partial(PresetBundle& bundle, const std::set<std::string>& skip_vendor_ids) const
{
bundle.reset(false);
for (const auto& cvp : vendor_profiles) {
if (skip_vendor_ids.count(cvp.id))
continue;
VendorProfile vp(cvp.id);
vp.name = cvp.name;
vp.config_update_url = cvp.config_update_url;
@@ -273,10 +288,12 @@ void SystemPresetsCache::apply(PresetBundle& bundle) const
bundle.vendors.emplace(cvp.id, std::move(vp));
}
auto apply_col = [&bundle](const std::vector<CachedPreset>& cached,
PresetCollection& coll,
bool is_filaments) {
auto apply_col = [&](const std::vector<CachedPreset>& cached,
PresetCollection& coll,
bool is_filaments) {
for (const auto& cp : cached) {
if (skip_vendor_ids.count(cp.vendor_id))
continue;
Semver version;
if (!cp.version.empty()) {
auto v = Semver::parse(cp.version);
@@ -284,13 +301,13 @@ void SystemPresetsCache::apply(PresetBundle& bundle) const
}
DynamicPrintConfig config = cp.config;
Preset& p = coll.load_preset(cp.file, cp.name, std::move(config), /*select=*/false, version);
p.is_system = true;
p.is_visible = cp.is_visible;
p.alias = cp.alias;
p.renamed_from = cp.renamed_from;
p.filament_id = cp.filament_id;
p.setting_id = cp.setting_id;
p.description = cp.description;
p.is_system = true;
p.is_visible = cp.is_visible;
p.alias = cp.alias;
p.renamed_from = cp.renamed_from;
p.filament_id = cp.filament_id;
p.setting_id = cp.setting_id;
p.description = cp.description;
p.m_from_orca_filament_lib = cp.m_from_orca_filament_lib;
if (!cp.vendor_id.empty()) {
auto it = bundle.vendors.find(cp.vendor_id);
@@ -307,9 +324,12 @@ void SystemPresetsCache::apply(PresetBundle& bundle) const
apply_col(sla_print_presets, bundle.sla_prints, false);
apply_col(sla_material_presets, bundle.sla_materials, false);
// config_maps and filament_id_maps are derived from ORCA_FILAMENT_LIBRARY.
// If that vendor is in skip_vendor_ids it will be re-loaded from JSON and will
// overwrite these; otherwise the cached values are still valid.
bundle.m_config_maps = config_maps;
bundle.m_filament_id_maps = filament_id_maps;
// Caller must invoke bundle.update_system_maps() after this (it is private to PresetBundle).
// Caller must invoke bundle.update_system_maps() after all loading is done.
}
bool SystemPresetsCache::load(const std::string& path)

View File

@@ -1,7 +1,8 @@
#pragma once
#pragma once
#include <cstdint>
#include <map>
#include <set>
#include <string>
#include <vector>
@@ -121,8 +122,14 @@ struct SystemPresetsCache {
static std::string cache_path();
static std::string bundled_cache_path();
bool is_valid(const std::string& system_dir) const;
// Returns false on structural mismatch (full re-parse required).
// On true, populates out_dirty with vendor IDs whose version strings changed.
// Empty out_dirty means fully valid (cache hit, no re-parse needed).
bool get_dirty_vendors(const std::string& system_dir, std::set<std::string>& out_dirty) const;
void capture(const PresetBundle& bundle, const std::string& system_dir);
void apply(PresetBundle& bundle) const;
// Same as apply() but skips vendors listed in skip_vendor_ids and their presets.
void apply_partial(PresetBundle& bundle, const std::set<std::string>& skip_vendor_ids) const;
bool load(const std::string& path);
void save(const std::string& path) const;
};