Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/lsan.suppressions.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# LSAN suppression for known false positives in third-party system libraries.

# D-Bus client library — leaked allocations in libdbus-1.so during static init/shutdown.
leak:libdbus-1.so

# NVIDIA OpenGL driver — leaked allocations from libnvidia-glcore.so during GL context setup.
leak:libnvidia-glcore.so

# NVIDIA Vulkan ICD — leaked allocations reached through libGLX_nvidia.so during Vulkan context
# setup in SAL. Requires VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING=1 so the ICD stays mapped at exit.
leak:libGLX_nvidia.so
36 changes: 26 additions & 10 deletions cmake/lagrange/lagrange_add_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function(lagrange_add_test)
# Retrieve options
set(options CUSTOM_MAIN)
set(oneValueArgs "")
set(multiValueArgs "")
set(multiValueArgs ENVIRONMENT)
cmake_parse_arguments(OPTIONS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

# Create test executable
Expand All @@ -44,13 +44,6 @@ function(lagrange_add_test)
include(FetchContent)
target_code_coverage(${test_target} AUTO ALL EXCLUDE "${FETCHCONTENT_BASE_DIR}/*")

# Sanitizer suppression files to be passed to catch_discover_tests
set(LAGRANGE_TESTS_ENVIRONMENT
"TSAN_OPTIONS=suppressions=${PROJECT_SOURCE_DIR}/.github/tsan.suppressions.ini"
"LSAN_OPTIONS=suppressions=${PROJECT_SOURCE_DIR}/.github/lsan.suppressions.ini"
"ASAN_SAVE_DUMPS=${module_name}.dmp"
)

# Output directory
set_target_properties(${test_target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests")

Expand Down Expand Up @@ -99,13 +92,36 @@ function(lagrange_add_test)
OUTPUT_DIR "${CMAKE_BINARY_DIR}/reports"
OUTPUT_SUFFIX ".xml"
DISCOVERY_MODE ${_discovery_mode}
PROPERTIES ENVIRONMENT ${LAGRANGE_TESTS_ENVIRONMENT}
TEST_LIST ${test_target}_TESTS
)
else()
catch_discover_tests(${test_target}
DISCOVERY_MODE ${_discovery_mode}
PROPERTIES ENVIRONMENT ${LAGRANGE_TESTS_ENVIRONMENT}
TEST_LIST ${test_target}_TESTS
)
endif()

# Assemble env vars for every discovered test: sanitizer suppressions plus any caller-supplied
# entries via the ENVIRONMENT argument. Caller entries are appended last so they can override.
set(_test_env
"TSAN_OPTIONS=suppressions=${PROJECT_SOURCE_DIR}/.github/tsan.suppressions.ini"
"LSAN_OPTIONS=suppressions=${PROJECT_SOURCE_DIR}/.github/lsan.suppressions.ini"
"ASAN_SAVE_DUMPS=${module_name}.dmp"
${OPTIONS_ENVIRONMENT}
)
list(JOIN _test_env ";" _test_env_joined)

# Work around Catch2 bug by applying the complete environment list after discovery;
# Catch populates ${test_target}_TESTS before this TEST_INCLUDE_FILES script runs.
# https://github.com/catchorg/Catch2/issues/2424
set(_sanitizer_env_fixup "${CMAKE_CURRENT_BINARY_DIR}/${test_target}_sanitizer_env.cmake")
file(WRITE "${_sanitizer_env_fixup}"
"foreach(_t IN LISTS ${test_target}_TESTS)\n"
" set_tests_properties(\"\${_t}\" PROPERTIES ENVIRONMENT\n"
" \"${_test_env_joined}\"\n"
" )\n"
"endforeach()\n"
)
set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${_sanitizer_env_fixup}")

endfunction()
2 changes: 1 addition & 1 deletion cmake/lagrange/lagrange_download_data.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function(lagrange_download_data)
PREFIX "${FETCHCONTENT_BASE_DIR}/lagrange-test-data"
SOURCE_DIR ${LAGRANGE_DATA_FOLDER}
GIT_REPOSITORY https://github.com/adobe/lagrange-test-data.git
GIT_TAG 009e99371d7495f7ad81d42d32080ba8afd4d4cd
GIT_TAG 819110152ce4ccc9ad8459bd046702564a71b568
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
Expand Down
3 changes: 3 additions & 0 deletions cmake/lagrange/lagrange_runtime_dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,14 @@ function(lagrange_populate_runtime_dependencies target)
endif()

# Instruction to copy target file if it exists
# TODO: Remove lock file guard when https://gitlab.kitware.com/cmake/cmake/-/work_items/27205 is fixed.
string(APPEND COPY_SCRIPT_CONTENT
"if(EXISTS \"$<TARGET_FILE:${DEPENDENCY}>\")\n"
" message(\"Copying dll file: $<TARGET_FILE_NAME:${DEPENDENCY}> for target ${target}\")\n"
" file(MAKE_DIRECTORY \"$<TARGET_FILE_DIR:${target}>\")\n"
" file(LOCK \"${CMAKE_BINARY_DIR}/runtime_deps/$<TARGET_FILE_NAME:${DEPENDENCY}>.lock\" GUARD PROCESS TIMEOUT 120)\n"
" file(COPY_FILE \"$<TARGET_FILE:${DEPENDENCY}>\" \"$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_NAME:${DEPENDENCY}>\" ONLY_IF_DIFFERENT INPUT_MAY_BE_RECENT)\n"
" file(LOCK \"${CMAKE_BINARY_DIR}/runtime_deps/$<TARGET_FILE_NAME:${DEPENDENCY}>.lock\" RELEASE)\n"
" if(NOT EXISTS \"$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_NAME:${DEPENDENCY}>\")\n"
" message(FATAL_ERROR \"Failed to copy dll file: $<TARGET_FILE_NAME:${DEPENDENCY}> for target ${target}. Target folder: $<TARGET_FILE_DIR:${target}>\")\n"
" endif()\n"
Expand Down
3 changes: 1 addition & 2 deletions cmake/lagrange/lagrange_vcpkg_toolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ endif()
CPMAddPackage(
NAME vcpkg
GIT_REPOSITORY https://github.com/microsoft/vcpkg.git
GIT_TAG 2025.01.13
GIT_TAG 2026.06.24
QUIET
)

set(ENV{VCPKG_ROOT} "${vcpkg_SOURCE_DIR}")
set(ENV{VCPKG_KEEP_ENV_VARS} "VCPKG_ROOT;$ENV{VCPKG_KEEP_ENV_VARS}")

if(WIN32)
CPMAddPackage(
Expand Down
21 changes: 17 additions & 4 deletions cmake/recipes/external/embree.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,29 @@ function(embree_import_target)
endif()
set(TBB_LIBRARIES TBB)

# Embree's MSVC AVX512 kernels have been observed to segfault at runtime on some
# Windows machines. Disable AVX512 (and APX, which depends on it) on MSVC until this is root-caused upstream.
# TODO: Report and fix issue upstream. See CGT-774 for internal tracking.
if(MSVC)
set(EMBREE_ISA_AVX512 OFF CACHE BOOL "Enables AVX512 ISA." FORCE)
set(EMBREE_ISA_APX OFF CACHE BOOL "Enables APX ISA." FORCE)
endif()

# Ready to include embree's atrocious CMake
include(CPM)
set(EMBREE_VERSION v4.4.0)
set(EMBREE_PATCHES "")
set(EMBREE_VERSION 3d9cb89b9ea099c630e6272d37767e7dd4e78e74) # ahead of 4.4.1
set(EMBREE_PATCHES)
if(EMSCRIPTEN)
# TODO: Remove when https://github.com/RenderKit/embree/pull/633 is merged
set(EMBREE_PATCHES PATCHES embree4.patch)
endif()
if(LAGRANGE_WITH_EMBREE_3)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
set(EMBREE_VERSION v3.13.5)
# Patch for emscripten compatibility. Fix available upstream in Embree 4+.
# https://github.com/RenderKit/embree/pull/365
# https://github.com/RenderKit/embree/issues/486
set(EMBREE_PATCHES PATCHES embree.patch)
set(EMBREE_PATCHES PATCHES embree3.patch)
endif()
CPMAddPackage(
NAME embree
Expand Down Expand Up @@ -155,7 +167,7 @@ function(embree_import_target)
endif()

# Suppress kernel dispatch function casts and AccelSet downcasts, not all UBSan checks.
# Keep exclusions private; GCC has no function-pointer sanitizer.
# https://github.com/RenderKit/embree/issues/635
if(USE_SANITIZER MATCHES "([Uu]ndefined)")
foreach(target IN ITEMS
embree
Expand All @@ -166,6 +178,7 @@ function(embree_import_target)
embree_apx
)
if(TARGET ${target})
# Note: GCC 13 has no function-pointer sanitizer.
target_compile_options(${target} PRIVATE
$<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang,AppleClang>:-fno-sanitize=vptr>
$<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-fno-sanitize=function>
Expand Down
16 changes: 16 additions & 0 deletions cmake/recipes/external/embree4.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
diff --git i/common/simd/vboolf4_sse2.h w/common/simd/vboolf4_sse2.h
index 9b85adb70..8c16d730a 100644
--- i/common/simd/vboolf4_sse2.h
+++ w/common/simd/vboolf4_sse2.h
@@ -58,10 +58,10 @@ namespace embree
#if !defined(__EMSCRIPTEN__)
__forceinline operator const __m128i() const { return _mm_castps_si128(v); }
__forceinline operator const __m128d() const { return _mm_castps_pd(v); }
+ #endif
/* kept for source compatibility with code that calls .m128i()/.m128d() explicitly */
__forceinline const __m128i m128i() const { return _mm_castps_si128(v); }
__forceinline const __m128d m128d() const { return _mm_castps_pd(v); }
- #endif
#endif

__forceinline vboolf(bool a)
2 changes: 1 addition & 1 deletion cmake/recipes/external/instant-meshes-core.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ include(CPM)
CPMAddPackage(
NAME instant-meshes-core
GITHUB_REPOSITORY qnzhou/instant-meshes-core
GIT_TAG 8c87f12bec4b98ce29febcf5dd63ebb90e957104
GIT_TAG 632605af06eae75c9aaa6a61a0e551c64de02b71
)

add_library(instant-meshes-core::instant-meshes-core ALIAS instant-meshes-core)
Expand Down
34 changes: 34 additions & 0 deletions cmake/recipes/external/pcdio.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# Copyright 2026 Adobe. All rights reserved.
# This file is licensed to you 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 REPRESENTATIONS
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.
#
if(TARGET pcdio::pcdio)
return()
endif()

message(STATUS "Third-party (external): creating target 'pcdio::pcdio'")

include(CPM)
block()
set(CMAKE_FOLDER "third_party")
CPMAddPackage(
NAME pcdio
GIT_REPOSITORY https://github.com/adobe/pcdio.git
GIT_TAG 2bf236d8a93a52957e4a752968d79926b7e30235
OPTIONS
"PCDIO_BUILD_TESTS OFF"
"PCDIO_BUILD_EXAMPLES OFF"
"PCDIO_PYTHON OFF"
)
endblock()

if(TARGET pcdio)
set_target_properties(pcdio PROPERTIES FOLDER third_party POSITION_INDEPENDENT_CODE ON)
endif()
1 change: 1 addition & 0 deletions docs/cpp/doxyfile-predefined.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ PREDEFINED = LA_IGNORE_DOCUMENTATION_WARNING_BEGIN= \
LA_SUBDIVISION_API= \
LA_TESTING_API= \
LA_TEXPROC_API= \
LA_TRIANGULATION_API= \
LA_UI_API= \
LA_USD_API= \
LA_VOLUME_API= \
Expand Down
9 changes: 5 additions & 4 deletions modules/bvh/include/lagrange/bvh/resolve_tjunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ struct ResolveTJunctionsOptions
/// Vertices are not moved: only edges and facets are subdivided so the topology conforms to the
/// existing vertex positions. The `tolerance` only controls detection, not geometric snapping.
///
/// Both triangle and polygonal meshes are supported. By default (`triangulate_affected == true`)
/// the facets touched by a split are triangulated, so a triangle-mesh input yields a triangle-mesh
/// output. Set `triangulate_affected` to false to instead keep those facets as polygons, with the
/// split points inserted as additional (collinear) boundary vertices.
/// Both triangle and polygonal meshes are supported. By default (`triangulate_affected == true`),
/// affected triangular facets are retriangulated directly, even in a hybrid mesh; affected
/// polygonal facets use polygon triangulation. Set `triangulate_affected` to false to instead
/// keep all affected facets as polygons, with the split points inserted as additional (collinear)
/// boundary vertices.
///
/// @param[in,out] mesh Input mesh (triangle or polygonal). Modified in place.
/// @param[in] options Optional settings.
Expand Down
36 changes: 17 additions & 19 deletions modules/bvh/src/internal/resolve_tjunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
*/
#include <lagrange/bvh/internal/resolve_tjunctions.h>

#include "split_tjunction_edges.h"

#include <lagrange/SurfaceMeshTypes.h>
#include <lagrange/bvh/AABB.h>
#include <lagrange/bvh/api.h>
#include <lagrange/internal/split_edges.h>
#include <lagrange/triangulate_polygonal_facets.h>
#include <lagrange/utils/assert.h>
#include <lagrange/utils/function_ref.h>
#include <lagrange/utils/span.h>
Expand All @@ -30,6 +30,8 @@
#include <Eigen/Geometry>

#include <algorithm>
#include <atomic>
#include <cstdint>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -76,8 +78,9 @@ void resolve_tjunctions_impl(
Tree tree;
tree.build({boxes.data(), boxes.size()});

// For each edge, query the tree with the edge's tolerance-expanded box for candidate vertices.
// Query split points and classify only facets incident to split edges in parallel.
std::vector<std::vector<std::pair<Scalar, Index>>> edge_splits(num_edges);
std::atomic<std::uint8_t> affected_types{0};
tbb::parallel_for(Index(0), num_edges, [&](Index e) {
if (options.boundary_only && !mesh.is_boundary_edge(e)) return;
auto ev = mesh.get_edge_vertices(e);
Expand Down Expand Up @@ -108,6 +111,9 @@ void resolve_tjunctions_impl(
edge_splits[e].emplace_back(t, v);
return true;
}));
if (options.triangulate_affected && !edge_splits[e].empty()) {
classify_affected_facets(mesh, e, affected_types);
}
std::sort(edge_splits[e].begin(), edge_splits[e].end(), [](const auto& a, const auto& b) {
return a.first < b.first;
});
Expand All @@ -123,23 +129,15 @@ void resolve_tjunctions_impl(

if (split_pts.empty()) return;

// Split edges without retriangulating: each affected facet gets a polygonal copy appended at
// id >= old_num_facets, leaving the originals (to be removed) in place.
const Index old_num_facets = mesh.get_num_facets();
auto facets_to_remove = lagrange::internal::split_edges_only(
auto get_edge_split_pts = [&](Index e) -> span<Index> {
const Index n = edge_split_offsets[e + 1] - edge_split_offsets[e];
return span<Index>(split_pts.data() + edge_split_offsets[e], n);
};
split_tjunction_edges(
mesh,
function_ref<span<Index>(Index)>([&](Index e) -> span<Index> {
const Index n = edge_split_offsets[e + 1] - edge_split_offsets[e];
return span<Index>(split_pts.data() + edge_split_offsets[e], n);
}),
function_ref<bool(Index)>([](Index) { return true; }));

// Optionally triangulate only the new facets, then drop the original split facets.
if (options.triangulate_affected) {
auto is_new_facet = [old_num_facets](Index f) { return f >= old_num_facets; };
triangulate_polygonal_facets(mesh, function_ref<bool(Index)>(is_new_facet));
}
mesh.remove_facets(facets_to_remove);
function_ref<span<Index>(Index)>(get_edge_split_pts),
options.triangulate_affected,
affected_types.load(std::memory_order_relaxed));
}

} // namespace
Expand Down
Loading
Loading