diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index d709a67cd..360a8b14e 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -698,27 +698,29 @@ struct EdgeToFace {
     bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
 };
 
-template<typename ThrowOnCancelCallback>
+template<typename FaceFilter, typename ThrowOnCancelCallback>
 static std::vector<EdgeToFace> create_edge_map(
-    const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
+    const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
 {
     std::vector<EdgeToFace> edges_map;
-    edges_map.assign(its.indices.size() * 3, EdgeToFace());
+    edges_map.reserve(its.indices.size() * 3);
     for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx)
-        for (int i = 0; i < 3; ++ i) {
-            EdgeToFace &e2f = edges_map[facet_idx * 3 + i];
-            e2f.vertex_low  = its.indices[facet_idx][i];
-            e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
-            e2f.face        = facet_idx;
-            // 1 based indexing, to be always strictly positive.
-            e2f.face_edge   = i + 1;
-            if (e2f.vertex_low > e2f.vertex_high) {
-                // Sort the vertices
-                std::swap(e2f.vertex_low, e2f.vertex_high);
-                // and make the face_edge negative to indicate a flipped edge.
-                e2f.face_edge = - e2f.face_edge;
+        if (face_filter(facet_idx))
+            for (int i = 0; i < 3; ++ i) {
+                edges_map.push_back({});
+                EdgeToFace &e2f = edges_map.back();
+                e2f.vertex_low  = its.indices[facet_idx][i];
+                e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
+                e2f.face        = facet_idx;
+                // 1 based indexing, to be always strictly positive.
+                e2f.face_edge   = i + 1;
+                if (e2f.vertex_low > e2f.vertex_high) {
+                    // Sort the vertices
+                    std::swap(e2f.vertex_low, e2f.vertex_high);
+                    // and make the face_edge negative to indicate a flipped edge.
+                    e2f.face_edge = - e2f.face_edge;
+                }
             }
-        }
     throw_on_cancel();
     std::sort(edges_map.begin(), edges_map.end());
 
@@ -727,12 +729,12 @@ static std::vector<EdgeToFace> create_edge_map(
 
 // Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 // Two neighbor faces share a unique edge identifier even if they are flipped.
-template<typename ThrowOnCancelCallback>
-static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
+template<typename FaceFilter, typename ThrowOnCancelCallback>
+static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, FaceFilter face_filter, ThrowOnCancelCallback throw_on_cancel)
 {
     std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
 
-    std::vector<EdgeToFace> edges_map = create_edge_map(its, throw_on_cancel);
+    std::vector<EdgeToFace> edges_map = create_edge_map(its, face_filter, throw_on_cancel);
 
     // Assign a unique common edge id to touching triangle edges.
     int num_edges = 0;
@@ -780,12 +782,17 @@ static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_s
 
 std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its)
 {
-    return its_face_edge_ids_impl(its, [](){});
+    return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, [](){});
 }
 
 std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
 {
-    return its_face_edge_ids_impl(its, throw_on_cancel_callback);
+    return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
+}
+
+std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask)
+{
+    return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){});
 }
 
 // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index 7aef3055a..c8c7e0dd7 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -118,6 +118,7 @@ private:
 // Used for chaining slice lines into polygons.
 std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
 std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
+std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask);
 // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
 std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);
 
diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp
index 681ec32f0..ef90402a9 100644
--- a/src/libslic3r/TriangleMeshSlicer.cpp
+++ b/src/libslic3r/TriangleMeshSlicer.cpp
@@ -362,6 +362,35 @@ static inline std::vector<IntersectionLines> slice_make_lines(
     return lines;
 }
 
+template<typename TransformVertex, typename FaceFilter>
+static inline IntersectionLines slice_make_lines(
+    const std::vector<stl_vertex>                   &mesh_vertices,
+    const TransformVertex                           &transform_vertex_fn,
+    const std::vector<stl_triangle_vertex_indices>  &mesh_faces,
+    const std::vector<Vec3i>                        &face_edge_ids,
+    const float                                      plane_z, 
+    FaceFilter                                       face_filter)
+{
+    IntersectionLines lines;
+    for (int face_idx = 0; face_idx < mesh_faces.size(); ++ face_idx)
+        if (face_filter(face_idx)) {
+            const Vec3i &indices = mesh_faces[face_idx];
+            stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
+            // find facet extents
+            const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
+            const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
+            assert(min_z <= plane_z && max_z >= plane_z);
+            int  idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);            
+            IntersectionLine il;
+            // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
+            if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
+                assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
+                lines.emplace_back(il);
+            }
+        }
+    return lines;
+}
+
 // For projecting triangle sets onto slice slabs.
 struct SlabLines {
     // Intersection lines of a slice with a triangle set, CCW oriented.
@@ -1720,6 +1749,69 @@ std::vector<Polygons> slice_mesh(
     return layers;
 }
 
+// Specialized version for a single slicing plane only, running on a single thread.
+Polygons slice_mesh(
+    const indexed_triangle_set       &mesh,
+    // Unscaled Zs
+    const float                       plane_z,
+    const MeshSlicingParams          &params)
+{
+    std::vector<IntersectionLines> lines;
+
+    {
+        bool                trafo_identity = is_identity(params.trafo);
+        Transform3f         tf;
+        std::vector<bool>   face_mask(mesh.indices.size(), false);
+
+        {
+            // 1) Mark vertices as below or above the slicing plane.
+            std::vector<char> vertex_side(mesh.vertices.size(), 0);
+            if (trafo_identity) {
+                for (size_t i = 0; i < mesh.vertices.size(); ++ i) {
+                    float z = mesh.vertices[i].z();
+                    char  s = z < plane_z ? -1 : z == plane_z ? 0 : 1;
+                    vertex_side[i] = s;
+                }
+            } else {
+                tf = make_trafo_for_slicing(params.trafo);
+                for (size_t i = 0; i < mesh.vertices.size(); ++ i) {
+                    //FIXME don't need to transform x & y, just Z.
+                    float z = (tf * mesh.vertices[i]).z();
+                    char  s = z < plane_z ? -1 : z == plane_z ? 0 : 1;
+                    vertex_side[i] = s;
+                }
+            }
+
+            // 2) Mark faces crossing the plane.
+            for (size_t i = 0; i < mesh.indices.size(); ++ i) {
+                const Vec3i &face = mesh.indices[i];
+                int sides[3] = { vertex_side[face(0)], vertex_side[face(1)], vertex_side[face(2)] };
+                face_mask[i] = sides[0] * sides[1] <= 0 || sides[1] * sides[2] <= 0 || sides[0] * sides[2] <= 0;
+            }
+        }
+
+        // 3) Calculate face neighbors for just the faces in face_mask.
+        std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_mask);
+
+        // 4) Slice "face_mask" triangles, collect line segments.
+        // It likely is not worthwile to copy the vertices. Apply the transformation in place.
+        if (trafo_identity) {
+            lines.emplace_back(slice_make_lines(
+                mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); }, 
+                mesh.indices, face_edge_ids, plane_z, [&face_mask](int face_idx) { return face_mask[face_idx]; }));
+        } else {
+            // Transform the vertices, scale up in XY, not in Z.
+            lines.emplace_back(slice_make_lines(mesh.vertices, [tf](const Vec3f& p) { return tf * p; }, mesh.indices, face_edge_ids, plane_z,
+                [&face_mask](int face_idx) { return face_mask[face_idx]; }));
+        }
+    }
+
+    // 5) Chain the line segments.
+    std::vector<Polygons> layers = make_loops(lines, params, [](){});
+    assert(layers.size() == 1);
+    return layers.front();
+}
+
 std::vector<ExPolygons> slice_mesh_ex(
     const indexed_triangle_set       &mesh,
     const std::vector<float>         &zs,
diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp
index 9b02f5573..7ea7ac3a9 100644
--- a/src/libslic3r/TriangleMeshSlicer.hpp
+++ b/src/libslic3r/TriangleMeshSlicer.hpp
@@ -52,6 +52,12 @@ std::vector<Polygons>           slice_mesh(
     const MeshSlicingParams          &params,
     std::function<void()>             throw_on_cancel = []{});
 
+// Specialized version for a single slicing plane only, running on a single thread.
+Polygons                        slice_mesh(
+    const indexed_triangle_set       &mesh,
+    const float                       plane_z,
+    const MeshSlicingParams          &params);
+
 std::vector<ExPolygons>         slice_mesh_ex(
     const indexed_triangle_set       &mesh,
     const std::vector<float>         &zs,
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index f2c2ee833..4e2c21cf6 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -302,7 +302,7 @@ void GLVolume::SinkingContours::update()
 #if ALG_SLICE == ALG_SLICE_MESH
             MeshSlicingParams slicing_params;
             slicing_params.trafo = m_parent.world_matrix();
-            std::vector<Polygons> list_of_polys = slice_mesh(mesh.its, std::vector<float>{ 0.0f }, slicing_params);
+            Polygons polygons = slice_mesh(mesh.its, 0.0f, slicing_params);
 
             auto append_polygon = [this](const Polygon& polygon, GUI::GLModel::InitializationData& data) {
                 if (!polygon.empty()) {
@@ -326,11 +326,9 @@ void GLVolume::SinkingContours::update()
 
             m_model.reset();
             GUI::GLModel::InitializationData init_data;
-            for (const Polygons& polygons : list_of_polys) {
-                for (const Polygon& polygon : polygons) {
-                    // contour
-                    append_polygon(polygon, init_data);
-                }
+            for (const Polygon& polygon : polygons) {
+                // contour
+                append_polygon(polygon, init_data);
             }
 #else
             MeshSlicingParamsEx slicing_params;