From 94accf7a946905c23d40fb1d256734a4a519f2cf Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Mon, 1 Jun 2026 16:30:16 +0100 Subject: [PATCH] Improve scheduling. Use beam search only on combined regions. Add buffer distance when prime towers are clamped to edge --- src/libslic3r/Fill/Fill.cpp | 285 ++++++++++++++------ src/libslic3r/Layer.hpp | 2 + src/libslic3r/PrintObject.cpp | 17 ++ src/libslic3r/TextureMappingContoning.cpp | 33 ++- src/libslic3r/TextureMappingContoning.hpp | 14 + src/slic3r/GUI/GLCanvas3D.cpp | 3 +- src/slic3r/GUI/MMUPaintedTexturePreview.cpp | 127 +++++++-- src/slic3r/GUI/PartPlate.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 90 ++++++- 9 files changed, 460 insertions(+), 114 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index b76a934dbc8..224962bd072 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -2070,14 +2070,17 @@ public: bool run_one() override { - int depth = -1; - { - std::lock_guard lock(m_mutex); - if (m_error || m_next >= m_depths.size()) - return false; - depth = m_depths[m_next++]; - ++m_active; + m_active.fetch_add(1, std::memory_order_acq_rel); + if (m_has_error.load(std::memory_order_acquire)) { + finish_active_call(); + return false; } + const size_t work_idx = m_next.fetch_add(1, std::memory_order_acq_rel); + if (work_idx >= m_depths.size()) { + finish_active_call(); + return false; + } + const int depth = m_depths[work_idx]; std::vector regions; std::exception_ptr error; @@ -2088,19 +2091,10 @@ public: error = std::current_exception(); } - int completed = 0; - bool done = false; - { - std::lock_guard lock(m_mutex); - if (error && !m_error) - m_error = error; - --m_active; - completed = ++m_completed; - done = (m_error || m_next >= m_depths.size()) && m_active == 0; - m_done = m_done || done; - } - if (done) - m_cv.notify_all(); + if (error) + store_error(error); + const int completed = int(m_completed.fetch_add(1, std::memory_order_acq_rel) + 1); + finish_active_call(); if (!error && m_progress) m_progress(completed); return true; @@ -2109,22 +2103,47 @@ public: void wait() { std::unique_lock lock(m_mutex); - m_cv.wait(lock, [this]() { return m_done; }); + m_cv.wait(lock, [this]() { return done(); }); if (m_error) std::rethrow_exception(m_error); } private: + bool done() const + { + return (m_has_error.load(std::memory_order_acquire) || + m_next.load(std::memory_order_acquire) >= m_depths.size()) && + m_active.load(std::memory_order_acquire) == 0; + } + + void store_error(std::exception_ptr error) + { + { + std::lock_guard lock(m_mutex); + if (!m_error) + m_error = error; + } + m_has_error.store(true, std::memory_order_release); + m_cv.notify_all(); + } + + void finish_active_call() + { + m_active.fetch_sub(1, std::memory_order_acq_rel); + if (done()) + m_cv.notify_all(); + } + std::mutex m_mutex; std::condition_variable m_cv; std::vector m_depths; BuildFn m_build; StoreFn m_store; ProgressFn m_progress; - size_t m_next { 0 }; - int m_active { 0 }; - int m_completed { 0 }; - bool m_done { false }; + std::atomic m_next { 0 }; + std::atomic m_active { 0 }; + std::atomic m_completed { 0 }; + std::atomic m_has_error { false }; std::exception_ptr m_error; }; @@ -2134,46 +2153,53 @@ public: using RunFn = std::function; using ProgressFn = std::function; - TopSurfaceImageContoningAnchoredIndexWork(size_t count, RunFn run, ProgressFn progress) + TopSurfaceImageContoningAnchoredIndexWork(size_t count, RunFn run, ProgressFn progress, size_t chunk_size = 64) : m_count(count) , m_run(std::move(run)) , m_progress(std::move(progress)) + , m_chunk_size(std::max(1, chunk_size)) { - m_done = m_count == 0; + } + + size_t task_count() const + { + return (m_count + m_chunk_size - 1) / m_chunk_size; } bool run_one() override { - size_t idx = 0; - { - std::lock_guard lock(m_mutex); - if (m_error || m_next >= m_count) - return false; - idx = m_next++; - ++m_active; + m_active.fetch_add(1, std::memory_order_acq_rel); + if (m_has_error.load(std::memory_order_acquire)) { + finish_active_call(); + return false; } + const size_t begin = m_next.fetch_add(m_chunk_size, std::memory_order_acq_rel); + if (begin >= m_count) { + finish_active_call(); + return false; + } + const size_t end = std::min(begin + m_chunk_size, m_count); std::exception_ptr error; + size_t processed = 0; try { - m_run(idx); + for (size_t idx = begin; idx < end; ++idx) { + if (m_has_error.load(std::memory_order_acquire)) + break; + m_run(idx); + ++processed; + } } catch (...) { error = std::current_exception(); } - size_t completed = 0; - bool done = false; - { - std::lock_guard lock(m_mutex); - if (error && !m_error) - m_error = error; - --m_active; - completed = ++m_completed; - done = (m_error || m_next >= m_count) && m_active == 0; - m_done = m_done || done; - } - if (done) - m_cv.notify_all(); - if (!error && m_progress) + if (error) + store_error(error); + const size_t completed = processed == 0 ? + m_completed.load(std::memory_order_acquire) : + m_completed.fetch_add(processed, std::memory_order_acq_rel) + processed; + finish_active_call(); + if (!error && processed > 0 && m_progress) m_progress(completed); return true; } @@ -2181,21 +2207,47 @@ public: void wait() { std::unique_lock lock(m_mutex); - m_cv.wait(lock, [this]() { return m_done; }); + m_cv.wait(lock, [this]() { return done(); }); if (m_error) std::rethrow_exception(m_error); } private: + bool done() const + { + return (m_has_error.load(std::memory_order_acquire) || + m_next.load(std::memory_order_acquire) >= m_count) && + m_active.load(std::memory_order_acquire) == 0; + } + + void store_error(std::exception_ptr error) + { + { + std::lock_guard lock(m_mutex); + if (!m_error) + m_error = error; + } + m_has_error.store(true, std::memory_order_release); + m_cv.notify_all(); + } + + void finish_active_call() + { + m_active.fetch_sub(1, std::memory_order_acq_rel); + if (done()) + m_cv.notify_all(); + } + std::mutex m_mutex; std::condition_variable m_cv; size_t m_count { 0 }; RunFn m_run; ProgressFn m_progress; - size_t m_next { 0 }; - int m_active { 0 }; - size_t m_completed { 0 }; - bool m_done { false }; + size_t m_chunk_size { 1 }; + std::atomic m_next { 0 }; + std::atomic m_active { 0 }; + std::atomic m_completed { 0 }; + std::atomic m_has_error { false }; std::exception_ptr m_error; }; @@ -2307,6 +2359,8 @@ struct TopSurfaceImageContoningStackPlanKey { bool variable_layer_height_compensation { false }; bool beam_search_stack_expansion { false }; int mix_model { TextureMappingZone::DefaultGenericSolverMixModel }; + int color_prediction_mode { TextureMappingZone::DefaultTopSurfaceContoningColorPredictionMode }; + std::string calibrated_stack_model_key; bool operator<(const TopSurfaceImageContoningStackPlanKey &rhs) const { @@ -2343,7 +2397,9 @@ struct TopSurfaceImageContoningStackPlanKey { td_effective_alpha_correction, variable_layer_height_compensation, beam_search_stack_expansion, - mix_model) < + mix_model, + color_prediction_mode, + calibrated_stack_model_key) < std::tie(rhs.source_layer, rhs.source_layer_id, rhs.target_layer, @@ -2377,7 +2433,9 @@ struct TopSurfaceImageContoningStackPlanKey { rhs.td_effective_alpha_correction, rhs.variable_layer_height_compensation, rhs.beam_search_stack_expansion, - rhs.mix_model); + rhs.mix_model, + rhs.color_prediction_mode, + rhs.calibrated_stack_model_key); } }; @@ -4606,6 +4664,31 @@ static std::optional top_surface_image_cont label_by_stack); } +static std::optional top_surface_image_contoning_solve_provisional_label( + const std::array &rgb, + int solve_layers, + const TextureMappingContoningSolver &solver, + bool lower_surface, + const std::vector &surface_to_deep_layer_heights_mm, + std::vector &labels, + std::map, int>, int> &label_by_stack) +{ + const int visible_layers = solve_layers; + TextureMappingContoningStack stack = + solver.solve_without_beam_search_stack_expansion(rgb, + solve_layers, + lower_surface, + visible_layers, + surface_to_deep_layer_heights_mm); + return top_surface_image_contoning_label_for_stack(stack, + solver, + lower_surface, + visible_layers, + surface_to_deep_layer_heights_mm, + labels, + label_by_stack); +} + static void top_surface_image_contoning_resolve_merged_grid_regions( std::vector &grid, int cols, @@ -4926,11 +5009,12 @@ static void top_surface_image_contoning_solve_anchored_region( cell_samples[grid_idx] = sample; cell_available_depths[grid_idx] = sample->available_depth; ++debug_sampled_cell_count; - cell_stacks[grid_idx] = solver.solve(sample->rgb, - sample->solve_layers, - lower_surface, - sample->available_depth, - source_context.surface_to_deep_layer_heights_mm); + cell_stacks[grid_idx] = + solver.solve_without_beam_search_stack_expansion(sample->rgb, + sample->solve_layers, + lower_surface, + sample->solve_layers, + source_context.surface_to_deep_layer_heights_mm); if (debug_stride > 0 && row % debug_stride == 0 && col % debug_stride == 0) { @@ -4969,7 +5053,7 @@ static void top_surface_image_contoning_solve_anchored_region( if (build_state != nullptr) build_state->set_work(sample_work); try { - tbb::parallel_for(tbb::blocked_range(0, grid_cells, 32), + tbb::parallel_for(tbb::blocked_range(0, sample_work->task_count(), 1), [&](const tbb::blocked_range &range) { for (size_t idx = range.begin(); idx != range.end(); ++idx) sample_work->run_one(); @@ -4990,7 +5074,7 @@ static void top_surface_image_contoning_solve_anchored_region( const size_t grid_idx = size_t(row * cols + col); if (!cell_samples[grid_idx]) continue; - label_cell(row, col, cell_stacks[grid_idx], cell_samples[grid_idx]->available_depth); + label_cell(row, col, cell_stacks[grid_idx], cell_samples[grid_idx]->solve_layers); } } if (debug_enabled) { @@ -5542,6 +5626,8 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_anchored key.variable_layer_height_compensation = plan.contoning_variable_layer_height_compensation_enabled; key.beam_search_stack_expansion = plan.contoning_beam_search_stack_expansion_enabled; key.mix_model = plan.contoning_generic_solver_mix_model; + key.color_prediction_mode = zone.effective_top_surface_contoning_color_prediction_mode(); + key.calibrated_stack_model_key = solver.calibrated_stack_model_key(); return key; } @@ -5667,17 +5753,16 @@ static std::vector top_surface_image_conto std::vector labels; std::map, int>, int> label_by_stack; - auto solve_cell = [&](int row, int col, const std::array &target_rgb, int solve_layers, int available_depth) { + auto solve_cell = [&](int row, int col, const std::array &target_rgb, int solve_layers, int) { std::optional solved = - top_surface_image_contoning_solve_label(target_rgb, - solve_layers, - available_depth, - solver, - source_surface == TopSurfaceImageSourceSurface::Bottom && - plan.contoning_td_adjustment_enabled, - source->surface_to_deep_layer_heights_mm, - labels, - label_by_stack); + top_surface_image_contoning_solve_provisional_label(target_rgb, + solve_layers, + solver, + source_surface == TopSurfaceImageSourceSurface::Bottom && + plan.contoning_td_adjustment_enabled, + source->surface_to_deep_layer_heights_mm, + labels, + label_by_stack); if (!solved) return std::optional(); grid[size_t(row * cols + col)] = solved->label; @@ -5884,15 +5969,14 @@ static std::shared_ptr top_surface_imag auto solve_cell = [&](int row, int col, const std::array &target_rgb, int solve_layers, int available_depth) { std::optional solved = - top_surface_image_contoning_solve_label(target_rgb, - solve_layers, - available_depth, - solver, - source_surface == TopSurfaceImageSourceSurface::Bottom && - plan.contoning_td_adjustment_enabled, - source_context->surface_to_deep_layer_heights_mm, - out->labels, - label_by_stack); + top_surface_image_contoning_solve_provisional_label(target_rgb, + solve_layers, + solver, + source_surface == TopSurfaceImageSourceSurface::Bottom && + plan.contoning_td_adjustment_enabled, + source_context->surface_to_deep_layer_heights_mm, + out->labels, + label_by_stack); if (!solved) return std::optional(); TopSurfaceImageContoningStackPlanCell &cell = out->cells[size_t(row * cols + col)]; @@ -6088,6 +6172,8 @@ static TopSurfaceImageContoningStackPlanKey top_surface_image_contoning_stack_pl key.variable_layer_height_compensation = plan.contoning_variable_layer_height_compensation_enabled; key.beam_search_stack_expansion = plan.contoning_beam_search_stack_expansion_enabled; key.mix_model = plan.contoning_generic_solver_mix_model; + key.color_prediction_mode = zone.effective_top_surface_contoning_color_prediction_mode(); + key.calibrated_stack_model_key = solver.calibrated_stack_model_key(); return key; } @@ -9826,6 +9912,41 @@ void export_group_fills_to_svg(const char *path, const std::vector #endif // friend to Layer +void Layer::prebuild_contoning_stack_plan_cache(std::function throw_if_canceled, + TopSurfaceImageContoningStackPlanCache *contoning_stack_plan_cache) const +{ + if (contoning_stack_plan_cache == nullptr) + return; + const ThrowIfCanceled *throw_if_canceled_ptr = throw_if_canceled ? &throw_if_canceled : nullptr; + check_canceled(throw_if_canceled_ptr); + const PrintObject *object = this->object(); + if (object == nullptr || object->print() == nullptr) + return; + const TextureMappingManager &texture_mgr = object->print()->texture_mapping_manager(); + bool has_surface_anchored_contoning = false; + for (const LayerRegion *layerm : m_regions) { + check_canceled(throw_if_canceled_ptr); + if (layerm == nullptr) + continue; + const int raw_zone_id = layerm->region().config().solid_infill_filament.value; + if (raw_zone_id <= 0) + continue; + const TextureMappingZone *zone = texture_mgr.zone_from_id(unsigned(raw_zone_id)); + if (zone != nullptr && + zone->enabled && + !zone->deleted && + zone->top_surface_image_printing_active() && + zone->top_surface_contoning_active() && + zone->effective_top_surface_contoning_surface_anchored_stacks_enabled()) { + has_surface_anchored_contoning = true; + break; + } + } + if (!has_surface_anchored_contoning) + return; + top_surface_image_region_plans(*this, contoning_stack_plan_cache, throw_if_canceled_ptr); +} + void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator, diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 93b4b2c2ffa..bf4f9f02a96 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -204,6 +204,8 @@ public: FillLightning::Generator* lightning_generator = nullptr, std::function throw_if_canceled = {}, TopSurfaceImageContoningStackPlanCache *contoning_stack_plan_cache = nullptr); + void prebuild_contoning_stack_plan_cache(std::function throw_if_canceled, + TopSurfaceImageContoningStackPlanCache *contoning_stack_plan_cache) const; Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree, FillAdaptive::Octree *support_fill_octree, FillLightning::Generator* lightning_generator) const; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7456adbaec9..df76aee9b74 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -992,6 +992,23 @@ void PrintObject::infill() const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; const std::function throw_if_canceled = [print]() { print->throw_if_canceled(); }; auto contoning_stack_plan_cache = make_top_surface_image_contoning_stack_plan_cache(); + const bool prebuild_contoning_stack_plans = + std::any_of(print->texture_mapping_manager().zones().begin(), + print->texture_mapping_manager().zones().end(), + [](const TextureMappingZone &zone) { + return zone.enabled && + !zone.deleted && + zone.top_surface_image_printing_active() && + zone.top_surface_contoning_active() && + zone.effective_top_surface_contoning_surface_anchored_stacks_enabled(); + }); + if (prebuild_contoning_stack_plans) { + for (Layer *layer : m_layers) { + print->throw_if_canceled(); + layer->prebuild_contoning_stack_plan_cache(throw_if_canceled, contoning_stack_plan_cache.get()); + } + m_print->throw_if_canceled(); + } BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( diff --git a/src/libslic3r/TextureMappingContoning.cpp b/src/libslic3r/TextureMappingContoning.cpp index d4099825356..5910011f34d 100644 --- a/src/libslic3r/TextureMappingContoning.cpp +++ b/src/libslic3r/TextureMappingContoning.cpp @@ -672,6 +672,37 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr bool lower_surface, int visible_stack_layers, const std::vector &surface_to_deep_layer_heights_mm) const +{ + return solve_impl(target_rgb, + stack_layers, + lower_surface, + visible_stack_layers, + surface_to_deep_layer_heights_mm, + m_beam_search_stack_expansion_enabled); +} + +TextureMappingContoningStack TextureMappingContoningSolver::solve_without_beam_search_stack_expansion( + const std::array &target_rgb, + int stack_layers, + bool lower_surface, + int visible_stack_layers, + const std::vector &surface_to_deep_layer_heights_mm) const +{ + return solve_impl(target_rgb, + stack_layers, + lower_surface, + visible_stack_layers, + surface_to_deep_layer_heights_mm, + false); +} + +TextureMappingContoningStack TextureMappingContoningSolver::solve_impl( + const std::array &target_rgb, + int stack_layers, + bool lower_surface, + int visible_stack_layers, + const std::vector &surface_to_deep_layer_heights_mm, + bool beam_search_stack_expansion_enabled) const { TextureMappingContoningStack out; if (!valid()) @@ -706,7 +737,7 @@ TextureMappingContoningStack TextureMappingContoningSolver::solve(const std::arr m_beer_lambert_rgb_correction_enabled, m_td_effective_alpha_correction_enabled, m_component_roles, - m_beam_search_stack_expansion_enabled, + beam_search_stack_expansion_enabled, depth_layer_opacities, m_calibrated_stack_model.valid() ? &m_calibrated_stack_model : nullptr); } diff --git a/src/libslic3r/TextureMappingContoning.hpp b/src/libslic3r/TextureMappingContoning.hpp index 4d28fa5e3c9..7a60004dc7d 100644 --- a/src/libslic3r/TextureMappingContoning.hpp +++ b/src/libslic3r/TextureMappingContoning.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace Slic3r { @@ -35,12 +36,19 @@ public: bool valid() const { return !m_component_ids.empty() && m_component_ids.size() == m_component_colors.size(); } const std::vector& component_ids() const { return m_component_ids; } const std::vector& components_bottom_to_top() const { return m_components_bottom_to_top; } + std::string calibrated_stack_model_key() const { return m_calibrated_stack_model.valid() ? m_calibrated_stack_model.cache_key() : std::string(); } TextureMappingContoningStack solve(const std::array &target_rgb, int stack_layers, bool lower_surface = false, int visible_stack_layers = 0, const std::vector &surface_to_deep_layer_heights_mm = {}) const; + TextureMappingContoningStack solve_without_beam_search_stack_expansion( + const std::array &target_rgb, + int stack_layers, + bool lower_surface = false, + int visible_stack_layers = 0, + const std::vector &surface_to_deep_layer_heights_mm = {}) const; unsigned int component_for_depth(const std::array &target_rgb, int stack_layers, int depth_from_top, bool lower_surface = false) const; std::optional> stack_rgb(const std::vector &bottom_to_top, bool lower_surface = false, @@ -61,6 +69,12 @@ private: std::optional component_index(unsigned int component_id) const; std::vector layer_opacities_by_depth(const std::vector &surface_to_deep_layer_heights_mm, int visible_depth) const; + TextureMappingContoningStack solve_impl(const std::array &target_rgb, + int stack_layers, + bool lower_surface, + int visible_stack_layers, + const std::vector &surface_to_deep_layer_heights_mm, + bool beam_search_stack_expansion_enabled) const; std::vector m_component_ids; std::vector m_components_bottom_to_top; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 66b564619c7..a512a2ed5ed 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -90,6 +90,7 @@ extern wxPopupWindow* wxCurrentPopupWindow; #endif static constexpr const float TRACKBALLSIZE = 0.8f; +static constexpr float WIPE_TOWER_EDGE_BUFFER = 20.f; static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR = { 0.906f, 0.906f, 0.906f, 1.0f }; static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR_DARK = { 0.329f, 0.329f, 0.353f, 1.0f }; @@ -2877,7 +2878,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const float texture_z_max = float(wipe_tower_size(2)); { - const float margin = WIPE_TOWER_MARGIN + brim_width; + const float margin = WIPE_TOWER_MARGIN + brim_width + WIPE_TOWER_EDGE_BUFFER; BoundingBoxf3 plate_bbox = part_plate->get_bounding_box(); BoundingBoxf plate_bbox_2d(Vec2d(plate_bbox.min(0), plate_bbox.min(1)), Vec2d(plate_bbox.max(0), plate_bbox.max(1))); const std::vector &extruder_areas = part_plate->get_extruder_areas(); diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index 8c1f1aec203..f5b9b6487c7 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -43,6 +43,8 @@ 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 unsigned int k_temporary_contoning_flat_surface_preview_max_edge = 384; +constexpr size_t k_temporary_contoning_flat_surface_preview_max_pixels = 384ull * 384ull; constexpr size_t k_surface_gradient_preview_max_components = 10; constexpr size_t k_surface_gradient_preview_lut_size = 33; constexpr size_t k_contoning_flat_surface_preview_max_candidates = 250000; @@ -99,6 +101,7 @@ struct TexturePreviewSimulationSettings bool contoning_flat_surface_beer_lambert_rgb_correction = false; bool contoning_flat_surface_td_effective_alpha_correction = false; bool contoning_flat_surface_beam_search_stack_expansion = false; + bool contoning_flat_surface_force_low_resolution = false; int contoning_flat_surface_pattern_filaments = TextureMappingZone::DefaultTopSurfaceContoningPatternFilaments; bool simulate_top_surface_lod = false; float top_surface_lod_pitch_mm = 0.f; @@ -176,6 +179,12 @@ struct TexturePreviewSimulationCacheEntry std::shared_ptr pending_job_state; }; +struct TexturePreviewSizeLimit +{ + unsigned int max_edge { k_simulated_texture_preview_max_edge }; + size_t max_pixels { k_simulated_texture_preview_max_pixels }; +}; + struct TexturePreviewVertexColorSimulationResult { size_t signature { 0 }; @@ -891,6 +900,16 @@ std::array simulated_texture_preview_size(unsigned int width, limited_simulated_texture_preview_size(width, height) : std::array{ width, height }; + if (settings.contoning_flat_surface_force_low_resolution) { + const std::array low_size = + limited_simulated_texture_preview_size(width, + height, + k_temporary_contoning_flat_surface_preview_max_edge, + k_temporary_contoning_flat_surface_preview_max_pixels); + size[0] = std::min(size[0], low_size[0]); + size[1] = std::min(size[1], low_size[1]); + } + if (settings.contoning_flat_surface_quantization && settings.simulate_top_surface_lod && std::isfinite(settings.texture_preview_mm_per_pixel) && @@ -922,26 +941,52 @@ bool simulated_texture_preview_needs_temporary_result(unsigned int width, unsigned int height, const TexturePreviewSimulationSettings &settings) { + auto final_and_temporary_pixels = [&settings, width, height]() { + const std::array final_size = simulated_texture_preview_size(width, height, settings); + const TexturePreviewSizeLimit temporary_limit = + settings.contoning_flat_surface_quantization ? + TexturePreviewSizeLimit{ k_temporary_contoning_flat_surface_preview_max_edge, + k_temporary_contoning_flat_surface_preview_max_pixels } : + TexturePreviewSizeLimit{ k_temporary_simulated_texture_preview_max_edge, + k_temporary_simulated_texture_preview_max_pixels }; + const std::array temporary_size = + simulated_texture_preview_size(width, + height, + settings, + temporary_limit.max_edge, + temporary_limit.max_pixels); + return std::array{ + size_t(final_size[0]) * size_t(final_size[1]), + size_t(temporary_size[0]) * size_t(temporary_size[1]) + }; + }; + + if (settings.contoning_flat_surface_quantization) { + const std::array pixels = final_and_temporary_pixels(); + return pixels[0] > pixels[1] && pixels[0] > pixels[1] * 5 / 4; + } + 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]); + const std::array pixels = final_and_temporary_pixels(); + return pixels[0] > pixels[1]; } -size_t temporary_simulated_texture_preview_signature(size_t signature) +TexturePreviewSizeLimit temporary_simulated_texture_preview_size_limit(const TexturePreviewSimulationSettings &settings) +{ + if (settings.contoning_flat_surface_quantization) + return { k_temporary_contoning_flat_surface_preview_max_edge, k_temporary_contoning_flat_surface_preview_max_pixels }; + return { k_temporary_simulated_texture_preview_max_edge, k_temporary_simulated_texture_preview_max_pixels }; +} + +size_t temporary_simulated_texture_preview_signature(size_t signature, const TexturePreviewSizeLimit &limit) { signature ^= 0x4a7c15f17e315123ull + (signature << 6) + (signature >> 2); - signature ^= std::hash{}(k_temporary_simulated_texture_preview_max_edge) + 0x9e3779b97f4a7c15ull + + signature ^= std::hash{}(limit.max_edge) + 0x9e3779b97f4a7c15ull + (signature << 6) + (signature >> 2); - signature ^= std::hash{}(k_temporary_simulated_texture_preview_max_pixels) + 0x9e3779b97f4a7c15ull + + signature ^= std::hash{}(limit.max_pixels) + 0x9e3779b97f4a7c15ull + (signature << 6) + (signature >> 2); return signature; } @@ -2782,6 +2827,8 @@ std::optional texture_preview_simulation_setti const int effective_color_prediction_mode = TextureMappingZone::effective_top_surface_contoning_color_prediction_mode( zone->top_surface_contoning_color_prediction_mode); + const bool calibrated_color_prediction = + TextureMappingZone::top_surface_contoning_color_prediction_mode_is_calibrated(effective_color_prediction_mode); settings.contoning_flat_surface_beer_lambert_rgb_correction = settings.contoning_flat_surface_td_adjustment && effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb); @@ -2791,6 +2838,8 @@ std::optional texture_preview_simulation_setti effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine)); settings.contoning_flat_surface_beam_search_stack_expansion = zone->effective_top_surface_contoning_beam_search_stack_expansion_enabled(); + settings.contoning_flat_surface_force_low_resolution = + settings.contoning_flat_surface_td_adjustment && calibrated_color_prediction; settings.contoning_flat_surface_surface_scatter = zone->effective_top_surface_contoning_surface_scatter_enabled() ? k_contoning_preview_surface_scatter : @@ -2908,6 +2957,7 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume, mix(std::hash{}(settings.contoning_flat_surface_beer_lambert_rgb_correction ? 1 : 0)); mix(std::hash{}(settings.contoning_flat_surface_td_effective_alpha_correction ? 1 : 0)); mix(std::hash{}(settings.contoning_flat_surface_beam_search_stack_expansion ? 1 : 0)); + mix(std::hash{}(settings.contoning_flat_surface_force_low_resolution ? 1 : 0)); if (settings.contoning_flat_surface_quantization) { mix(std::hash{}(settings.contoning_flat_surface_pattern_filaments)); mix(std::hash{}(settings.simulate_top_surface_lod ? 1 : 0)); @@ -3732,8 +3782,9 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model 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 TexturePreviewSizeLimit temporary_size_limit = temporary_simulated_texture_preview_size_limit(*settings); const size_t temporary_signature = needs_temporary_result ? - temporary_simulated_texture_preview_signature(simulation_signature) : + temporary_simulated_texture_preview_signature(simulation_signature, temporary_size_limit) : size_t(0); consume_temporary_simulated_texture_preview_result(entry); @@ -3783,10 +3834,9 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model source_raw_component_channels = std::move(source_raw_component_channels), background_color, job_state, + temporary_size_limit, 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, @@ -3797,8 +3847,8 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model source_raw_component_channels, background_color, simulation_settings, - temp_edge, - temp_pixels); + temporary_size_limit.max_edge, + temporary_size_limit.max_pixels); std::lock_guard lock(job_state->mutex); job_state->temporary_result = std::move(temporary_result); } @@ -5837,6 +5887,7 @@ static size_t texture_preview_settings_signature_impl(size_t num_physical, signature_mix(std::hash{}(zone.top_surface_contoning_pattern_filaments)); signature_mix(std::hash{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_td_adjustment_enabled ? 1 : 0)); + signature_mix(std::hash{}(zone.effective_top_surface_contoning_color_prediction_mode())); signature_mix(std::hash{}(zone.effective_top_surface_contoning_surface_scatter_enabled() ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_beer_lambert_rgb_correction_enabled ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_td_effective_alpha_correction_enabled ? 1 : 0)); @@ -5994,6 +6045,7 @@ static void texture_preview_mix_zone_baked_model_settings(size_t &signature, signature_mix(std::hash{}(zone.top_surface_contoning_pattern_filaments)); signature_mix(std::hash{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_td_adjustment_enabled ? 1 : 0)); + signature_mix(std::hash{}(zone.effective_top_surface_contoning_color_prediction_mode())); signature_mix(std::hash{}(zone.effective_top_surface_contoning_surface_scatter_enabled() ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_beer_lambert_rgb_correction_enabled ? 1 : 0)); signature_mix(std::hash{}(zone.top_surface_contoning_td_effective_alpha_correction_enabled ? 1 : 0)); @@ -6186,7 +6238,7 @@ void render_model_texture_preview_models( const bool force_original_texture = color_match_active || texture_preview_halftone_simulation_enabled_for_filament(filament_ids[idx], num_physical, texture_mgr); - const GUI::GLTexture *preview_texture = force_original_texture ? + const GUI::GLTexture *base_preview_texture = force_original_texture ? &texture : simulated_texture_preview_texture_for_filament(model_volume, model_matrix, @@ -6195,15 +6247,9 @@ void render_model_texture_preview_models( texture_mgr, texture_signature, texture); - if (preview_texture == nullptr || preview_texture->get_id() == 0) + if (base_preview_texture == nullptr || base_preview_texture->get_id() == 0) continue; - if (preview_texture->get_id() != bound_texture_id) { - glsafe(::glActiveTexture(GL_TEXTURE0)); - glsafe(::glBindTexture(GL_TEXTURE_2D, preview_texture->get_id())); - bound_texture_id = preview_texture->get_id(); - } - bool contoning_flat_top_ready = false; bool contoning_flat_bottom_ready = false; bool contoning_flat_top_quantization = false; @@ -6256,6 +6302,20 @@ void render_model_texture_preview_models( contoning_flat_top_ready && contoning_flat_texture != nullptr && contoning_flat_texture->get_id() != 0; const bool contoning_flat_bottom_enabled = contoning_flat_bottom_ready && contoning_flat_bottom_texture != nullptr && contoning_flat_bottom_texture->get_id() != 0; + const bool contoning_flat_pending = + use_contoning_flat_texture && + ((!contoning_flat_enabled && (contoning_flat_top_quantization || contoning_flat_top_pattern_blend)) || + (!contoning_flat_bottom_enabled && (contoning_flat_bottom_quantization || contoning_flat_bottom_pattern_blend))); + const GUI::GLTexture *preview_texture = contoning_flat_pending ? &texture : base_preview_texture; + if (preview_texture == nullptr || preview_texture->get_id() == 0) + continue; + + if (preview_texture->get_id() != bound_texture_id) { + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, preview_texture->get_id())); + bound_texture_id = preview_texture->get_id(); + } + if (contoning_flat_enabled && contoning_flat_texture->get_id() != bound_contoning_flat_texture_id) { glsafe(::glActiveTexture(GL_TEXTURE1)); glsafe(::glBindTexture(GL_TEXTURE_2D, contoning_flat_texture->get_id())); @@ -6324,7 +6384,7 @@ void render_model_texture_preview_model( const size_t texture_signature = model_volume_texture_preview_signature(model_volume); const bool force_original_texture = color_match_active || texture_preview_halftone_simulation_enabled_for_filament(filament_id, num_physical, texture_mgr); - const GUI::GLTexture *preview_texture = force_original_texture ? + const GUI::GLTexture *base_preview_texture = force_original_texture ? &texture : simulated_texture_preview_texture_for_filament(model_volume, model_matrix, @@ -6333,7 +6393,7 @@ void render_model_texture_preview_model( texture_mgr, texture_signature, texture); - if (preview_texture == nullptr || preview_texture->get_id() == 0) + if (base_preview_texture == nullptr || base_preview_texture->get_id() == 0) return; const TexturePreviewRenderState render_state = begin_render_state(opaque); @@ -6349,8 +6409,6 @@ void render_model_texture_preview_model( print_volume_z); set_color_match_preview_uniforms(*shader, color_match); - glsafe(::glActiveTexture(GL_TEXTURE0)); - glsafe(::glBindTexture(GL_TEXTURE_2D, preview_texture->get_id())); shader->set_uniform("uniform_texture", 0); shader->set_uniform("contoning_flat_surface_texture", 1); shader->set_uniform("contoning_flat_surface_bottom_texture", 2); @@ -6406,6 +6464,19 @@ void render_model_texture_preview_model( contoning_flat_top_ready && contoning_flat_texture != nullptr && contoning_flat_texture->get_id() != 0; const bool contoning_flat_bottom_enabled = contoning_flat_bottom_ready && contoning_flat_bottom_texture != nullptr && contoning_flat_bottom_texture->get_id() != 0; + const bool contoning_flat_pending = + use_contoning_flat_texture && + ((!contoning_flat_enabled && (contoning_flat_top_quantization || contoning_flat_top_pattern_blend)) || + (!contoning_flat_bottom_enabled && (contoning_flat_bottom_quantization || contoning_flat_bottom_pattern_blend))); + const GUI::GLTexture *preview_texture = contoning_flat_pending ? &texture : base_preview_texture; + if (preview_texture == nullptr || preview_texture->get_id() == 0) { + shader->stop_using(); + restore_render_state(render_state); + return; + } + + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, preview_texture->get_id())); if (contoning_flat_enabled) { glsafe(::glActiveTexture(GL_TEXTURE1)); glsafe(::glBindTexture(GL_TEXTURE_2D, contoning_flat_texture->get_id())); diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 5d7d3601a1b..36225f8ec0a 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -64,6 +64,7 @@ static const int PARTPLATE_TEXT_OFFSET_X1 = 3; static const int PARTPLATE_TEXT_OFFSET_X2 = 1; static const int PARTPLATE_TEXT_OFFSET_Y = 1; static const int PARTPLATE_PLATENAME_OFFSET_Y = 10; +static const float WIPE_TOWER_EDGE_BUFFER = 20.f; const float WIPE_TOWER_DEFAULT_X_POS = 165.; const float WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y @@ -4443,7 +4444,7 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini brim_width = brim_opt->value; if (brim_width < 0) brim_width = WipeTower::get_auto_brim_by_height((float) wipe_tower_size.z()); } - const float margin = WIPE_TOWER_MARGIN + brim_width; + const float margin = WIPE_TOWER_MARGIN + brim_width + WIPE_TOWER_EDGE_BUFFER; // clamp wipe tower position within plate boundaries { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c98c4c5c0d3..f80d7646ca2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -84,6 +84,7 @@ #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/GCode/WipeTower.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/PresetBundle.hpp" @@ -242,6 +243,7 @@ wxDEFINE_EVENT(EVT_DEL_FILAMENT, SimpleEvent); wxDEFINE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent); wxDEFINE_EVENT(EVT_NOTICE_CHILDE_SIZE_CHANGED, SimpleEvent); wxDEFINE_EVENT(EVT_NOTICE_FULL_SCREEN_CHANGED, IntEvent); +static constexpr float WIPE_TOWER_EDGE_BUFFER = 20.f; #define PRINTER_THUMBNAIL_SIZE (wxSize(40, 40)) // ORCA #define PRINTER_PANEL_SIZE ( wxSize(70, 60)) // ORCA #define PRINTER_PANEL_RADIUS (6) // ORCA @@ -378,6 +380,90 @@ static wxString temp_dir; namespace { +static void clamp_wipe_tower_positions_for_slicing(PartPlateList &partplate_list, PresetBundle &preset_bundle) +{ + DynamicPrintConfig &proj_cfg = preset_bundle.project_config; + ConfigOptionFloats *wipe_tower_x = proj_cfg.option("wipe_tower_x", true); + ConfigOptionFloats *wipe_tower_y = proj_cfg.option("wipe_tower_y", true); + if (wipe_tower_x == nullptr || wipe_tower_y == nullptr || wipe_tower_x->values.empty() || wipe_tower_y->values.empty()) + return; + + const DynamicPrintConfig &print_cfg = preset_bundle.prints.get_edited_preset().config; + const ConfigOptionBool *enable_prime_tower = print_cfg.option("enable_prime_tower"); + if (enable_prime_tower == nullptr || !enable_prime_tower->value) + return; + + wipe_tower_x->values.resize(partplate_list.get_plate_count(), wipe_tower_x->values.front()); + wipe_tower_y->values.resize(partplate_list.get_plate_count(), wipe_tower_y->values.front()); + + const ConfigOptionEnum *print_sequence = print_cfg.option>("print_sequence"); + const ConfigOptionEnum *timelapse_type = print_cfg.option>("timelapse_type"); + const ConfigOptionBool *wrapping_opt = print_cfg.option("enable_wrapping_detection"); + const bool need_wipe_tower = (timelapse_type != nullptr && timelapse_type->value == TimelapseType::tlSmooth) || + (wrapping_opt != nullptr && wrapping_opt->value); + const float width = print_cfg.opt_float("prime_tower_width"); + const float prime_volume = print_cfg.opt_float("prime_volume"); + const int nozzle_nums = preset_bundle.get_printer_extruder_count(); + + for (int plate_id = 0; plate_id < partplate_list.get_plate_count(); ++plate_id) { + PartPlate *part_plate = partplate_list.get_plate(plate_id); + if (part_plate == nullptr || part_plate->get_objects_on_this_plate().empty()) + continue; + if (part_plate->get_print_seq() == PrintSequence::ByObject || + (part_plate->get_print_seq() == PrintSequence::ByDefault && print_sequence != nullptr && print_sequence->value == PrintSequence::ByObject)) { + if (part_plate->printable_instance_size() != 1) + continue; + } + + const size_t texture_mapping_filaments_count = part_plate->estimate_wipe_tower_filaments_count(&proj_cfg); + const size_t wipe_tower_filaments_count = need_wipe_tower ? + std::max(1, texture_mapping_filaments_count) : + texture_mapping_filaments_count; + if (!need_wipe_tower && wipe_tower_filaments_count < 2) + continue; + + Vec3d wipe_tower_size = part_plate->estimate_wipe_tower_size(print_cfg, + width, + prime_volume, + nozzle_nums, + int(wipe_tower_filaments_count), + false, + wrapping_opt != nullptr && wrapping_opt->value); + if (wipe_tower_size(0) <= EPSILON || wipe_tower_size(1) <= EPSILON) + continue; + + float brim_width = print_cfg.opt_float("prime_tower_brim_width"); + if (brim_width < 0) + brim_width = WipeTower::get_auto_brim_by_height((float) wipe_tower_size.z()); + const float margin = float(WIPE_TOWER_MARGIN) + brim_width + WIPE_TOWER_EDGE_BUFFER; + + const Vec3d plate_origin = part_plate->get_origin(); + BoundingBoxf3 plate_bbox = part_plate->get_bounding_box(); + BoundingBoxf plate_bbox_2d(Vec2d(plate_bbox.min(0), plate_bbox.min(1)), Vec2d(plate_bbox.max(0), plate_bbox.max(1))); + const std::vector &extruder_areas = part_plate->get_extruder_areas(); + for (const Pointfs &points : extruder_areas) { + BoundingBoxf bboxf(points); + plate_bbox_2d.min = plate_bbox_2d.min(0) >= bboxf.min(0) ? plate_bbox_2d.min : bboxf.min; + plate_bbox_2d.max = plate_bbox_2d.max(0) <= bboxf.max(0) ? plate_bbox_2d.max : bboxf.max; + } + + const coordf_t min_x = plate_bbox_2d.min(0) - plate_origin(0); + const coordf_t max_x = plate_bbox_2d.max(0) - plate_origin(0); + const coordf_t min_y = plate_bbox_2d.min(1) - plate_origin(1); + const coordf_t max_y = plate_bbox_2d.max(1) - plate_origin(1); + auto clamp_axis = [](float value, coordf_t min_value, coordf_t max_value, double size, float margin) { + const float axis_min = float(min_value) + margin; + const float axis_max = float(max_value) - margin - float(size); + return axis_max < axis_min ? axis_min : std::min(std::max(value, axis_min), axis_max); + }; + + ConfigOptionFloat x_opt(clamp_axis(wipe_tower_x->get_at(plate_id), min_x, max_x, wipe_tower_size(0), margin)); + ConfigOptionFloat y_opt(clamp_axis(wipe_tower_y->get_at(plate_id), min_y, max_y, wipe_tower_size(1), margin)); + wipe_tower_x->set_at(&x_opt, plate_id, 0); + wipe_tower_y->set_at(&y_opt, plate_id, 0); + } +} + static std::map active_texture_zone_ids_by_stable_id_for_import(const TextureMappingManager &manager) { std::map ids; @@ -14541,8 +14627,10 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool this->preview->update_gcode_result(partplate_list.get_current_slice_result()); } - if (PresetBundle *preset_bundle = wxGetApp().preset_bundle; preset_bundle != nullptr) + if (PresetBundle *preset_bundle = wxGetApp().preset_bundle; preset_bundle != nullptr) { canonicalize_texture_mapping_config(*preset_bundle, true); + clamp_wipe_tower_positions_for_slicing(this->partplate_list, *preset_bundle); + } background_process.fff_print()->set_check_multi_filaments_compatibility(wxGetApp().app_config->get("enable_high_low_temp_mixed_printing") == "false");