manifold: Update to 3.3.2

This commit is contained in:
Rémi Verschelde 2025-12-12 22:27:27 +01:00
parent 08e6cd181f
commit 3a48029fc2
No known key found for this signature in database
GPG Key ID: C3336907360768E1
15 changed files with 189 additions and 369 deletions

View File

@ -658,7 +658,7 @@ See `linuxbsd_headers/README.md`.
## manifold
- Upstream: https://github.com/elalish/manifold
- Version: git (76208dc02b069d2be50ed2d8a9279ee5622fa5fd, 2025)
- Version: 3.3.2 (798d83c8d7fabcddd23c1617097b95ba40f2597c, 2025)
- License: Apache 2.0
File extracted from upstream source:

View File

@ -488,80 +488,12 @@ constexpr double DEFAULT_LENGTH = 1.0;
*/
class Quality {
private:
inline static int circularSegments_ = DEFAULT_SEGMENTS;
inline static double circularAngle_ = DEFAULT_ANGLE;
inline static double circularEdgeLength_ = DEFAULT_LENGTH;
public:
/**
* Sets an angle constraint the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. The number of segments will be rounded up
* to the nearest factor of four.
*
* @param angle The minimum angle in degrees between consecutive segments. The
* angle will increase if the the segments hit the minimum edge length.
* Default is 10 degrees.
*/
static void SetMinCircularAngle(double angle) {
if (angle <= 0) return;
circularAngle_ = angle;
}
/**
* Sets a length constraint the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. The number of segments will be rounded up
* to the nearest factor of four.
*
* @param length The minimum length of segments. The length will
* increase if the the segments hit the minimum angle. Default is 1.0.
*/
static void SetMinCircularEdgeLength(double length) {
if (length <= 0) return;
circularEdgeLength_ = length;
}
/**
* Sets the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. Overrides the edge length and angle
* constraints and sets the number of segments to exactly this value.
*
* @param number Number of circular segments. Default is 0, meaning no
* constraint is applied.
*/
static void SetCircularSegments(int number) {
if (number < 3 && number != 0) return;
circularSegments_ = number;
}
/**
* Determine the result of the SetMinCircularAngle(),
* SetMinCircularEdgeLength(), and SetCircularSegments() defaults.
*
* @param radius For a given radius of circle, determine how many default
* segments there will be.
*/
static int GetCircularSegments(double radius) {
if (circularSegments_ > 0) return circularSegments_;
int nSegA = 360.0 / circularAngle_;
int nSegL = 2.0 * radius * kPi / circularEdgeLength_;
int nSeg = fmin(nSegA, nSegL) + 3;
nSeg -= nSeg % 4;
return std::max(nSeg, 4);
}
/**
* Resets the circular construction parameters to their defaults if
* SetMinCircularAngle, SetMinCircularEdgeLength, or SetCircularSegments have
* been called.
*/
static void ResetToDefaults() {
circularSegments_ = DEFAULT_SEGMENTS;
circularAngle_ = DEFAULT_ANGLE;
circularEdgeLength_ = DEFAULT_LENGTH;
}
static void SetMinCircularAngle(double angle);
static void SetMinCircularEdgeLength(double length);
static void SetCircularSegments(int number);
static int GetCircularSegments(double radius);
static void ResetToDefaults();
};
/** @} */
@ -600,39 +532,6 @@ struct ExecutionParams {
/** @} */
#ifdef MANIFOLD_DEBUG
template <class T>
std::ostream& operator<<(std::ostream& out, const la::vec<T, 1>& v) {
return out << '{' << v[0] << '}';
}
template <class T>
std::ostream& operator<<(std::ostream& out, const la::vec<T, 2>& v) {
return out << '{' << v[0] << ',' << v[1] << '}';
}
template <class T>
std::ostream& operator<<(std::ostream& out, const la::vec<T, 3>& v) {
return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}';
}
template <class T>
std::ostream& operator<<(std::ostream& out, const la::vec<T, 4>& v) {
return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const la::mat<T, M, 1>& m) {
return out << '{' << m[0] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const la::mat<T, M, 2>& m) {
return out << '{' << m[0] << ',' << m[1] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const la::mat<T, M, 3>& m) {
return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const la::mat<T, M, 4>& m) {
return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}';
}
inline std::ostream& operator<<(std::ostream& stream, const Box& box) {
return stream << "min: " << box.min << ", "

View File

@ -2360,6 +2360,46 @@ struct converter<std::array<T, 4>, vec<T, 4>> {
/** @} */
} // namespace linalg
#ifdef MANIFOLD_DEBUG
#include <iostream>
namespace linalg {
template <class T>
std::ostream& operator<<(std::ostream& out, const vec<T, 1>& v) {
return out << '{' << v[0] << '}';
}
template <class T>
std::ostream& operator<<(std::ostream& out, const vec<T, 2>& v) {
return out << '{' << v[0] << ',' << v[1] << '}';
}
template <class T>
std::ostream& operator<<(std::ostream& out, const vec<T, 3>& v) {
return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}';
}
template <class T>
std::ostream& operator<<(std::ostream& out, const vec<T, 4>& v) {
return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const mat<T, M, 1>& m) {
return out << '{' << m[0] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const mat<T, M, 2>& m) {
return out << '{' << m[0] << ',' << m[1] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const mat<T, M, 3>& m) {
return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}';
}
template <class T, int M>
std::ostream& operator<<(std::ostream& out, const mat<T, M, 4>& m) {
return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}';
}
} // namespace linalg
#endif
namespace std {
/** @addtogroup hash
* @ingroup LinAlg

View File

@ -878,10 +878,6 @@ Manifold::Impl Boolean3::Result(OpType op) const {
#endif
// Level 6
if (ManifoldParams().intermediateChecks)
DEBUG_ASSERT(outR.IsManifold(), logicErr, "polygon mesh is not manifold!");
outR.Face2Tri(faceEdge, halfedgeRef);
halfedgeRef.clear();
faceEdge.clear();

View File

@ -342,7 +342,9 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
std::vector<std::shared_ptr<CsgLeafNode>> tmp;
#if MANIFOLD_PAR == 1
tbb::task_group group;
std::mutex mutex;
// make sure the order of result is deterministic
std::vector<std::shared_ptr<CsgLeafNode>> parallelTmp;
for (int i = 0; i < 4; i++) parallelTmp.push_back(nullptr);
#endif
while (results.size() > 1) {
for (size_t i = 0; i < 4 && results.size() > 1; i++) {
@ -353,11 +355,8 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
auto b = std::move(results.back());
results.pop_back();
#if MANIFOLD_PAR == 1
group.run([&, a, b]() {
auto result = SimpleBoolean(*a->GetImpl(), *b->GetImpl(), operation);
mutex.lock();
tmp.push_back(result);
mutex.unlock();
group.run([&, i, a, b]() {
parallelTmp[i] = SimpleBoolean(*a->GetImpl(), *b->GetImpl(), operation);
});
#else
auto result = SimpleBoolean(*a->GetImpl(), *b->GetImpl(), operation);
@ -366,6 +365,8 @@ std::shared_ptr<CsgLeafNode> BatchBoolean(
}
#if MANIFOLD_PAR == 1
group.wait();
for (int i = 0; i < 4 && parallelTmp[i]; i++)
tmp.emplace_back(std::move(parallelTmp[i]));
#endif
for (auto result : tmp) {
results.push_back(result);
@ -617,33 +618,35 @@ std::shared_ptr<CsgLeafNode> CsgOpNode::ToLeafNode() const {
std::shared_ptr<CsgStackFrame> frame = stack.back();
auto impl = frame->op_node->impl_.GetGuard();
if (frame->finalize) {
switch (frame->op_node->op_) {
case OpType::Add:
*impl = {BatchUnion(frame->positive_children)};
break;
case OpType::Intersect: {
*impl = {BatchBoolean(OpType::Intersect, frame->positive_children)};
break;
};
case OpType::Subtract:
if (frame->positive_children.empty()) {
// nothing to subtract from, so the result is empty.
*impl = {std::make_shared<CsgLeafNode>()};
} else {
auto positive = BatchUnion(frame->positive_children);
if (frame->negative_children.empty()) {
// nothing to subtract, result equal to the LHS.
*impl = {frame->positive_children[0]};
if (!frame->op_node->cache_) {
switch (frame->op_node->op_) {
case OpType::Add:
*impl = {BatchUnion(frame->positive_children)};
break;
case OpType::Intersect: {
*impl = {BatchBoolean(OpType::Intersect, frame->positive_children)};
break;
};
case OpType::Subtract:
if (frame->positive_children.empty()) {
// nothing to subtract from, so the result is empty.
*impl = {std::make_shared<CsgLeafNode>()};
} else {
auto negative = BatchUnion(frame->negative_children);
*impl = {SimpleBoolean(*positive->GetImpl(), *negative->GetImpl(),
OpType::Subtract)};
auto positive = BatchUnion(frame->positive_children);
if (frame->negative_children.empty()) {
// nothing to subtract, result equal to the LHS.
*impl = {frame->positive_children[0]};
} else {
auto negative = BatchUnion(frame->negative_children);
*impl = {SimpleBoolean(*positive->GetImpl(),
*negative->GetImpl(), OpType::Subtract)};
}
}
}
break;
break;
}
frame->op_node->cache_ = std::static_pointer_cast<CsgLeafNode>(
(*impl)[0]->Transform(frame->op_node->transform_));
}
frame->op_node->cache_ = std::static_pointer_cast<CsgLeafNode>(
(*impl)[0]->Transform(frame->op_node->transform_));
if (frame->positive_dest != nullptr)
frame->positive_dest->push_back(std::static_pointer_cast<CsgLeafNode>(
frame->op_node->cache_->Transform(frame->transform)));

View File

@ -202,6 +202,7 @@ namespace manifold {
*/
void Manifold::Impl::CleanupTopology() {
if (!halfedge_.size()) return;
DEBUG_ASSERT(IsManifold(), logicErr, "polygon mesh is not manifold!");
// In the case of a very bad triangulation, it is possible to create pinched
// verts. They must be removed before edge collapse.

View File

@ -149,8 +149,12 @@ int GetLabels(std::vector<int>& components,
namespace manifold {
#if (MANIFOLD_PAR == 1)
#if (TBB_VERSION_MAJOR < 2021)
tbb::task_arena gc_arena(1, 1);
#else
tbb::task_arena gc_arena(1, 1, tbb::task_arena::priority::low);
#endif
#endif
std::atomic<uint32_t> Manifold::Impl::meshIDCounter_(1);
@ -484,8 +488,9 @@ void Manifold::Impl::CreateHalfedges(const Vec<ivec3>& triProp,
const int pair1 = ids[k];
Halfedge& h1 = halfedge_[pair1];
if (h0.startVert != h1.endVert || h0.endVert != h1.startVert) break;
if (halfedge_[NextHalfedge(pair0)].endVert ==
halfedge_[NextHalfedge(pair1)].endVert) {
if (h1.pairedHalfedge != kRemovedHalfedge &&
halfedge_[NextHalfedge(pair0)].endVert ==
halfedge_[NextHalfedge(pair1)].endVert) {
h0.pairedHalfedge = h1.pairedHalfedge = kRemovedHalfedge;
// Reorder so that remaining edges pair up
if (k != i + numEdge) std::swap(ids[i + numEdge], ids[k]);

View File

@ -68,6 +68,16 @@ struct Manifold::Impl {
const uint32_t numVert = meshGL.NumVert();
const uint32_t numTri = meshGL.NumTri();
if (numVert == 0 && numTri == 0) {
MakeEmpty(Error::NoError);
return;
}
if (numVert < 4 || numTri < 4) {
MakeEmpty(Error::NotManifold);
return;
}
if (meshGL.numProp < 3) {
MakeEmpty(Error::MissingPositionProperties);
return;

View File

@ -200,6 +200,80 @@ MeshGLP<Precision, I> GetMeshGLImpl(const manifold::Manifold::Impl& impl,
namespace manifold {
static int circularSegments_ = DEFAULT_SEGMENTS;
static double circularAngle_ = DEFAULT_ANGLE;
static double circularEdgeLength_ = DEFAULT_LENGTH;
/**
* Sets an angle constraint the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. The number of segments will be rounded up
* to the nearest factor of four.
*
* @param angle The minimum angle in degrees between consecutive segments. The
* angle will increase if the the segments hit the minimum edge length.
* Default is 10 degrees.
*/
void Quality::SetMinCircularAngle(double angle) {
if (angle <= 0) return;
circularAngle_ = angle;
}
/**
* Sets a length constraint the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. The number of segments will be rounded up
* to the nearest factor of four.
*
* @param length The minimum length of segments. The length will
* increase if the the segments hit the minimum angle. Default is 1.0.
*/
void Quality::SetMinCircularEdgeLength(double length) {
if (length <= 0) return;
circularEdgeLength_ = length;
}
/**
* Sets the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. Overrides the edge length and angle
* constraints and sets the number of segments to exactly this value.
*
* @param number Number of circular segments. Default is 0, meaning no
* constraint is applied.
*/
void Quality::SetCircularSegments(int number) {
if (number < 3 && number != 0) return;
circularSegments_ = number;
}
/**
* Determine the result of the SetMinCircularAngle(),
* SetMinCircularEdgeLength(), and SetCircularSegments() defaults.
*
* @param radius For a given radius of circle, determine how many default
* segments there will be.
*/
int Quality::GetCircularSegments(double radius) {
if (circularSegments_ > 0) return circularSegments_;
int nSegA = 360.0 / circularAngle_;
int nSegL = 2.0 * radius * kPi / circularEdgeLength_;
int nSeg = fmin(nSegA, nSegL) + 3;
nSeg -= nSeg % 4;
return std::max(nSeg, 4);
}
/**
* Resets the circular construction parameters to their defaults if
* SetMinCircularAngle, SetMinCircularEdgeLength, or SetCircularSegments have
* been called.
*/
void Quality::ResetToDefaults() {
circularSegments_ = DEFAULT_SEGMENTS;
circularAngle_ = DEFAULT_ANGLE;
circularEdgeLength_ = DEFAULT_LENGTH;
}
/**
* Construct an empty Manifold.
*

View File

@ -73,7 +73,13 @@ struct CheckHalfedges {
bool operator()(size_t edge) const {
const Halfedge halfedge = halfedges[edge];
if (halfedge.startVert == -1 || halfedge.endVert == -1) return true;
if (halfedge.startVert == -1 && halfedge.endVert == -1 &&
halfedge.pairedHalfedge == -1)
return true;
if (halfedges[NextHalfedge(edge)].startVert == -1 ||
halfedges[NextHalfedge(NextHalfedge(edge))].startVert == -1) {
return false;
}
if (halfedge.pairedHalfedge == -1) return false;
const Halfedge paired = halfedges[halfedge.pairedHalfedge];
@ -114,6 +120,7 @@ namespace manifold {
*/
bool Manifold::Impl::IsManifold() const {
if (halfedge_.size() == 0) return true;
if (halfedge_.size() % 3 != 0) return false;
return all_of(countAt(0_uz), countAt(halfedge_.size()),
CheckHalfedges({halfedge_}));
}

View File

@ -18,7 +18,9 @@
#include "quickhull.h"
#include <algorithm>
#include <cstddef>
#include <limits>
#include <unordered_map>
#include "impl.h"

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unordered_map>
#include "impl.h"
#include "parallel.h"

View File

@ -1,225 +0,0 @@
// Copyright 2021 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "./parallel.h"
#include "./utils.h"
#include "./vec.h"
#include "manifold/common.h"
#include "manifold/optional_assert.h"
namespace {
template <typename T>
inline bool FirstFinite(T v) {
return std::isfinite(v[0]);
}
template <>
inline bool FirstFinite<double>(double v) {
return std::isfinite(v);
}
} // namespace
namespace manifold {
/** @ingroup Private */
class SparseIndices {
// sparse indices where {p1: q1, p2: q2, ...} are laid out as
// p1 q1 p2 q2 or q1 p1 q2 p2, depending on endianness
// such that the indices are sorted by (p << 32) | q
public:
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || \
defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || \
defined(__MIBSEB__)
static constexpr size_t pOffset = 0;
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \
defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \
defined(__MIPSEL) || defined(__MIPSEL__) || defined(__EMSCRIPTEN__) || \
defined(_WIN32)
static constexpr size_t pOffset = 1;
#else
#error "unknown architecture"
#endif
static constexpr int64_t EncodePQ(int p, int q) {
return (int64_t(p) << 32) | q;
}
SparseIndices() = default;
SparseIndices(size_t size) { data_ = Vec<char>(size * sizeof(int64_t)); }
void Clear() { data_.clear(false); }
void FromIndices(const std::vector<SparseIndices>& indices) {
std::vector<size_t> sizes;
size_t total_size = 0;
for (const auto& ind : indices) {
sizes.push_back(total_size);
total_size += ind.data_.size();
}
data_ = Vec<char>(total_size);
for_each_n(ExecutionPolicy::Par, countAt(0), indices.size(), [&](size_t i) {
std::copy(indices[i].data_.begin(), indices[i].data_.end(),
data_.begin() + sizes[i]);
});
}
size_t size() const { return data_.size() / sizeof(int64_t); }
Vec<int> Copy(bool use_q) const {
Vec<int> out(size());
size_t offset = pOffset;
if (use_q) offset = 1 - offset;
const int* p = ptr();
for_each(autoPolicy(out.size()), countAt(0_uz), countAt(out.size()),
[&](size_t i) { out[i] = p[i * 2 + offset]; });
return out;
}
void Sort() {
VecView<int64_t> view = AsVec64();
stable_sort(view.begin(), view.end());
}
void Resize(size_t size) { data_.resize(size * sizeof(int64_t), -1); }
inline int& Get(size_t i, bool use_q) {
if (use_q)
return ptr()[2 * i + 1 - pOffset];
else
return ptr()[2 * i + pOffset];
}
inline int Get(size_t i, bool use_q) const {
if (use_q)
return ptr()[2 * i + 1 - pOffset];
else
return ptr()[2 * i + pOffset];
}
inline int64_t GetPQ(size_t i) const {
VecView<const int64_t> view = AsVec64();
return view[i];
}
inline void Set(size_t i, int p, int q) {
VecView<int64_t> view = AsVec64();
view[i] = EncodePQ(p, q);
}
inline void SetPQ(size_t i, int64_t pq) {
VecView<int64_t> view = AsVec64();
view[i] = pq;
}
VecView<int64_t> AsVec64() {
return VecView<int64_t>(reinterpret_cast<int64_t*>(data_.data()),
data_.size() / sizeof(int64_t));
}
VecView<const int64_t> AsVec64() const {
return VecView<const int64_t>(
reinterpret_cast<const int64_t*>(data_.data()),
data_.size() / sizeof(int64_t));
}
VecView<int32_t> AsVec32() {
return VecView<int32_t>(reinterpret_cast<int32_t*>(data_.data()),
data_.size() / sizeof(int32_t));
}
VecView<const int32_t> AsVec32() const {
return VecView<const int32_t>(
reinterpret_cast<const int32_t*>(data_.data()),
data_.size() / sizeof(int32_t));
}
inline void Add(int p, int q, bool seq = false) {
data_.extend(sizeof(int64_t), seq);
Set(size() - 1, p, q);
}
void Unique() {
Sort();
VecView<int64_t> view = AsVec64();
size_t newSize = unique(view.begin(), view.end()) - view.begin();
Resize(newSize);
}
size_t RemoveZeros(Vec<int>& S) {
DEBUG_ASSERT(S.size() == size(), userErr,
"Different number of values than indicies!");
Vec<size_t> new2Old(S.size());
sequence(new2Old.begin(), new2Old.end());
size_t size = copy_if(countAt(0_uz), countAt(S.size()), new2Old.begin(),
[&S](const size_t i) { return S[i] != 0; }) -
new2Old.begin();
new2Old.resize(size);
Permute(S, new2Old);
Vec<char> tmp(std::move(data_));
Resize(size);
gather(new2Old.begin(), new2Old.end(),
reinterpret_cast<int64_t*>(tmp.data()),
reinterpret_cast<int64_t*>(data_.data()));
return size;
}
template <typename T>
size_t KeepFinite(Vec<T>& v, Vec<int>& x) {
DEBUG_ASSERT(x.size() == size(), userErr,
"Different number of values than indicies!");
Vec<int> new2Old(v.size());
size_t size = copy_if(countAt(0_uz), countAt(v.size()), new2Old.begin(),
[&v](size_t i) { return FirstFinite(v[i]); }) -
new2Old.begin();
new2Old.resize(size);
Permute(v, new2Old);
Permute(x, new2Old);
Vec<char> tmp(std::move(data_));
Resize(size);
gather(new2Old.begin(), new2Old.end(),
reinterpret_cast<int64_t*>(tmp.data()),
reinterpret_cast<int64_t*>(data_.data()));
return size;
}
#ifdef MANIFOLD_DEBUG
void Dump() const {
std::cout << "SparseIndices = " << std::endl;
const int* p = ptr();
for (size_t i = 0; i < size(); ++i) {
std::cout << i << ", p = " << Get(i, false) << ", q = " << Get(i, true)
<< std::endl;
}
std::cout << std::endl;
}
#endif
private:
Vec<char> data_;
inline int* ptr() { return reinterpret_cast<int32_t*>(data_.data()); }
inline const int* ptr() const {
return reinterpret_cast<const int32_t*>(data_.data());
}
};
} // namespace manifold

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unordered_map>
#include "impl.h"
#include "parallel.h"

View File

@ -228,10 +228,14 @@ class Vec : public VecView<T> {
// Currently it is set to 64 pages (4kB page).
constexpr size_t ASYNC_FREE_THRESHOLD = 1 << 18;
TracyFreeS(ptr, 3);
#if defined(__has_feature)
#if !__has_feature(address_sanitizer)
#if (MANIFOLD_PAR == 1)
if (size * sizeof(T) > ASYNC_FREE_THRESHOLD)
gc_arena.enqueue([ptr]() { free(ptr); });
else
#endif
#endif
#endif
free(ptr);
}