Compare commits

..
Author SHA1 Message Date
ExPikaPaka d7b86a106d Stop cloning config defaults twice and building selectors for unpainted meshes
create_default_option() cloned the default value only to ask whether it is
nullable, then cloned again in both branches and returned, leaving the delete
unreachable. That is one leaked ConfigOption per enum definition per config
load, and it was the only unreachable allocation a leak checker found anywhere
in the slicing path.

get_extruders() built a full TriangleSelector, one node per facet, to ask which
extruders a volume paints, even when it carries no painting at all. Measured on
an unpainted 597k facet volume: 38.8 MB retained and 40 to 52 ms per call, both
down to nothing, with the same answer. get_facets() takes the same short circuit
for any state other than NONE, where an empty annotation can only return an empty
set anyway.
2026-10-01 09:15:49 +02:00
9 changed files with 69 additions and 57 deletions
+1 -3
View File
@@ -304,8 +304,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const
return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
if (type == coEnums) {
auto dft = this->default_value->clone();
if (dft->nullable()) {
if (this->default_value->nullable()) {
ConfigOptionEnumsGenericNullable *opt = dynamic_cast<ConfigOptionEnumsGenericNullable *>(this->default_value->clone());
opt->keys_map = this->enum_keys_map;
return opt;
@@ -314,7 +313,6 @@ ConfigOption* ConfigOptionDef::create_default_option() const
opt->keys_map = this->enum_keys_map;
return opt;
}
delete dft;
}
return this->default_value->clone();
+3 -3
View File
@@ -360,7 +360,7 @@ void Layer::simplify_support_entity_collection(ExtrusionEntityCollection* entity
//BBS: method to simplify support path
void Layer::simplify_support_path(ExtrusionPath * path)
{
const PrintConfig &print_config = this->object()->print()->config();
const auto print_config = this->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -375,7 +375,7 @@ void Layer::simplify_support_path(ExtrusionPath * path)
//BBS: method to simplify support path
void Layer::simplify_support_multi_path(ExtrusionMultiPath* multipath)
{
const PrintConfig &print_config = this->object()->print()->config();
const auto print_config = this->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -392,7 +392,7 @@ void Layer::simplify_support_multi_path(ExtrusionMultiPath* multipath)
//BBS: method to simplify support path
void Layer::simplify_support_loop(ExtrusionLoop* loop)
{
const PrintConfig &print_config = this->object()->print()->config();
const auto print_config = this->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
+3 -3
View File
@@ -1074,7 +1074,7 @@ void LayerRegion::simplify_entity_collection(ExtrusionEntityCollection* entity_c
void LayerRegion::simplify_path(ExtrusionPath* path)
{
const PrintConfig &print_config = this->layer()->object()->print()->config();
const auto print_config = this->layer()->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -1092,7 +1092,7 @@ void LayerRegion::simplify_path(ExtrusionPath* path)
void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
{
const PrintConfig &print_config = this->layer()->object()->print()->config();
const auto print_config = this->layer()->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
@@ -1112,7 +1112,7 @@ void LayerRegion::simplify_multi_path(ExtrusionMultiPath* multipath)
void LayerRegion::simplify_loop(ExtrusionLoop* loop)
{
const PrintConfig &print_config = this->layer()->object()->print()->config();
const auto print_config = this->layer()->object()->print()->config();
const bool spiral_mode = print_config.spiral_mode;
const bool enable_arc_fitting = print_config.enable_arc_fitting;
const auto scaled_resolution = scaled<double>(print_config.resolution.value);
+18 -17
View File
@@ -2087,11 +2087,6 @@ void ModelVolume::reset_extra_facets()
this->seam_facets.reset();
this->mmu_segmentation_facets.reset();
this->fuzzy_skin_facets.reset();
// Texture-displacement paint data has no remap-across-topology-change support yet (see
// build_texture_displacement()'s documented limitation), so it must be dropped here rather
// than left referring to a mesh that no longer matches it.
for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
this->texture_displacement_facet(i).reset();
}
std::optional<TriangleSelector::SavedPainting> ModelVolume::save_painting() const
@@ -2653,16 +2648,22 @@ std::vector<int> ModelVolume::get_extruders() const
return std::vector<int>();
if (mmu_segmentation_facets.timestamp() != mmuseg_ts) {
std::vector<indexed_triangle_set> its_per_type;
mmuseg_extruders.clear();
mmuseg_ts = mmu_segmentation_facets.timestamp();
mmu_segmentation_facets.get_facets(*this, its_per_type);
for (int idx = 1; idx < its_per_type.size(); idx++) {
indexed_triangle_set& its = its_per_type[idx];
if (its.indices.empty())
continue;
// ORCA: without painting data every facet keeps its default (NONE) state, so no extruder
// other than the volume's own one can be painted on it. Skip get_facets() then: it builds a
// TriangleSelector with one node per facet over the whole mesh (tens of MiB on a dense one)
// only to hand back empty sets for every extruder.
if (! mmu_segmentation_facets.empty()) {
std::vector<indexed_triangle_set> its_per_type;
mmu_segmentation_facets.get_facets(*this, its_per_type);
for (int idx = 1; idx < its_per_type.size(); idx++) {
indexed_triangle_set& its = its_per_type[idx];
if (its.indices.empty())
continue;
mmuseg_extruders.push_back(idx);
mmuseg_extruders.push_back(idx);
}
}
}
@@ -2994,11 +2995,6 @@ void ModelVolume::assign_new_unique_ids_recursive()
seam_facets.set_new_unique_id();
mmu_segmentation_facets.set_new_unique_id();
fuzzy_skin_facets.set_new_unique_id();
// As set_new_unique_id() already does: the undo/redo stack stores FacetsAnnotation contents keyed
// by ObjectID, so a clone left sharing these ids with its source can be handed the source's mask
// on an undo - after which a paint mask and the mesh it was recorded against no longer match.
for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
texture_displacement_facet(i).set_new_unique_id();
}
void ModelVolume::rotate(double angle, Axis axis)
@@ -3636,6 +3632,11 @@ ModelInstanceEPrintVolumeState ModelInstance::calc_print_volume_state(const Buil
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
{
// ORCA: nothing is painted, so only the NONE state can have facets. Answering for any other
// state needs no TriangleSelector (one node per facet of the whole mesh) at all.
if (this->empty() && type != EnforcerBlockerType::NONE)
return {};
TriangleSelector selector(mv.mesh());
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
selector.deserialize(m_data, false);
+11 -13
View File
@@ -2604,11 +2604,6 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
void PrintObject::set_shared_object(PrintObject *object)
{
// Orca: from now on m_layers / m_support_layers only alias the shared object's layers, so release the
// ones this object still owns (it may have sliced itself before it became shareable again).
// Both are no-ops once m_shared_object is set, so this cannot free layers owned by another object.
clear_support_layers();
clear_layers();
m_shared_object = object;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, found shared object from %2%")%this%m_shared_object;
}
@@ -4334,9 +4329,9 @@ bool Print::is_dynamic_group_reorder() const
return true;
}
int Print::get_filament_config_indx(int filament_id, int layer_id)
int Print::get_filament_config_indx(int filament_id, int layer_id, bool use_cache)
{
return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, m_filament_index_map);
return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, use_cache ? &m_filament_index_map : nullptr);
}
void Print::update_filament_self_index_cache()
@@ -4379,7 +4374,7 @@ int Print::get_nozzle_config_index(int filament_id, int layer_id)
return get_config_index(filament_id, layer_id, m_default_region_config.print_extruder_variant.values, m_default_region_config.print_extruder_id.values, m_nozzle_index_map);
}
int Print::get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap &index_map)
int Print::get_config_index(int filament_id, int layer_id, const std::vector<std::string> &variant_list, const std::vector<int>& self_index_list, FilamentIndexMap *index_map)
{
auto group_result = get_layered_nozzle_group_result();
// Orca: defensive — when no grouping producer has published a result yet, fall back to the
@@ -4390,7 +4385,8 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vector<std
if (!nozzle_info.has_value()) {
// Orca: this fallback runs per-filament/per-layer in the g-code hot path — log once per filament
// (reset each slice) instead of flooding thousands of identical lines that bury the real error.
if (m_missing_nozzle_group_logged.insert(filament_id).second)
// Without the cache, the log set is left alone too; the cached caller reports the same filament.
if (index_map && m_missing_nozzle_group_logged.insert(filament_id).second)
BOOST_LOG_TRIVIAL(error) << __FUNCTION__
<< boost::format(", Line %1%: could not found group_nozzle_info corresponding to filament_id %2%, layer_id %3% (further occurrences for this filament suppressed)") % __LINE__ % filament_id %
layer_id;
@@ -4399,15 +4395,17 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vector<std
ExtruderType extruder_type = ExtruderType(m_config.extruder_type.get_at(nozzle_info->extruder_id));
NozzleVolumeType nozzle_volume_type = nozzle_info->volume_type;
if (!index_map)
return get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list);
FilamentIndexKey key{filament_id, extruder_type, nozzle_volume_type};
auto iter = index_map.find(key);
if (iter == index_map.end()) {
auto iter = index_map->find(key);
if (iter == index_map->end()) {
int index = get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list);
index_map[key] = index;
(*index_map)[key] = index;
return index;
} else {
return index_map[key];
return iter->second;
}
}
-3
View File
@@ -987,9 +987,6 @@ void PrintObject::generate_support_material()
this->_generate_support_material();
m_print->throw_if_canceled();
}
// Orca: the tree support collision/avoidance caches and support nodes are only used while this step runs
// (detect_overhangs() rebuilds them from scratch), so don't keep them resident until the next slice.
this->clear_tree_support_preview_cache();
this->set_done(posSupportMaterial);
}
}
+1 -1
View File
@@ -1346,7 +1346,7 @@ void PrintObject::slice_volumes()
if (min_growth < 0.f || elfoot > 0.f) {
// Apply the negative XY compensation. (the ones that is <0)
ExPolygons trimming;
const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
if (elfoot > 0.f) {
ExPolygons expolygons_to_compensate = offset_ex(layer->merged(eps), -eps);
lslices_elfoot_uncompensated[layer_id] = expolygons_to_compensate;
+30 -12
View File
@@ -1820,15 +1820,37 @@ coordf_t TreeSupport::get_radius(const SupportNode* node)
return node->radius;
}
// Orca: these are hit up to several times per node per layer in drop_nodes(), so hand out a
// reference into the TreeSupportData cache instead of copying the ExPolygons out of it.
const ExPolygons& TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr)
ExPolygons TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr)
{
#if USE_SUPPORT_3D
if (m_model_volumes) {
bool on_build_plate = m_object_config->support_on_build_plate_only.value;
const Polygons& avoid_polys = m_model_volumes->getAvoidance(radius, obj_layer_nr, TreeSupport3D::TreeModelVolumes::AvoidanceType::FastSafe, on_build_plate, true);
ExPolygons expolys;
for (auto& poly : avoid_polys)
expolys.emplace_back(std::move(poly));
return expolys;
}
return ExPolygons();
#else
return m_ts_data->get_avoidance(radius, obj_layer_nr);
#endif
}
const ExPolygons& TreeSupport::get_collision(coordf_t radius, size_t layer_nr)
ExPolygons TreeSupport::get_collision(coordf_t radius, size_t layer_nr)
{
#if USE_SUPPORT_3D
if (m_model_volumes) {
bool on_build_plate = m_object_config->support_on_build_plate_only.value;
const Polygons& collision_polys = m_model_volumes->getCollision(radius, layer_nr, true);
ExPolygons expolys;
for (auto& poly : collision_polys)
expolys.emplace_back(std::move(poly));
return expolys;
}
#else
return m_ts_data->get_collision(radius, layer_nr);
#endif
return ExPolygons();
}
Polygons TreeSupport::get_collision_polys(coordf_t radius, size_t layer_nr)
{
@@ -2617,11 +2639,7 @@ void TreeSupport::draw_circles()
#endif // SUPPORT_TREE_DEBUG_TO_SVG
SupportLayerPtrs& ts_layers = m_object->support_layers();
// Orca: the vector owns its layers, so the dropped ones have to be deleted, not just unlinked.
// std::stable_partition (unlike std::remove_if) leaves exactly the dropped layers in the tail.
auto iter = std::stable_partition(ts_layers.begin(), ts_layers.end(), [](SupportLayer* ts_layer) { return ts_layer->height >= EPSILON; });
for (auto it = iter; it != ts_layers.end(); ++it)
delete *it;
auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](SupportLayer* ts_layer) { return ts_layer->height < EPSILON; });
ts_layers.erase(iter, ts_layers.end());
for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) {
ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr;
@@ -2864,7 +2882,7 @@ void TreeSupport::drop_nodes()
//Insert a completely new node and let both original nodes fade.
Point next_position = (node.position + neighbours[0]) / 2; //Average position of the two nodes.
coordf_t next_radius = calc_radius(node.dist_mm_to_top+height_next);
const ExPolygons& avoid_layer = get_avoidance(next_radius, obj_layer_nr_next);
auto avoid_layer = get_avoidance(next_radius, obj_layer_nr_next);
if (group_index == 0)
{
//Avoid collisions.
@@ -3051,7 +3069,7 @@ void TreeSupport::drop_nodes()
}
#endif
coordf_t next_radius = calc_radius(node.dist_mm_to_top + height_next);
const ExPolygons& avoidance_next = get_avoidance(next_radius, obj_layer_nr_next);
auto avoidance_next = get_avoidance(next_radius, obj_layer_nr_next);
Point to_outside = projection_onto(avoidance_next, node.position);
Point direction_to_outer = to_outside - node.position;
@@ -3105,7 +3123,7 @@ void TreeSupport::drop_nodes()
if (is_outside) { next_layer_vertex = candidate_vertex; }
}
}
const ExPolygons& next_collision = get_collision(0, obj_layer_nr_next);
auto next_collision = get_collision(0, obj_layer_nr_next);
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[obj_layer_nr_next], next_layer_vertex);
// don't increase radius if next node will collide partially with the object (STUDIO-7883)
to_outside = projection_onto(next_collision, next_layer_vertex);
+2 -2
View File
@@ -511,9 +511,9 @@ private:
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true);
coordf_t calc_radius(coordf_t mm_to_top);
coordf_t get_radius(const SupportNode* node);
const ExPolygons& get_avoidance(coordf_t radius, size_t obj_layer_nr);
ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr);
// layer's expolygon expanded by radius+m_xy_distance
const ExPolygons& get_collision(coordf_t radius, size_t layer_nr);
ExPolygons get_collision(coordf_t radius, size_t layer_nr);
// get Polygons instead of ExPolygons
Polygons get_collision_polys(coordf_t radius, size_t layer_nr);