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
15 changes: 13 additions & 2 deletions .github/workflows/continuous.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,18 @@ jobs:
docker-images: true
swap-storage: true

- name: Show disk space
- name: Set swap space
if: runner.os == 'Linux' && matrix.compiler == 'gcc' && matrix.config == 'RelWithDebInfo'
uses: pierotofy/set-swap-space@fc79b3f67fa8a838184ce84a674ca12238d2c761
with:
swap-size-gb: 16

- name: Show disk and memory space
run: |
echo "disk usage:"
df -h
echo "memory:"
if command -v free &>/dev/null; then free -h; else sysctl hw.memsize; fi

- name: Select build dir (Linux)
if: runner.os == 'Linux'
Expand Down Expand Up @@ -96,6 +104,9 @@ jobs:
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@master

- name: Install mold
uses: rui314/setup-mold@v1

- name: Dependencies (Linux)
if: runner.os == 'Linux'
run: |
Expand All @@ -109,7 +120,7 @@ jobs:
echo "CC=clang" >> $GITHUB_ENV
echo "CXX=clang++" >> $GITHUB_ENV
fi
sudo apt-get install xorg-dev
sudo apt-get install -y xorg-dev

- name: Dependencies (macOS)
if: runner.os == 'macOS'
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ uv.lock
*.out
*.app

# Lagrange serialized mesh/scene
*.lgs
*.lgm

# Mesh files
*.obj
*.off
Expand Down Expand Up @@ -96,3 +100,8 @@ keyfile.txt

# ctags
tags

# Claude
.claude/agent-memory/
/.tmp/
.claude/settings.local.json
47 changes: 24 additions & 23 deletions LagrangeOptions.cmake.sample
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,30 @@
# option(LAGRANGE_ALL "Build all lagrange modules" ON)

# Optional modules in alphabetical order.
# option(LAGRANGE_MODULE_BVH "Build module lagrange::bvh" ON)
# option(LAGRANGE_MODULE_FILTERING "Build module lagrange::filtering" ON)
# option(LAGRANGE_MODULE_FS "Build module lagrange::fs" ON)
# option(LAGRANGE_MODULE_GEODESIC "Build module lagrange::geodesic" ON)
# option(LAGRANGE_MODULE_IMAGE "Build module lagrange::image" ON)
# option(LAGRANGE_MODULE_IMAGE_IO "Build module lagrange::image_io" ON)
# option(LAGRANGE_MODULE_IO "Build module lagrange::io" ON)
# option(LAGRANGE_MODULE_PACKING "Build module lagrange::packing" ON)
# option(LAGRANGE_MODULE_PARTITIONING "Build module lagrange::partitioning" ON)
# option(LAGRANGE_MODULE_POISSON "Build module lagrange::poisson" ON)
# option(LAGRANGE_MODULE_POLYDDG "Build module lagrange::polyddg" ON)
# option(LAGRANGE_MODULE_POLYSCOPE "Build module lagrange::polyscope" ON)
# option(LAGRANGE_MODULE_PRIMITIVE "Build module lagrange::primitive" ON)
# option(LAGRANGE_MODULE_PYTHON "Build module lagrange::python" ON)
# option(LAGRANGE_MODULE_RAYCASTING "Build module lagrange::raycasting" ON)
# option(LAGRANGE_MODULE_REMESHING_IM "Build module lagrange::remeshing_im" ON)
# option(LAGRANGE_MODULE_SCENE "Build module lagrange::scene" ON)
# option(LAGRANGE_MODULE_SOLVER "Build module lagrange::solver" ON)
# option(LAGRANGE_MODULE_SUBDIVISION "Build module lagrange::subdivision" ON)
# option(LAGRANGE_MODULE_TEXPROC "Build module lagrange::texproc" ON)
# option(LAGRANGE_MODULE_UI "Build module lagrange::ui" ON)
# option(LAGRANGE_MODULE_VOLUME "Build module lagrange::volume" ON)
# option(LAGRANGE_MODULE_WINDING "Build module lagrange::winding" ON)
# option(LAGRANGE_MODULE_BVH "Build module lagrange::bvh" ON)
# option(LAGRANGE_MODULE_FILTERING "Build module lagrange::filtering" ON)
# option(LAGRANGE_MODULE_FS "Build module lagrange::fs" ON)
# option(LAGRANGE_MODULE_GEODESIC "Build module lagrange::geodesic" ON)
# option(LAGRANGE_MODULE_IMAGE "Build module lagrange::image" ON)
# option(LAGRANGE_MODULE_IMAGE_IO "Build module lagrange::image_io" ON)
# option(LAGRANGE_MODULE_IO "Build module lagrange::io" ON)
# option(LAGRANGE_MODULE_PACKING "Build module lagrange::packing" ON)
# option(LAGRANGE_MODULE_PARTITIONING "Build module lagrange::partitioning" ON)
# option(LAGRANGE_MODULE_POISSON "Build module lagrange::poisson" ON)
# option(LAGRANGE_MODULE_POLYDDG "Build module lagrange::polyddg" ON)
# option(LAGRANGE_MODULE_POLYSCOPE "Build module lagrange::polyscope" ON)
# option(LAGRANGE_MODULE_PRIMITIVE "Build module lagrange::primitive" ON)
# option(LAGRANGE_MODULE_PYTHON "Build module lagrange::python" ON)
# option(LAGRANGE_MODULE_RAYCASTING "Build module lagrange::raycasting" ON)
# option(LAGRANGE_MODULE_REMESHING_IM "Build module lagrange::remeshing_im" ON)
# option(LAGRANGE_MODULE_SCENE "Build module lagrange::scene" ON)
# option(LAGRANGE_MODULE_SERIALIZATION2 "Build module lagrange::serialization2" ON)
# option(LAGRANGE_MODULE_SOLVER "Build module lagrange::solver" ON)
# option(LAGRANGE_MODULE_SUBDIVISION "Build module lagrange::subdivision" ON)
# option(LAGRANGE_MODULE_TEXPROC "Build module lagrange::texproc" ON)
# option(LAGRANGE_MODULE_UI "Build module lagrange::ui" ON)
# option(LAGRANGE_MODULE_VOLUME "Build module lagrange::volume" ON)
# option(LAGRANGE_MODULE_WINDING "Build module lagrange::winding" ON)

# General options
# option(LAGRANGE_COMPILE_TESTS "Enable compilation tests" ON)
Expand Down
2 changes: 1 addition & 1 deletion cmake/lagrange/lagrangeMklModules.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
anorigami;baking;cad_io;contouring;decal;deformers;filtering;meshproc;quadrangulation;solver;texproc
anorigami;baking;cad_io;contouring;decal;deformers;filtering;meshproc;polyddg;quadrangulation;solver;texproc
7 changes: 7 additions & 0 deletions cmake/lagrange/lagrange_add_python_binding.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# governing permissions and limitations under the License.
#
function(lagrange_add_python_binding)
cmake_parse_arguments(PARSE_ARGV 0 ARG "" "PYTHON_NAME" "")

# Retrieve module name
get_filename_component(module_path "${CMAKE_CURRENT_SOURCE_DIR}/.." REALPATH)
get_filename_component(module_name "${module_path}" NAME)
Expand Down Expand Up @@ -39,4 +41,9 @@ function(lagrange_add_python_binding)

# Keep track of active modules
set_property(TARGET lagrange_python APPEND PROPERTY LAGRANGE_ACTIVE_MODULES ${module_name})

# Optional: override the Python submodule name (e.g. expose "serialization2" as "serialization")
if(ARG_PYTHON_NAME)
set_property(TARGET lagrange_python PROPERTY LAGRANGE_PYTHON_NAME_${module_name} ${ARG_PYTHON_NAME})
endif()
endfunction()
13 changes: 13 additions & 0 deletions cmake/lagrange/lagrange_add_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,28 @@ function(lagrange_add_test)
# Register tests
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/reports")

# When cross-compiling with Emscripten, test discovery at build time can produce truncated JSON
# output due to stdout flushing issues with PROXY_TO_PTHREAD. Use PRE_TEST to defer discovery to
# ctest runtime instead. See:
# - https://github.com/emscripten-core/emscripten/issues/15186
# - https://github.com/emscripten-core/emscripten/issues/20059
if(EMSCRIPTEN)
set(_discovery_mode PRE_TEST)
else()
set(_discovery_mode POST_BUILD)
endif()

if(LAGRANGE_TOPLEVEL_PROJECT AND NOT USE_SANITIZER MATCHES "([Tt]hread)")
catch_discover_tests(${test_target}
REPORTER junit
OUTPUT_DIR "${CMAKE_BINARY_DIR}/reports"
OUTPUT_SUFFIX ".xml"
DISCOVERY_MODE ${_discovery_mode}
PROPERTIES ENVIRONMENT ${LAGRANGE_TESTS_ENVIRONMENT}
)
else()
catch_discover_tests(${test_target}
DISCOVERY_MODE ${_discovery_mode}
PROPERTIES ENVIRONMENT ${LAGRANGE_TESTS_ENVIRONMENT}
)
endif()
Expand Down
1 change: 1 addition & 0 deletions cmake/lagrange/lagrange_find_package.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function(lagrange_find_package name)
span-lite
spdlog
TBB
zstd
)

# Defer to find_package() if desired. In the future we will switch to a dependency provider.
Expand Down
94 changes: 35 additions & 59 deletions cmake/lagrange/lagrange_limit_parallelism.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,81 +10,57 @@
# governing permissions and limitations under the License.
#
function(lagrange_limit_parallelism)
# Query system information
cmake_host_system_information(RESULT NUMBER_OF_PHYSICAL_CORES QUERY NUMBER_OF_PHYSICAL_CORES)
cmake_host_system_information(RESULT AVAILABLE_PHYSICAL_MEMORY QUERY AVAILABLE_PHYSICAL_MEMORY)
cmake_host_system_information(RESULT AVAILABLE_VIRTUAL_MEMORY QUERY AVAILABLE_VIRTUAL_MEMORY)
cmake_host_system_information(RESULT TOTAL_VIRTUAL_MEMORY QUERY TOTAL_VIRTUAL_MEMORY)
cmake_host_system_information(RESULT TOTAL_PHYSICAL_MEMORY QUERY TOTAL_PHYSICAL_MEMORY)

# Peak memory computed "manually" for each platform (in MB)
# Use a hard coded limit of 2 parallel linking jobs
set(max_rss_linux_debug 3000)
set(max_rss_linux_release 6000) # Force -j3 on a 16G memory machine.
set(max_rss_darwin_debug 729)
set(max_rss_darwin_release 395)
set(max_rss_windows_debug 2100)
set(max_rss_windows_release 1300)

# Use "release" limit only for matching single-config mode
if(GENERATOR_IS_MULTI_CONFIG OR NOT DEFINED CMAKE_BUILD_TYPE)
message(STATUS "Defaulting to debug")
set(_postfix "debug")
# Determine build type for memory estimation
get_property(_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_is_multi_config OR NOT DEFINED CMAKE_BUILD_TYPE)
set(_build_type "debug")
else()
string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
if(_type STREQUAL release)
set(_postfix "release")
else()
set(_postfix "debug")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" _build_type)
endif()

string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} _system)
# Estimated peak RSS per link job (in MB), by build type.
# Measure with: /usr/bin/time -v cmake --build --preset <preset> -j1 --target lagrange_python
set(_default_link_memory_release 16000) # 16GB
set(_default_link_memory_relwithdebinfo 32000) # 32GB
set(_default_link_memory_debug 32000) # 32GB

# Use a 3/2 factor safety margin compared to observed memory usage
math(EXPR num_cpu_memory "${AVAILABLE_PHYSICAL_MEMORY} * 3 / 2 / ${max_rss_${_system}_${_postfix}}")
# LTO links can require significantly more memory
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
set(_default_link_memory_release 64000) # 64GB
set(_default_link_memory_relwithdebinfo 64000) # 64GB
endif()

# Compute limits for link/compile steps
set(num_cpu_link 2)
set(num_cpu_compile ${NUMBER_OF_PHYSICAL_CORES})
if(num_cpu_link GREATER num_cpu_memory)
set(num_cpu_link ${num_cpu_memory})
if(DEFINED _default_link_memory_${_build_type})
set(_link_memory ${_default_link_memory_${_build_type}})
else()
set(_link_memory ${_default_link_memory_debug})
endif()
if(num_cpu_compile GREATER num_cpu_memory)
set(num_cpu_compile ${num_cpu_memory})

math(EXPR num_link_jobs "${TOTAL_PHYSICAL_MEMORY} / ${_link_memory}")
if(num_link_jobs LESS 1)
set(num_link_jobs 1)
endif()

if(CMAKE_SCRIPT_MODE_FILE)
# The message() command, without any mode, will print to stderr. But jenkins only allows us
# to capture stdout. To print a clean message without hyphens, we use cmake's echo command.
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${num_cpu_compile}")
# Script mode: echo the number of physical cores for use as the -j flag in Jenkins.
# Link parallelism is handled separately via Ninja job pools at configure time.
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${NUMBER_OF_PHYSICAL_CORES}")
else()
message(STATUS "Parallelism: Available physical memory: ${AVAILABLE_PHYSICAL_MEMORY} / ${TOTAL_PHYSICAL_MEMORY}")
message(STATUS "Parallelism: Available virtual memory: ${AVAILABLE_VIRTUAL_MEMORY} / ${TOTAL_VIRTUAL_MEMORY}")
message(STATUS "Parallelism: Number of physical cores: ${NUMBER_OF_PHYSICAL_CORES}")
message(STATUS "Parallelism: Limiting link pool to ${num_cpu_link}")
message(STATUS "Parallelism: Limiting compile pool to ${num_cpu_compile}")
endif()
message(STATUS "Parallelism: Total physical memory: ${TOTAL_PHYSICAL_MEMORY} MB")
message(STATUS "Parallelism: Link job memory budget: ${_link_memory} MB (${_build_type})")
message(STATUS "Parallelism: Limiting link pool to ${num_link_jobs}")

# Limit parallelism based on number of physical cores + available memory.
set_property(GLOBAL PROPERTY JOB_POOLS
pool-link=${num_cpu_link}
pool-compile=${num_cpu_compile}
pool-precompile-header=${num_cpu_compile}
)
set(CMAKE_JOB_POOL_LINK "pool-link" CACHE STRING "Job pool for linking" FORCE)
set(CMAKE_JOB_POOL_COMPILE "pool-compile" CACHE STRING "Job pool for compiling" FORCE)
set(CMAKE_JOB_POOL_PRECOMPILE_HEADER "pool-precompile-header" CACHE STRING "Job pool for generating pre-compiled headers" FORCE)

# Note: We cannot set directly CMAKE_BUILD_PARALLEL_LEVEL or CTEST_PARALLEL_LEVEL from this CMake file,
# since those are environment variables [1]: they are not cached and do not affect subsequent CMake calls.
# In practice, the parallelism for Ninja should be limited by our job pools, so the only thing we need is
# to run ctest in parallel.
# [1]: https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#cmake-language-environment-variables
set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs})
set(CMAKE_JOB_POOL_LINK "pool-link" CACHE STRING "Job pool for linking" FORCE)
endif()
endfunction()

# If this file is run in script mode, calling this function will simply echo the total number of
# cores that we desire to build/test with.
# If this file is run in script mode, it echoes the number of physical cores for use as
# the -j flag for cmake --build and ctest. Link parallelism is not relevant here — it is
# enforced by Ninja job pools set during the configure step.
if(CMAKE_SCRIPT_MODE_FILE)
if(DEFINED CMAKE_ARGV3)
# We need to extract build type from preset
Expand Down
10 changes: 7 additions & 3 deletions cmake/recipes/external/CPM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
# accordance with the terms of the Adobe license agreement accompanying
# it.
#
set(CPM_DOWNLOAD_VERSION 0.42.1)

# we use a fork slightly ahead of this until https://github.com/cpm-cmake/CPM.cmake/pull/688 is merged
set(CPM_DOWNLOAD_VERSION 0.42.1-f50a6c0)

if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
Expand All @@ -22,8 +24,10 @@ get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
function(download_cpm)
message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}")
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION}
# Revert to upstream URL once https://github.com/cpm-cmake/CPM.cmake/pull/688 is merged:
# https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
https://raw.githubusercontent.com/jdumas/CPM.cmake/f50a6c0ad986fdd407ae14a46f08b38716f36bc8/cmake/CPM.cmake
${CPM_DOWNLOAD_LOCATION}
)
endfunction()

Expand Down
30 changes: 13 additions & 17 deletions cmake/recipes/external/Eigen3.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,19 @@ endif()
option(EIGEN_WITH_MKL "Use Eigen with MKL" OFF)
option(EIGEN_DONT_VECTORIZE "Disable Eigen vectorization" OFF)

if(EIGEN_ROOT)
message(STATUS "Third-party (external): creating target 'Eigen3::Eigen' for external path: ${EIGEN_ROOT}")
set(EIGEN_INCLUDE_DIRS ${EIGEN_ROOT})
else()
message(STATUS "Third-party (external): creating target 'Eigen3::Eigen'")
message(STATUS "Third-party (external): creating target 'Eigen3::Eigen'")

include(CPM)
CPMAddPackage(
NAME eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.1
DOWNLOAD_ONLY ON
)
set(EIGEN_INCLUDE_DIRS ${eigen_SOURCE_DIR})
set(EIGEN_VERSION "5.0.1" CACHE STRING "Version of Eigen to use")

install(DIRECTORY ${EIGEN_INCLUDE_DIRS}/Eigen
DESTINATION include
)
endif()
include(CPM)
CPMAddPackage(
NAME eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG ${EIGEN_VERSION}
DOWNLOAD_ONLY ON
)
FetchContent_GetProperties(eigen)
set(EIGEN_INCLUDE_DIRS ${eigen_SOURCE_DIR})

add_library(Eigen3_Eigen INTERFACE)
add_library(Eigen3::Eigen ALIAS Eigen3_Eigen)
Expand All @@ -44,6 +38,8 @@ target_include_directories(Eigen3_Eigen SYSTEM INTERFACE
$<BUILD_INTERFACE:${EIGEN_INCLUDE_DIRS}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

# Not necessary after Eigen 5, but required for older versions. Doesn't hurt to keep it.
target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_MPL2_ONLY)

if(EIGEN_DONT_VECTORIZE)
Expand Down
38 changes: 38 additions & 0 deletions cmake/recipes/external/cista.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#
# 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 cista::cista)
return()
endif()

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

include(CPM)
CPMAddPackage(
NAME cista
GITHUB_REPOSITORY felixguendling/cista
GIT_TAG 4356022b0020dd924ab8afb3bf0199c07e9a9943 # ahead of v0.16 because of 6f9a254
DOWNLOAD_ONLY ON
OPTIONS
"CISTA_FMT OFF"
)

add_library(cista INTERFACE)
add_library(cista::cista ALIAS cista)

target_include_directories(cista SYSTEM INTERFACE "${cista_SOURCE_DIR}/include")

set_target_properties(cista PROPERTIES FOLDER "third_party")

# Install rules
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME cista)
install(TARGETS cista EXPORT Cista_Targets INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT Cista_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cista NAMESPACE cista::)
Loading
Loading