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
2 changed files with 19 additions and 20 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();
+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);