Test That Slab Slicing Does Not Depend on the Thread Schedule

Projects a dense, tilted sphere with slice_mesh_slabs() on one thread and
then three times multi-threaded, and requires the polygons to match exactly,
vertex order included. Fails without the canonical line sort, passes with it.
This commit is contained in:
Hanif Koh
2026-09-25 17:03:42 +08:00
parent 4e43ab8306
commit 13f4959e5f
2 changed files with 51 additions and 0 deletions
+1
View File
@@ -36,6 +36,7 @@ add_executable(${_TEST_NAME}_tests
test_stl.cpp
test_triangle_selector.cpp
test_meshboolean.cpp
test_trianglemesh_slicer.cpp
test_marchingsquares.cpp
test_lay_on_face.cpp
test_model.cpp
@@ -0,0 +1,50 @@
#include <catch2/catch_all.hpp>
#include <tbb/global_control.h>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"
using namespace Slic3r;
// The slab slicer collects each slab's intersection lines from a parallel loop over the facets.
// Its loops, and therefore the projected polygons, are derived from the order of those lines, so
// the order has to be canonical or the same mesh projects to different polygons run to run.
// The single-threaded projection is the reference; every multi-threaded run must reproduce it
// exactly, vertex order included.
TEST_CASE("Slab slicing projects the same polygons whatever the thread schedule", "[TriangleMeshSlicer]")
{
// A dense sphere, tilted so no facet is axis aligned: thousands of upward and downward
// facing facets spread over every slab.
indexed_triangle_set mesh = its_make_sphere(10., 0.05);
Transform3d trafo = Transform3d::Identity();
trafo.rotate(Eigen::AngleAxisd(0.37, Vec3d(0.3, 0.5, 1.).normalized()));
trafo.translate(Vec3d(1., 2., 0.));
std::vector<float> zs;
for (float z = -9.7f; z < 9.7f; z += 0.2f)
zs.emplace_back(z);
auto project = [&mesh, &trafo, &zs]() {
std::vector<Polygons> top, bottom;
slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, nullptr, []{});
return std::make_pair(std::move(top), std::move(bottom));
};
std::pair<std::vector<Polygons>, std::vector<Polygons>> reference;
{
tbb::global_control single_thread(tbb::global_control::max_allowed_parallelism, 1);
reference = project();
}
REQUIRE(reference.first.size() == zs.size());
REQUIRE(std::any_of(reference.first.begin(), reference.first.end(), [](const Polygons &p) { return !p.empty(); }));
REQUIRE(std::any_of(reference.second.begin(), reference.second.end(), [](const Polygons &p) { return !p.empty(); }));
for (int run = 0; run < 3; ++run) {
DYNAMIC_SECTION("multi-threaded run " << run)
{
auto parallel = project();
CHECK(parallel.first == reference.first);
CHECK(parallel.second == reference.second);
}
}
}