From 5823c8369132a8c3e9cdedfb7f9080cdd1a18efa Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Sun, 10 May 2026 08:24:22 +0100 Subject: [PATCH] Generate lower resolution color preview first when 'preview result colors' is enabled --- src/slic3r/GUI/GLCanvas3D.cpp | 5 +- src/slic3r/GUI/MMUPaintedTexturePreview.cpp | 299 +++++++++++++++----- src/slic3r/GUI/MMUPaintedTexturePreview.hpp | 1 + 3 files changed, 233 insertions(+), 72 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b0300250500..c31e0c1a4da 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7961,6 +7961,7 @@ void GLCanvas3D::_render_overlays() m_labels.render(sorted_instances); if (Slic3r::texture_preview_simulation_is_pending()) { + const bool temporary_texture_preview_pending = Slic3r::texture_preview_simulation_has_temporary_pending(); ImGuiWrapper &imgui = *wxGetApp().imgui(); const Size cnv_size = get_canvas_size(); const float scale = imgui.get_style_scaling(); @@ -7977,7 +7978,9 @@ void GLCanvas3D::_render_overlays() ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - imgui.text(_L("Generating simulated color preview...")); + imgui.text(temporary_texture_preview_pending ? + _L("Generating simulated color preview (1/2)...") : + _L("Generating simulated color preview (2/2)...")); imgui.end(); ImGui::PopStyleColor(2); ImGui::PopStyleVar(2); diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index 24d295b0c03..5ace4851204 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,9 @@ constexpr float k_polygon_offset_units = -1.f; constexpr float k_epsilon = 1e-6f; constexpr unsigned int k_simulated_texture_preview_max_edge = 2048; constexpr size_t k_simulated_texture_preview_max_pixels = 2048ull * 2048ull; +constexpr unsigned int k_temporary_simulated_texture_preview_max_edge = 1024; +constexpr size_t k_temporary_simulated_texture_preview_max_pixels = 1024ull * 1024ull; +constexpr size_t k_temporary_simulated_texture_preview_min_source_pixels = k_temporary_simulated_texture_preview_max_pixels * 3 / 2; constexpr const char *TEXTURE_MAPPING_BACKGROUND_COLOR_CONFIG_KEY = "texture_mapping_background_color"; struct TexturePreviewMixCandidate @@ -54,6 +58,12 @@ struct TexturePreviewMixCandidateKdNode uint8_t axis { 0 }; }; +struct TexturePreviewGenericMixCandidateCache +{ + std::once_flag init; + ColorSolverCandidateSet candidates; +}; + struct TexturePreviewSimulationSettings { int mapping_mode = int(TextureMappingZone::TextureMappingFilamentBlending); @@ -72,7 +82,7 @@ struct TexturePreviewSimulationSettings std::vector> component_colors; std::vector component_strength_factors; std::vector semantic_component_indices; - ColorSolverCandidateSet generic_mix_candidates; + std::shared_ptr generic_mix_candidate_cache; }; struct SurfaceGradientPreviewSettings @@ -107,13 +117,21 @@ struct TexturePreviewSimulationResult std::vector rgba; }; +struct TexturePreviewSimulationJobState +{ + std::mutex mutex; + std::optional temporary_result; +}; + struct TexturePreviewSimulationCacheEntry { std::unique_ptr texture; size_t uploaded_signature { 0 }; size_t pending_signature { 0 }; + size_t temporary_pending_signature { 0 }; bool ready_frame_requested { false }; std::future pending_future; + std::shared_ptr pending_job_state; }; struct TexturePreviewVertexColorSimulationResult @@ -448,28 +466,31 @@ void configure_texture_preview_sampler(const GUI::GLTexture &texture) glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } -std::array limited_simulated_texture_preview_size(unsigned int width, unsigned int height) +std::array limited_simulated_texture_preview_size(unsigned int width, + unsigned int height, + unsigned int max_edge = k_simulated_texture_preview_max_edge, + size_t max_pixels = k_simulated_texture_preview_max_pixels) { if (width == 0 || height == 0) return { 0, 0 }; double scale = 1.0; - const unsigned int max_edge = std::max(width, height); - if (max_edge > k_simulated_texture_preview_max_edge) - scale = std::min(scale, double(k_simulated_texture_preview_max_edge) / double(max_edge)); + const unsigned int source_max_edge = std::max(width, height); + if (source_max_edge > max_edge) + scale = std::min(scale, double(max_edge) / double(source_max_edge)); const double pixel_count = double(width) * double(height); - if (pixel_count * scale * scale > double(k_simulated_texture_preview_max_pixels)) - scale = std::min(scale, std::sqrt(double(k_simulated_texture_preview_max_pixels) / pixel_count)); + if (pixel_count * scale * scale > double(max_pixels)) + scale = std::min(scale, std::sqrt(double(max_pixels) / pixel_count)); if (scale >= 1.0) return { width, height }; unsigned int limited_width = std::max(1u, unsigned(std::lround(double(width) * scale))); unsigned int limited_height = std::max(1u, unsigned(std::lround(double(height) * scale))); - while (limited_width > k_simulated_texture_preview_max_edge || - limited_height > k_simulated_texture_preview_max_edge || - size_t(limited_width) * size_t(limited_height) > k_simulated_texture_preview_max_pixels) { + while (limited_width > max_edge || + limited_height > max_edge || + size_t(limited_width) * size_t(limited_height) > max_pixels) { if (limited_width >= limited_height && limited_width > 1) --limited_width; else if (limited_height > 1) @@ -480,6 +501,47 @@ std::array limited_simulated_texture_preview_size(unsigned int return { limited_width, limited_height }; } +std::array simulated_texture_preview_size(unsigned int width, + unsigned int height, + const TexturePreviewSimulationSettings &settings, + unsigned int max_edge = 0, + size_t max_pixels = 0) +{ + if (max_edge > 0 && max_pixels > 0) + return limited_simulated_texture_preview_size(width, height, max_edge, max_pixels); + return settings.limit_texture_resolution ? + limited_simulated_texture_preview_size(width, height) : + std::array{ width, height }; +} + +bool simulated_texture_preview_needs_temporary_result(unsigned int width, + unsigned int height, + const TexturePreviewSimulationSettings &settings) +{ + const size_t source_pixels = size_t(width) * size_t(height); + if (source_pixels <= k_temporary_simulated_texture_preview_min_source_pixels) + return false; + + const std::array final_size = simulated_texture_preview_size(width, height, settings); + const std::array temporary_size = + simulated_texture_preview_size(width, + height, + settings, + k_temporary_simulated_texture_preview_max_edge, + k_temporary_simulated_texture_preview_max_pixels); + return size_t(final_size[0]) * size_t(final_size[1]) > size_t(temporary_size[0]) * size_t(temporary_size[1]); +} + +size_t temporary_simulated_texture_preview_signature(size_t signature) +{ + signature ^= 0x4a7c15f17e315123ull + (signature << 6) + (signature >> 2); + signature ^= std::hash{}(k_temporary_simulated_texture_preview_max_edge) + 0x9e3779b97f4a7c15ull + + (signature << 6) + (signature >> 2); + signature ^= std::hash{}(k_temporary_simulated_texture_preview_max_pixels) + 0x9e3779b97f4a7c15ull + + (signature << 6) + (signature >> 2); + return signature; +} + std::array sample_texture_preview_rgba_bilinear(const std::vector &rgba, unsigned int width, unsigned int height, @@ -1390,9 +1452,10 @@ std::vector component_weights_for_texture_preview(const TexturePreviewSim if (optimized.size() == component_count) desired = std::move(optimized); else { - std::vector best = !settings.generic_mix_candidates.empty() ? + std::vector best = settings.generic_mix_candidate_cache != nullptr && + !settings.generic_mix_candidate_cache->candidates.empty() ? solve_color_solver_weights_for_target( - settings.generic_mix_candidates, + settings.generic_mix_candidate_cache->candidates, target, color_solver_lookup_mode_from_index(settings.generic_solver_lookup_mode), color_solver_mode_from_index(settings.generic_solver_mode)) : @@ -1427,12 +1490,18 @@ void prepare_texture_preview_simulation_settings(TexturePreviewSimulationSetting semantic_component_indices_for_texture_preview(settings.component_colors, settings.filament_color_mode, settings.force_sequential_filaments); - if (texture_preview_uses_generic_solver(settings)) - settings.generic_mix_candidates = - build_color_solver_candidates(generic_solver_component_colors(settings), - color_solver_mix_model_from_index(settings.generic_solver_mix_model)); - else - settings.generic_mix_candidates = ColorSolverCandidateSet(); + if (texture_preview_uses_generic_solver(settings)) { + if (settings.generic_mix_candidate_cache == nullptr) + settings.generic_mix_candidate_cache = std::make_shared(); + const std::vector> component_colors = generic_solver_component_colors(settings); + const ColorSolverMixModel mix_model = color_solver_mix_model_from_index(settings.generic_solver_mix_model); + std::shared_ptr cache = settings.generic_mix_candidate_cache; + std::call_once(cache->init, [cache, component_colors, mix_model]() { + cache->candidates = build_color_solver_candidates(component_colors, mix_model); + }); + } else { + settings.generic_mix_candidate_cache.reset(); + } } ColorRGBA simulated_texture_preview_color_for_vertex_color(const ColorRGBA *source_color, @@ -1558,21 +1627,22 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume, TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t signature, unsigned int width, unsigned int height, - std::vector source_rgba, - std::vector source_raw_offsets, + const std::vector &source_rgba, + const std::vector &source_raw_offsets, unsigned int source_raw_channels, - std::vector source_raw_component_channels, + const std::vector &source_raw_component_channels, ColorRGBA background_color, - TexturePreviewSimulationSettings settings) + TexturePreviewSimulationSettings settings, + unsigned int max_preview_edge = 0, + size_t max_preview_pixels = 0) { TexturePreviewSimulationResult result; result.signature = signature; if (width == 0 || height == 0 || source_rgba.size() < size_t(width) * size_t(height) * 4) return result; - const std::array preview_size = settings.limit_texture_resolution ? - limited_simulated_texture_preview_size(width, height) : - std::array{ width, height }; + const std::array preview_size = + simulated_texture_preview_size(width, height, settings, max_preview_edge, max_preview_pixels); result.width = preview_size[0]; result.height = preview_size[1]; result.rgba.resize(size_t(result.width) * size_t(result.height) * 4, 0); @@ -1699,17 +1769,17 @@ size_t &texture_preview_vertex_color_simulation_generation() return *generation; } -void discard_ready_texture_preview_future(TexturePreviewSimulationCacheEntry &entry) +void discard_ready_texture_preview_future(std::future &future, size_t &pending_signature) { - if (!entry.pending_future.valid() || - entry.pending_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) + if (!future.valid() || + future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) return; try { - (void) entry.pending_future.get(); + (void) future.get(); } catch (...) { } - entry.pending_signature = 0; + pending_signature = 0; } void discard_ready_texture_preview_vertex_color_future(TexturePreviewVertexColorSimulationCacheEntry &entry) @@ -1794,10 +1864,12 @@ void clear_texture_preview_simulation_cache() } entry->uploaded_signature = 0; entry->pending_signature = 0; + entry->temporary_pending_signature = 0; + entry->pending_job_state.reset(); if (entry->pending_future.valid()) { if (entry->pending_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - discard_ready_texture_preview_future(*entry); + discard_ready_texture_preview_future(entry->pending_future, entry->pending_signature); } else { abandoned_texture_preview_futures().emplace_back(std::move(entry->pending_future)); } @@ -1843,29 +1915,22 @@ bool texture_preview_simulation_is_pending_impl() continue; } - if (!entry->pending_future.valid()) { - ++it; - continue; + const bool has_future = entry->pending_future.valid(); + if (has_future) { + if (entry->pending_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { + pending = true; + entry->ready_frame_requested = false; + } else if (entry->pending_signature != 0 && !entry->ready_frame_requested) { + pending = true; + entry->ready_frame_requested = true; + } } - if (entry->pending_future.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { - pending = true; - entry->ready_frame_requested = false; - ++it; - continue; - } - - if (entry->texture == nullptr && entry->pending_signature == 0) { - discard_ready_texture_preview_future(*entry); + if (!has_future && entry->texture == nullptr) { it = cache.erase(it); continue; } - if (entry->pending_signature != 0 && !entry->ready_frame_requested) { - pending = true; - entry->ready_frame_requested = true; - } - ++it; } @@ -1918,6 +1983,24 @@ bool texture_preview_simulation_is_pending_impl() return pending; } +bool texture_preview_simulation_has_temporary_pending_impl() +{ + auto &cache = texture_preview_simulation_cache(); + for (auto it = cache.begin(); it != cache.end();) { + const std::shared_ptr &entry = it->second; + if (entry == nullptr) { + it = cache.erase(it); + continue; + } + + if (entry->pending_future.valid() && entry->temporary_pending_signature != 0) + return true; + + ++it; + } + return false; +} + size_t texture_preview_simulation_cache_key(const ModelVolume &model_volume, unsigned int filament_id) { size_t key = reinterpret_cast(&model_volume); @@ -1925,6 +2008,47 @@ size_t texture_preview_simulation_cache_key(const ModelVolume &model_volume, uns return key; } +bool upload_simulated_texture_preview_result(TexturePreviewSimulationCacheEntry &entry, + TexturePreviewSimulationResult result, + size_t expected_signature) +{ + if (result.signature != expected_signature || result.rgba.empty() || result.width == 0 || result.height == 0) + return false; + + if (entry.texture == nullptr) + entry.texture = std::make_unique(); + else + entry.texture->reset(); + + if (!entry.texture->load_from_raw_data(std::move(result.rgba), result.width, result.height)) { + entry.uploaded_signature = 0; + return false; + } + + configure_texture_preview_sampler(*entry.texture); + entry.uploaded_signature = result.signature; + return true; +} + +void consume_temporary_simulated_texture_preview_result(TexturePreviewSimulationCacheEntry &entry) +{ + if (entry.temporary_pending_signature == 0 || entry.pending_job_state == nullptr) + return; + + std::optional result; + { + std::lock_guard lock(entry.pending_job_state->mutex); + if (!entry.pending_job_state->temporary_result.has_value()) + return; + result = std::move(entry.pending_job_state->temporary_result); + entry.pending_job_state->temporary_result.reset(); + } + + if (result.has_value()) + upload_simulated_texture_preview_result(entry, std::move(*result), entry.temporary_pending_signature); + entry.temporary_pending_signature = 0; +} + const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const ModelVolume &model_volume, unsigned int filament_id, size_t num_physical, @@ -1947,23 +2071,22 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model entry_ref = std::make_shared(); TexturePreviewSimulationCacheEntry &entry = *entry_ref; + const unsigned int width = model_volume.imported_texture_width; + const unsigned int height = model_volume.imported_texture_height; + const bool needs_temporary_result = simulated_texture_preview_needs_temporary_result(width, height, *settings); + const size_t temporary_signature = needs_temporary_result ? + temporary_simulated_texture_preview_signature(simulation_signature) : + size_t(0); + + consume_temporary_simulated_texture_preview_result(entry); + if (entry.pending_future.valid() && entry.pending_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { TexturePreviewSimulationResult result = entry.pending_future.get(); - if (result.signature == entry.pending_signature && !result.rgba.empty() && result.width > 0 && result.height > 0) { - if (entry.texture == nullptr) - entry.texture = std::make_unique(); - else - entry.texture->reset(); - - if (entry.texture->load_from_raw_data(std::move(result.rgba), result.width, result.height)) { - configure_texture_preview_sampler(*entry.texture); - entry.uploaded_signature = result.signature; - } else { - entry.uploaded_signature = 0; - } - } + upload_simulated_texture_preview_result(entry, std::move(result), entry.pending_signature); entry.pending_signature = 0; + entry.temporary_pending_signature = 0; entry.ready_frame_requested = false; + entry.pending_job_state.reset(); } if (entry.texture != nullptr && entry.uploaded_signature == simulation_signature && entry.texture->get_id() != 0) @@ -1971,41 +2094,70 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model if (!entry.pending_future.valid()) { entry.pending_signature = simulation_signature; + entry.temporary_pending_signature = temporary_signature; entry.ready_frame_requested = false; - const unsigned int width = model_volume.imported_texture_width; - const unsigned int height = model_volume.imported_texture_height; - std::vector source_rgba(model_volume.imported_texture_rgba.begin(), model_volume.imported_texture_rgba.end()); - std::vector source_raw_offsets(model_volume.imported_texture_raw_filament_offsets.begin(), - model_volume.imported_texture_raw_filament_offsets.end()); + entry.pending_job_state = std::make_shared(); + auto source_rgba = std::make_shared>(model_volume.imported_texture_rgba.begin(), + model_volume.imported_texture_rgba.end()); + auto source_raw_offsets = std::make_shared>(model_volume.imported_texture_raw_filament_offsets.begin(), + model_volume.imported_texture_raw_filament_offsets.end()); const unsigned int source_raw_channels = model_volume.imported_texture_raw_channels; TexturePreviewSimulationSettings simulation_settings = *settings; + simulation_settings.generic_mix_candidate_cache = std::make_shared(); std::vector source_raw_component_channels = raw_component_source_channels_for_texture_preview(model_volume.imported_texture_raw_metadata_json, source_raw_channels, simulation_settings.filament_color_mode, simulation_settings.component_colors.size()); + std::shared_ptr job_state = entry.pending_job_state; entry.pending_future = std::async(std::launch::async, [simulation_signature, + temporary_signature, width, height, - source_rgba = std::move(source_rgba), - source_raw_offsets = std::move(source_raw_offsets), + source_rgba, + source_raw_offsets, source_raw_channels, source_raw_component_channels = std::move(source_raw_component_channels), background_color, + job_state, simulation_settings = std::move(simulation_settings)]() mutable { + if (temporary_signature != 0 && job_state != nullptr) { + const unsigned int temp_edge = k_temporary_simulated_texture_preview_max_edge; + const size_t temp_pixels = k_temporary_simulated_texture_preview_max_pixels; + TexturePreviewSimulationResult temporary_result = + build_simulated_texture_preview_result(temporary_signature, + width, + height, + *source_rgba, + *source_raw_offsets, + source_raw_channels, + source_raw_component_channels, + background_color, + simulation_settings, + temp_edge, + temp_pixels); + std::lock_guard lock(job_state->mutex); + job_state->temporary_result = std::move(temporary_result); + } return build_simulated_texture_preview_result(simulation_signature, width, height, - std::move(source_rgba), - std::move(source_raw_offsets), + *source_rgba, + *source_raw_offsets, source_raw_channels, - std::move(source_raw_component_channels), + source_raw_component_channels, background_color, std::move(simulation_settings)); }); } + if (temporary_signature != 0 && + entry.texture != nullptr && + entry.uploaded_signature == temporary_signature && + entry.texture->get_id() != 0) + return entry.texture.get(); + return &fallback_texture; } @@ -3185,6 +3337,11 @@ bool texture_preview_simulation_is_pending() return texture_preview_simulation_is_pending_impl(); } +bool texture_preview_simulation_has_temporary_pending() +{ + return texture_preview_simulation_has_temporary_pending_impl(); +} + bool texture_preview_simulation_enabled_for_filament(unsigned int filament_id, size_t num_physical, const TextureMappingManager *texture_mgr) diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.hpp b/src/slic3r/GUI/MMUPaintedTexturePreview.hpp index 359c224feba..e23895e6956 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.hpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.hpp @@ -66,6 +66,7 @@ bool ensure_model_volume_texture_preview(const ModelVolume &model_volume, size_t &texture_signature); bool texture_preview_simulation_is_pending(); +bool texture_preview_simulation_has_temporary_pending(); void clear_texture_preview_simulation_cache(); bool texture_preview_simulation_enabled_for_filament(unsigned int filament_id, size_t num_physical,