diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-05-12 16:08:18 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-05-12 16:08:18 +0300 |
commit | 1328d9a575d28a2756594f01a6ef162e1c5afb8e (patch) | |
tree | 4452b9409c2bcf1d6ab057847226429f8dbb4ef0 | |
parent | 2e8e7bd7b9632122e5d8e83b1ac0690522433c15 (diff) | |
parent | 31202ea628d2da2a5a2e6494d12bf32b7886f4cf (diff) |
Merge branch 'master' into temp-viewport-compositor-merge
678 files changed, 14500 insertions, 9499 deletions
diff --git a/.clang-tidy b/.clang-tidy index 1cc0e6e7f4a..42ce52d58ca 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,8 @@ # The warnings below are disabled because they are too pedantic and not worth fixing. # Some of them will be enabled as part of the Clang-Tidy task, see T78535. +# NOTE: No comments in the list below is allowed. Clang-tidy will ignore items after comments in the lists flag list. +# This is because the comment is not a valid list item and it will stop parsing flags if a list item is a comment. Checks: > -*, readability-*, @@ -14,10 +16,9 @@ Checks: > -readability-make-member-function-const, -readability-suspicious-call-argument, -readability-redundant-member-init, - -readability-misleading-indentation, - -readability-use-anyofallof, + -readability-identifier-length, -readability-function-cognitive-complexity, @@ -35,6 +36,8 @@ Checks: > -bugprone-redundant-branch-condition, + -bugprone-suspicious-include, + modernize-*, -modernize-use-auto, -modernize-use-trailing-return-type, @@ -42,8 +45,6 @@ Checks: > -modernize-use-nodiscard, -modernize-loop-convert, -modernize-pass-by-value, - # Cannot be enabled yet, because using raw string literals in tests breaks - # the windows compiler currently. -modernize-raw-string-literal, -modernize-return-braced-init-list diff --git a/CMakeLists.txt b/CMakeLists.txt index f5660b3653b..13f4e6b9cc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -781,7 +781,9 @@ set_and_warn_dependency(WITH_BOOST WITH_OPENCOLORIO OFF) set_and_warn_dependency(WITH_BOOST WITH_QUADRIFLOW OFF) set_and_warn_dependency(WITH_BOOST WITH_USD OFF) set_and_warn_dependency(WITH_BOOST WITH_ALEMBIC OFF) -set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF) +if(WITH_CYCLES) + set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF) +endif() set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF) if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR @@ -1456,14 +1458,6 @@ if(WITH_LIBMV OR WITH_GTESTS OR (WITH_CYCLES AND WITH_CYCLES_LOGGING)) endif() #----------------------------------------------------------------------------- -# Configure Ceres - -if(WITH_LIBMV) - # We always have C++11 which includes unordered_map. - set(CERES_DEFINES "-DCERES_STD_UNORDERED_MAP;-DCERES_USE_CXX_THREADS") -endif() - -#----------------------------------------------------------------------------- # Extra limits to number of jobs running in parallel for some kind os tasks. # Only supported by Ninja build system currently. if("${CMAKE_GENERATOR}" MATCHES "Ninja" AND WITH_NINJA_POOL_JOBS) @@ -1539,7 +1533,6 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ALL -Wall) - ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_CAST_ALIGN -Wcast-align) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_IMPLICIT_FUNCTION_DECLARATION -Werror=implicit-function-declaration) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_RETURN_TYPE -Werror=return-type) ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_ERROR_VLA -Werror=vla) diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 8f4738d1f1c..b63e86a3ac2 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -54,9 +54,6 @@ include(cmake/freetype.cmake) include(cmake/freeglut.cmake) include(cmake/glew.cmake) include(cmake/alembic.cmake) -include(cmake/glfw.cmake) -include(cmake/clew.cmake) -include(cmake/cuew.cmake) include(cmake/opensubdiv.cmake) include(cmake/sdl.cmake) include(cmake/opencollada.cmake) diff --git a/build_files/build_environment/cmake/clew.cmake b/build_files/build_environment/cmake/clew.cmake deleted file mode 100644 index 8c965d52d2c..00000000000 --- a/build_files/build_environment/cmake/clew.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(CLEW_EXTRA_ARGS) - -ExternalProject_Add(external_clew - URL file://${PACKAGE_DIR}/${CLEW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${CLEW_HASH_TYPE}=${CLEW_HASH} - PREFIX ${BUILD_DIR}/clew - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/clew -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${CLEW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/clew -) diff --git a/build_files/build_environment/cmake/cuew.cmake b/build_files/build_environment/cmake/cuew.cmake deleted file mode 100644 index 8beb8b4fade..00000000000 --- a/build_files/build_environment/cmake/cuew.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(CUEW_EXTRA_ARGS) - -ExternalProject_Add(external_cuew - URL file://${PACKAGE_DIR}/${CUEW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${CUEW_HASH_TYPE}=${CUEW_HASH} - PREFIX ${BUILD_DIR}/cuew - PATCH_COMMAND ${PATCH_CMD} --verbose -p 0 -N -d ${BUILD_DIR}/cuew/src/external_cuew < ${PATCH_DIR}/cuew.diff - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/cuew -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${CUEW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/cuew -) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index 80c53d05c28..81e7f7ab3fe 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -39,10 +39,6 @@ download_source(FREETYPE) download_source(GLEW) download_source(FREEGLUT) download_source(ALEMBIC) -download_source(GLFW) -download_source(CLEW) -download_source(GLFW) -download_source(CUEW) download_source(OPENSUBDIV) download_source(SDL) download_source(OPENCOLLADA) diff --git a/build_files/build_environment/cmake/glfw.cmake b/build_files/build_environment/cmake/glfw.cmake deleted file mode 100644 index 29fce7609e5..00000000000 --- a/build_files/build_environment/cmake/glfw.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(GLFW_EXTRA_ARGS) - -ExternalProject_Add(external_glfw - URL file://${PACKAGE_DIR}/${GLFW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${GLFW_HASH_TYPE}=${GLFW_HASH} - PREFIX ${BUILD_DIR}/glfw - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/glfw -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${GLFW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/glfw -) diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake index 2246031be83..4e2d1503406 100644 --- a/build_files/build_environment/cmake/opencolorio.cmake +++ b/build_files/build_environment/cmake/opencolorio.cmake @@ -53,7 +53,8 @@ add_dependencies( external_opencolorio external_yamlcpp external_expat - external_openexr + external_imath + external_pystring ) if(WIN32) diff --git a/build_files/build_environment/cmake/opensubdiv.cmake b/build_files/build_environment/cmake/opensubdiv.cmake index 6d6e3568406..a142160b9cc 100644 --- a/build_files/build_environment/cmake/opensubdiv.cmake +++ b/build_files/build_environment/cmake/opensubdiv.cmake @@ -10,20 +10,16 @@ set(OPENSUBDIV_EXTRA_ARGS -DNO_OMP=ON -DNO_TBB=OFF -DNO_CUDA=ON - -DNO_OPENCL=OFF - -DNO_CLEW=OFF + -DNO_OPENCL=ON + -DNO_CLEW=ON -DNO_OPENGL=OFF -DNO_METAL=OFF -DNO_DX=ON -DNO_TESTS=ON -DNO_GLTESTS=ON - -DNO_GLEW=OFF - -DNO_GLFW=OFF + -DNO_GLEW=ON + -DNO_GLFW=ON -DNO_GLFW_X11=ON - -DGLEW_INCLUDE_DIR=${LIBDIR}/glew/include - -DGLEW_LIBRARY=${LIBDIR}/glew/lib/libGLEW${LIBEXT} - -DGLFW_INCLUDE_DIR=${LIBDIR}/glfw/include - -DGLFW_LIBRARIES=${LIBDIR}/glfw/lib/glfw3${LIBEXT} ) if(WIN32) @@ -31,19 +27,12 @@ if(WIN32) ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/tbb.lib - -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL - -DCLEW_LIBRARY=${LIBDIR}/clew/lib/clew${LIBEXT} - -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include - -DCUEW_LIBRARY=${LIBDIR}/cuew/lib/cuew${LIBEXT} ) else() set(OPENSUBDIV_EXTRA_ARGS ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include -DTBB_tbb_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}tbb_static${LIBEXT} - -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include - -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL - -DCLEW_LIBRARY=${LIBDIR}/clew/lib/static/${LIBPREFIX}clew${LIBEXT} ) endif() @@ -75,9 +64,5 @@ endif() add_dependencies( external_opensubdiv - external_glew - external_glfw - external_clew - external_cuew external_tbb ) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 25267273db0..550be86b6b6 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -98,27 +98,6 @@ set(ALEMBIC_HASH 2cd8d6e5a3ac4a014e24a4b04f4fadf9) set(ALEMBIC_HASH_TYPE MD5) set(ALEMBIC_FILE alembic-${ALEMBIC_VERSION}.tar.gz) -# hash is for 3.1.2 -set(GLFW_GIT_UID 30306e54705c3adae9fe082c816a3be71963485c) -set(GLFW_URI https://github.com/glfw/glfw/archive/${GLFW_GIT_UID}.zip) -set(GLFW_HASH 20cacb1613da7eeb092f3ac4f6b2b3d0) -set(GLFW_HASH_TYPE MD5) -set(GLFW_FILE glfw-${GLFW_GIT_UID}.zip) - -# latest uid in git as of 2016-04-01 -set(CLEW_GIT_UID 277db43f6cafe8b27c6f1055f69dc67da4aeb299) -set(CLEW_URI https://github.com/OpenCLWrangler/clew/archive/${CLEW_GIT_UID}.zip) -set(CLEW_HASH 2c699d10ed78362e71f56fae2a4c5f98) -set(CLEW_HASH_TYPE MD5) -set(CLEW_FILE clew-${CLEW_GIT_UID}.zip) - -# latest uid in git as of 2016-04-01 -set(CUEW_GIT_UID 1744972026de9cf27c8a7dc39cf39cd83d5f922f) -set(CUEW_URI https://github.com/CudaWrangler/cuew/archive/${CUEW_GIT_UID}.zip) -set(CUEW_HASH 86760d62978ebfd96cd93f5aa1abaf4a) -set(CUEW_HASH_TYPE MD5) -set(CUEW_FILE cuew-${CUEW_GIT_UID}.zip) - set(OPENSUBDIV_VERSION v3_4_4) set(OPENSUBDIV_URI https://github.com/PixarAnimationStudios/OpenSubdiv/archive/${OPENSUBDIV_VERSION}.tar.gz) set(OPENSUBDIV_HASH 39ecc5caf0abebc943d1ce131855e76e) diff --git a/build_files/build_environment/dependencies.dot b/build_files/build_environment/dependencies.dot index 62949f3de62..7e8637fbced 100644 --- a/build_files/build_environment/dependencies.dot +++ b/build_files/build_environment/dependencies.dot @@ -37,10 +37,6 @@ graph[autosize = false, size = "25.7,8.3!", resolution = 300, overlap = false, s external_openimageio -- external_webp; external_openimageio -- external_opencolorio_extra; external_openmp -- external_clang; - external_opensubdiv -- external_glew; - external_opensubdiv -- external_glfw; - external_opensubdiv -- external_clew; - external_opensubdiv -- external_cuew; external_opensubdiv -- external_tbb; openvdb -- external_tbb; openvdb -- external_boost; diff --git a/build_files/build_environment/patches/cuew.diff b/build_files/build_environment/patches/cuew.diff deleted file mode 100644 index 0363034cd93..00000000000 --- a/build_files/build_environment/patches/cuew.diff +++ /dev/null @@ -1,26 +0,0 @@ ---- CmakeLists.txt.orig 2015-12-31 03:46:41 -0700 -+++ CMakeLists.txt 2016-04-01 13:28:33 -0600 -@@ -22,3 +22,10 @@ - - add_executable(testcuew cuewTest/cuewTest.c include/cuew.h) - target_link_libraries(testcuew cuew ${CMAKE_DL_LIBS}) -+ -+install(TARGETS cuew -+ LIBRARY DESTINATION lib COMPONENT libraries -+ ARCHIVE DESTINATION lib/static COMPONENT libraries) -+ -+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/cuew.h -+ DESTINATION include/) -\ No newline at end of file ---- src/cuew.c 2016-04-01 13:41:43 -0600 -+++ src/cuew.c 2016-04-01 13:41:11 -0600 -@@ -15,7 +15,9 @@ - */ - - #ifdef _MSC_VER -+#if _MSC_VER < 1900 - # define snprintf _snprintf -+#endif - # define popen _popen - # define pclose _pclose - # define _CRT_SECURE_NO_WARNINGS diff --git a/build_files/cmake/project_info.py b/build_files/cmake/project_info.py index 46fd538f675..0661db3a18c 100755 --- a/build_files/cmake/project_info.py +++ b/build_files/cmake/project_info.py @@ -170,7 +170,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl project_path = create_eclipse_project() if not exists(project_path): - print("Generating Eclipse Prokect File Failed: %r not found" % project_path) + print("Generating Eclipse Project File Failed: %r not found" % project_path) return None, None from xml.dom.minidom import parse diff --git a/doc/python_api/examples/bpy.types.Image.py b/doc/python_api/examples/bpy.types.Image.py index 715623f6f76..f0c1a780e24 100644 --- a/doc/python_api/examples/bpy.types.Image.py +++ b/doc/python_api/examples/bpy.types.Image.py @@ -44,4 +44,3 @@ image_dest = image_src.copy() image_dest.update() print(image_dest.size) print(image_dest.pixels[0:4]) - diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py index be164a03028..5576b2d0bfe 100644 --- a/doc/python_api/examples/gpu.6.py +++ b/doc/python_api/examples/gpu.6.py @@ -29,3 +29,36 @@ def draw(): bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL') + +""" +3D Image +-------- + +Similar to the 2D Image shader, but works with 3D positions for the image vertices. +To use this example you have to provide an image that should be displayed. +""" +import bpy +import gpu +from gpu_extras.batch import batch_for_shader + +IMAGE_NAME = "Untitled" +image = bpy.data.images[IMAGE_NAME] +texture = gpu.texture.from_image(image) + +shader = gpu.shader.from_builtin('3D_IMAGE') +batch = batch_for_shader( + shader, 'TRIS', + { + "pos": ((0, 0, 0), (0, 1, 1), (1, 1, 1), (1, 1, 1), (1, 0, 0), (0, 0, 0)), + "texCoord": ((0, 0), (0, 1), (1, 1), (1, 1), (1, 0), (0, 0)), + }, +) + + +def draw(): + shader.bind() + shader.uniform_sampler("image", texture) + batch.draw(shader) + + +bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt index 1d8a611b107..b723a4466fb 100644 --- a/extern/ceres/CMakeLists.txt +++ b/extern/ceres/CMakeLists.txt @@ -1,16 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2012 Blender Foundation. All rights reserved. -# NOTE: This file is automatically generated by bundle.sh script -# If you're doing changes in this file, please update template -# in that script too - set(INC . include internal config - ../gflags/src ) set(INC_SYS @@ -20,279 +15,296 @@ set(INC_SYS ) set(SRC - internal/ceres/accelerate_sparse.cc - internal/ceres/array_utils.cc - internal/ceres/blas.cc - internal/ceres/block_evaluate_preparer.cc - internal/ceres/block_jacobian_writer.cc - internal/ceres/block_jacobi_preconditioner.cc - internal/ceres/block_random_access_dense_matrix.cc - internal/ceres/block_random_access_diagonal_matrix.cc - internal/ceres/block_random_access_matrix.cc - internal/ceres/block_random_access_sparse_matrix.cc - internal/ceres/block_sparse_matrix.cc - internal/ceres/block_structure.cc - internal/ceres/callbacks.cc - internal/ceres/canonical_views_clustering.cc - internal/ceres/c_api.cc - internal/ceres/cgnr_solver.cc - internal/ceres/compressed_col_sparse_matrix_utils.cc - internal/ceres/compressed_row_jacobian_writer.cc - internal/ceres/compressed_row_sparse_matrix.cc - internal/ceres/conditioned_cost_function.cc - internal/ceres/conjugate_gradients_solver.cc - internal/ceres/context.cc - internal/ceres/context_impl.cc - internal/ceres/coordinate_descent_minimizer.cc - internal/ceres/corrector.cc - internal/ceres/covariance.cc - internal/ceres/covariance_impl.cc - internal/ceres/cxsparse.cc - internal/ceres/dense_normal_cholesky_solver.cc - internal/ceres/dense_qr_solver.cc - internal/ceres/dense_sparse_matrix.cc - internal/ceres/detect_structure.cc - internal/ceres/dogleg_strategy.cc - internal/ceres/dynamic_compressed_row_jacobian_writer.cc - internal/ceres/dynamic_compressed_row_sparse_matrix.cc - internal/ceres/dynamic_sparse_normal_cholesky_solver.cc - internal/ceres/eigensparse.cc - internal/ceres/evaluator.cc - internal/ceres/file.cc - internal/ceres/float_cxsparse.cc - internal/ceres/float_suitesparse.cc - internal/ceres/function_sample.cc - internal/ceres/generated/partitioned_matrix_view_d_d_d.cc - internal/ceres/generated/schur_eliminator_d_d_d.cc - internal/ceres/gradient_checker.cc - internal/ceres/gradient_checking_cost_function.cc - internal/ceres/gradient_problem.cc - internal/ceres/gradient_problem_solver.cc - internal/ceres/implicit_schur_complement.cc - internal/ceres/inner_product_computer.cc - internal/ceres/is_close.cc - internal/ceres/iterative_refiner.cc - internal/ceres/iterative_schur_complement_solver.cc - internal/ceres/lapack.cc - internal/ceres/levenberg_marquardt_strategy.cc - internal/ceres/linear_least_squares_problems.cc - internal/ceres/linear_operator.cc - internal/ceres/linear_solver.cc - internal/ceres/line_search.cc - internal/ceres/line_search_direction.cc - internal/ceres/line_search_minimizer.cc - internal/ceres/line_search_preprocessor.cc - internal/ceres/local_parameterization.cc - internal/ceres/loss_function.cc - internal/ceres/low_rank_inverse_hessian.cc - internal/ceres/minimizer.cc - internal/ceres/normal_prior.cc - internal/ceres/parallel_for_cxx.cc - internal/ceres/parallel_for_nothreads.cc - internal/ceres/parallel_for_openmp.cc - internal/ceres/parallel_utils.cc - internal/ceres/parameter_block_ordering.cc - internal/ceres/partitioned_matrix_view.cc - internal/ceres/polynomial.cc - internal/ceres/preconditioner.cc - internal/ceres/preprocessor.cc - internal/ceres/problem.cc - internal/ceres/problem_impl.cc - internal/ceres/program.cc - internal/ceres/reorder_program.cc - internal/ceres/residual_block.cc - internal/ceres/residual_block_utils.cc - internal/ceres/schur_complement_solver.cc - internal/ceres/schur_eliminator.cc - internal/ceres/schur_jacobi_preconditioner.cc - internal/ceres/schur_templates.cc - internal/ceres/scratch_evaluate_preparer.cc - internal/ceres/single_linkage_clustering.cc - internal/ceres/solver.cc - internal/ceres/solver_utils.cc - internal/ceres/sparse_cholesky.cc - internal/ceres/sparse_matrix.cc - internal/ceres/sparse_normal_cholesky_solver.cc - internal/ceres/split.cc - internal/ceres/stringprintf.cc - internal/ceres/subset_preconditioner.cc - internal/ceres/suitesparse.cc - internal/ceres/thread_pool.cc - internal/ceres/thread_token_provider.cc - internal/ceres/triplet_sparse_matrix.cc - internal/ceres/trust_region_minimizer.cc - internal/ceres/trust_region_preprocessor.cc - internal/ceres/trust_region_step_evaluator.cc - internal/ceres/trust_region_strategy.cc - internal/ceres/types.cc - internal/ceres/visibility_based_preconditioner.cc - internal/ceres/visibility.cc - internal/ceres/wall_time.cc + include/ceres/autodiff_cost_function.h + include/ceres/autodiff_first_order_function.h + include/ceres/autodiff_local_parameterization.h + include/ceres/autodiff_manifold.h + include/ceres/c_api.h + include/ceres/ceres.h + include/ceres/conditioned_cost_function.h + include/ceres/context.h + include/ceres/cost_function.h + include/ceres/cost_function_to_functor.h + include/ceres/covariance.h + include/ceres/crs_matrix.h + include/ceres/cubic_interpolation.h + include/ceres/dynamic_autodiff_cost_function.h + include/ceres/dynamic_cost_function.h + include/ceres/dynamic_cost_function_to_functor.h + include/ceres/dynamic_numeric_diff_cost_function.h + include/ceres/evaluation_callback.h + include/ceres/first_order_function.h + include/ceres/gradient_checker.h + include/ceres/gradient_problem.h + include/ceres/gradient_problem_solver.h + include/ceres/iteration_callback.h + include/ceres/jet.h + include/ceres/jet_fwd.h + include/ceres/line_manifold.h + include/ceres/local_parameterization.h + include/ceres/loss_function.h + include/ceres/manifold.h + include/ceres/manifold_test_utils.h + include/ceres/normal_prior.h + include/ceres/numeric_diff_cost_function.h + include/ceres/numeric_diff_first_order_function.h + include/ceres/numeric_diff_options.h + include/ceres/ordered_groups.h + include/ceres/problem.h + include/ceres/product_manifold.h + include/ceres/rotation.h + include/ceres/sized_cost_function.h + include/ceres/solver.h + include/ceres/sphere_manifold.h + include/ceres/tiny_solver.h + include/ceres/tiny_solver_autodiff_function.h + include/ceres/tiny_solver_cost_function_adapter.h + include/ceres/types.h + include/ceres/version.h + + include/ceres/internal/array_selector.h + include/ceres/internal/autodiff.h + include/ceres/internal/disable_warnings.h + include/ceres/internal/eigen.h + include/ceres/internal/fixed_array.h + include/ceres/internal/householder_vector.h + include/ceres/internal/integer_sequence_algorithm.h + include/ceres/internal/jet_traits.h + include/ceres/internal/line_parameterization.h + include/ceres/internal/memory.h + include/ceres/internal/numeric_diff.h + include/ceres/internal/parameter_dims.h + include/ceres/internal/port.h + include/ceres/internal/reenable_warnings.h + include/ceres/internal/sphere_manifold_functions.h + include/ceres/internal/variadic_evaluate.h + + internal/ceres/accelerate_sparse.cc + internal/ceres/accelerate_sparse.h + internal/ceres/array_utils.cc + internal/ceres/array_utils.h + internal/ceres/block_evaluate_preparer.cc + internal/ceres/block_evaluate_preparer.h + internal/ceres/block_jacobi_preconditioner.cc + internal/ceres/block_jacobi_preconditioner.h + internal/ceres/block_jacobian_writer.cc + internal/ceres/block_jacobian_writer.h + internal/ceres/block_random_access_dense_matrix.cc + internal/ceres/block_random_access_dense_matrix.h + internal/ceres/block_random_access_diagonal_matrix.cc + internal/ceres/block_random_access_diagonal_matrix.h + internal/ceres/block_random_access_matrix.cc + internal/ceres/block_random_access_matrix.h + internal/ceres/block_random_access_sparse_matrix.cc + internal/ceres/block_random_access_sparse_matrix.h + internal/ceres/block_sparse_matrix.cc + internal/ceres/block_sparse_matrix.h + internal/ceres/block_structure.cc + internal/ceres/block_structure.h + internal/ceres/c_api.cc + internal/ceres/callbacks.cc + internal/ceres/callbacks.h + internal/ceres/canonical_views_clustering.cc + internal/ceres/canonical_views_clustering.h + internal/ceres/casts.h + internal/ceres/cgnr_linear_operator.h + internal/ceres/cgnr_solver.cc + internal/ceres/cgnr_solver.h + internal/ceres/compressed_col_sparse_matrix_utils.cc + internal/ceres/compressed_col_sparse_matrix_utils.h + internal/ceres/compressed_row_jacobian_writer.cc + internal/ceres/compressed_row_jacobian_writer.h + internal/ceres/compressed_row_sparse_matrix.cc + internal/ceres/compressed_row_sparse_matrix.h + internal/ceres/concurrent_queue.h + internal/ceres/conditioned_cost_function.cc + internal/ceres/conjugate_gradients_solver.cc + internal/ceres/conjugate_gradients_solver.h + internal/ceres/context.cc + internal/ceres/context_impl.cc + internal/ceres/context_impl.h + internal/ceres/coordinate_descent_minimizer.cc + internal/ceres/coordinate_descent_minimizer.h + internal/ceres/corrector.cc + internal/ceres/corrector.h + internal/ceres/cost_function.cc + internal/ceres/covariance.cc + internal/ceres/covariance_impl.cc + internal/ceres/covariance_impl.h + internal/ceres/cuda_buffer.h + internal/ceres/cxsparse.cc + internal/ceres/cxsparse.h + internal/ceres/dense_cholesky.cc + internal/ceres/dense_cholesky.h + internal/ceres/dense_jacobian_writer.h + internal/ceres/dense_normal_cholesky_solver.cc + internal/ceres/dense_normal_cholesky_solver.h + internal/ceres/dense_qr.cc + internal/ceres/dense_qr.h + internal/ceres/dense_qr_solver.cc + internal/ceres/dense_qr_solver.h + internal/ceres/dense_sparse_matrix.cc + internal/ceres/dense_sparse_matrix.h + internal/ceres/detect_structure.cc + internal/ceres/detect_structure.h + internal/ceres/dogleg_strategy.cc + internal/ceres/dogleg_strategy.h + internal/ceres/dynamic_compressed_row_finalizer.h + internal/ceres/dynamic_compressed_row_jacobian_writer.cc + internal/ceres/dynamic_compressed_row_jacobian_writer.h + internal/ceres/dynamic_compressed_row_sparse_matrix.cc + internal/ceres/dynamic_compressed_row_sparse_matrix.h + internal/ceres/dynamic_sparse_normal_cholesky_solver.cc + internal/ceres/dynamic_sparse_normal_cholesky_solver.h + internal/ceres/eigensparse.cc + internal/ceres/eigensparse.h + internal/ceres/evaluation_callback.cc + internal/ceres/evaluator.cc + internal/ceres/evaluator.h + internal/ceres/execution_summary.h + internal/ceres/file.cc + internal/ceres/file.h + internal/ceres/first_order_function.cc + internal/ceres/float_cxsparse.cc + internal/ceres/float_cxsparse.h + internal/ceres/float_suitesparse.cc + internal/ceres/float_suitesparse.h + internal/ceres/function_sample.cc + internal/ceres/function_sample.h + internal/ceres/gradient_checker.cc + internal/ceres/gradient_checking_cost_function.cc + internal/ceres/gradient_checking_cost_function.h + internal/ceres/gradient_problem.cc + internal/ceres/gradient_problem_evaluator.h + internal/ceres/gradient_problem_solver.cc + internal/ceres/graph.h + internal/ceres/graph_algorithms.h + internal/ceres/implicit_schur_complement.cc + internal/ceres/implicit_schur_complement.h + internal/ceres/inner_product_computer.cc + internal/ceres/inner_product_computer.h + internal/ceres/invert_psd_matrix.h + internal/ceres/is_close.cc + internal/ceres/is_close.h + internal/ceres/iteration_callback.cc + internal/ceres/iterative_refiner.cc + internal/ceres/iterative_refiner.h + internal/ceres/iterative_schur_complement_solver.cc + internal/ceres/iterative_schur_complement_solver.h + internal/ceres/levenberg_marquardt_strategy.cc + internal/ceres/levenberg_marquardt_strategy.h + internal/ceres/line_search.cc + internal/ceres/line_search.h + internal/ceres/line_search_direction.cc + internal/ceres/line_search_direction.h + internal/ceres/line_search_minimizer.cc + internal/ceres/line_search_minimizer.h + internal/ceres/line_search_preprocessor.cc + internal/ceres/line_search_preprocessor.h + internal/ceres/linear_least_squares_problems.cc + internal/ceres/linear_least_squares_problems.h + internal/ceres/linear_operator.cc + internal/ceres/linear_operator.h + internal/ceres/linear_solver.cc + internal/ceres/linear_solver.h + internal/ceres/local_parameterization.cc + internal/ceres/loss_function.cc + internal/ceres/low_rank_inverse_hessian.cc + internal/ceres/low_rank_inverse_hessian.h + internal/ceres/manifold.cc + internal/ceres/manifold_adapter.h + internal/ceres/map_util.h + internal/ceres/minimizer.cc + internal/ceres/minimizer.h + internal/ceres/normal_prior.cc + internal/ceres/pair_hash.h + internal/ceres/parallel_for.h + internal/ceres/parallel_for_cxx.cc + internal/ceres/parallel_for_nothreads.cc + internal/ceres/parallel_for_openmp.cc + internal/ceres/parallel_utils.cc + internal/ceres/parallel_utils.h + internal/ceres/parameter_block.h + internal/ceres/parameter_block_ordering.cc + internal/ceres/parameter_block_ordering.h + internal/ceres/partitioned_matrix_view.cc + internal/ceres/partitioned_matrix_view.h + internal/ceres/partitioned_matrix_view_impl.h + internal/ceres/polynomial.cc + internal/ceres/polynomial.h + internal/ceres/preconditioner.cc + internal/ceres/preconditioner.h + internal/ceres/preprocessor.cc + internal/ceres/preprocessor.h + internal/ceres/problem.cc + internal/ceres/problem_impl.cc + internal/ceres/problem_impl.h + internal/ceres/program.cc + internal/ceres/program.h + internal/ceres/program_evaluator.h + internal/ceres/random.h + internal/ceres/reorder_program.cc + internal/ceres/reorder_program.h + internal/ceres/residual_block.cc + internal/ceres/residual_block.h + internal/ceres/residual_block_utils.cc + internal/ceres/residual_block_utils.h + internal/ceres/schur_complement_solver.cc + internal/ceres/schur_complement_solver.h + internal/ceres/schur_eliminator.cc + internal/ceres/schur_eliminator.h + internal/ceres/schur_eliminator_impl.h + internal/ceres/schur_jacobi_preconditioner.cc + internal/ceres/schur_jacobi_preconditioner.h + internal/ceres/schur_templates.cc + internal/ceres/schur_templates.h + internal/ceres/scoped_thread_token.h + internal/ceres/scratch_evaluate_preparer.cc + internal/ceres/scratch_evaluate_preparer.h + internal/ceres/single_linkage_clustering.cc + internal/ceres/single_linkage_clustering.h + internal/ceres/small_blas.h + internal/ceres/small_blas_generic.h + internal/ceres/solver.cc + internal/ceres/solver_utils.cc + internal/ceres/solver_utils.h + internal/ceres/sparse_cholesky.cc + internal/ceres/sparse_cholesky.h + internal/ceres/sparse_matrix.cc + internal/ceres/sparse_matrix.h + internal/ceres/sparse_normal_cholesky_solver.cc + internal/ceres/sparse_normal_cholesky_solver.h + internal/ceres/stl_util.h + internal/ceres/stringprintf.cc + internal/ceres/stringprintf.h + internal/ceres/subset_preconditioner.cc + internal/ceres/subset_preconditioner.h + internal/ceres/suitesparse.cc + internal/ceres/suitesparse.h + internal/ceres/thread_pool.cc + internal/ceres/thread_pool.h + internal/ceres/thread_token_provider.cc + internal/ceres/thread_token_provider.h + internal/ceres/triplet_sparse_matrix.cc + internal/ceres/triplet_sparse_matrix.h + internal/ceres/trust_region_minimizer.cc + internal/ceres/trust_region_minimizer.h + internal/ceres/trust_region_preprocessor.cc + internal/ceres/trust_region_preprocessor.h + internal/ceres/trust_region_step_evaluator.cc + internal/ceres/trust_region_step_evaluator.h + internal/ceres/trust_region_strategy.cc + internal/ceres/trust_region_strategy.h + internal/ceres/types.cc + internal/ceres/visibility.cc + internal/ceres/visibility.h + internal/ceres/visibility_based_preconditioner.cc + internal/ceres/visibility_based_preconditioner.h + internal/ceres/wall_time.cc + internal/ceres/wall_time.h - include/ceres/autodiff_cost_function.h - include/ceres/autodiff_first_order_function.h - include/ceres/autodiff_local_parameterization.h - include/ceres/c_api.h - include/ceres/ceres.h - include/ceres/conditioned_cost_function.h - include/ceres/context.h - include/ceres/cost_function.h - include/ceres/cost_function_to_functor.h - include/ceres/covariance.h - include/ceres/crs_matrix.h - include/ceres/cubic_interpolation.h - include/ceres/dynamic_autodiff_cost_function.h - include/ceres/dynamic_cost_function.h - include/ceres/dynamic_cost_function_to_functor.h - include/ceres/dynamic_numeric_diff_cost_function.h - include/ceres/evaluation_callback.h - include/ceres/first_order_function.h - include/ceres/gradient_checker.h - include/ceres/gradient_problem.h - include/ceres/gradient_problem_solver.h - include/ceres/internal/array_selector.h - include/ceres/internal/autodiff.h - include/ceres/internal/disable_warnings.h - include/ceres/internal/eigen.h - include/ceres/internal/fixed_array.h - include/ceres/internal/householder_vector.h - include/ceres/internal/integer_sequence_algorithm.h - include/ceres/internal/line_parameterization.h - include/ceres/internal/memory.h - include/ceres/internal/numeric_diff.h - include/ceres/internal/parameter_dims.h - include/ceres/internal/port.h - include/ceres/internal/reenable_warnings.h - include/ceres/internal/variadic_evaluate.h - include/ceres/iteration_callback.h - include/ceres/jet.h - include/ceres/local_parameterization.h - include/ceres/loss_function.h - include/ceres/normal_prior.h - include/ceres/numeric_diff_cost_function.h - include/ceres/numeric_diff_options.h - include/ceres/ordered_groups.h - include/ceres/problem.h - include/ceres/rotation.h - include/ceres/sized_cost_function.h - include/ceres/solver.h - include/ceres/tiny_solver_autodiff_function.h - include/ceres/tiny_solver_cost_function_adapter.h - include/ceres/tiny_solver.h - include/ceres/types.h - include/ceres/version.h - internal/ceres/accelerate_sparse.h - internal/ceres/array_utils.h - internal/ceres/blas.h - internal/ceres/block_evaluate_preparer.h - internal/ceres/block_jacobian_writer.h - internal/ceres/block_jacobi_preconditioner.h - internal/ceres/block_random_access_dense_matrix.h - internal/ceres/block_random_access_diagonal_matrix.h - internal/ceres/block_random_access_matrix.h - internal/ceres/block_random_access_sparse_matrix.h - internal/ceres/block_sparse_matrix.h - internal/ceres/block_structure.h - internal/ceres/callbacks.h - internal/ceres/canonical_views_clustering.h - internal/ceres/casts.h - internal/ceres/cgnr_linear_operator.h - internal/ceres/cgnr_solver.h - internal/ceres/compressed_col_sparse_matrix_utils.h - internal/ceres/compressed_row_jacobian_writer.h - internal/ceres/compressed_row_sparse_matrix.h - internal/ceres/concurrent_queue.h - internal/ceres/conjugate_gradients_solver.h - internal/ceres/context_impl.h - internal/ceres/coordinate_descent_minimizer.h - internal/ceres/corrector.h - internal/ceres/covariance_impl.h - internal/ceres/cxsparse.h - internal/ceres/dense_jacobian_writer.h - internal/ceres/dense_normal_cholesky_solver.h - internal/ceres/dense_qr_solver.h - internal/ceres/dense_sparse_matrix.h - internal/ceres/detect_structure.h - internal/ceres/dogleg_strategy.h - internal/ceres/dynamic_compressed_row_finalizer.h - internal/ceres/dynamic_compressed_row_jacobian_writer.h - internal/ceres/dynamic_compressed_row_sparse_matrix.h - internal/ceres/dynamic_sparse_normal_cholesky_solver.h - internal/ceres/eigensparse.h - internal/ceres/evaluator.h - internal/ceres/execution_summary.h - internal/ceres/file.h - internal/ceres/float_cxsparse.h - internal/ceres/float_suitesparse.h - internal/ceres/function_sample.h - internal/ceres/gradient_checking_cost_function.h - internal/ceres/gradient_problem_evaluator.h - internal/ceres/graph_algorithms.h - internal/ceres/graph.h - internal/ceres/implicit_schur_complement.h - internal/ceres/inner_product_computer.h - internal/ceres/invert_psd_matrix.h - internal/ceres/is_close.h - internal/ceres/iterative_refiner.h - internal/ceres/iterative_schur_complement_solver.h - internal/ceres/lapack.h - internal/ceres/levenberg_marquardt_strategy.h - internal/ceres/linear_least_squares_problems.h - internal/ceres/linear_operator.h - internal/ceres/linear_solver.h - internal/ceres/line_search_direction.h - internal/ceres/line_search.h - internal/ceres/line_search_minimizer.h - internal/ceres/line_search_preprocessor.h - internal/ceres/low_rank_inverse_hessian.h - internal/ceres/map_util.h - internal/ceres/minimizer.h - internal/ceres/pair_hash.h - internal/ceres/parallel_for.h - internal/ceres/parallel_utils.h - internal/ceres/parameter_block.h - internal/ceres/parameter_block_ordering.h - internal/ceres/partitioned_matrix_view.h - internal/ceres/partitioned_matrix_view_impl.h - internal/ceres/polynomial.h - internal/ceres/preconditioner.h - internal/ceres/preprocessor.h - internal/ceres/problem_impl.h - internal/ceres/program_evaluator.h - internal/ceres/program.h - internal/ceres/random.h - internal/ceres/reorder_program.h - internal/ceres/residual_block.h - internal/ceres/residual_block_utils.h - internal/ceres/schur_complement_solver.h - internal/ceres/schur_eliminator.h - internal/ceres/schur_eliminator_impl.h - internal/ceres/schur_jacobi_preconditioner.h - internal/ceres/schur_templates.h - internal/ceres/scoped_thread_token.h - internal/ceres/scratch_evaluate_preparer.h - internal/ceres/single_linkage_clustering.h - internal/ceres/small_blas_generic.h - internal/ceres/small_blas.h - internal/ceres/solver_utils.h - internal/ceres/sparse_cholesky.h - internal/ceres/sparse_matrix.h - internal/ceres/sparse_normal_cholesky_solver.h - internal/ceres/split.h - internal/ceres/stl_util.h - internal/ceres/stringprintf.h - internal/ceres/subset_preconditioner.h - internal/ceres/suitesparse.h - internal/ceres/thread_pool.h - internal/ceres/thread_token_provider.h - internal/ceres/triplet_sparse_matrix.h - internal/ceres/trust_region_minimizer.h - internal/ceres/trust_region_preprocessor.h - internal/ceres/trust_region_step_evaluator.h - internal/ceres/trust_region_strategy.h - internal/ceres/visibility_based_preconditioner.h - internal/ceres/visibility.h - internal/ceres/wall_time.h + internal/ceres/generated/partitioned_matrix_view_d_d_d.cc + internal/ceres/generated/schur_eliminator_d_d_d.cc ) set(LIB @@ -302,48 +314,48 @@ set(LIB if(WITH_LIBMV_SCHUR_SPECIALIZATIONS) list(APPEND SRC - internal/ceres/generated/partitioned_matrix_view_2_2_2.cc - internal/ceres/generated/partitioned_matrix_view_2_2_3.cc - internal/ceres/generated/partitioned_matrix_view_2_2_4.cc - internal/ceres/generated/partitioned_matrix_view_2_2_d.cc - internal/ceres/generated/partitioned_matrix_view_2_3_3.cc - internal/ceres/generated/partitioned_matrix_view_2_3_4.cc - internal/ceres/generated/partitioned_matrix_view_2_3_6.cc - internal/ceres/generated/partitioned_matrix_view_2_3_9.cc - internal/ceres/generated/partitioned_matrix_view_2_3_d.cc - internal/ceres/generated/partitioned_matrix_view_2_4_3.cc - internal/ceres/generated/partitioned_matrix_view_2_4_4.cc - internal/ceres/generated/partitioned_matrix_view_2_4_6.cc - internal/ceres/generated/partitioned_matrix_view_2_4_8.cc - internal/ceres/generated/partitioned_matrix_view_2_4_9.cc - internal/ceres/generated/partitioned_matrix_view_2_4_d.cc - internal/ceres/generated/partitioned_matrix_view_2_d_d.cc - internal/ceres/generated/partitioned_matrix_view_3_3_3.cc - internal/ceres/generated/partitioned_matrix_view_4_4_2.cc - internal/ceres/generated/partitioned_matrix_view_4_4_3.cc - internal/ceres/generated/partitioned_matrix_view_4_4_4.cc - internal/ceres/generated/partitioned_matrix_view_4_4_d.cc - internal/ceres/generated/schur_eliminator_2_2_2.cc - internal/ceres/generated/schur_eliminator_2_2_3.cc - internal/ceres/generated/schur_eliminator_2_2_4.cc - internal/ceres/generated/schur_eliminator_2_2_d.cc - internal/ceres/generated/schur_eliminator_2_3_3.cc - internal/ceres/generated/schur_eliminator_2_3_4.cc - internal/ceres/generated/schur_eliminator_2_3_6.cc - internal/ceres/generated/schur_eliminator_2_3_9.cc - internal/ceres/generated/schur_eliminator_2_3_d.cc - internal/ceres/generated/schur_eliminator_2_4_3.cc - internal/ceres/generated/schur_eliminator_2_4_4.cc - internal/ceres/generated/schur_eliminator_2_4_6.cc - internal/ceres/generated/schur_eliminator_2_4_8.cc - internal/ceres/generated/schur_eliminator_2_4_9.cc - internal/ceres/generated/schur_eliminator_2_4_d.cc - internal/ceres/generated/schur_eliminator_2_d_d.cc - internal/ceres/generated/schur_eliminator_3_3_3.cc - internal/ceres/generated/schur_eliminator_4_4_2.cc - internal/ceres/generated/schur_eliminator_4_4_3.cc - internal/ceres/generated/schur_eliminator_4_4_4.cc - internal/ceres/generated/schur_eliminator_4_4_d.cc + internal/ceres/generated/partitioned_matrix_view_2_2_2.cc + internal/ceres/generated/partitioned_matrix_view_2_2_3.cc + internal/ceres/generated/partitioned_matrix_view_2_2_4.cc + internal/ceres/generated/partitioned_matrix_view_2_2_d.cc + internal/ceres/generated/partitioned_matrix_view_2_3_3.cc + internal/ceres/generated/partitioned_matrix_view_2_3_4.cc + internal/ceres/generated/partitioned_matrix_view_2_3_6.cc + internal/ceres/generated/partitioned_matrix_view_2_3_9.cc + internal/ceres/generated/partitioned_matrix_view_2_3_d.cc + internal/ceres/generated/partitioned_matrix_view_2_4_3.cc + internal/ceres/generated/partitioned_matrix_view_2_4_4.cc + internal/ceres/generated/partitioned_matrix_view_2_4_6.cc + internal/ceres/generated/partitioned_matrix_view_2_4_8.cc + internal/ceres/generated/partitioned_matrix_view_2_4_9.cc + internal/ceres/generated/partitioned_matrix_view_2_4_d.cc + internal/ceres/generated/partitioned_matrix_view_2_d_d.cc + internal/ceres/generated/partitioned_matrix_view_3_3_3.cc + internal/ceres/generated/partitioned_matrix_view_4_4_2.cc + internal/ceres/generated/partitioned_matrix_view_4_4_3.cc + internal/ceres/generated/partitioned_matrix_view_4_4_4.cc + internal/ceres/generated/partitioned_matrix_view_4_4_d.cc + internal/ceres/generated/schur_eliminator_2_2_2.cc + internal/ceres/generated/schur_eliminator_2_2_3.cc + internal/ceres/generated/schur_eliminator_2_2_4.cc + internal/ceres/generated/schur_eliminator_2_2_d.cc + internal/ceres/generated/schur_eliminator_2_3_3.cc + internal/ceres/generated/schur_eliminator_2_3_4.cc + internal/ceres/generated/schur_eliminator_2_3_6.cc + internal/ceres/generated/schur_eliminator_2_3_9.cc + internal/ceres/generated/schur_eliminator_2_3_d.cc + internal/ceres/generated/schur_eliminator_2_4_3.cc + internal/ceres/generated/schur_eliminator_2_4_4.cc + internal/ceres/generated/schur_eliminator_2_4_6.cc + internal/ceres/generated/schur_eliminator_2_4_8.cc + internal/ceres/generated/schur_eliminator_2_4_9.cc + internal/ceres/generated/schur_eliminator_2_4_d.cc + internal/ceres/generated/schur_eliminator_2_d_d.cc + internal/ceres/generated/schur_eliminator_3_3_3.cc + internal/ceres/generated/schur_eliminator_4_4_2.cc + internal/ceres/generated/schur_eliminator_4_4_3.cc + internal/ceres/generated/schur_eliminator_4_4_4.cc + internal/ceres/generated/schur_eliminator_4_4_d.cc ) else() add_definitions(-DCERES_RESTRICT_SCHUR_SPECIALIZATION) @@ -351,16 +363,5 @@ endif() add_definitions(${GFLAGS_DEFINES}) add_definitions(${GLOG_DEFINES}) -add_definitions(${CERES_DEFINES}) - -add_definitions( - -DCERES_HAVE_PTHREAD - -DCERES_NO_SUITESPARSE - -DCERES_NO_CXSPARSE - -DCERES_NO_LAPACK - -DCERES_NO_ACCELERATE_SPARSE - -DCERES_HAVE_RWLOCK - -DCERES_USE_CXX_THREADS -) blender_add_lib(extern_ceres "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/extern/ceres/ChangeLog b/extern/ceres/ChangeLog deleted file mode 100644 index 40fe3f16bac..00000000000 --- a/extern/ceres/ChangeLog +++ /dev/null @@ -1,596 +0,0 @@ -commit 399cda773035d99eaf1f4a129a666b3c4df9d1b1 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Fri Oct 23 19:36:08 2020 +0100 - - Update build documentation to reflect detection of Eigen via config mode - - Change-Id: I18d5f0fc1eb51ea630164c911d935e9bffea35ce - -commit bb127272f9b57672bca48424f2d83bc430a46eb8 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 19 09:28:34 2020 -0700 - - Fix typos. - - Contributed by Ishamis@, IanBoyanZhang@, gkrobner@ & mithunjacob@. - - Change-Id: Iab3c19a07a6f3db2486e3557dcb55bfe5de2aee5 - -commit a0ec5c32af5c5f5a52168dc2748be910dba14810 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sun Oct 18 15:20:36 2020 -0700 - - Update version history for 2.0.0RC2 - - Change-Id: I75b7515fbf9880bd8eaea6ecd5e72ce1ae4a3a86 - -commit 3f6d2736769044e7c08c873c41a184849eea73ab -Author: Taylor Braun-Jones <taylor@braun-jones.org> -Date: Tue Jan 28 12:09:30 2020 -0500 - - Unify symbol visibility configuration for all compilers - - This makes it possible to build unit tests with shared libraries on MSVC. - - Change-Id: I1db66a80b2c78c4f3d354e35235244d17bac9809 - -commit 29c2912ee635c77f3ddf2e382a5d6a9cf9805a3d -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Oct 13 12:07:06 2020 -0700 - - Unbreak the bazel build some more - - Change-Id: I6bbf3df977a473b9b5e16a9e59da5f535f8cdc24 - -commit bf47e1a36829f62697b930241d0a353932f34090 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Oct 13 10:00:22 2020 -0700 - - Fix the Bazel build. - - 1. Fix the path to eigen, now that it uses gitlab instead of bitbucket. - 2. Remove an unrecognized compiler option. - 3. Remove an obsolete benchmark. - - This CL only unbreaks the build, it is likely that it is still not - at par with the cmake build. - - https://github.com/ceres-solver/ceres-solver/issues/628 - - Change-Id: I470209cbb48b6a4f499564a86b52436e0c8d98ef - -commit 600e8c529ebbb4bb89d5baefa3d5ab6ad923706a -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Oct 12 23:00:39 2020 +0200 - - fix minor typos - - all timing values in the summary are initialized to -1, so the one - +1 is likely an oversight. - - Change-Id: Ie355f3b7da08a56d49d19ca9a5bc48fe5581dee3 - -commit bdcdcc78af61a0cb85317ebee52dc804bf4ea975 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 7 01:48:50 2020 +0200 - - update docs for changed cmake usage - - - update links to cmake docs to version 3.5 - - highlight difference between dependencies with and without custom - find modules - - point out removal of CERES_INCLUDE_DIRS - - point out that TBB might be linked if SuiteSparseQR is found - - added 'Migration' section - - fixed typos - - Change-Id: Icbcc0e723d11f12246fb3cf09b9d7c6206195a82 - -commit 3f69e5b36a49b44344e96a26b39693a914ba80c6 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 12 11:46:40 2020 -0700 - - Corrections from William Rucklidge - - Change-Id: I0b5d4808be48f68df7829c70ec93ffa67d81315d - -commit 8bfdb02fb18551bbd5f222c5472e45eddecd42b9 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 12 10:07:13 2020 -0700 - - Rewrite uses of VLOG_IF and LOG_IF. - - VLOG_IF's evaluation order is ambiguous - does it mean - `if (cond) VLOG(lvl)` or `if (VLOG_IS_ON(lvl) && cond) LOG(INFO)`? - In particular, the way it works now is inconsistent with the way the - rest of the LOG macros evaluate their arguments. - Fixing this would be hard, and the macro's behavior would still surprise - some people. Replacing it with an if statement is simple, clear, and unambiguous. - - Change-Id: I97a92d17a932c0a5344a1bf98d676308793ba877 - -commit d1b35ffc161fd857c7c433574ca82aa9b2db7f98 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Oct 12 10:58:05 2020 -0700 - - Corrections from William Rucklidge - - Change-Id: Ifb50e87aa915d00f9861fe1a6da0acee11bc0a94 - -commit f34e80e91f600014a3030915cf9ea28bcbc576e7 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Thu Oct 8 12:34:53 2020 -0700 - - Add dividers between licenses. - - Change-Id: I4e4aaa15e0621c5648550cfa622fe0a79f1f4f9f - -commit 65c397daeca77da53d16e73720b9a17edd6757ab -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Oct 7 14:34:52 2020 -0700 - - Fix formatting - - Change-Id: Ib4ca8a097059dbb8d2f3a6a888222c0188cb126e - -commit f63b1fea9cfa48ae4530c327b10efa4985e69631 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Oct 7 14:30:54 2020 -0700 - - Add the MIT license text corresponding to the libmv derived files. - - Change-Id: Ie72fb45ae96a7892c00411eee6873db7f0e365a8 - -commit 542613c13d8b7469822aff5eec076f2cad4507ec -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Tue Oct 6 22:48:59 2020 +0200 - - minor formatting fix for trust_region_minimizer.cc - - Change-Id: I18ba27825fc23dd0e9e3e15dc13fc0833db01b5b - -commit 6d9e9843d8c61cfb04cc55b9def9518f823a592a -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Sep 28 11:35:37 2020 -0700 - - Remove inclusion of ceres/eigen.h - - The initial reason for this is because of a previous reformatting CL - triggered a macro redefinition warning in the schur eliminator. But - actually it was worse because the reordering had caused the macro - definition to be ignored and caused a performance regression. - - This simplifies the generated files, fixes some formatting errors - and recovers the performance. - - Change-Id: I9dbeffc38743b3f24b25843feec2e26a73188413 - -commit eafeca5dcb7af8688d40a9c14b0d2fcb856c96fc -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Sep 28 11:12:59 2020 -0700 - - Fix a logging bug in TrustRegionMinimizer. - - Upon encountering an unsuccessful step (one where the cost goes up) - the the trust region minimizer failed to populate the gradient norm - in the IterationSummary. This would cause the gradient norm to be - logged as zero which is incorrect. Instead it should be the gradient - norm at the current point. - - This CL fixes this issue. - - Before: - iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time - 0 1.115206e+07 0.00e+00 1.90e+07 0.00e+00 0.00e+00 1.00e+04 0 2.72e-01 1.33e+00 - 1 3.687552e+06 7.46e+06 1.84e+08 2.86e+03 6.91e-01 1.06e+04 1 1.32e+00 2.65e+00 - 2 3.670266e+10 -3.67e+10 0.00e+00 3.27e+03 -1.07e+04 5.30e+03 1 7.52e-01 3.40e+00 - 3 4.335397e+07 -3.97e+07 0.00e+00 2.74e+03 -1.16e+01 1.32e+03 1 7.28e-01 4.13e+00 - 4 1.345488e+06 2.34e+06 4.12e+07 1.55e+03 6.87e-01 1.40e+03 1 9.31e-01 5.06e+00 - 5 5.376653e+05 8.08e+05 9.99e+06 6.64e+02 7.46e-01 1.59e+03 1 9.64e-01 6.03e+00 - - After: - iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time - 0 1.115206e+07 0.00e+00 1.90e+07 0.00e+00 0.00e+00 1.00e+04 0 2.37e-01 1.13e+00 - 1 3.687552e+06 7.46e+06 1.84e+08 2.86e+03 6.91e-01 1.06e+04 1 1.08e+00 2.21e+00 - 2 3.670266e+10 -3.67e+10 1.84e+08 3.27e+03 -1.07e+04 5.30e+03 1 7.50e-01 2.96e+00 - 3 4.335397e+07 -3.97e+07 1.84e+08 2.74e+03 -1.16e+01 1.32e+03 1 7.13e-01 3.67e+00 - 4 1.345488e+06 2.34e+06 4.12e+07 1.55e+03 6.87e-01 1.40e+03 1 9.01e-01 4.57e+00 - 5 5.376653e+05 8.08e+05 9.99e+06 6.64e+02 7.46e-01 1.59e+03 1 9.36e-01 5.51e+00 - - Change-Id: Iae538fe089be07c7bb219337a6f1392f7213acfe - -commit 1fd0be916dd4ff4241bd52264b9e9170bc7e4339 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Sep 28 18:54:33 2020 +0100 - - Fix default initialisation of IterationCallback::cost - - Change-Id: I9f529093fc09424c90dbff8e9648b90b16990623 - -commit 137bbe845577929a87f8eef979196df6a8b30ee4 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 28 02:17:32 2020 +0200 - - add info about clang-format to contributing docs - - Change-Id: I2f4dcbda2e4f36096df217d76de370103ffaa43e - -commit d3f66d77f45482b90d01af47938289c32dd2cc08 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 28 02:01:43 2020 +0200 - - fix formatting generated files (best effort) - - - update file generator scripts / templates so generated files adhere - to clang-format - - A few exceptions are not fixed, where the file generation results in - lines of different width. To properly fix this would make the code - more complicated and it's not that important for generated files - anyway. - - note that generated files are excluded in ./scripts/format_all.sh - - Change-Id: I4f42c83d1fec01242eada5e7ce6c1a5192234d37 - -commit a9c7361c8dc1d37e78d216754a4c03e8a8f1e74f -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Mon Sep 28 02:14:29 2020 +0200 - - minor formatting fix (wrongly updated in earlier commit) - - Change-Id: I544635fd936cb5b7f7bd9255876641cd5a9590c6 - -commit 7b8f675bfdb1d924af6a2dcc1f79bda5ace7e886 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Sun Sep 20 21:45:24 2020 +0200 - - fix formatting for (non-generated) internal source files - - - Change formatting standard to Cpp11. Main difference is not having - the space between two closing >> for nested templates. We don't - choose c++14, because older versions of clang-format (version 9 - and earlier) don't know this value yet, and it doesn't make a - difference in the formatting. - - Apply clang-format to all (non generated) internal source files. - - Manually fix some code sections (clang-format on/off) and c-strings - - Exclude some embedded external files with very different formatting - (gtest/gmock) - - Add script to format all source files - - Change-Id: Ic6cea41575ad6e37c9e136dbce176b0d505dc44d - -commit 921368ce31c42ee793cf131860abba291a7e39ad -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Sep 9 09:15:37 2020 -0700 - - Fix a number of typos in covariance.h - - Also some minor cleanups in covariance_impl.h - - Thanks to Lorenzo Lamia for pointing these out. - - Change-Id: Icb4012a367fdd1f249bc1e7019e0114c868e45b6 - -commit 7b6b2491cc1be0b3abb67338366d8d69bef3a402 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Tue Sep 8 17:51:32 2020 +0200 - - fix formatting for examples - - This is mostly just applying the existing clang format config, except: - - Use NOLINT on overlong comment lines. - - Wrap some sections in 'clang-format off' / 'clang format on'. - - Manually split or join some multi-line strings. - - Change-Id: Ia1a40eeb92112e12c3a169309afe087af55b2f4f - -commit 82275d8a4eac4fc0bd07e17c3a41a6e429e72bfb -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Tue Sep 8 02:00:21 2020 +0200 - - some fixes for Linux and macOS install docs - - Linux: - - Remove workaround for Ubuntu 14.04, which is EOL. libsuitesparse-dev - seems to come with a shared library on 16.04 and later, so linking - to a shared build of ceres doesn't seem to be an issue any more. - - Add missing libgflags-dev. - - macOS: - - OS X is now called macOS. - - Update homebrew link. - - Mac homebrew the preferred method of installation. - - Fix OpenMP instructions. - - Remove reference to homebrew/science. Everything is in core. - - Add missing gflags. - - Change-Id: I633b3c7ea84a87886bfd823f8187fdd0a84737c9 - -commit 9d762d74f06b946bbd2f098de7216032d0e7b51d -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Sun Sep 6 21:04:24 2020 +0200 - - fix formatting for public header files - - - ensure all public headers files adhere to clang-format - - preserve one-per-line for enums by adding trailing comma - - preserve include order for en/disable_warning.h - - Change-Id: I78dbd0527a294ab2ec5f074fb426e48b20c393e6 - -commit c76478c4898f3af11a6a826ac89c261205f4dd96 -Author: Nikolaus Demmel <nikolaus@nikolaus-demmel.de> -Date: Sun Sep 6 23:29:56 2020 +0200 - - gitignore *.pyc - - Change-Id: Ic6238a617a3c7ce92df7dcefcc44bae20c32b30b - -commit 4e69a475cd7d7cbed983f5aebf79ae13a46e5415 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Tue Sep 1 10:15:23 2020 +0100 - - Fix potential for mismatched release/debug TBB libraries - - - Protect against the case when the user has multiple installs of TBB - in their search paths and the first install does not contain debug - libraries. In this case it is possible to get mismatched versions - of TBB inserted into TBB_LIBRARIES. - - Also suppresses warning about use of TBB_ROOT on modern versions of - CMake due to CMP0074. - - Change-Id: I2eaafdde4a028cbf6c500c63771973d85bc4723d - -commit 8e1d8e32ad0d28c0d4d1d7b2b1ce7fc01d90b7b0 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Thu Sep 3 10:49:20 2020 -0700 - - A number of small changes. - - 1. Add a move constructor to NumericDiffCostFunction, DynamicAutoDiffCostfunction - and DynamicNumericDiffCostFunction. - 2. Add optional ownership of the underlying functor. - 3. Update docs to reflect this as well as the variadic templates that allow an - arbitrary number of parameter blocks. - - Change-Id: I57bbb51fb9e75f36ec2a661b603beda270f30a19 - -commit 368a738e5281039f19587545806b7bc6f35e78f9 -Author: Julian Kent <jkflying@gmail.com> -Date: Thu May 7 12:54:35 2020 +0200 - - AutoDiffCostFunction: optional ownership - - Add Ownership semantics to the AutoDiffCostFunction - - This allows several benefits, such as pointer ordering always being the - same for numerical repeatability (due to blocks being ordered by - pointer address), memory adjacency for better cache performance, and - reduced allocator pressure / overhead. - - This is then made use of in libmv by preallocating the errors and - cost functions into vectors - - Change-Id: Ia5b97e7249b55a463264b6e26f7a02291927c9f2 - -commit 8cbd721c199c69f127af6ef7c187ddf7e8f116f9 -Author: Morten Hannemose <morten@hannemose.dk> -Date: Thu Sep 3 17:54:20 2020 +0200 - - Add erf and erfc to jet.h, including tests in jet_test.cc - - erf is necessary for evaluating Gaussian functions. - erfc was added because it is so similar to erf. - - Change-Id: I5e470dbe013cc938fabb87cde3b0ebf26a90fff4 - -commit 31366cff299cf2a8d97b43a7533d953ff28fdc29 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Sep 1 09:23:34 2020 -0700 - - Benchmarks for dynamic autodiff. - - This patch is from Clement Courbet. courbet@google.com - - Change-Id: I886390663644733bfa5b7b52b0c883079e793726 - -commit 29fb08aeae1ce691851724af7209fea6127523a9 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Tue Sep 1 10:23:31 2020 +0100 - - Use CMAKE_PREFIX_PATH to pass Homebrew install location - - - Passing HINTS disables the MODULE mode of find_package() which - precludes users from creating their own find modules to provide - Ceres' dependencies. - - Change-Id: I6f2edf429331d13fe67bf61ac4b79d17579d9a57 - -commit 242c703b501ffd64d645f4016d63c8b41c381038 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 21:02:11 2020 -0700 - - Minor fixes to the documentation - - Change-Id: I65e6f648d963b8aa640078684ce02dcde6acb87d - -commit 79bbf95103672fa4b5485e055ff7692ee4a1f9da -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 18:26:02 2020 -0700 - - Add changelog for 2.0.0 - - Change-Id: I8acad62bfe629454ae5032732693e43fe37b97ff - -commit 41d05f13d0ffb230d7a5a9d67ed31b0cfb35d669 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 14:56:33 2020 -0700 - - Fix lint errors in evaluation_callback_test.cc - - Change-Id: I63eb069544ad0d8f495490fe4caa07b9f04f7ec2 - -commit 4b67903c1f96037048c83a723028c5d0991c09cf -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 14:40:50 2020 -0700 - - Remove unused variables from problem_test.cc - - Change-Id: Ia1a13cfc6e462f6d249dcbf169ad34831dd93ec2 - -commit 10449fc3664c96d4b5454c092195432df79412f8 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 4 14:30:25 2020 -0700 - - Add Apache license to the LICENSE file for FixedArray - - FixedArray implementation comes from ABSL which is Apache - licensed. - - Change-Id: I566dbe9d236814c95945732c6347d3bf7b508283 - -commit 8c3ecec6db26d7a66f5de8dc654475ec7aa0df14 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue May 26 04:44:11 2020 -0700 - - Fix some minor errors in IterationCallback docs - - Change-Id: Id3d7f21a523ff8466868cdec542921c566bbbfa9 - -commit 7d3ffcb4234632dc51ee84c8a509d9428263070b -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 19:42:16 2020 +0100 - - Remove forced CONFIG from find_package(Eigen3) - - - Ceres will fail to configure if Eigen3::Eigen target is not found, and - the minimum required Eigen version specified (3.3) exports Eigen as - a CMake package and this is reflected in the default Ubuntu 18.04 - packages. - - This permits users to specify their own Eigen3 detection should they - choose to do so, but they must do so via an imported target. - - Change-Id: I5edff117c8001770004f49012ac1ae63b66ec9c1 - -commit a029fc0f93817f20b387b707bc578dc1f1a269ae -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 18:44:59 2020 +0100 - - Use latest FindTBB.cmake from VTK project - - - Retrieved from [1], SHA: 0d9bbf9beb97f8f696c43a9edf1e52c082b3639b on - 2020-07-26 - - [1]: https://gitlab.kitware.com/vtk/vtk/blob/master/CMake/FindTBB.cmake - - Change-Id: I953a8c87802a974d30ccc7c80f5229683826efbd - -commit aa1abbc578797c6b17ee7221db31535dc249ae66 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 19:57:31 2020 +0100 - - Replace use of GFLAGS_LIBRARIES with export gflags target - - - As our minimum required version of gflags (2.2) exports itself as - a CMake package and this is the case for the default 18.04 package - we can use the gflags target directly. - - Replaces forced use of CONFIG in find_package(gflags) with a check - that the gflags imported target exists to avoid ambiguity with - libgflags if installed in a default location. This permits users to - override the gflags detection should they so choose, provided that - they do so via an imported target. - - Also removes some previously removed legacy GLAGS_ vars from the - installation docs. - - Change-Id: I015f5a751e5b22f956bbf9df692e63a6825c9f0d - -commit db2af1be8780bbe88944775400baa2dbd3592b7d -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Aug 3 04:57:08 2020 -0700 - - Add Problem::EvaluateResidualBlockAssumingParametersUnchanged - - Simplify the semantics for Problem::EvaluateResidualBlock to - not ignore the presence of EvaluationCallback and add another method - EvaluateResidualBlockAssumingParametersUnchanged to handle the case - where the user has an EvaluationCallback but knows that the parameter - blocks do not change between calls. - - Updated the documentation for the methods and EvaluationCallback to - reflect these semantics. - - Also added tests for Evaluation related methods calling i - EvaluationCallback when its present. - - https://github.com/ceres-solver/ceres-solver/issues/483 - - Change-Id: If0a0c95c2f1f92e9183a90df240104a69a71c46d - -commit ab4ed32cda004befd29a0b4b02f1d907e0c4dab7 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Aug 3 04:17:33 2020 -0700 - - Replace NULL with nullptr in the documentation. - - Change-Id: I995f68770e2a4b6027c0a1d3edf5eb5132b081d7 - -commit ee280e27a6140295ef6258d24c92305628f3d508 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Fri Jul 31 16:48:06 2020 -0700 - - Allow SubsetParameterization to accept an empty vector of constant parameters. - - Thanks to Frédéric Devernay for reporting this and providing an initial fix. - - Change-Id: Id86a2051ab7841ecafdcfb00f4634b353a7ef3b4 - -commit 4b8c731d8a4f3fda53c642ff14a25fab6c233918 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Fri Jul 31 10:05:52 2020 -0700 - - Fix a bug in DynamicAutoDiffCostFunction - - DynamicAutoDiffCostFunction::Evaluate when provided with a jacobians - array that was non-empty but all its entries are nullptr, would - compute num_active_parameters = 0, and then skip over all the loops - that evaluated the CostFunctor. - - The fix is to check if num_active_parameters == 0, and then treat - it as the case where jacobians array is null. - - Thanks to Ky Waegel for reporting and providing a reproduction for this. - - Change-Id: Ib86930c2c3f722724d249f662bf88238679bbf98 - -commit 5cb5b35a930c1702278083c75769dbb4e5801045 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jul 26 20:42:12 2020 +0100 - - Fixed incorrect argument name in RotationMatrixToQuaternion() - - - Raised as: https://github.com/ceres-solver/ceres-solver/pull/607 by - Frank Dellaert - - Change-Id: Id3e9f190e814cf18206e2f8c3b1b67b995c21dd5 - -commit e39d9ed1d60dfeb58dd2a0df4622c683f87b28e3 -Author: Carl Dehlin <carl@dehlin.com> -Date: Tue Jun 16 09:02:05 2020 +0200 - - Add a missing term and remove a superfluous word - - Change-Id: I25f40f0bf241302b975e6fc14690aa863c0728b0 - -commit 27cab77b699a1a2b5354820c57a91c92eaeb21e3 -Author: Carl Dehlin <carl@dehlin.com> -Date: Mon Jun 15 20:01:18 2020 +0200 - - Reformulate some sentences - - Change-Id: I4841aa8e8522008dd816261d9ad98e5fb8ad1758 - -commit 8ac6655ce85a4462f2882fcb9e9118a7057ebe09 -Author: Carl Dehlin <carl@dehlin.com> -Date: Mon Jun 15 19:10:12 2020 +0200 - - Fix documentation formatting issues - - Change-Id: Iea3a6e75dc3a7376eda866ab24e535a6df84f8ea diff --git a/extern/ceres/LICENSE b/extern/ceres/LICENSE index 2e3ead5ed45..cf69df2e02f 100644 --- a/extern/ceres/LICENSE +++ b/extern/ceres/LICENSE @@ -1,6 +1,6 @@ Ceres Solver - A fast non-linear least squares minimizer -Copyright 2010, 2011, 2012 Google Inc. All rights reserved. -http://code.google.com/p/ceres-solver/ +Copyright 2015 Google Inc. All rights reserved. +http://ceres-solver.org/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -25,3 +25,233 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +------------------------------------------------------------------------------------------------------------------------------- + +Some of the code in the examples directory derives from libmv, which is +distributed under the MIT license as described below + + +Copyright (c) 2007-2011 libmv authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/extern/ceres/README b/extern/ceres/README deleted file mode 100644 index 8dd8ccf91a1..00000000000 --- a/extern/ceres/README +++ /dev/null @@ -1,3 +0,0 @@ -Ceres Solver - A non-linear least squares minimizer -================================================== -Please see ceres.pdf in docs/ for a tutorial and reference. diff --git a/extern/ceres/README.blender b/extern/ceres/README.blender index 20e5dda6f57..8d43f8d16c5 100644 --- a/extern/ceres/README.blender +++ b/extern/ceres/README.blender @@ -1,5 +1,5 @@ Project: Ceres Solver URL: http://ceres-solver.org/ License: BSD 3-Clause -Upstream version 2.0.0 +Upstream version 2.1.0 Local modifications: None diff --git a/extern/ceres/README.md b/extern/ceres/README.md new file mode 100644 index 00000000000..7de8f0deed0 --- /dev/null +++ b/extern/ceres/README.md @@ -0,0 +1,18 @@ +[![Android](https://github.com/ceres-solver/ceres-solver/actions/workflows/android.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/android.yml) +[![Linux](https://github.com/ceres-solver/ceres-solver/actions/workflows/linux.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/linux.yml) +[![macOS](https://github.com/ceres-solver/ceres-solver/actions/workflows/macos.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/macos.yml) +[![Windows](https://github.com/ceres-solver/ceres-solver/actions/workflows/windows.yml/badge.svg)](https://github.com/ceres-solver/ceres-solver/actions/workflows/windows.yml) + +Ceres Solver +============ + +Ceres Solver is an open source C++ library for modeling and solving +large, complicated optimization problems. It is a feature rich, mature +and performant library which has been used in production at Google +since 2010. Ceres Solver can solve two kinds of problems. + +1. Non-linear Least Squares problems with bounds constraints. +2. General unconstrained optimization problems. + +Please see [ceres-solver.org](http://ceres-solver.org/) for more +information. diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh deleted file mode 100755 index e2d2dce8779..00000000000 --- a/extern/ceres/bundle.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/sh - -if [ "x$1" = "x--i-really-know-what-im-doing" ] ; then - echo Proceeding as requested by command line ... -else - echo "*** Please run again with --i-really-know-what-im-doing ..." - exit 1 -fi - -repo="https://ceres-solver.googlesource.com/ceres-solver" -#branch="master" -tag="2.0.0" -tmp=`mktemp -d` -checkout="$tmp/ceres" - -GIT="git --git-dir $tmp/ceres/.git --work-tree $checkout" - -git clone $repo $checkout - -if [ $branch != "master" ]; then - $GIT checkout -t remotes/origin/$branch -else - if [ "x$tag" != "x" ]; then - $GIT checkout $tag - fi -fi - -$GIT log -n 50 > ChangeLog - -for p in `cat ./patches/series`; do - echo "Applying patch $p..." - cat ./patches/$p | patch -d $tmp/ceres -p1 -done - -find include -type f -not -iwholename '*.svn*' -exec rm -rf {} \; -find internal -type f -not -iwholename '*.svn*' -exec rm -rf {} \; - -cat "files.txt" | while read f; do - mkdir -p `dirname $f` - cp $tmp/ceres/$f $f -done - -rm -rf $tmp - -sources=`find ./include ./internal -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t/' | \ - grep -v -E 'schur_eliminator_[0-9]_[0-9d]_[0-9d].cc' | \ - grep -v -E 'partitioned_matrix_view_[0-9]_[0-9d]_[0-9d].cc' | sort -d` -generated_sources=`find ./include ./internal -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t\t/' | \ - grep -E 'schur_eliminator_[0-9]_[0-9d]_[0-9d].cc|partitioned_matrix_view_[0-9]_[0-9d]_[0-9d].cc' | sort -d` -headers=`find ./include ./internal -type f -iname '*.h' | sed -r 's/^\.\//\t/' | sort -d` - -src_dir=`find ./internal -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t/' | sort -d | uniq` -src="" -for x in $src_dir $src_third_dir; do - t="" - - if test `echo "$x" | grep -c glog ` -eq 1; then - continue; - fi - - if test `echo "$x" | grep -c generated` -eq 1; then - continue; - fi - - if stat $x/*.cpp > /dev/null 2>&1; then - t="src += env.Glob('`echo $x'/*.cpp'`')" - fi - - if stat $x/*.c > /dev/null 2>&1; then - if [ -z "$t" ]; then - t="src += env.Glob('`echo $x'/*.c'`')" - else - t="$t + env.Glob('`echo $x'/*.c'`')" - fi - fi - - if stat $x/*.cc > /dev/null 2>&1; then - if [ -z "$t" ]; then - t="src += env.Glob('`echo $x'/*.cc'`')" - else - t="$t + env.Glob('`echo $x'/*.cc'`')" - fi - fi - - if [ -z "$src" ]; then - src=$t - else - src=`echo "$src\n$t"` - fi -done - -cat > CMakeLists.txt << EOF -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The Original Code is Copyright (C) 2012, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -# NOTE: This file is automatically generated by bundle.sh script -# If you're doing changes in this file, please update template -# in that script too - -set(INC - . - include - internal - config - ../gflags/src -) - -set(INC_SYS - \${EIGEN3_INCLUDE_DIRS} - \${GFLAGS_INCLUDE_DIRS} - \${GLOG_INCLUDE_DIRS} -) - -set(SRC -${sources} - -${headers} -) - -set(LIB - \${GLOG_LIBRARIES} - \${GFLAGS_LIBRARIES} -) - -if(WITH_LIBMV_SCHUR_SPECIALIZATIONS) - list(APPEND SRC -${generated_sources} - ) -else() - add_definitions(-DCERES_RESTRICT_SCHUR_SPECIALIZATION) -endif() - -add_definitions(\${GFLAGS_DEFINES}) -add_definitions(\${GLOG_DEFINES}) -add_definitions(\${CERES_DEFINES}) - -add_definitions( - -DCERES_HAVE_PTHREAD - -DCERES_NO_SUITESPARSE - -DCERES_NO_CXSPARSE - -DCERES_NO_LAPACK - -DCERES_NO_ACCELERATE_SPARSE - -DCERES_HAVE_RWLOCK - -DCERES_USE_CXX_THREADS -) - -blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}" "\${LIB}") -EOF diff --git a/extern/ceres/config/ceres/internal/config.h b/extern/ceres/config/ceres/internal/config.h index 1cf034ded5f..2566945e084 100644 --- a/extern/ceres/config/ceres/internal/config.h +++ b/extern/ceres/config/ceres/internal/config.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -28,21 +28,98 @@ // // Author: alexs.mac@gmail.com (Alex Stewart) -// Default (empty) configuration options for Ceres. +// Configuration options for Ceres. // -// IMPORTANT: Most users of Ceres will not use this file, when -// compiling Ceres with CMake, CMake will configure a new -// config.h with the currently selected Ceres compile -// options in <BUILD_DIR>/config, which will be added to -// the include path for compilation, and installed with the -// public Ceres headers. However, for some users of Ceres -// who compile without CMake (Android), this file ensures -// that Ceres will compile, with the user either specifying -// manually the Ceres compile options, or passing them -// directly through the compiler. +// Do not edit this file, it was automatically configured by CMake when +// Ceres was compiled with the relevant configuration for the machine +// on which Ceres was compiled. +// +// Ceres Developers: All options should have the same name as their mapped +// CMake options, in the preconfigured version of this file +// all options should be enclosed in '@'. #ifndef CERES_PUBLIC_INTERNAL_CONFIG_H_ #define CERES_PUBLIC_INTERNAL_CONFIG_H_ +// If defined, use the LGPL code in Eigen. +#define CERES_USE_EIGEN_SPARSE + +// If defined, Ceres was compiled without LAPACK. +#define CERES_NO_LAPACK + +// If defined, Ceres was compiled without SuiteSparse. +#define CERES_NO_SUITESPARSE + +// If defined, Ceres was compiled without CXSparse. +#define CERES_NO_CXSPARSE + +// If defined, Ceres was compiled without CUDA. +#define CERES_NO_CUDA + +// If defined, Ceres was compiled without Apple's Accelerate framework solvers. +#define CERES_NO_ACCELERATE_SPARSE + +#if defined(CERES_NO_SUITESPARSE) && \ + defined(CERES_NO_ACCELERATE_SPARSE) && \ + defined(CERES_NO_CXSPARSE) && \ + !defined(CERES_USE_EIGEN_SPARSE) // NOLINT +// If defined Ceres was compiled without any sparse linear algebra support. +#define CERES_NO_SPARSE +#endif + +// If defined, Ceres was compiled without Schur specializations. +// #define CERES_RESTRICT_SCHUR_SPECIALIZATION + +// If defined, Ceres was compiled to use Eigen instead of hardcoded BLAS +// routines. +// #define CERES_NO_CUSTOM_BLAS + +// If defined, Ceres was compiled without multithreading support. +// #define CERES_NO_THREADS +// If defined Ceres was compiled with OpenMP multithreading. +// #define CERES_USE_OPENMP +// If defined Ceres was compiled with modern C++ multithreading. +#define CERES_USE_CXX_THREADS + +// If defined, Ceres was compiled with a version MSVC >= 2005 which +// deprecated the standard POSIX names for bessel functions, replacing them +// with underscore prefixed versions (e.g. j0() -> _j0()). +#ifdef _MSC_VER +#define CERES_MSVC_USE_UNDERSCORE_PREFIXED_BESSEL_FUNCTIONS +#endif + +#if defined(CERES_USE_OPENMP) +#if defined(CERES_USE_CXX_THREADS) || defined(CERES_NO_THREADS) +#error CERES_USE_OPENMP is mutually exclusive to CERES_USE_CXX_THREADS and CERES_NO_THREADS +#endif +#elif defined(CERES_USE_CXX_THREADS) +#if defined(CERES_USE_OPENMP) || defined(CERES_NO_THREADS) +#error CERES_USE_CXX_THREADS is mutually exclusive to CERES_USE_OPENMP, CERES_USE_CXX_THREADS and CERES_NO_THREADS +#endif +#elif defined(CERES_NO_THREADS) +#if defined(CERES_USE_OPENMP) || defined(CERES_USE_CXX_THREADS) +#error CERES_NO_THREADS is mutually exclusive to CERES_USE_OPENMP and CERES_USE_CXX_THREADS +#endif +#else +# error One of CERES_USE_OPENMP, CERES_USE_CXX_THREADS or CERES_NO_THREADS must be defined. +#endif + +// CERES_NO_SPARSE should be automatically defined by config.h if Ceres was +// compiled without any sparse back-end. Verify that it has not subsequently +// been inconsistently redefined. +#if defined(CERES_NO_SPARSE) +#if !defined(CERES_NO_SUITESPARSE) +#error CERES_NO_SPARSE requires CERES_NO_SUITESPARSE. +#endif +#if !defined(CERES_NO_CXSPARSE) +#error CERES_NO_SPARSE requires CERES_NO_CXSPARSE +#endif +#if !defined(CERES_NO_ACCELERATE_SPARSE) +#error CERES_NO_SPARSE requires CERES_NO_ACCELERATE_SPARSE +#endif +#if defined(CERES_USE_EIGEN_SPARSE) +#error CERES_NO_SPARSE requires !CERES_USE_EIGEN_SPARSE +#endif +#endif #endif // CERES_PUBLIC_INTERNAL_CONFIG_H_ diff --git a/extern/ceres/config/ceres/internal/export.h b/extern/ceres/config/ceres/internal/export.h new file mode 100644 index 00000000000..c85bc5ca65d --- /dev/null +++ b/extern/ceres/config/ceres/internal/export.h @@ -0,0 +1,42 @@ + +#ifndef CERES_EXPORT_H +#define CERES_EXPORT_H + +#ifdef CERES_STATIC_DEFINE +# define CERES_EXPORT +# define CERES_NO_EXPORT +#else +# ifndef CERES_EXPORT +# ifdef ceres_EXPORTS + /* We are building this library */ +# define CERES_EXPORT +# else + /* We are using this library */ +# define CERES_EXPORT +# endif +# endif + +# ifndef CERES_NO_EXPORT +# define CERES_NO_EXPORT +# endif +#endif + +#ifndef CERES_DEPRECATED +# define CERES_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef CERES_DEPRECATED_EXPORT +# define CERES_DEPRECATED_EXPORT CERES_EXPORT CERES_DEPRECATED +#endif + +#ifndef CERES_DEPRECATED_NO_EXPORT +# define CERES_DEPRECATED_NO_EXPORT CERES_NO_EXPORT CERES_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef CERES_NO_DEPRECATED +# define CERES_NO_DEPRECATED +# endif +#endif + +#endif /* CERES_EXPORT_H */ diff --git a/extern/ceres/files.txt b/extern/ceres/files.txt deleted file mode 100644 index bfbd57090c9..00000000000 --- a/extern/ceres/files.txt +++ /dev/null @@ -1,318 +0,0 @@ -include/ceres/autodiff_cost_function.h -include/ceres/autodiff_first_order_function.h -include/ceres/autodiff_local_parameterization.h -include/ceres/c_api.h -include/ceres/ceres.h -include/ceres/conditioned_cost_function.h -include/ceres/context.h -include/ceres/cost_function.h -include/ceres/cost_function_to_functor.h -include/ceres/covariance.h -include/ceres/crs_matrix.h -include/ceres/cubic_interpolation.h -include/ceres/dynamic_autodiff_cost_function.h -include/ceres/dynamic_cost_function.h -include/ceres/dynamic_cost_function_to_functor.h -include/ceres/dynamic_numeric_diff_cost_function.h -include/ceres/evaluation_callback.h -include/ceres/first_order_function.h -include/ceres/gradient_checker.h -include/ceres/gradient_problem.h -include/ceres/gradient_problem_solver.h -include/ceres/internal/array_selector.h -include/ceres/internal/autodiff.h -include/ceres/internal/disable_warnings.h -include/ceres/internal/eigen.h -include/ceres/internal/fixed_array.h -include/ceres/internal/householder_vector.h -include/ceres/internal/integer_sequence_algorithm.h -include/ceres/internal/line_parameterization.h -include/ceres/internal/memory.h -include/ceres/internal/numeric_diff.h -include/ceres/internal/parameter_dims.h -include/ceres/internal/port.h -include/ceres/internal/reenable_warnings.h -include/ceres/internal/variadic_evaluate.h -include/ceres/iteration_callback.h -include/ceres/jet.h -include/ceres/local_parameterization.h -include/ceres/loss_function.h -include/ceres/normal_prior.h -include/ceres/numeric_diff_cost_function.h -include/ceres/numeric_diff_options.h -include/ceres/ordered_groups.h -include/ceres/problem.h -include/ceres/rotation.h -include/ceres/sized_cost_function.h -include/ceres/solver.h -include/ceres/tiny_solver_autodiff_function.h -include/ceres/tiny_solver_cost_function_adapter.h -include/ceres/tiny_solver.h -include/ceres/types.h -include/ceres/version.h -internal/ceres/accelerate_sparse.cc -internal/ceres/accelerate_sparse.h -internal/ceres/array_utils.cc -internal/ceres/array_utils.h -internal/ceres/blas.cc -internal/ceres/blas.h -internal/ceres/block_evaluate_preparer.cc -internal/ceres/block_evaluate_preparer.h -internal/ceres/block_jacobian_writer.cc -internal/ceres/block_jacobian_writer.h -internal/ceres/block_jacobi_preconditioner.cc -internal/ceres/block_jacobi_preconditioner.h -internal/ceres/block_random_access_dense_matrix.cc -internal/ceres/block_random_access_dense_matrix.h -internal/ceres/block_random_access_diagonal_matrix.cc -internal/ceres/block_random_access_diagonal_matrix.h -internal/ceres/block_random_access_matrix.cc -internal/ceres/block_random_access_matrix.h -internal/ceres/block_random_access_sparse_matrix.cc -internal/ceres/block_random_access_sparse_matrix.h -internal/ceres/block_sparse_matrix.cc -internal/ceres/block_sparse_matrix.h -internal/ceres/block_structure.cc -internal/ceres/block_structure.h -internal/ceres/callbacks.cc -internal/ceres/callbacks.h -internal/ceres/canonical_views_clustering.cc -internal/ceres/canonical_views_clustering.h -internal/ceres/c_api.cc -internal/ceres/casts.h -internal/ceres/cgnr_linear_operator.h -internal/ceres/cgnr_solver.cc -internal/ceres/cgnr_solver.h -internal/ceres/compressed_col_sparse_matrix_utils.cc -internal/ceres/compressed_col_sparse_matrix_utils.h -internal/ceres/compressed_row_jacobian_writer.cc -internal/ceres/compressed_row_jacobian_writer.h -internal/ceres/compressed_row_sparse_matrix.cc -internal/ceres/compressed_row_sparse_matrix.h -internal/ceres/concurrent_queue.h -internal/ceres/conditioned_cost_function.cc -internal/ceres/conjugate_gradients_solver.cc -internal/ceres/conjugate_gradients_solver.h -internal/ceres/context.cc -internal/ceres/context_impl.cc -internal/ceres/context_impl.h -internal/ceres/coordinate_descent_minimizer.cc -internal/ceres/coordinate_descent_minimizer.h -internal/ceres/corrector.cc -internal/ceres/corrector.h -internal/ceres/covariance.cc -internal/ceres/covariance_impl.cc -internal/ceres/covariance_impl.h -internal/ceres/cxsparse.cc -internal/ceres/cxsparse.h -internal/ceres/dense_jacobian_writer.h -internal/ceres/dense_normal_cholesky_solver.cc -internal/ceres/dense_normal_cholesky_solver.h -internal/ceres/dense_qr_solver.cc -internal/ceres/dense_qr_solver.h -internal/ceres/dense_sparse_matrix.cc -internal/ceres/dense_sparse_matrix.h -internal/ceres/detect_structure.cc -internal/ceres/detect_structure.h -internal/ceres/dogleg_strategy.cc -internal/ceres/dogleg_strategy.h -internal/ceres/dynamic_compressed_row_finalizer.h -internal/ceres/dynamic_compressed_row_jacobian_writer.cc -internal/ceres/dynamic_compressed_row_jacobian_writer.h -internal/ceres/dynamic_compressed_row_sparse_matrix.cc -internal/ceres/dynamic_compressed_row_sparse_matrix.h -internal/ceres/dynamic_sparse_normal_cholesky_solver.cc -internal/ceres/dynamic_sparse_normal_cholesky_solver.h -internal/ceres/eigensparse.cc -internal/ceres/eigensparse.h -internal/ceres/evaluator.cc -internal/ceres/evaluator.h -internal/ceres/execution_summary.h -internal/ceres/file.cc -internal/ceres/file.h -internal/ceres/float_cxsparse.cc -internal/ceres/float_cxsparse.h -internal/ceres/float_suitesparse.cc -internal/ceres/float_suitesparse.h -internal/ceres/function_sample.cc -internal/ceres/function_sample.h -internal/ceres/generated/partitioned_matrix_view_2_2_2.cc -internal/ceres/generated/partitioned_matrix_view_2_2_3.cc -internal/ceres/generated/partitioned_matrix_view_2_2_4.cc -internal/ceres/generated/partitioned_matrix_view_2_2_d.cc -internal/ceres/generated/partitioned_matrix_view_2_3_3.cc -internal/ceres/generated/partitioned_matrix_view_2_3_4.cc -internal/ceres/generated/partitioned_matrix_view_2_3_6.cc -internal/ceres/generated/partitioned_matrix_view_2_3_9.cc -internal/ceres/generated/partitioned_matrix_view_2_3_d.cc -internal/ceres/generated/partitioned_matrix_view_2_4_3.cc -internal/ceres/generated/partitioned_matrix_view_2_4_4.cc -internal/ceres/generated/partitioned_matrix_view_2_4_6.cc -internal/ceres/generated/partitioned_matrix_view_2_4_8.cc -internal/ceres/generated/partitioned_matrix_view_2_4_9.cc -internal/ceres/generated/partitioned_matrix_view_2_4_d.cc -internal/ceres/generated/partitioned_matrix_view_2_d_d.cc -internal/ceres/generated/partitioned_matrix_view_3_3_3.cc -internal/ceres/generated/partitioned_matrix_view_4_4_2.cc -internal/ceres/generated/partitioned_matrix_view_4_4_3.cc -internal/ceres/generated/partitioned_matrix_view_4_4_4.cc -internal/ceres/generated/partitioned_matrix_view_4_4_d.cc -internal/ceres/generated/partitioned_matrix_view_d_d_d.cc -internal/ceres/generated/schur_eliminator_2_2_2.cc -internal/ceres/generated/schur_eliminator_2_2_3.cc -internal/ceres/generated/schur_eliminator_2_2_4.cc -internal/ceres/generated/schur_eliminator_2_2_d.cc -internal/ceres/generated/schur_eliminator_2_3_3.cc -internal/ceres/generated/schur_eliminator_2_3_4.cc -internal/ceres/generated/schur_eliminator_2_3_6.cc -internal/ceres/generated/schur_eliminator_2_3_9.cc -internal/ceres/generated/schur_eliminator_2_3_d.cc -internal/ceres/generated/schur_eliminator_2_4_3.cc -internal/ceres/generated/schur_eliminator_2_4_4.cc -internal/ceres/generated/schur_eliminator_2_4_6.cc -internal/ceres/generated/schur_eliminator_2_4_8.cc -internal/ceres/generated/schur_eliminator_2_4_9.cc -internal/ceres/generated/schur_eliminator_2_4_d.cc -internal/ceres/generated/schur_eliminator_2_d_d.cc -internal/ceres/generated/schur_eliminator_3_3_3.cc -internal/ceres/generated/schur_eliminator_4_4_2.cc -internal/ceres/generated/schur_eliminator_4_4_3.cc -internal/ceres/generated/schur_eliminator_4_4_4.cc -internal/ceres/generated/schur_eliminator_4_4_d.cc -internal/ceres/generated/schur_eliminator_d_d_d.cc -internal/ceres/generate_template_specializations.py -internal/ceres/gradient_checker.cc -internal/ceres/gradient_checking_cost_function.cc -internal/ceres/gradient_checking_cost_function.h -internal/ceres/gradient_problem.cc -internal/ceres/gradient_problem_evaluator.h -internal/ceres/gradient_problem_solver.cc -internal/ceres/graph_algorithms.h -internal/ceres/graph.h -internal/ceres/implicit_schur_complement.cc -internal/ceres/implicit_schur_complement.h -internal/ceres/inner_product_computer.cc -internal/ceres/inner_product_computer.h -internal/ceres/invert_psd_matrix.h -internal/ceres/is_close.cc -internal/ceres/is_close.h -internal/ceres/iterative_refiner.cc -internal/ceres/iterative_refiner.h -internal/ceres/iterative_schur_complement_solver.cc -internal/ceres/iterative_schur_complement_solver.h -internal/ceres/lapack.cc -internal/ceres/lapack.h -internal/ceres/levenberg_marquardt_strategy.cc -internal/ceres/levenberg_marquardt_strategy.h -internal/ceres/linear_least_squares_problems.cc -internal/ceres/linear_least_squares_problems.h -internal/ceres/linear_operator.cc -internal/ceres/linear_operator.h -internal/ceres/linear_solver.cc -internal/ceres/linear_solver.h -internal/ceres/line_search.cc -internal/ceres/line_search_direction.cc -internal/ceres/line_search_direction.h -internal/ceres/line_search.h -internal/ceres/line_search_minimizer.cc -internal/ceres/line_search_minimizer.h -internal/ceres/line_search_preprocessor.cc -internal/ceres/line_search_preprocessor.h -internal/ceres/local_parameterization.cc -internal/ceres/loss_function.cc -internal/ceres/low_rank_inverse_hessian.cc -internal/ceres/low_rank_inverse_hessian.h -internal/ceres/map_util.h -internal/ceres/minimizer.cc -internal/ceres/minimizer.h -internal/ceres/normal_prior.cc -internal/ceres/pair_hash.h -internal/ceres/parallel_for_cxx.cc -internal/ceres/parallel_for.h -internal/ceres/parallel_for_nothreads.cc -internal/ceres/parallel_for_openmp.cc -internal/ceres/parallel_utils.cc -internal/ceres/parallel_utils.h -internal/ceres/parameter_block.h -internal/ceres/parameter_block_ordering.cc -internal/ceres/parameter_block_ordering.h -internal/ceres/partitioned_matrix_view.cc -internal/ceres/partitioned_matrix_view.h -internal/ceres/partitioned_matrix_view_impl.h -internal/ceres/partitioned_matrix_view_template.py -internal/ceres/polynomial.cc -internal/ceres/polynomial.h -internal/ceres/preconditioner.cc -internal/ceres/preconditioner.h -internal/ceres/preprocessor.cc -internal/ceres/preprocessor.h -internal/ceres/problem.cc -internal/ceres/problem_impl.cc -internal/ceres/problem_impl.h -internal/ceres/program.cc -internal/ceres/program_evaluator.h -internal/ceres/program.h -internal/ceres/random.h -internal/ceres/reorder_program.cc -internal/ceres/reorder_program.h -internal/ceres/residual_block.cc -internal/ceres/residual_block.h -internal/ceres/residual_block_utils.cc -internal/ceres/residual_block_utils.h -internal/ceres/schur_complement_solver.cc -internal/ceres/schur_complement_solver.h -internal/ceres/schur_eliminator.cc -internal/ceres/schur_eliminator.h -internal/ceres/schur_eliminator_impl.h -internal/ceres/schur_eliminator_template.py -internal/ceres/schur_jacobi_preconditioner.cc -internal/ceres/schur_jacobi_preconditioner.h -internal/ceres/schur_templates.cc -internal/ceres/schur_templates.h -internal/ceres/scoped_thread_token.h -internal/ceres/scratch_evaluate_preparer.cc -internal/ceres/scratch_evaluate_preparer.h -internal/ceres/single_linkage_clustering.cc -internal/ceres/single_linkage_clustering.h -internal/ceres/small_blas_generic.h -internal/ceres/small_blas.h -internal/ceres/solver.cc -internal/ceres/solver_utils.cc -internal/ceres/solver_utils.h -internal/ceres/sparse_cholesky.cc -internal/ceres/sparse_cholesky.h -internal/ceres/sparse_matrix.cc -internal/ceres/sparse_matrix.h -internal/ceres/sparse_normal_cholesky_solver.cc -internal/ceres/sparse_normal_cholesky_solver.h -internal/ceres/split.cc -internal/ceres/split.h -internal/ceres/stl_util.h -internal/ceres/stringprintf.cc -internal/ceres/stringprintf.h -internal/ceres/subset_preconditioner.cc -internal/ceres/subset_preconditioner.h -internal/ceres/suitesparse.cc -internal/ceres/suitesparse.h -internal/ceres/thread_pool.cc -internal/ceres/thread_pool.h -internal/ceres/thread_token_provider.cc -internal/ceres/thread_token_provider.h -internal/ceres/triplet_sparse_matrix.cc -internal/ceres/triplet_sparse_matrix.h -internal/ceres/trust_region_minimizer.cc -internal/ceres/trust_region_minimizer.h -internal/ceres/trust_region_preprocessor.cc -internal/ceres/trust_region_preprocessor.h -internal/ceres/trust_region_step_evaluator.cc -internal/ceres/trust_region_step_evaluator.h -internal/ceres/trust_region_strategy.cc -internal/ceres/trust_region_strategy.h -internal/ceres/types.cc -internal/ceres/visibility_based_preconditioner.cc -internal/ceres/visibility_based_preconditioner.h -internal/ceres/visibility.cc -internal/ceres/visibility.h -internal/ceres/wall_time.cc -internal/ceres/wall_time.h -config/ceres/internal/config.h diff --git a/extern/ceres/include/ceres/autodiff_cost_function.h b/extern/ceres/include/ceres/autodiff_cost_function.h index 207f0a41688..cd256432a98 100644 --- a/extern/ceres/include/ceres/autodiff_cost_function.h +++ b/extern/ceres/include/ceres/autodiff_cost_function.h @@ -151,7 +151,8 @@ namespace ceres { template <typename CostFunctor, int kNumResiduals, // Number of residuals, or ceres::DYNAMIC. int... Ns> // Number of parameters in each parameter block. -class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { +class AutoDiffCostFunction final + : public SizedCostFunction<kNumResiduals, Ns...> { public: // Takes ownership of functor by default. Uses the template-provided // value for the number of residuals ("kNumResiduals"). @@ -178,7 +179,7 @@ class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { SizedCostFunction<kNumResiduals, Ns...>::set_num_residuals(num_residuals); } - explicit AutoDiffCostFunction(AutoDiffCostFunction&& other) + AutoDiffCostFunction(AutoDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} virtual ~AutoDiffCostFunction() { @@ -215,6 +216,8 @@ class AutoDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { jacobians); }; + const CostFunctor& functor() const { return *functor_; } + private: std::unique_ptr<CostFunctor> functor_; Ownership ownership_; diff --git a/extern/ceres/include/ceres/autodiff_first_order_function.h b/extern/ceres/include/ceres/autodiff_first_order_function.h index b98d845655b..7c13f4239a6 100644 --- a/extern/ceres/include/ceres/autodiff_first_order_function.h +++ b/extern/ceres/include/ceres/autodiff_first_order_function.h @@ -102,7 +102,7 @@ namespace ceres { // seen where instead of using a_ directly, a_ is wrapped with T(a_). template <typename FirstOrderFunctor, int kNumParameters> -class AutoDiffFirstOrderFunction : public FirstOrderFunction { +class AutoDiffFirstOrderFunction final : public FirstOrderFunction { public: // Takes ownership of functor. explicit AutoDiffFirstOrderFunction(FirstOrderFunctor* functor) @@ -110,8 +110,6 @@ class AutoDiffFirstOrderFunction : public FirstOrderFunction { static_assert(kNumParameters > 0, "kNumParameters must be positive"); } - virtual ~AutoDiffFirstOrderFunction() {} - bool Evaluate(const double* const parameters, double* cost, double* gradient) const override { @@ -119,7 +117,7 @@ class AutoDiffFirstOrderFunction : public FirstOrderFunction { return (*functor_)(parameters, cost); } - typedef Jet<double, kNumParameters> JetT; + using JetT = Jet<double, kNumParameters>; internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters); for (int i = 0; i < kNumParameters; ++i) { x[i].a = parameters[i]; @@ -142,6 +140,8 @@ class AutoDiffFirstOrderFunction : public FirstOrderFunction { int NumParameters() const override { return kNumParameters; } + const FirstOrderFunctor& functor() const { return *functor_; } + private: std::unique_ptr<FirstOrderFunctor> functor_; }; diff --git a/extern/ceres/include/ceres/autodiff_local_parameterization.h b/extern/ceres/include/ceres/autodiff_local_parameterization.h index d694376fdd1..5f9b04d0670 100644 --- a/extern/ceres/include/ceres/autodiff_local_parameterization.h +++ b/extern/ceres/include/ceres/autodiff_local_parameterization.h @@ -40,6 +40,10 @@ namespace ceres { +// WARNING: LocalParameterizations are deprecated, so is +// AutoDiffLocalParameterization. They will be removed from Ceres Solver in +// version 2.2.0. Please use Manifolds and AutoDiffManifold instead. + // Create local parameterization with Jacobians computed via automatic // differentiation. For more information on local parameterizations, // see include/ceres/local_parameterization.h @@ -106,7 +110,8 @@ namespace ceres { // seen where instead of using k_ directly, k_ is wrapped with T(k_). template <typename Functor, int kGlobalSize, int kLocalSize> -class AutoDiffLocalParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use AutoDiffManifold instead.") + AutoDiffLocalParameterization : public LocalParameterization { public: AutoDiffLocalParameterization() : functor_(new Functor()) {} @@ -114,7 +119,6 @@ class AutoDiffLocalParameterization : public LocalParameterization { explicit AutoDiffLocalParameterization(Functor* functor) : functor_(functor) {} - virtual ~AutoDiffLocalParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override { @@ -133,7 +137,7 @@ class AutoDiffLocalParameterization : public LocalParameterization { } const double* parameter_ptrs[2] = {x, zero_delta}; - double* jacobian_ptrs[2] = {NULL, jacobian}; + double* jacobian_ptrs[2] = {nullptr, jacobian}; return internal::AutoDifferentiate< kGlobalSize, internal::StaticParameterDims<kGlobalSize, kLocalSize>>( @@ -143,6 +147,8 @@ class AutoDiffLocalParameterization : public LocalParameterization { int GlobalSize() const override { return kGlobalSize; } int LocalSize() const override { return kLocalSize; } + const Functor& functor() const { return *functor_; } + private: std::unique_ptr<Functor> functor_; }; diff --git a/extern/ceres/include/ceres/autodiff_manifold.h b/extern/ceres/include/ceres/autodiff_manifold.h new file mode 100644 index 00000000000..3063e19e802 --- /dev/null +++ b/extern/ceres/include/ceres/autodiff_manifold.h @@ -0,0 +1,259 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_PUBLIC_AUTODIFF_MANIFOLD_H_ +#define CERES_PUBLIC_AUTODIFF_MANIFOLD_H_ + +#include <memory> + +#include "ceres/internal/autodiff.h" +#include "ceres/manifold.h" + +namespace ceres { + +// Create a Manifold with Jacobians computed via automatic differentiation. For +// more information on manifolds, see include/ceres/manifold.h +// +// To get an auto differentiated manifold, you must define a class/struct with +// templated Plus and Minus functions that compute +// +// x_plus_delta = Plus(x, delta); +// y_minus_x = Minus(y, x); +// +// Where, x, y and x_plus_y are vectors on the manifold in the ambient space (so +// they are kAmbientSize vectors) and delta, y_minus_x are vectors in the +// tangent space (so they are kTangentSize vectors). +// +// The Functor should have the signature: +// +// struct Functor { +// template <typename T> +// bool Plus(const T* x, const T* delta, T* x_plus_delta) const; +// +// template <typename T> +// bool Minus(const T* y, const T* x, T* y_minus_x) const; +// }; +// +// Observe that the Plus and Minus operations are templated on the parameter T. +// The autodiff framework substitutes appropriate "Jet" objects for T in order +// to compute the derivative when necessary. This is the same mechanism that is +// used to compute derivatives when using AutoDiffCostFunction. +// +// Plus and Minus should return true if the computation is successful and false +// otherwise, in which case the result will not be used. +// +// Given this Functor, the corresponding Manifold can be constructed as: +// +// AutoDiffManifold<Functor, kAmbientSize, kTangentSize> manifold; +// +// As a concrete example consider the case of Quaternions. Quaternions form a +// three dimensional manifold embedded in R^4, i.e. they have an ambient +// dimension of 4 and their tangent space has dimension 3. The following Functor +// (taken from autodiff_manifold_test.cc) defines the Plus and Minus operations +// on the Quaternion manifold: +// +// NOTE: The following is only used for illustration purposes. Ceres Solver +// ships with optimized production grade QuaternionManifold implementation. See +// manifold.h. +// +// This functor assumes that the quaternions are laid out as [w,x,y,z] in +// memory, i.e. the real or scalar part is the first coordinate. +// +// struct QuaternionFunctor { +// template <typename T> +// bool Plus(const T* x, const T* delta, T* x_plus_delta) const { +// const T squared_norm_delta = +// delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]; +// +// T q_delta[4]; +// if (squared_norm_delta > T(0.0)) { +// T norm_delta = sqrt(squared_norm_delta); +// const T sin_delta_by_delta = sin(norm_delta) / norm_delta; +// q_delta[0] = cos(norm_delta); +// q_delta[1] = sin_delta_by_delta * delta[0]; +// q_delta[2] = sin_delta_by_delta * delta[1]; +// q_delta[3] = sin_delta_by_delta * delta[2]; +// } else { +// // We do not just use q_delta = [1,0,0,0] here because that is a +// // constant and when used for automatic differentiation will +// // lead to a zero derivative. Instead we take a first order +// // approximation and evaluate it at zero. +// q_delta[0] = T(1.0); +// q_delta[1] = delta[0]; +// q_delta[2] = delta[1]; +// q_delta[3] = delta[2]; +// } +// +// QuaternionProduct(q_delta, x, x_plus_delta); +// return true; +// } +// +// template <typename T> +// bool Minus(const T* y, const T* x, T* y_minus_x) const { +// T minus_x[4] = {x[0], -x[1], -x[2], -x[3]}; +// T ambient_y_minus_x[4]; +// QuaternionProduct(y, minus_x, ambient_y_minus_x); +// T u_norm = sqrt(ambient_y_minus_x[1] * ambient_y_minus_x[1] + +// ambient_y_minus_x[2] * ambient_y_minus_x[2] + +// ambient_y_minus_x[3] * ambient_y_minus_x[3]); +// if (u_norm > 0.0) { +// T theta = atan2(u_norm, ambient_y_minus_x[0]); +// y_minus_x[0] = theta * ambient_y_minus_x[1] / u_norm; +// y_minus_x[1] = theta * ambient_y_minus_x[2] / u_norm; +// y_minus_x[2] = theta * ambient_y_minus_x[3] / u_norm; +// } else { +// // We do not use [0,0,0] here because even though the value part is +// // a constant, the derivative part is not. +// y_minus_x[0] = ambient_y_minus_x[1]; +// y_minus_x[1] = ambient_y_minus_x[2]; +// y_minus_x[2] = ambient_y_minus_x[3]; +// } +// return true; +// } +// }; +// +// Then given this struct, the auto differentiated Quaternion Manifold can now +// be constructed as +// +// Manifold* manifold = new AutoDiffManifold<QuaternionFunctor, 4, 3>; + +template <typename Functor, int kAmbientSize, int kTangentSize> +class AutoDiffManifold final : public Manifold { + public: + AutoDiffManifold() : functor_(std::make_unique<Functor>()) {} + + // Takes ownership of functor. + explicit AutoDiffManifold(Functor* functor) : functor_(functor) {} + + int AmbientSize() const override { return kAmbientSize; } + int TangentSize() const override { return kTangentSize; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override { + return functor_->Plus(x, delta, x_plus_delta); + } + + bool PlusJacobian(const double* x, double* jacobian) const override; + + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override { + return functor_->Minus(y, x, y_minus_x); + } + + bool MinusJacobian(const double* x, double* jacobian) const override; + + const Functor& functor() const { return *functor_; } + + private: + std::unique_ptr<Functor> functor_; +}; + +namespace internal { + +// The following two helper structs are needed to interface the Plus and Minus +// methods of the ManifoldFunctor with the automatic differentiation which +// expects a Functor with operator(). +template <typename Functor> +struct PlusWrapper { + explicit PlusWrapper(const Functor& functor) : functor(functor) {} + template <typename T> + bool operator()(const T* x, const T* delta, T* x_plus_delta) const { + return functor.Plus(x, delta, x_plus_delta); + } + const Functor& functor; +}; + +template <typename Functor> +struct MinusWrapper { + explicit MinusWrapper(const Functor& functor) : functor(functor) {} + template <typename T> + bool operator()(const T* y, const T* x, T* y_minus_x) const { + return functor.Minus(y, x, y_minus_x); + } + const Functor& functor; +}; +} // namespace internal + +template <typename Functor, int kAmbientSize, int kTangentSize> +bool AutoDiffManifold<Functor, kAmbientSize, kTangentSize>::PlusJacobian( + const double* x, double* jacobian) const { + double zero_delta[kTangentSize]; + for (int i = 0; i < kTangentSize; ++i) { + zero_delta[i] = 0.0; + } + + double x_plus_delta[kAmbientSize]; + for (int i = 0; i < kAmbientSize; ++i) { + x_plus_delta[i] = 0.0; + } + + const double* parameter_ptrs[2] = {x, zero_delta}; + + // PlusJacobian is D_2 Plus(x,0) so we only need to compute the Jacobian + // w.r.t. the second argument. + double* jacobian_ptrs[2] = {nullptr, jacobian}; + return internal::AutoDifferentiate< + kAmbientSize, + internal::StaticParameterDims<kAmbientSize, kTangentSize>>( + internal::PlusWrapper<Functor>(*functor_), + parameter_ptrs, + kAmbientSize, + x_plus_delta, + jacobian_ptrs); +} + +template <typename Functor, int kAmbientSize, int kTangentSize> +bool AutoDiffManifold<Functor, kAmbientSize, kTangentSize>::MinusJacobian( + const double* x, double* jacobian) const { + double y_minus_x[kTangentSize]; + for (int i = 0; i < kTangentSize; ++i) { + y_minus_x[i] = 0.0; + } + + const double* parameter_ptrs[2] = {x, x}; + + // MinusJacobian is D_1 Minus(x,x), so we only need to compute the Jacobian + // w.r.t. the first argument. + double* jacobian_ptrs[2] = {jacobian, nullptr}; + return internal::AutoDifferentiate< + kTangentSize, + internal::StaticParameterDims<kAmbientSize, kAmbientSize>>( + internal::MinusWrapper<Functor>(*functor_), + parameter_ptrs, + kTangentSize, + y_minus_x, + jacobian_ptrs); +} + +} // namespace ceres + +#endif // CERES_PUBLIC_AUTODIFF_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/c_api.h b/extern/ceres/include/ceres/c_api.h index 91b82bf995f..1be8ca2e077 100644 --- a/extern/ceres/include/ceres/c_api.h +++ b/extern/ceres/include/ceres/c_api.h @@ -39,7 +39,7 @@ #define CERES_PUBLIC_C_API_H_ // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/internal/disable_warnings.h" // clang-format on diff --git a/extern/ceres/include/ceres/ceres.h b/extern/ceres/include/ceres/ceres.h index d249351694c..c32477d4254 100644 --- a/extern/ceres/include/ceres/ceres.h +++ b/extern/ceres/include/ceres/ceres.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -35,7 +35,9 @@ #define CERES_PUBLIC_CERES_H_ #include "ceres/autodiff_cost_function.h" +#include "ceres/autodiff_first_order_function.h" #include "ceres/autodiff_local_parameterization.h" +#include "ceres/autodiff_manifold.h" #include "ceres/conditioned_cost_function.h" #include "ceres/context.h" #include "ceres/cost_function.h" @@ -47,19 +49,25 @@ #include "ceres/dynamic_cost_function_to_functor.h" #include "ceres/dynamic_numeric_diff_cost_function.h" #include "ceres/evaluation_callback.h" +#include "ceres/first_order_function.h" #include "ceres/gradient_checker.h" #include "ceres/gradient_problem.h" #include "ceres/gradient_problem_solver.h" #include "ceres/iteration_callback.h" #include "ceres/jet.h" +#include "ceres/line_manifold.h" #include "ceres/local_parameterization.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" #include "ceres/numeric_diff_cost_function.h" +#include "ceres/numeric_diff_first_order_function.h" #include "ceres/numeric_diff_options.h" #include "ceres/ordered_groups.h" #include "ceres/problem.h" +#include "ceres/product_manifold.h" #include "ceres/sized_cost_function.h" #include "ceres/solver.h" +#include "ceres/sphere_manifold.h" #include "ceres/types.h" #include "ceres/version.h" diff --git a/extern/ceres/include/ceres/conditioned_cost_function.h b/extern/ceres/include/ceres/conditioned_cost_function.h index a57ee209b80..e4c3decbfd5 100644 --- a/extern/ceres/include/ceres/conditioned_cost_function.h +++ b/extern/ceres/include/ceres/conditioned_cost_function.h @@ -71,18 +71,18 @@ namespace ceres { // ccf_residual[i] = f_i(my_cost_function_residual[i]) // // and the Jacobian will be affected appropriately. -class CERES_EXPORT ConditionedCostFunction : public CostFunction { +class CERES_EXPORT ConditionedCostFunction final : public CostFunction { public: // Builds a cost function based on a wrapped cost function, and a // per-residual conditioner. Takes ownership of all of the wrapped cost // functions, or not, depending on the ownership parameter. Conditioners - // may be NULL, in which case the corresponding residual is not modified. + // may be nullptr, in which case the corresponding residual is not modified. // // The conditioners can repeat. ConditionedCostFunction(CostFunction* wrapped_cost_function, const std::vector<CostFunction*>& conditioners, Ownership ownership); - virtual ~ConditionedCostFunction(); + ~ConditionedCostFunction() override; bool Evaluate(double const* const* parameters, double* residuals, diff --git a/extern/ceres/include/ceres/context.h b/extern/ceres/include/ceres/context.h index d08e32b31a8..6c6e8f4c953 100644 --- a/extern/ceres/include/ceres/context.h +++ b/extern/ceres/include/ceres/context.h @@ -31,6 +31,8 @@ #ifndef CERES_PUBLIC_CONTEXT_H_ #define CERES_PUBLIC_CONTEXT_H_ +#include "ceres/internal/export.h" + namespace ceres { // A global context for processing data in Ceres. This provides a mechanism to @@ -39,13 +41,13 @@ namespace ceres { // Problems, either serially or in parallel. When using it with multiple // Problems at the same time, they may end up contending for resources // (e.g. threads) managed by the Context. -class Context { +class CERES_EXPORT Context { public: - Context() {} + Context(); Context(const Context&) = delete; void operator=(const Context&) = delete; - virtual ~Context() {} + virtual ~Context(); // Creates a context object and the caller takes ownership. static Context* Create(); diff --git a/extern/ceres/include/ceres/cost_function.h b/extern/ceres/include/ceres/cost_function.h index d1550c119e8..fef972b75af 100644 --- a/extern/ceres/include/ceres/cost_function.h +++ b/extern/ceres/include/ceres/cost_function.h @@ -48,7 +48,7 @@ #include <vector> #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -63,11 +63,11 @@ namespace ceres { // when added with AddResidualBlock(). class CERES_EXPORT CostFunction { public: - CostFunction() : num_residuals_(0) {} + CostFunction(); CostFunction(const CostFunction&) = delete; void operator=(const CostFunction&) = delete; - virtual ~CostFunction() {} + virtual ~CostFunction(); // Inputs: // @@ -92,8 +92,8 @@ class CERES_EXPORT CostFunction { // jacobians[i][r*parameter_block_size_[i] + c] = // d residual[r] / d parameters[i][c] // - // If jacobians is NULL, then no derivatives are returned; this is - // the case when computing cost only. If jacobians[i] is NULL, then + // If jacobians is nullptr, then no derivatives are returned; this is + // the case when computing cost only. If jacobians[i] is nullptr, then // the jacobian block corresponding to the i'th parameter block must // not to be returned. // diff --git a/extern/ceres/include/ceres/cost_function_to_functor.h b/extern/ceres/include/ceres/cost_function_to_functor.h index 9364293afc5..08a8050c5f8 100644 --- a/extern/ceres/include/ceres/cost_function_to_functor.h +++ b/extern/ceres/include/ceres/cost_function_to_functor.h @@ -94,10 +94,11 @@ #include "ceres/cost_function.h" #include "ceres/dynamic_cost_function_to_functor.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" #include "ceres/internal/parameter_dims.h" -#include "ceres/internal/port.h" #include "ceres/types.h" +#include "glog/logging.h" namespace ceres { diff --git a/extern/ceres/include/ceres/covariance.h b/extern/ceres/include/ceres/covariance.h index 2fe025df3ce..60bcc80b80f 100644 --- a/extern/ceres/include/ceres/covariance.h +++ b/extern/ceres/include/ceres/covariance.h @@ -35,8 +35,9 @@ #include <utility> #include <vector> +#include "ceres/internal/config.h" #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -145,7 +146,7 @@ class CovarianceImpl; // a. The rank deficiency arises from overparameterization. e.g., a // four dimensional quaternion used to parameterize SO(3), which is // a three dimensional manifold. In cases like this, the user should -// use an appropriate LocalParameterization. Not only will this lead +// use an appropriate LocalParameterization/Manifold. Not only will this lead // to better numerical behaviour of the Solver, it will also expose // the rank deficiency to the Covariance object so that it can // handle it correctly. diff --git a/extern/ceres/include/ceres/crs_matrix.h b/extern/ceres/include/ceres/crs_matrix.h index bc618fa0905..faa0f988528 100644 --- a/extern/ceres/include/ceres/crs_matrix.h +++ b/extern/ceres/include/ceres/crs_matrix.h @@ -34,17 +34,17 @@ #include <vector> #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { // A compressed row sparse matrix used primarily for communicating the // Jacobian matrix to the user. struct CERES_EXPORT CRSMatrix { - CRSMatrix() : num_rows(0), num_cols(0) {} + CRSMatrix() = default; - int num_rows; - int num_cols; + int num_rows{0}; + int num_cols{0}; // A compressed row matrix stores its contents in three arrays, // rows, cols and values. diff --git a/extern/ceres/include/ceres/cubic_interpolation.h b/extern/ceres/include/ceres/cubic_interpolation.h index 9b9ea4a942c..3ca6b11b407 100644 --- a/extern/ceres/include/ceres/cubic_interpolation.h +++ b/extern/ceres/include/ceres/cubic_interpolation.h @@ -32,7 +32,7 @@ #define CERES_PUBLIC_CUBIC_INTERPOLATION_H_ #include "Eigen/Core" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -59,8 +59,8 @@ namespace ceres { // http://en.wikipedia.org/wiki/Cubic_Hermite_spline // http://en.wikipedia.org/wiki/Bicubic_interpolation // -// f if not NULL will contain the interpolated function values. -// dfdx if not NULL will contain the interpolated derivative values. +// f if not nullptr will contain the interpolated function values. +// dfdx if not nullptr will contain the interpolated derivative values. template <int kDataDimension> void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0, const Eigen::Matrix<double, kDataDimension, 1>& p1, @@ -69,7 +69,7 @@ void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0, const double x, double* f, double* dfdx) { - typedef Eigen::Matrix<double, kDataDimension, 1> VType; + using VType = Eigen::Matrix<double, kDataDimension, 1>; const VType a = 0.5 * (-p0 + 3.0 * p1 - 3.0 * p2 + p3); const VType b = 0.5 * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3); const VType c = 0.5 * (-p0 + p2); @@ -79,12 +79,12 @@ void CubicHermiteSpline(const Eigen::Matrix<double, kDataDimension, 1>& p0, // derivative. // f = ax^3 + bx^2 + cx + d - if (f != NULL) { + if (f != nullptr) { Eigen::Map<VType>(f, kDataDimension) = d + x * (c + x * (b + x * a)); } // dfdx = 3ax^2 + 2bx + c - if (dfdx != NULL) { + if (dfdx != nullptr) { Eigen::Map<VType>(dfdx, kDataDimension) = c + x * (2.0 * b + 3.0 * a * x); } } @@ -143,7 +143,7 @@ class CubicInterpolator { // The following two Evaluate overloads are needed for interfacing // with automatic differentiation. The first is for when a scalar // evaluation is done, and the second one is for when Jets are used. - void Evaluate(const double& x, double* f) const { Evaluate(x, f, NULL); } + void Evaluate(const double& x, double* f) const { Evaluate(x, f, nullptr); } template <typename JetT> void Evaluate(const JetT& x, JetT* f) const { @@ -191,7 +191,7 @@ struct Grid1D { } EIGEN_STRONG_INLINE void GetValue(const int n, double* f) const { - const int idx = std::min(std::max(begin_, n), end_ - 1) - begin_; + const int idx = (std::min)((std::max)(begin_, n), end_ - 1) - begin_; if (kInterleaved) { for (int i = 0; i < kDataDimension; ++i) { f[i] = static_cast<double>(data_[kDataDimension * idx + i]); @@ -317,10 +317,10 @@ class BiCubicInterpolator { // Interpolate vertically the interpolated value from each row and // compute the derivative along the columns. CubicHermiteSpline<Grid::DATA_DIMENSION>(f0, f1, f2, f3, r - row, f, dfdr); - if (dfdc != NULL) { + if (dfdc != nullptr) { // Interpolate vertically the derivative along the columns. CubicHermiteSpline<Grid::DATA_DIMENSION>( - df0dc, df1dc, df2dc, df3dc, r - row, dfdc, NULL); + df0dc, df1dc, df2dc, df3dc, r - row, dfdc, nullptr); } } @@ -328,7 +328,7 @@ class BiCubicInterpolator { // with automatic differentiation. The first is for when a scalar // evaluation is done, and the second one is for when Jets are used. void Evaluate(const double& r, const double& c, double* f) const { - Evaluate(r, c, f, NULL, NULL); + Evaluate(r, c, f, nullptr, nullptr); } template <typename JetT> @@ -402,9 +402,9 @@ struct Grid2D { EIGEN_STRONG_INLINE void GetValue(const int r, const int c, double* f) const { const int row_idx = - std::min(std::max(row_begin_, r), row_end_ - 1) - row_begin_; + (std::min)((std::max)(row_begin_, r), row_end_ - 1) - row_begin_; const int col_idx = - std::min(std::max(col_begin_, c), col_end_ - 1) - col_begin_; + (std::min)((std::max)(col_begin_, c), col_end_ - 1) - col_begin_; const int n = (kRowMajor) ? num_cols_ * row_idx + col_idx : num_rows_ * col_idx + row_idx; diff --git a/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h b/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h index 7ccf6a88c32..c21d0517f27 100644 --- a/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_autodiff_cost_function.h @@ -77,17 +77,17 @@ namespace ceres { // pass. There is a tradeoff with the size of the passes; you may want // to experiment with the stride. template <typename CostFunctor, int Stride = 4> -class DynamicAutoDiffCostFunction : public DynamicCostFunction { +class DynamicAutoDiffCostFunction final : public DynamicCostFunction { public: // Takes ownership by default. - DynamicAutoDiffCostFunction(CostFunctor* functor, - Ownership ownership = TAKE_OWNERSHIP) + explicit DynamicAutoDiffCostFunction(CostFunctor* functor, + Ownership ownership = TAKE_OWNERSHIP) : functor_(functor), ownership_(ownership) {} - explicit DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) + DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} - virtual ~DynamicAutoDiffCostFunction() { + ~DynamicAutoDiffCostFunction() override { // Manually release pointer if configured to not take ownership // rather than deleting only if ownership is taken. This is to // stay maximally compatible to old user code which may have @@ -105,7 +105,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { << "You must call DynamicAutoDiffCostFunction::SetNumResiduals() " << "before DynamicAutoDiffCostFunction::Evaluate()."; - if (jacobians == NULL) { + if (jacobians == nullptr) { return (*functor_)(parameters, residuals); } @@ -150,7 +150,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { jet_parameters[i] = &input_jets[parameter_cursor]; const int parameter_block_size = parameter_block_sizes()[i]; - if (jacobians[i] != NULL) { + if (jacobians[i] != nullptr) { if (!in_derivative_section) { start_derivative_section.push_back(parameter_cursor); in_derivative_section = true; @@ -209,7 +209,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { parameter_cursor >= (start_derivative_section[current_derivative_section] + current_derivative_section_cursor)) { - if (jacobians[i] != NULL) { + if (jacobians[i] != nullptr) { input_jets[parameter_cursor].v[active_parameter_count] = 1.0; ++active_parameter_count; ++current_derivative_section_cursor; @@ -238,7 +238,7 @@ class DynamicAutoDiffCostFunction : public DynamicCostFunction { parameter_cursor >= (start_derivative_section[current_derivative_section] + current_derivative_section_cursor)) { - if (jacobians[i] != NULL) { + if (jacobians[i] != nullptr) { for (int k = 0; k < num_residuals(); ++k) { jacobians[i][k * parameter_block_sizes()[i] + j] = output_jets[k].v[active_parameter_count]; diff --git a/extern/ceres/include/ceres/dynamic_cost_function.h b/extern/ceres/include/ceres/dynamic_cost_function.h index 6e8a076ecd0..c84a366dafb 100644 --- a/extern/ceres/include/ceres/dynamic_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_cost_function.h @@ -32,6 +32,7 @@ #define CERES_PUBLIC_DYNAMIC_COST_FUNCTION_H_ #include "ceres/cost_function.h" +#include "ceres/internal/disable_warnings.h" namespace ceres { @@ -40,8 +41,6 @@ namespace ceres { // parameter blocks and set the number of residuals at run time. class CERES_EXPORT DynamicCostFunction : public CostFunction { public: - ~DynamicCostFunction() {} - virtual void AddParameterBlock(int size) { mutable_parameter_block_sizes()->push_back(size); } @@ -53,4 +52,6 @@ class CERES_EXPORT DynamicCostFunction : public CostFunction { } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_DYNAMIC_COST_FUNCTION_H_ diff --git a/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h b/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h index 8d174d8ecc2..5b5feaaf58e 100644 --- a/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h +++ b/extern/ceres/include/ceres/dynamic_cost_function_to_functor.h @@ -37,8 +37,10 @@ #include <vector> #include "ceres/dynamic_cost_function.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" -#include "ceres/internal/port.h" +#include "glog/logging.h" namespace ceres { @@ -100,7 +102,7 @@ namespace ceres { // private: // DynamicCostFunctionToFunctor intrinsic_projection_; // }; -class DynamicCostFunctionToFunctor { +class CERES_EXPORT DynamicCostFunctionToFunctor { public: // Takes ownership of cost_function. explicit DynamicCostFunctionToFunctor(CostFunction* cost_function) @@ -109,7 +111,7 @@ class DynamicCostFunctionToFunctor { } bool operator()(double const* const* parameters, double* residuals) const { - return cost_function_->Evaluate(parameters, residuals, NULL); + return cost_function_->Evaluate(parameters, residuals, nullptr); } template <typename JetT> @@ -187,4 +189,6 @@ class DynamicCostFunctionToFunctor { } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_DYNAMIC_COST_FUNCTION_TO_FUNCTOR_H_ diff --git a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h index ccc8f66db43..e1892e8ba4a 100644 --- a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h @@ -77,7 +77,7 @@ namespace ceres { // cost_function.AddParameterBlock(10); // cost_function.SetNumResiduals(21); template <typename CostFunctor, NumericDiffMethodType method = CENTRAL> -class DynamicNumericDiffCostFunction : public DynamicCostFunction { +class DynamicNumericDiffCostFunction final : public DynamicCostFunction { public: explicit DynamicNumericDiffCostFunction( const CostFunctor* functor, @@ -85,11 +85,10 @@ class DynamicNumericDiffCostFunction : public DynamicCostFunction { const NumericDiffOptions& options = NumericDiffOptions()) : functor_(functor), ownership_(ownership), options_(options) {} - explicit DynamicNumericDiffCostFunction( - DynamicNumericDiffCostFunction&& other) + DynamicNumericDiffCostFunction(DynamicNumericDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} - virtual ~DynamicNumericDiffCostFunction() { + ~DynamicNumericDiffCostFunction() override { if (ownership_ != TAKE_OWNERSHIP) { functor_.release(); } @@ -111,7 +110,7 @@ class DynamicNumericDiffCostFunction : public DynamicCostFunction { const bool status = internal::VariadicEvaluate<internal::DynamicParameterDims>( *functor_.get(), parameters, residuals); - if (jacobians == NULL || !status) { + if (jacobians == nullptr || !status) { return status; } @@ -133,7 +132,7 @@ class DynamicNumericDiffCostFunction : public DynamicCostFunction { } for (size_t block = 0; block < block_sizes.size(); ++block) { - if (jacobians[block] != NULL && + if (jacobians[block] != nullptr && !NumericDiff<CostFunctor, method, ceres::DYNAMIC, diff --git a/extern/ceres/include/ceres/evaluation_callback.h b/extern/ceres/include/ceres/evaluation_callback.h index b9f5bbb5194..495d565047a 100644 --- a/extern/ceres/include/ceres/evaluation_callback.h +++ b/extern/ceres/include/ceres/evaluation_callback.h @@ -31,7 +31,7 @@ #ifndef CERES_PUBLIC_EVALUATION_CALLBACK_H_ #define CERES_PUBLIC_EVALUATION_CALLBACK_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -62,7 +62,7 @@ namespace ceres { // execute faster. class CERES_EXPORT EvaluationCallback { public: - virtual ~EvaluationCallback() {} + virtual ~EvaluationCallback(); // Called before Ceres requests residuals or jacobians for a given setting of // the parameters. User parameters (the double* values provided to the cost diff --git a/extern/ceres/include/ceres/first_order_function.h b/extern/ceres/include/ceres/first_order_function.h index 1420153b2aa..d718b6679ce 100644 --- a/extern/ceres/include/ceres/first_order_function.h +++ b/extern/ceres/include/ceres/first_order_function.h @@ -31,7 +31,7 @@ #ifndef CERES_PUBLIC_FIRST_ORDER_FUNCTION_H_ #define CERES_PUBLIC_FIRST_ORDER_FUNCTION_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -39,7 +39,7 @@ namespace ceres { // and its gradient. class CERES_EXPORT FirstOrderFunction { public: - virtual ~FirstOrderFunction() {} + virtual ~FirstOrderFunction(); // cost is never null. gradient may be null. The return value // indicates whether the evaluation was successful or not. diff --git a/extern/ceres/include/ceres/gradient_checker.h b/extern/ceres/include/ceres/gradient_checker.h index b79cf86b314..178fa2b0dd2 100644 --- a/extern/ceres/include/ceres/gradient_checker.h +++ b/extern/ceres/include/ceres/gradient_checker.h @@ -40,9 +40,12 @@ #include "ceres/cost_function.h" #include "ceres/dynamic_numeric_diff_cost_function.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" #include "ceres/local_parameterization.h" +#include "ceres/manifold.h" #include "glog/logging.h" namespace ceres { @@ -65,19 +68,42 @@ namespace ceres { // CostFunction, and then call Probe(). Check that the return value is 'true'. class CERES_EXPORT GradientChecker { public: - // This will not take ownership of the cost function or local + // This constructor will not take ownership of the cost function or local // parameterizations. // // function: The cost function to probe. - // local_parameterizations: A vector of local parameterizations for each - // parameter. May be NULL or contain NULL pointers to indicate that the + // + // local_parameterizations: A vector of local parameterizations, one for each + // parameter block. May be nullptr or contain nullptrs to indicate that the // respective parameter does not have a local parameterization. + // // options: Options to use for numerical differentiation. + // + // NOTE: This constructor is deprecated and will be removed in the next public + // release of Ceres Solver. Please transition to using the Manifold based + // version. + CERES_DEPRECATED_WITH_MSG( + "Local Parameterizations are deprecated. Use the constructor that uses " + "Manifolds instead.") GradientChecker( const CostFunction* function, const std::vector<const LocalParameterization*>* local_parameterizations, const NumericDiffOptions& options); + // This will not take ownership of the cost function or manifolds. + // + // function: The cost function to probe. + // + // manifolds: A vector of manifolds for each parameter. May be nullptr or + // contain nullptrs to indicate that the respective parameter blocks are + // Euclidean. + // + // options: Options to use for numerical differentiation. + GradientChecker(const CostFunction* function, + const std::vector<const Manifold*>* manifolds, + const NumericDiffOptions& options); + ~GradientChecker(); + // Contains results from a call to Probe for later inspection. struct CERES_EXPORT ProbeResults { // The return value of the cost function. @@ -87,11 +113,11 @@ class CERES_EXPORT GradientChecker { Vector residuals; // The sizes of the Jacobians below are dictated by the cost function's - // parameter block size and residual block sizes. If a parameter block - // has a local parameterization associated with it, the size of the "local" - // Jacobian will be determined by the local parameterization dimension and - // residual block size, otherwise it will be identical to the regular - // Jacobian. + // parameter block size and residual block sizes. If a parameter block has a + // manifold associated with it, the size of the "local" Jacobian will be + // determined by the dimension of the manifold (which is the same as the + // dimension of the tangent space) and residual block size, otherwise it + // will be identical to the regular Jacobian. // Derivatives as computed by the cost function. std::vector<Matrix> jacobians; @@ -114,20 +140,20 @@ class CERES_EXPORT GradientChecker { }; // Call the cost function, compute alternative Jacobians using finite - // differencing and compare results. If local parameterizations are given, - // the Jacobians will be multiplied by the local parameterization Jacobians - // before performing the check, which effectively means that all errors along - // the null space of the local parameterization will be ignored. - // Returns false if the Jacobians don't match, the cost function return false, - // or if the cost function returns different residual when called with a - // Jacobian output argument vs. calling it without. Otherwise returns true. + // differencing and compare results. If manifolds are given, the Jacobians + // will be multiplied by the manifold Jacobians before performing the check, + // which effectively means that all errors along the null space of the + // manifold will be ignored. Returns false if the Jacobians don't match, the + // cost function return false, or if a cost function returns a different + // residual when called with a Jacobian output argument vs. calling it + // without. Otherwise returns true. // // parameters: The parameter values at which to probe. // relative_precision: A threshold for the relative difference between the // Jacobians. If the Jacobians differ by more than this amount, then the // probe fails. // results: On return, the Jacobians (and other information) will be stored - // here. May be NULL. + // here. May be nullptr. // // Returns true if no problems are detected and the difference between the // Jacobians is less than error_tolerance. @@ -140,11 +166,24 @@ class CERES_EXPORT GradientChecker { GradientChecker(const GradientChecker&) = delete; void operator=(const GradientChecker&) = delete; - std::vector<const LocalParameterization*> local_parameterizations_; + // This bool is used to determine whether the constructor with the + // LocalParameterizations is called or the one with Manifolds is called. If + // the former, then the vector of manifolds is a vector of ManifoldAdapter + // objects which we own and should be deleted. If the latter then they are + // real Manifold objects owned by the caller and will not be deleted. + // + // This bool is only needed during the LocalParameterization to Manifold + // transition, once this transition is complete the LocalParameterization + // based constructor and this bool will be removed. + const bool delete_manifolds_ = false; + + std::vector<const Manifold*> manifolds_; const CostFunction* function_; std::unique_ptr<CostFunction> finite_diff_cost_function_; }; } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_GRADIENT_CHECKER_H_ diff --git a/extern/ceres/include/ceres/gradient_problem.h b/extern/ceres/include/ceres/gradient_problem.h index 49d605ea2d6..b6a8b867421 100644 --- a/extern/ceres/include/ceres/gradient_problem.h +++ b/extern/ceres/include/ceres/gradient_problem.h @@ -34,8 +34,10 @@ #include <memory> #include "ceres/first_order_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/local_parameterization.h" +#include "ceres/manifold.h" namespace ceres { @@ -43,23 +45,22 @@ class FirstOrderFunction; // Instances of GradientProblem represent general non-linear // optimization problems that must be solved using just the value of -// the objective function and its gradient. Unlike the Problem class, -// which can only be used to model non-linear least squares problems, -// instances of GradientProblem not restricted in the form of the -// objective function. +// the objective function and its gradient. + +// Unlike the Problem class, which can only be used to model non-linear least +// squares problems, instances of GradientProblem are not restricted in the form +// of the objective function. // -// Structurally GradientProblem is a composition of a -// FirstOrderFunction and optionally a LocalParameterization. +// Structurally GradientProblem is a composition of a FirstOrderFunction and +// optionally a Manifold. // -// The FirstOrderFunction is responsible for evaluating the cost and -// gradient of the objective function. +// The FirstOrderFunction is responsible for evaluating the cost and gradient of +// the objective function. // -// The LocalParameterization is responsible for going back and forth -// between the ambient space and the local tangent space. (See -// local_parameterization.h for more details). When a -// LocalParameterization is not provided, then the tangent space is -// assumed to coincide with the ambient Euclidean space that the -// gradient vector lives in. +// The Manifold is responsible for going back and forth between the ambient +// space and the local tangent space. (See manifold.h for more details). When a +// Manifold is not provided, then the tangent space is assumed to coincide with +// the ambient Euclidean space that the gradient vector lives in. // // Example usage: // @@ -78,7 +79,7 @@ class FirstOrderFunction; // const double y = parameters[1]; // // cost[0] = (1.0 - x) * (1.0 - x) + 100.0 * (y - x * x) * (y - x * x); -// if (gradient != NULL) { +// if (gradient != nullptr) { // gradient[0] = -2.0 * (1.0 - x) - 200.0 * (y - x * x) * 2.0 * x; // gradient[1] = 200.0 * (y - x * x); // } @@ -89,28 +90,96 @@ class FirstOrderFunction; // }; // // ceres::GradientProblem problem(new Rosenbrock()); +// +// NOTE: We are currently in the process of transitioning from +// LocalParameterization to Manifolds in the Ceres API. During this period, +// GradientProblem will support using both Manifold and LocalParameterization +// objects interchangably. For methods in the API affected by this change, see +// their documentation below. class CERES_EXPORT GradientProblem { public: // Takes ownership of the function. explicit GradientProblem(FirstOrderFunction* function); // Takes ownership of the function and the parameterization. + // + // NOTE: This constructor is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using the Manifold based + // constructor. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Please use the constructor that " + "uses Manifold instead.") GradientProblem(FirstOrderFunction* function, LocalParameterization* parameterization); + // Takes ownership of the function and the manifold. + GradientProblem(FirstOrderFunction* function, Manifold* manifold); + int NumParameters() const; - int NumLocalParameters() const; + + // Dimension of the manifold (and its tangent space). + // + // During the transition from LocalParameterization to Manifold, this method + // reports the LocalSize of the LocalParameterization or the TangentSize of + // the Manifold object associated with this problem. + int NumTangentParameters() const; + + // Dimension of the manifold (and its tangent space). + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using NumTangentParameters() + // instead. + int NumLocalParameters() const { return NumTangentParameters(); } // This call is not thread safe. bool Evaluate(const double* parameters, double* cost, double* gradient) const; bool Plus(const double* x, const double* delta, double* x_plus_delta) const; + const FirstOrderFunction* function() const { return function_.get(); } + FirstOrderFunction* mutable_function() { return function_.get(); } + + // NOTE: During the transition from LocalParameterization to Manifold we need + // to support both The LocalParameterization and Manifold based constructors. + // + // When the user uses the LocalParameterization, internally the solver will + // wrap it in a ManifoldAdapter object and return it when manifold or + // mutable_manifold are called. + // + // As a result this method will return a non-nullptr result if a Manifold or a + // LocalParameterization was used when constructing the GradientProblem. + const Manifold* manifold() const { return manifold_.get(); } + Manifold* mutable_manifold() { return manifold_.get(); } + + // If the problem is constructed without a LocalParameterization or with a + // Manifold this method will return a nullptr. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. + CERES_DEPRECATED_WITH_MSG("Use Manifolds instead.") + const LocalParameterization* parameterization() const { + return parameterization_.get(); + } + + // If the problem is constructed without a LocalParameterization or with a + // Manifold this method will return a nullptr. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. + CERES_DEPRECATED_WITH_MSG("Use Manifolds instead.") + LocalParameterization* mutable_parameterization() { + return parameterization_.get(); + } + private: std::unique_ptr<FirstOrderFunction> function_; + CERES_DEPRECATED_WITH_MSG("") std::unique_ptr<LocalParameterization> parameterization_; + std::unique_ptr<Manifold> manifold_; std::unique_ptr<double[]> scratch_; }; } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_GRADIENT_PROBLEM_H_ diff --git a/extern/ceres/include/ceres/gradient_problem_solver.h b/extern/ceres/include/ceres/gradient_problem_solver.h index 9fab62e6d94..b6290c80c28 100644 --- a/extern/ceres/include/ceres/gradient_problem_solver.h +++ b/extern/ceres/include/ceres/gradient_problem_solver.h @@ -36,6 +36,7 @@ #include <vector> #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" #include "ceres/iteration_callback.h" #include "ceres/types.h" @@ -305,8 +306,12 @@ class CERES_EXPORT GradientProblemSolver { int num_parameters = -1; // Dimension of the tangent space of the problem. + CERES_DEPRECATED_WITH_MSG("Use num_tangent_parameters.") int num_local_parameters = -1; + // Dimension of the tangent space of the problem. + int num_tangent_parameters = -1; + // Type of line search direction used. LineSearchDirectionType line_search_direction_type = LBFGS; diff --git a/extern/ceres/include/ceres/internal/array_selector.h b/extern/ceres/include/ceres/internal/array_selector.h index 841797f4c69..b4db012f00b 100644 --- a/extern/ceres/include/ceres/internal/array_selector.h +++ b/extern/ceres/include/ceres/internal/array_selector.h @@ -73,20 +73,22 @@ struct ArraySelector<T, true, fits_on_stack> : ceres::internal::FixedArray<T, max_num_elements_on_stack> { - ArraySelector(int s) + explicit ArraySelector(int s) : ceres::internal::FixedArray<T, max_num_elements_on_stack>(s) {} }; template <typename T, int num_elements, int max_num_elements_on_stack> struct ArraySelector<T, num_elements, max_num_elements_on_stack, false, true> : std::array<T, num_elements> { - ArraySelector(int s) { CHECK_EQ(s, num_elements); } + explicit ArraySelector(int s) { CHECK_EQ(s, num_elements); } }; template <typename T, int num_elements, int max_num_elements_on_stack> struct ArraySelector<T, num_elements, max_num_elements_on_stack, false, false> : std::vector<T> { - ArraySelector(int s) : std::vector<T>(s) { CHECK_EQ(s, num_elements); } + explicit ArraySelector(int s) : std::vector<T>(s) { + CHECK_EQ(s, num_elements); + } }; } // namespace internal diff --git a/extern/ceres/include/ceres/internal/autodiff.h b/extern/ceres/include/ceres/internal/autodiff.h index 9d7de758508..c796618cd2d 100644 --- a/extern/ceres/include/ceres/internal/autodiff.h +++ b/extern/ceres/include/ceres/internal/autodiff.h @@ -132,17 +132,16 @@ // respectively. This is how autodiff works for functors taking multiple vector // valued arguments (up to 6). // -// Jacobian NULL pointers -// ---------------------- -// In general, the functions below will accept NULL pointers for all or some of -// the Jacobian parameters, meaning that those Jacobians will not be computed. +// Jacobian null pointers (nullptr) +// -------------------------------- +// In general, the functions below will accept nullptr for all or some of the +// Jacobian parameters, meaning that those Jacobians will not be computed. #ifndef CERES_PUBLIC_INTERNAL_AUTODIFF_H_ #define CERES_PUBLIC_INTERNAL_AUTODIFF_H_ -#include <stddef.h> - #include <array> +#include <cstddef> #include <utility> #include "ceres/internal/array_selector.h" @@ -198,7 +197,7 @@ struct Make1stOrderPerturbation { template <int N, int Offset, typename T, typename JetT> struct Make1stOrderPerturbation<N, N, Offset, T, JetT> { public: - static void Apply(const T* src, JetT* dst) {} + static void Apply(const T* /* NOT USED */, JetT* /* NOT USED */) {} }; // Calls Make1stOrderPerturbation for every parameter block. @@ -311,7 +310,7 @@ inline bool AutoDifferentiate(const Functor& functor, int dynamic_num_outputs, T* function_value, T** jacobians) { - typedef Jet<T, ParameterDims::kNumParameters> JetT; + using JetT = Jet<T, ParameterDims::kNumParameters>; using Parameters = typename ParameterDims::Parameters; if (kNumResiduals != DYNAMIC) { diff --git a/extern/ceres/include/ceres/internal/eigen.h b/extern/ceres/include/ceres/internal/eigen.h index b6d0b7f610c..111cc7a07bb 100644 --- a/extern/ceres/include/ceres/internal/eigen.h +++ b/extern/ceres/include/ceres/internal/eigen.h @@ -35,39 +35,39 @@ namespace ceres { -typedef Eigen::Matrix<double, Eigen::Dynamic, 1> Vector; -typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> - Matrix; -typedef Eigen::Map<Vector> VectorRef; -typedef Eigen::Map<Matrix> MatrixRef; -typedef Eigen::Map<const Vector> ConstVectorRef; -typedef Eigen::Map<const Matrix> ConstMatrixRef; +using Vector = Eigen::Matrix<double, Eigen::Dynamic, 1>; +using Matrix = + Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; +using VectorRef = Eigen::Map<Vector>; +using MatrixRef = Eigen::Map<Matrix>; +using ConstVectorRef = Eigen::Map<const Vector>; +using ConstMatrixRef = Eigen::Map<const Matrix>; // Column major matrices for DenseSparseMatrix/DenseQRSolver -typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> - ColMajorMatrix; +using ColMajorMatrix = + Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>; -typedef Eigen::Map<ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>> - ColMajorMatrixRef; +using ColMajorMatrixRef = + Eigen::Map<ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>>; -typedef Eigen::Map<const ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>> - ConstColMajorMatrixRef; +using ConstColMajorMatrixRef = + Eigen::Map<const ColMajorMatrix, 0, Eigen::Stride<Eigen::Dynamic, 1>>; // C++ does not support templated typdefs, thus the need for this // struct so that we can support statically sized Matrix and Maps. template <int num_rows = Eigen::Dynamic, int num_cols = Eigen::Dynamic> struct EigenTypes { - typedef Eigen::Matrix<double, - num_rows, - num_cols, - num_cols == 1 ? Eigen::ColMajor : Eigen::RowMajor> - Matrix; + using Matrix = + Eigen::Matrix<double, + num_rows, + num_cols, + num_cols == 1 ? Eigen::ColMajor : Eigen::RowMajor>; - typedef Eigen::Map<Matrix> MatrixRef; - typedef Eigen::Map<const Matrix> ConstMatrixRef; - typedef Eigen::Matrix<double, num_rows, 1> Vector; - typedef Eigen::Map<Eigen::Matrix<double, num_rows, 1>> VectorRef; - typedef Eigen::Map<const Eigen::Matrix<double, num_rows, 1>> ConstVectorRef; + using MatrixRef = Eigen::Map<Matrix>; + using ConstMatrixRef = Eigen::Map<const Matrix>; + using Vector = Eigen::Matrix<double, num_rows, 1>; + using VectorRef = Eigen::Map<Eigen::Matrix<double, num_rows, 1>>; + using ConstVectorRef = Eigen::Map<const Eigen::Matrix<double, num_rows, 1>>; }; } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/householder_vector.h b/extern/ceres/include/ceres/internal/householder_vector.h index 55f68e526a0..7700208be22 100644 --- a/extern/ceres/include/ceres/internal/householder_vector.h +++ b/extern/ceres/include/ceres/internal/householder_vector.h @@ -82,6 +82,14 @@ void ComputeHouseholderVector(const XVectorType& x, v->head(v->rows() - 1) /= v_pivot; } +template <typename XVectorType, typename Derived> +typename Derived::PlainObject ApplyHouseholderVector( + const XVectorType& y, + const Eigen::MatrixBase<Derived>& v, + const typename Derived::Scalar& beta) { + return (y - v * (beta * (v.transpose() * y))); +} + } // namespace internal } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h b/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h index 8c0f3bc8ac4..777c119a77f 100644 --- a/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h +++ b/extern/ceres/include/ceres/internal/integer_sequence_algorithm.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2018 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -27,6 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // // Author: jodebo_beck@gmx.de (Johannes Beck) +// sergiu.deitsch@gmail.com (Sergiu Deitsch) // // Algorithms to be used together with integer_sequence, like computing the sum // or the exclusive scan (sometimes called exclusive prefix sum) at compile @@ -37,6 +38,8 @@ #include <utility> +#include "ceres/jet_fwd.h" + namespace ceres { namespace internal { @@ -164,6 +167,124 @@ class ExclusiveScanT { template <typename Seq> using ExclusiveScan = typename ExclusiveScanT<Seq>::Type; +// Removes all elements from a integer sequence corresponding to specified +// ValueToRemove. +// +// This type should not be used directly but instead RemoveValue. +template <typename T, T ValueToRemove, typename... Sequence> +struct RemoveValueImpl; + +// Final filtered sequence +template <typename T, T ValueToRemove, T... Values> +struct RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Values...>, + std::integer_sequence<T>> { + using type = std::integer_sequence<T, Values...>; +}; + +// Found a matching value +template <typename T, T ValueToRemove, T... Head, T... Tail> +struct RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head...>, + std::integer_sequence<T, ValueToRemove, Tail...>> + : RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head...>, + std::integer_sequence<T, Tail...>> {}; + +// Move one element from the tail to the head +template <typename T, T ValueToRemove, T... Head, T MiddleValue, T... Tail> +struct RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head...>, + std::integer_sequence<T, MiddleValue, Tail...>> + : RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T, Head..., MiddleValue>, + std::integer_sequence<T, Tail...>> {}; + +// Start recursion by splitting the integer sequence into two separate ones +template <typename T, T ValueToRemove, T... Tail> +struct RemoveValueImpl<T, ValueToRemove, std::integer_sequence<T, Tail...>> + : RemoveValueImpl<T, + ValueToRemove, + std::integer_sequence<T>, + std::integer_sequence<T, Tail...>> {}; + +// RemoveValue takes an integer Sequence of arbitrary type and removes all +// elements matching ValueToRemove. +// +// In contrast to RemoveValueImpl, this implementation deduces the value type +// eliminating the need to specify it explicitly. +// +// As an example, RemoveValue<std::integer_sequence<int, 1, 2, 3>, 4>::type will +// not transform the type of the original sequence. However, +// RemoveValue<std::integer_sequence<int, 0, 0, 2>, 2>::type will generate a new +// sequence of type std::integer_sequence<int, 0, 0> by removing the value 2. +template <typename Sequence, typename Sequence::value_type ValueToRemove> +struct RemoveValue + : RemoveValueImpl<typename Sequence::value_type, ValueToRemove, Sequence> { +}; + +// Convenience template alias for RemoveValue. +template <typename Sequence, typename Sequence::value_type ValueToRemove> +using RemoveValue_t = typename RemoveValue<Sequence, ValueToRemove>::type; + +// Determines whether the values of an integer sequence are all the same. +// +// The integer sequence must contain at least one value. The predicate is +// undefined for empty sequences. The evaluation result of the predicate for a +// sequence containing only one value is defined to be true. +template <typename... Sequence> +struct AreAllEqual; + +// The predicate result for a sequence containing one element is defined to be +// true. +template <typename T, T Value> +struct AreAllEqual<std::integer_sequence<T, Value>> : std::true_type {}; + +// Recursion end. +template <typename T, T Value1, T Value2> +struct AreAllEqual<std::integer_sequence<T, Value1, Value2>> + : std::integral_constant<bool, Value1 == Value2> {}; + +// Recursion for sequences containing at least two elements. +template <typename T, T Value1, T Value2, T... Values> +// clang-format off +struct AreAllEqual<std::integer_sequence<T, Value1, Value2, Values...> > + : std::integral_constant +< + bool, + AreAllEqual<std::integer_sequence<T, Value1, Value2> >::value && + AreAllEqual<std::integer_sequence<T, Value2, Values...> >::value +> +// clang-format on +{}; + +// Convenience variable template for AreAllEqual. +template <class Sequence> +constexpr bool AreAllEqual_v = AreAllEqual<Sequence>::value; + +// Predicate determining whether an integer sequence is either empty or all +// values are equal. +template <typename Sequence> +struct IsEmptyOrAreAllEqual; + +// Empty case. +template <typename T> +struct IsEmptyOrAreAllEqual<std::integer_sequence<T>> : std::true_type {}; + +// General case for sequences containing at least one value. +template <typename T, T HeadValue, T... Values> +struct IsEmptyOrAreAllEqual<std::integer_sequence<T, HeadValue, Values...>> + : AreAllEqual<std::integer_sequence<T, HeadValue, Values...>> {}; + +// Convenience variable template for IsEmptyOrAreAllEqual. +template <class Sequence> +constexpr bool IsEmptyOrAreAllEqual_v = IsEmptyOrAreAllEqual<Sequence>::value; + } // namespace internal } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/jet_traits.h b/extern/ceres/include/ceres/internal/jet_traits.h new file mode 100644 index 00000000000..2a38c05b7da --- /dev/null +++ b/extern/ceres/include/ceres/internal/jet_traits.h @@ -0,0 +1,223 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sergiu.deitsch@gmail.com (Sergiu Deitsch) +// + +#ifndef CERES_PUBLIC_INTERNAL_JET_TRAITS_H_ +#define CERES_PUBLIC_INTERNAL_JET_TRAITS_H_ + +#include <tuple> +#include <type_traits> +#include <utility> + +#include "ceres/internal/integer_sequence_algorithm.h" +#include "ceres/jet_fwd.h" + +namespace ceres { +namespace internal { + +// Predicate that determines whether T is a Jet. +template <typename T, typename E = void> +struct IsJet : std::false_type {}; + +template <typename T, int N> +struct IsJet<Jet<T, N>> : std::true_type {}; + +// Convenience variable template for IsJet. +template <typename T> +constexpr bool IsJet_v = IsJet<T>::value; + +// Predicate that determines whether any of the Types is a Jet. +template <typename... Types> +struct AreAnyJet : std::false_type {}; + +template <typename T, typename... Types> +struct AreAnyJet<T, Types...> : AreAnyJet<Types...> {}; + +template <typename T, int N, typename... Types> +struct AreAnyJet<Jet<T, N>, Types...> : std::true_type {}; + +// Convenience variable template for AreAnyJet. +template <typename... Types> +constexpr bool AreAnyJet_v = AreAnyJet<Types...>::value; + +// Extracts the underlying floating-point from a type T. +template <typename T, typename E = void> +struct UnderlyingScalar { + using type = T; +}; + +template <typename T, int N> +struct UnderlyingScalar<Jet<T, N>> : UnderlyingScalar<T> {}; + +// Convenience template alias for UnderlyingScalar type trait. +template <typename T> +using UnderlyingScalar_t = typename UnderlyingScalar<T>::type; + +// Predicate determining whether all Types in the pack are the same. +// +// Specifically, the predicate applies std::is_same recursively to pairs of +// Types in the pack. +// +// The predicate is defined only for template packs containing at least two +// types. +template <typename T1, typename T2, typename... Types> +// clang-format off +struct AreAllSame : std::integral_constant +< + bool, + AreAllSame<T1, T2>::value && + AreAllSame<T2, Types...>::value +> +// clang-format on +{}; + +// AreAllSame pairwise test. +template <typename T1, typename T2> +struct AreAllSame<T1, T2> : std::is_same<T1, T2> {}; + +// Convenience variable template for AreAllSame. +template <typename... Types> +constexpr bool AreAllSame_v = AreAllSame<Types...>::value; + +// Determines the rank of a type. This allows to ensure that types passed as +// arguments are compatible to each other. The rank of Jet is determined by the +// dimensions of the dual part. The rank of a scalar is always 0. +// Non-specialized types default to a rank of -1. +template <typename T, typename E = void> +struct Rank : std::integral_constant<int, -1> {}; + +// The rank of a scalar is 0. +template <typename T> +struct Rank<T, std::enable_if_t<std::is_scalar<T>::value>> + : std::integral_constant<int, 0> {}; + +// The rank of a Jet is given by its dimensionality. +template <typename T, int N> +struct Rank<Jet<T, N>> : std::integral_constant<int, N> {}; + +// Convenience variable template for Rank. +template <typename T> +constexpr int Rank_v = Rank<T>::value; + +// Constructs an integer sequence of ranks for each of the Types in the pack. +template <typename... Types> +using Ranks_t = std::integer_sequence<int, Rank_v<Types>...>; + +// Returns the scalar part of a type. This overload acts as an identity. +template <typename T> +constexpr decltype(auto) AsScalar(T&& value) noexcept { + return std::forward<T>(value); +} + +// Recursively unwraps the scalar part of a Jet until a non-Jet scalar type is +// encountered. +template <typename T, int N> +constexpr decltype(auto) AsScalar(const Jet<T, N>& value) noexcept( + noexcept(AsScalar(value.a))) { + return AsScalar(value.a); +} + +} // namespace internal + +// Type trait ensuring at least one of the types is a Jet, +// the underlying scalar types are the same and Jet dimensions match. +// +// The type trait can be further specialized if necessary. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +// clang-format off +struct CompatibleJetOperands : std::integral_constant +< + bool, + // At least one of the types is a Jet + internal::AreAnyJet_v<Types...> && + // The underlying floating-point types are exactly the same + internal::AreAllSame_v<internal::UnderlyingScalar_t<Types>...> && + // Non-zero ranks of types are equal + internal::IsEmptyOrAreAllEqual_v<internal::RemoveValue_t<internal::Ranks_t<Types...>, 0>> +> +// clang-format on +{}; + +// Single Jet operand is always compatible. +template <typename T, int N> +struct CompatibleJetOperands<Jet<T, N>> : std::true_type {}; + +// Single non-Jet operand is always incompatible. +template <typename T> +struct CompatibleJetOperands<T> : std::false_type {}; + +// Empty operands are always incompatible. +template <> +struct CompatibleJetOperands<> : std::false_type {}; + +// Convenience variable template ensuring at least one of the types is a Jet, +// the underlying scalar types are the same and Jet dimensions match. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +constexpr bool CompatibleJetOperands_v = CompatibleJetOperands<Types...>::value; + +// Type trait ensuring at least one of the types is a Jet, +// the underlying scalar types are compatible among each other and Jet +// dimensions match. +// +// The type trait can be further specialized if necessary. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +// clang-format off +struct PromotableJetOperands : std::integral_constant +< + bool, + // Types can be compatible among each other + internal::AreAnyJet_v<Types...> && + // Non-zero ranks of types are equal + internal::IsEmptyOrAreAllEqual_v<internal::RemoveValue_t<internal::Ranks_t<Types...>, 0>> +> +// clang-format on +{}; + +// Convenience variable template ensuring at least one of the types is a Jet, +// the underlying scalar types are compatible among each other and Jet +// dimensions match. +// +// This trait is a candidate for a concept definition once C++20 features can +// be used. +template <typename... Types> +constexpr bool PromotableJetOperands_v = PromotableJetOperands<Types...>::value; + +} // namespace ceres + +#endif // CERES_PUBLIC_INTERNAL_JET_TRAITS_H_ diff --git a/extern/ceres/include/ceres/internal/numeric_diff.h b/extern/ceres/include/ceres/internal/numeric_diff.h index ff7a2c345e4..351845c05fb 100644 --- a/extern/ceres/include/ceres/internal/numeric_diff.h +++ b/extern/ceres/include/ceres/internal/numeric_diff.h @@ -86,18 +86,18 @@ struct NumericDiff { (kParameterBlockSize != ceres::DYNAMIC ? kParameterBlockSize : parameter_block_size); - typedef Matrix<double, kNumResiduals, 1> ResidualVector; - typedef Matrix<double, kParameterBlockSize, 1> ParameterVector; + using ResidualVector = Matrix<double, kNumResiduals, 1>; + using ParameterVector = Matrix<double, kParameterBlockSize, 1>; // The convoluted reasoning for choosing the Row/Column major // ordering of the matrix is an artifact of the restrictions in // Eigen that prevent it from creating RowMajor matrices with a // single column. In these cases, we ask for a ColMajor matrix. - typedef Matrix<double, - kNumResiduals, - kParameterBlockSize, - (kParameterBlockSize == 1) ? ColMajor : RowMajor> - JacobianMatrix; + using JacobianMatrix = + Matrix<double, + kNumResiduals, + kParameterBlockSize, + (kParameterBlockSize == 1) ? ColMajor : RowMajor>; Map<JacobianMatrix> parameter_jacobian( jacobian, num_residuals_internal, parameter_block_size_internal); @@ -121,7 +121,7 @@ struct NumericDiff { // thus ridders_relative_initial_step_size is used. if (kMethod == RIDDERS) { min_step_size = - std::max(min_step_size, options.ridders_relative_initial_step_size); + (std::max)(min_step_size, options.ridders_relative_initial_step_size); } // For each parameter in the parameter block, use finite differences to @@ -132,7 +132,7 @@ struct NumericDiff { num_residuals_internal); for (int j = 0; j < parameter_block_size_internal; ++j) { - const double delta = std::max(min_step_size, step_size(j)); + const double delta = (std::max)(min_step_size, step_size(j)); if (kMethod == RIDDERS) { if (!EvaluateRiddersJacobianColumn(functor, @@ -184,8 +184,8 @@ struct NumericDiff { using Eigen::Map; using Eigen::Matrix; - typedef Matrix<double, kNumResiduals, 1> ResidualVector; - typedef Matrix<double, kParameterBlockSize, 1> ParameterVector; + using ResidualVector = Matrix<double, kNumResiduals, 1>; + using ParameterVector = Matrix<double, kParameterBlockSize, 1>; Map<const ParameterVector> x(x_ptr, parameter_block_size); Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size); @@ -260,10 +260,10 @@ struct NumericDiff { using Eigen::Map; using Eigen::Matrix; - typedef Matrix<double, kNumResiduals, 1> ResidualVector; - typedef Matrix<double, kNumResiduals, Eigen::Dynamic> - ResidualCandidateMatrix; - typedef Matrix<double, kParameterBlockSize, 1> ParameterVector; + using ResidualVector = Matrix<double, kNumResiduals, 1>; + using ResidualCandidateMatrix = + Matrix<double, kNumResiduals, Eigen::Dynamic>; + using ParameterVector = Matrix<double, kParameterBlockSize, 1>; Map<const ParameterVector> x(x_ptr, parameter_block_size); Map<ParameterVector> x_plus_delta(x_plus_delta_ptr, parameter_block_size); @@ -296,7 +296,7 @@ struct NumericDiff { // norm_error is supposed to decrease as the finite difference tableau // generation progresses, serving both as an estimate for differentiation // error and as a measure of differentiation numerical stability. - double norm_error = std::numeric_limits<double>::max(); + double norm_error = (std::numeric_limits<double>::max)(); // Loop over decreasing step sizes until: // 1. Error is smaller than a given value (ridders_epsilon), @@ -342,7 +342,7 @@ struct NumericDiff { options.ridders_step_shrink_factor; // Compute the difference between the previous value and the current. - double candidate_error = std::max( + double candidate_error = (std::max)( (current_candidates->col(k) - current_candidates->col(k - 1)) .norm(), (current_candidates->col(k) - previous_candidates->col(k - 1)) diff --git a/extern/ceres/include/ceres/internal/port.h b/extern/ceres/include/ceres/internal/port.h index 040a1efba02..4275b0e15c3 100644 --- a/extern/ceres/include/ceres/internal/port.h +++ b/extern/ceres/include/ceres/internal/port.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,80 +31,58 @@ #ifndef CERES_PUBLIC_INTERNAL_PORT_H_ #define CERES_PUBLIC_INTERNAL_PORT_H_ -// This file needs to compile as c code. -#include "ceres/internal/config.h" - -#if defined(CERES_USE_OPENMP) -#if defined(CERES_USE_CXX_THREADS) || defined(CERES_NO_THREADS) -#error CERES_USE_OPENMP is mutually exclusive to CERES_USE_CXX_THREADS and CERES_NO_THREADS -#endif -#elif defined(CERES_USE_CXX_THREADS) -#if defined(CERES_USE_OPENMP) || defined(CERES_NO_THREADS) -#error CERES_USE_CXX_THREADS is mutually exclusive to CERES_USE_OPENMP, CERES_USE_CXX_THREADS and CERES_NO_THREADS -#endif -#elif defined(CERES_NO_THREADS) -#if defined(CERES_USE_OPENMP) || defined(CERES_USE_CXX_THREADS) -#error CERES_NO_THREADS is mutually exclusive to CERES_USE_OPENMP and CERES_USE_CXX_THREADS -#endif -#else -# error One of CERES_USE_OPENMP, CERES_USE_CXX_THREADS or CERES_NO_THREADS must be defined. -#endif - -// CERES_NO_SPARSE should be automatically defined by config.h if Ceres was -// compiled without any sparse back-end. Verify that it has not subsequently -// been inconsistently redefined. -#if defined(CERES_NO_SPARSE) -#if !defined(CERES_NO_SUITESPARSE) -#error CERES_NO_SPARSE requires CERES_NO_SUITESPARSE. -#endif -#if !defined(CERES_NO_CXSPARSE) -#error CERES_NO_SPARSE requires CERES_NO_CXSPARSE -#endif -#if !defined(CERES_NO_ACCELERATE_SPARSE) -#error CERES_NO_SPARSE requires CERES_NO_ACCELERATE_SPARSE -#endif -#if defined(CERES_USE_EIGEN_SPARSE) -#error CERES_NO_SPARSE requires !CERES_USE_EIGEN_SPARSE -#endif -#endif - -// A macro to signal which functions and classes are exported when -// building a shared library. +// A macro to mark a function/variable/class as deprecated. +// We use compiler specific attributes rather than the c++ +// attribute because they do not mix well with each other. #if defined(_MSC_VER) -#define CERES_API_SHARED_IMPORT __declspec(dllimport) -#define CERES_API_SHARED_EXPORT __declspec(dllexport) +#define CERES_DEPRECATED_WITH_MSG(message) __declspec(deprecated(message)) #elif defined(__GNUC__) -#define CERES_API_SHARED_IMPORT __attribute__((visibility("default"))) -#define CERES_API_SHARED_EXPORT __attribute__((visibility("default"))) +#define CERES_DEPRECATED_WITH_MSG(message) __attribute__((deprecated(message))) #else -#define CERES_API_SHARED_IMPORT -#define CERES_API_SHARED_EXPORT +// In the worst case fall back to c++ attribute. +#define CERES_DEPRECATED_WITH_MSG(message) [[deprecated(message)]] #endif -// CERES_BUILDING_SHARED_LIBRARY is only defined locally when Ceres itself is -// compiled as a shared library, it is never exported to users. In order that -// we do not have to configure config.h separately when building Ceres as either -// a static or dynamic library, we define both CERES_USING_SHARED_LIBRARY and -// CERES_BUILDING_SHARED_LIBRARY when building as a shared library. -#if defined(CERES_USING_SHARED_LIBRARY) -#if defined(CERES_BUILDING_SHARED_LIBRARY) -// Compiling Ceres itself as a shared library. -#define CERES_EXPORT CERES_API_SHARED_EXPORT -#else -// Using Ceres as a shared library. -#define CERES_EXPORT CERES_API_SHARED_IMPORT -#endif -#else -// Ceres was compiled as a static library, export everything. -#define CERES_EXPORT +#ifndef CERES_GET_FLAG +#define CERES_GET_FLAG(X) X #endif -// Unit tests reach in and test internal functionality so we need a way to make -// those symbols visible -#ifdef CERES_EXPORT_INTERNAL_SYMBOLS -#define CERES_EXPORT_INTERNAL CERES_EXPORT -#else -#define CERES_EXPORT_INTERNAL -#endif +// Indicates whether C++17 is currently active +#ifndef CERES_HAS_CPP17 +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CERES_HAS_CPP17 +#endif // __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= + // 201703L) +#endif // !defined(CERES_HAS_CPP17) + +// Indicates whether C++20 is currently active +#ifndef CERES_HAS_CPP20 +#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define CERES_HAS_CPP20 +#endif // __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= + // 202002L) +#endif // !defined(CERES_HAS_CPP20) + +// Prevents symbols from being substituted by the corresponding macro definition +// under the same name. For instance, min and max are defined as macros on +// Windows (unless NOMINMAX is defined) which causes compilation errors when +// defining or referencing symbols under the same name. +// +// To be robust in all cases particularly when NOMINMAX cannot be used, use this +// macro to annotate min/max declarations/definitions. Examples: +// +// int max CERES_PREVENT_MACRO_SUBSTITUTION(); +// min CERES_PREVENT_MACRO_SUBSTITUTION(a, b); +// max CERES_PREVENT_MACRO_SUBSTITUTION(a, b); +// +// NOTE: In case the symbols for which the substitution must be prevented are +// used within another macro, the substitution must be inhibited using parens as +// +// (std::numerical_limits<double>::max)() +// +// since the helper macro will not work here. Do not use this technique in +// general case, because it will prevent argument-dependent lookup (ADL). +// +#define CERES_PREVENT_MACRO_SUBSTITUTION // Yes, it's empty #endif // CERES_PUBLIC_INTERNAL_PORT_H_ diff --git a/extern/ceres/include/ceres/internal/sphere_manifold_functions.h b/extern/ceres/include/ceres/internal/sphere_manifold_functions.h new file mode 100644 index 00000000000..5be3321a579 --- /dev/null +++ b/extern/ceres/include/ceres/internal/sphere_manifold_functions.h @@ -0,0 +1,162 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Mike Vitus) +// jodebo_beck@gmx.de (Johannes Beck) + +#ifndef CERES_PUBLIC_INTERNAL_SPHERE_MANIFOLD_HELPERS_H_ +#define CERES_PUBLIC_INTERNAL_SPHERE_MANIFOLD_HELPERS_H_ + +#include "ceres/internal/householder_vector.h" + +// This module contains functions to compute the SphereManifold plus and minus +// operator and their Jacobians. +// +// As the parameters to these functions are shared between them, they are +// described here: The following variable names are used: +// Plus(x, delta) = x + delta = x_plus_delta, +// Minus(y, x) = y - x = y_minus_x. +// +// The remaining ones are v and beta which describe the Householder +// transformation of x, and norm_delta which is the norm of delta. +// +// The types of x, y, x_plus_delta and y_minus_x need to be equivalent to +// Eigen::Matrix<double, AmbientSpaceDimension, 1> and the type of delta needs +// to be equivalent to Eigen::Matrix<double, TangentSpaceDimension, 1>. +// +// The type of Jacobian plus needs to be equivalent to Eigen::Matrix<double, +// AmbientSpaceDimension, TangentSpaceDimension, Eigen::RowMajor> and for +// Jacobian minus Eigen::Matrix<double, TangentSpaceDimension, +// AmbientSpaceDimension, Eigen::RowMajor>. +// +// For all vector / matrix inputs and outputs, template parameters are +// used in order to allow also Eigen::Ref and Eigen block expressions to +// be passed to the function. + +namespace ceres { +namespace internal { + +template <typename VT, typename XT, typename DeltaT, typename XPlusDeltaT> +inline void ComputeSphereManifoldPlus(const VT& v, + double beta, + const XT& x, + const DeltaT& delta, + double norm_delta, + XPlusDeltaT* x_plus_delta) { + constexpr int AmbientDim = VT::RowsAtCompileTime; + + // Map the delta from the minimum representation to the over parameterized + // homogeneous vector. See B.2 p.25 equation (106) - (107) for more details. + const double norm_delta_div_2 = 0.5 * norm_delta; + const double sin_delta_by_delta = + std::sin(norm_delta_div_2) / norm_delta_div_2; + + Eigen::Matrix<double, AmbientDim, 1> y(v.size()); + y << 0.5 * sin_delta_by_delta * delta, std::cos(norm_delta_div_2); + + // Apply the delta update to remain on the sphere. + *x_plus_delta = x.norm() * ApplyHouseholderVector(y, v, beta); +} + +template <typename VT, typename JacobianT> +inline void ComputeSphereManifoldPlusJacobian(const VT& x, + JacobianT* jacobian) { + constexpr int AmbientSpaceDim = VT::RowsAtCompileTime; + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>; + const int ambient_size = x.size(); + const int tangent_size = x.size() - 1; + + AmbientVector v(ambient_size); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + ComputeHouseholderVector<VT, double, AmbientSpaceDim>(x, &v, &beta); + + // The Jacobian is equal to J = 0.5 * H.leftCols(size_ - 1) where H is the + // Householder matrix (H = I - beta * v * v'). + for (int i = 0; i < tangent_size; ++i) { + (*jacobian).col(i) = -0.5 * beta * v(i) * v; + (*jacobian)(i, i) += 0.5; + } + (*jacobian) *= x.norm(); +} + +template <typename VT, typename XT, typename YT, typename YMinusXT> +inline void ComputeSphereManifoldMinus( + const VT& v, double beta, const XT& x, const YT& y, YMinusXT* y_minus_x) { + constexpr int AmbientSpaceDim = VT::RowsAtCompileTime; + constexpr int TangentSpaceDim = + AmbientSpaceDim == Eigen::Dynamic ? Eigen::Dynamic : AmbientSpaceDim - 1; + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>; + + const int tanget_size = v.size() - 1; + + const AmbientVector hy = ApplyHouseholderVector(y, v, beta) / x.norm(); + + // Calculate y - x. See B.2 p.25 equation (108). + double y_last = hy[tanget_size]; + double hy_norm = hy.template head<TangentSpaceDim>(tanget_size).norm(); + if (hy_norm == 0.0) { + y_minus_x->setZero(); + } else { + *y_minus_x = 2.0 * std::atan2(hy_norm, y_last) / hy_norm * + hy.template head<TangentSpaceDim>(tanget_size); + } +} + +template <typename VT, typename JacobianT> +inline void ComputeSphereManifoldMinusJacobian(const VT& x, + JacobianT* jacobian) { + constexpr int AmbientSpaceDim = VT::RowsAtCompileTime; + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>; + const int ambient_size = x.size(); + const int tangent_size = x.size() - 1; + + AmbientVector v(ambient_size); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + ComputeHouseholderVector<VT, double, AmbientSpaceDim>(x, &v, &beta); + + // The Jacobian is equal to J = 2.0 * H.leftCols(size_ - 1) where H is the + // Householder matrix (H = I - beta * v * v'). + for (int i = 0; i < tangent_size; ++i) { + (*jacobian).row(i) = -2.0 * beta * v(i) * v; + (*jacobian)(i, i) += 2.0; + } + (*jacobian) /= x.norm(); +} + +} // namespace internal +} // namespace ceres + +#endif diff --git a/extern/ceres/include/ceres/internal/variadic_evaluate.h b/extern/ceres/include/ceres/internal/variadic_evaluate.h index 47ff6b18fa0..b8408237cc3 100644 --- a/extern/ceres/include/ceres/internal/variadic_evaluate.h +++ b/extern/ceres/include/ceres/internal/variadic_evaluate.h @@ -33,8 +33,7 @@ #ifndef CERES_PUBLIC_INTERNAL_VARIADIC_EVALUATE_H_ #define CERES_PUBLIC_INTERNAL_VARIADIC_EVALUATE_H_ -#include <stddef.h> - +#include <cstddef> #include <type_traits> #include <utility> diff --git a/extern/ceres/include/ceres/iteration_callback.h b/extern/ceres/include/ceres/iteration_callback.h index 4507fdf748c..3d7e8e94f30 100644 --- a/extern/ceres/include/ceres/iteration_callback.h +++ b/extern/ceres/include/ceres/iteration_callback.h @@ -36,6 +36,7 @@ #define CERES_PUBLIC_ITERATION_CALLBACK_H_ #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -164,8 +165,6 @@ struct CERES_EXPORT IterationSummary { // explicit LoggingCallback(bool log_to_stdout) // : log_to_stdout_(log_to_stdout) {} // -// ~LoggingCallback() {} -// // CallbackReturnType operator()(const IterationSummary& summary) { // const char* kReportRowFormat = // "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e " @@ -194,7 +193,7 @@ struct CERES_EXPORT IterationSummary { // class CERES_EXPORT IterationCallback { public: - virtual ~IterationCallback() {} + virtual ~IterationCallback(); virtual CallbackReturnType operator()(const IterationSummary& summary) = 0; }; diff --git a/extern/ceres/include/ceres/jet.h b/extern/ceres/include/ceres/jet.h index da49f32019f..fba1e2ab6e0 100644 --- a/extern/ceres/include/ceres/jet.h +++ b/extern/ceres/include/ceres/jet.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -158,20 +158,59 @@ #define CERES_PUBLIC_JET_H_ #include <cmath> +#include <complex> #include <iosfwd> #include <iostream> // NOLINT #include <limits> +#include <numeric> #include <string> +#include <type_traits> #include "Eigen/Core" +#include "ceres/internal/jet_traits.h" #include "ceres/internal/port.h" +#include "ceres/jet_fwd.h" + +// Here we provide partial specializations of std::common_type for the Jet class +// to allow determining a Jet type with a common underlying arithmetic type. +// Such an arithmetic type can be either a scalar or an another Jet. An example +// for a common type, say, between a float and a Jet<double, N> is a Jet<double, +// N> (i.e., std::common_type_t<float, ceres::Jet<double, N>> and +// ceres::Jet<double, N> refer to the same type.) +// +// The partial specialization are also used for determining compatible types by +// means of SFINAE and thus allow such types to be expressed as operands of +// logical comparison operators. Missing (partial) specialization of +// std::common_type for a particular (custom) type will therefore disable the +// use of comparison operators defined by Ceres. +// +// Since these partial specializations are used as SFINAE constraints, they +// enable standard promotion rules between various scalar types and consequently +// their use in comparison against a Jet without providing implicit +// conversions from a scalar, such as an int, to a Jet (see the implementation +// of logical comparison operators below). + +template <typename T, int N, typename U> +struct std::common_type<T, ceres::Jet<U, N>> { + using type = ceres::Jet<common_type_t<T, U>, N>; +}; + +template <typename T, int N, typename U> +struct std::common_type<ceres::Jet<T, N>, U> { + using type = ceres::Jet<common_type_t<T, U>, N>; +}; + +template <typename T, int N, typename U> +struct std::common_type<ceres::Jet<T, N>, ceres::Jet<U, N>> { + using type = ceres::Jet<common_type_t<T, U>, N>; +}; namespace ceres { template <typename T, int N> struct Jet { enum { DIMENSION = N }; - typedef T Scalar; + using Scalar = T; // Default-construct "a" because otherwise this can lead to false errors about // uninitialized uses when other classes relying on default constructed T @@ -352,19 +391,21 @@ inline Jet<T, N> operator/(const Jet<T, N>& f, T s) { return Jet<T, N>(f.a * s_inverse, f.v * s_inverse); } -// Binary comparison operators for both scalars and jets. -#define CERES_DEFINE_JET_COMPARISON_OPERATOR(op) \ - template <typename T, int N> \ - inline bool operator op(const Jet<T, N>& f, const Jet<T, N>& g) { \ - return f.a op g.a; \ - } \ - template <typename T, int N> \ - inline bool operator op(const T& s, const Jet<T, N>& g) { \ - return s op g.a; \ - } \ - template <typename T, int N> \ - inline bool operator op(const Jet<T, N>& f, const T& s) { \ - return f.a op s; \ +// Binary comparison operators for both scalars and jets. At least one of the +// operands must be a Jet. Promotable scalars (e.g., int, float, double etc.) +// can appear on either side of the operator. std::common_type_t is used as an +// SFINAE constraint to selectively enable compatible operand types. This allows +// comparison, for instance, against int literals without implicit conversion. +// In case the Jet arithmetic type is a Jet itself, a recursive expansion of Jet +// value is performed. +#define CERES_DEFINE_JET_COMPARISON_OPERATOR(op) \ + template <typename Lhs, \ + typename Rhs, \ + std::enable_if_t<PromotableJetOperands_v<Lhs, Rhs>>* = nullptr> \ + constexpr bool operator op(const Lhs& f, const Rhs& g) noexcept( \ + noexcept(internal::AsScalar(f) op internal::AsScalar(g))) { \ + using internal::AsScalar; \ + return AsScalar(f) op AsScalar(g); \ } CERES_DEFINE_JET_COMPARISON_OPERATOR(<) // NOLINT CERES_DEFINE_JET_COMPARISON_OPERATOR(<=) // NOLINT @@ -386,43 +427,138 @@ using std::atan; using std::atan2; using std::cbrt; using std::ceil; +using std::copysign; using std::cos; using std::cosh; using std::erf; using std::erfc; using std::exp; using std::exp2; +using std::expm1; +using std::fdim; using std::floor; +using std::fma; using std::fmax; using std::fmin; +using std::fpclassify; using std::hypot; using std::isfinite; using std::isinf; using std::isnan; using std::isnormal; using std::log; +using std::log10; +using std::log1p; using std::log2; +using std::norm; using std::pow; +using std::signbit; using std::sin; using std::sinh; using std::sqrt; using std::tan; using std::tanh; +// MSVC (up to 1930) defines quiet comparison functions as template functions +// which causes compilation errors due to ambiguity in the template parameter +// type resolution for using declarations in the ceres namespace. Workaround the +// issue by defining specific overload and bypass MSVC standard library +// definitions. +#if defined(_MSC_VER) +inline bool isgreater(double lhs, + double rhs) noexcept(noexcept(std::isgreater(lhs, rhs))) { + return std::isgreater(lhs, rhs); +} +inline bool isless(double lhs, + double rhs) noexcept(noexcept(std::isless(lhs, rhs))) { + return std::isless(lhs, rhs); +} +inline bool islessequal(double lhs, + double rhs) noexcept(noexcept(std::islessequal(lhs, + rhs))) { + return std::islessequal(lhs, rhs); +} +inline bool isgreaterequal(double lhs, double rhs) noexcept( + noexcept(std::isgreaterequal(lhs, rhs))) { + return std::isgreaterequal(lhs, rhs); +} +inline bool islessgreater(double lhs, double rhs) noexcept( + noexcept(std::islessgreater(lhs, rhs))) { + return std::islessgreater(lhs, rhs); +} +inline bool isunordered(double lhs, + double rhs) noexcept(noexcept(std::isunordered(lhs, + rhs))) { + return std::isunordered(lhs, rhs); +} +#else +using std::isgreater; +using std::isgreaterequal; +using std::isless; +using std::islessequal; +using std::islessgreater; +using std::isunordered; +#endif + +#ifdef CERES_HAS_CPP20 +using std::lerp; +using std::midpoint; +#endif // defined(CERES_HAS_CPP20) + // Legacy names from pre-C++11 days. // clang-format off +CERES_DEPRECATED_WITH_MSG("ceres::IsFinite will be removed in a future Ceres Solver release. Please use ceres::isfinite.") inline bool IsFinite(double x) { return std::isfinite(x); } +CERES_DEPRECATED_WITH_MSG("ceres::IsInfinite will be removed in a future Ceres Solver release. Please use ceres::isinf.") inline bool IsInfinite(double x) { return std::isinf(x); } +CERES_DEPRECATED_WITH_MSG("ceres::IsNaN will be removed in a future Ceres Solver release. Please use ceres::isnan.") inline bool IsNaN(double x) { return std::isnan(x); } +CERES_DEPRECATED_WITH_MSG("ceres::IsNormal will be removed in a future Ceres Solver release. Please use ceres::isnormal.") inline bool IsNormal(double x) { return std::isnormal(x); } // clang-format on // In general, f(a + h) ~= f(a) + f'(a) h, via the chain rule. -// abs(x + h) ~= x + h or -(x + h) +// abs(x + h) ~= abs(x) + sgn(x)h template <typename T, int N> inline Jet<T, N> abs(const Jet<T, N>& f) { - return (f.a < T(0.0) ? -f : f); + return Jet<T, N>(abs(f.a), copysign(T(1), f.a) * f.v); +} + +// copysign(a, b) composes a float with the magnitude of a and the sign of b. +// Therefore, the function can be formally defined as +// +// copysign(a, b) = sgn(b)|a| +// +// where +// +// d/dx |x| = sgn(x) +// d/dx sgn(x) = 2δ(x) +// +// sgn(x) being the signum function. Differentiating copysign(a, b) with respect +// to a and b gives: +// +// d/da sgn(b)|a| = sgn(a) sgn(b) +// d/db sgn(b)|a| = 2|a|δ(b) +// +// with the dual representation given by +// +// copysign(a + da, b + db) ~= sgn(b)|a| + (sgn(a)sgn(b) da + 2|a|δ(b) db) +// +// where δ(b) is the Dirac delta function. +template <typename T, int N> +inline Jet<T, N> copysign(const Jet<T, N>& f, const Jet<T, N> g) { + // The Dirac delta function δ(b) is undefined at b=0 (here it's + // infinite) and 0 everywhere else. + T d = fpclassify(g) == FP_ZERO ? std::numeric_limits<T>::infinity() : T(0); + T sa = copysign(T(1), f.a); // sgn(a) + T sb = copysign(T(1), g.a); // sgn(b) + // The second part of the infinitesimal is 2|a|δ(b) which is either infinity + // or 0 unless a or any of the values of the b infinitesimal are 0. In the + // latter case, the corresponding values become NaNs (multiplying 0 by + // infinity gives NaN). We drop the constant factor 2 since it does not change + // the result (its values will still be either 0, infinity or NaN). + return Jet<T, N>(copysign(f.a, g.a), sa * sb * f.v + abs(f.a) * d * g.v); } // log(a + h) ~= log(a) + h / a @@ -432,6 +568,21 @@ inline Jet<T, N> log(const Jet<T, N>& f) { return Jet<T, N>(log(f.a), f.v * a_inverse); } +// log10(a + h) ~= log10(a) + h / (a log(10)) +template <typename T, int N> +inline Jet<T, N> log10(const Jet<T, N>& f) { + // Most compilers will expand log(10) to a constant. + const T a_inverse = T(1.0) / (f.a * log(T(10.0))); + return Jet<T, N>(log10(f.a), f.v * a_inverse); +} + +// log1p(a + h) ~= log1p(a) + h / (1 + a) +template <typename T, int N> +inline Jet<T, N> log1p(const Jet<T, N>& f) { + const T a_inverse = T(1.0) / (T(1.0) + f.a); + return Jet<T, N>(log1p(f.a), f.v * a_inverse); +} + // exp(a + h) ~= exp(a) + exp(a) h template <typename T, int N> inline Jet<T, N> exp(const Jet<T, N>& f) { @@ -439,6 +590,14 @@ inline Jet<T, N> exp(const Jet<T, N>& f) { return Jet<T, N>(tmp, tmp * f.v); } +// expm1(a + h) ~= expm1(a) + exp(a) h +template <typename T, int N> +inline Jet<T, N> expm1(const Jet<T, N>& f) { + const T tmp = expm1(f.a); + const T expa = tmp + T(1.0); // exp(a) = expm1(a) + 1 + return Jet<T, N>(tmp, expa * f.v); +} + // sqrt(a + h) ~= sqrt(a) + h / (2 sqrt(a)) template <typename T, int N> inline Jet<T, N> sqrt(const Jet<T, N>& f) { @@ -565,29 +724,101 @@ inline Jet<T, N> hypot(const Jet<T, N>& x, const Jet<T, N>& y) { return Jet<T, N>(tmp, x.a / tmp * x.v + y.a / tmp * y.v); } +#ifdef CERES_HAS_CPP17 +// Like sqrt(x^2 + y^2 + z^2), +// but acts to prevent underflow/overflow for small/large x/y/z. +// Note that the function is non-smooth at x=y=z=0, +// so the derivative is undefined there. template <typename T, int N> -inline Jet<T, N> fmax(const Jet<T, N>& x, const Jet<T, N>& y) { - return x < y ? y : x; +inline Jet<T, N> hypot(const Jet<T, N>& x, + const Jet<T, N>& y, + const Jet<T, N>& z) { + // d/da sqrt(a) = 0.5 / sqrt(a) + // d/dx x^2 + y^2 + z^2 = 2x + // So by the chain rule: + // d/dx sqrt(x^2 + y^2 + z^2) + // = 0.5 / sqrt(x^2 + y^2 + z^2) * 2x + // = x / sqrt(x^2 + y^2 + z^2) + // d/dy sqrt(x^2 + y^2 + z^2) = y / sqrt(x^2 + y^2 + z^2) + // d/dz sqrt(x^2 + y^2 + z^2) = z / sqrt(x^2 + y^2 + z^2) + const T tmp = hypot(x.a, y.a, z.a); + return Jet<T, N>(tmp, x.a / tmp * x.v + y.a / tmp * y.v + z.a / tmp * z.v); } +#endif // defined(CERES_HAS_CPP17) +// Like x * y + z but rounded only once. template <typename T, int N> -inline Jet<T, N> fmin(const Jet<T, N>& x, const Jet<T, N>& y) { - return y < x ? y : x; +inline Jet<T, N> fma(const Jet<T, N>& x, + const Jet<T, N>& y, + const Jet<T, N>& z) { + // d/dx fma(x, y, z) = y + // d/dy fma(x, y, z) = x + // d/dz fma(x, y, z) = 1 + return Jet<T, N>(fma(x.a, y.a, z.a), y.a * x.v + x.a * y.v + z.v); +} + +// Returns the larger of the two arguments. NaNs are treated as missing data. +// +// NOTE: This function is NOT subject to any of the error conditions specified +// in `math_errhandling`. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline decltype(auto) fmax(const Lhs& f, const Rhs& g) { + using J = std::common_type_t<Lhs, Rhs>; + return (isnan(g) || isgreater(f, g)) ? J{f} : J{g}; +} + +// Returns the smaller of the two arguments. NaNs are treated as missing data. +// +// NOTE: This function is NOT subject to any of the error conditions specified +// in `math_errhandling`. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline decltype(auto) fmin(const Lhs& f, const Rhs& g) { + using J = std::common_type_t<Lhs, Rhs>; + return (isnan(f) || isless(g, f)) ? J{g} : J{f}; +} + +// Returns the positive difference (f - g) of two arguments and zero if f <= g. +// If at least one argument is NaN, a NaN is return. +// +// NOTE At least one of the argument types must be a Jet, the other one can be a +// scalar. In case both arguments are Jets, their dimensionality must match. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline decltype(auto) fdim(const Lhs& f, const Rhs& g) { + using J = std::common_type_t<Lhs, Rhs>; + if (isnan(f) || isnan(g)) { + return std::numeric_limits<J>::quiet_NaN(); + } + return isgreater(f, g) ? J{f - g} : J{}; } -// erf is defined as an integral that cannot be expressed analyticaly +// erf is defined as an integral that cannot be expressed analytically // however, the derivative is trivial to compute // erf(x + h) = erf(x) + h * 2*exp(-x^2)/sqrt(pi) template <typename T, int N> inline Jet<T, N> erf(const Jet<T, N>& x) { - return Jet<T, N>(erf(x.a), x.v * M_2_SQRTPI * exp(-x.a * x.a)); + // We evaluate the constant as follows: + // 2 / sqrt(pi) = 1 / sqrt(atan(1.)) + // On POSIX sytems it is defined as M_2_SQRTPI, but this is not + // portable and the type may not be T. The above expression + // evaluates to full precision with IEEE arithmetic and, since it's + // constant, the compiler can generate exactly the same code. gcc + // does so even at -O0. + return Jet<T, N>(erf(x.a), x.v * exp(-x.a * x.a) * (T(1) / sqrt(atan(T(1))))); } // erfc(x) = 1-erf(x) // erfc(x + h) = erfc(x) + h * (-2*exp(-x^2)/sqrt(pi)) template <typename T, int N> inline Jet<T, N> erfc(const Jet<T, N>& x) { - return Jet<T, N>(erfc(x.a), -x.v * M_2_SQRTPI * exp(-x.a * x.a)); + // See in erf() above for the evaluation of the constant in the derivative. + return Jet<T, N>(erfc(x.a), + -x.v * exp(-x.a * x.a) * (T(1) / sqrt(atan(T(1))))); } // Bessel functions of the first kind with integer order equal to 0, 1, n. @@ -648,80 +879,210 @@ inline Jet<T, N> BesselJn(int n, const Jet<T, N>& f) { T(0.5) * (BesselJn(n - 1, f.a) - BesselJn(n + 1, f.a)) * f.v); } -// Jet Classification. It is not clear what the appropriate semantics are for -// these classifications. This picks that std::isfinite and std::isnormal are -// "all" operations, i.e. all elements of the jet must be finite for the jet -// itself to be finite (or normal). For IsNaN and IsInfinite, the answer is less -// clear. This takes a "any" approach for IsNaN and IsInfinite such that if any -// part of a jet is nan or inf, then the entire jet is nan or inf. This leads -// to strange situations like a jet can be both IsInfinite and IsNaN, but in -// practice the "any" semantics are the most useful for e.g. checking that -// derivatives are sane. - -// The jet is finite if all parts of the jet are finite. +// Classification and comparison functionality referencing only the scalar part +// of a Jet. To classify the derivatives (e.g., for sanity checks), the dual +// part should be referenced explicitly. For instance, to check whether the +// derivatives of a Jet 'f' are reasonable, one can use +// +// isfinite(f.v.array()).all() +// !isnan(f.v.array()).any() +// +// etc., depending on the desired semantics. +// +// NOTE: Floating-point classification and comparison functions and operators +// should be used with care as no derivatives can be propagated by such +// functions directly but only by expressions resulting from corresponding +// conditional statements. At the same time, conditional statements can possibly +// introduce a discontinuity in the cost function making it impossible to +// evaluate its derivative and thus the optimization problem intractable. + +// Determines whether the scalar part of the Jet is finite. template <typename T, int N> inline bool isfinite(const Jet<T, N>& f) { - // Branchless implementation. This is more efficient for the false-case and - // works with the codegen system. - auto result = isfinite(f.a); - for (int i = 0; i < N; ++i) { - result = result & isfinite(f.v[i]); - } - return result; + return isfinite(f.a); } -// The jet is infinite if any part of the Jet is infinite. +// Determines whether the scalar part of the Jet is infinite. template <typename T, int N> inline bool isinf(const Jet<T, N>& f) { - auto result = isinf(f.a); - for (int i = 0; i < N; ++i) { - result = result | isinf(f.v[i]); - } - return result; + return isinf(f.a); } -// The jet is NaN if any part of the jet is NaN. +// Determines whether the scalar part of the Jet is NaN. template <typename T, int N> inline bool isnan(const Jet<T, N>& f) { - auto result = isnan(f.a); - for (int i = 0; i < N; ++i) { - result = result | isnan(f.v[i]); - } - return result; + return isnan(f.a); } -// The jet is normal if all parts of the jet are normal. +// Determines whether the scalar part of the Jet is neither zero, subnormal, +// infinite, nor NaN. template <typename T, int N> inline bool isnormal(const Jet<T, N>& f) { - auto result = isnormal(f.a); - for (int i = 0; i < N; ++i) { - result = result & isnormal(f.v[i]); - } - return result; + return isnormal(f.a); +} + +// Determines whether the scalar part of the Jet f is less than the scalar +// part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isless(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isless(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is greater than the scalar +// part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isgreater(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isgreater(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is less than or equal to the +// scalar part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool islessequal(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return islessequal(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is less than or greater than +// (f < g || f > g) the scalar part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool islessgreater(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return islessgreater(AsScalar(f), AsScalar(g)); +} + +// Determines whether the scalar part of the Jet f is greater than or equal to +// the scalar part of g. +// +// NOTE: This function does NOT set any floating-point exceptions. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isgreaterequal(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isgreaterequal(AsScalar(f), AsScalar(g)); +} + +// Determines if either of the scalar parts of the arguments are NaN and +// thus cannot be ordered with respect to each other. +template <typename Lhs, + typename Rhs, + std::enable_if_t<CompatibleJetOperands_v<Lhs, Rhs>>* = nullptr> +inline bool isunordered(const Lhs& f, const Rhs& g) { + using internal::AsScalar; + return isunordered(AsScalar(f), AsScalar(g)); +} + +// Categorize scalar part as zero, subnormal, normal, infinite, NaN, or +// implementation-defined. +template <typename T, int N> +inline int fpclassify(const Jet<T, N>& f) { + return fpclassify(f.a); +} + +// Determines whether the scalar part of the argument is negative. +template <typename T, int N> +inline bool signbit(const Jet<T, N>& f) { + return signbit(f.a); } // Legacy functions from the pre-C++11 days. template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsFinite will be removed in a future Ceres Solver release. Please " + "use ceres::isfinite.") inline bool IsFinite(const Jet<T, N>& f) { return isfinite(f); } template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsNaN will be removed in a future Ceres Solver release. Please use " + "ceres::isnan.") inline bool IsNaN(const Jet<T, N>& f) { return isnan(f); } template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsNormal will be removed in a future Ceres Solver release. Please " + "use ceres::isnormal.") inline bool IsNormal(const Jet<T, N>& f) { return isnormal(f); } // The jet is infinite if any part of the jet is infinite. template <typename T, int N> +CERES_DEPRECATED_WITH_MSG( + "ceres::IsInfinite will be removed in a future Ceres Solver release. " + "Please use ceres::isinf.") inline bool IsInfinite(const Jet<T, N>& f) { return isinf(f); } +#ifdef CERES_HAS_CPP20 +// Computes the linear interpolation a + t(b - a) between a and b at the value +// t. For arguments outside of the range 0 <= t <= 1, the values are +// extrapolated. +// +// Differentiating lerp(a, b, t) with respect to a, b, and t gives: +// +// d/da lerp(a, b, t) = 1 - t +// d/db lerp(a, b, t) = t +// d/dt lerp(a, b, t) = b - a +// +// with the dual representation given by +// +// lerp(a + da, b + db, t + dt) +// ~= lerp(a, b, t) + (1 - t) da + t db + (b - a) dt . +template <typename T, int N> +inline Jet<T, N> lerp(const Jet<T, N>& a, + const Jet<T, N>& b, + const Jet<T, N>& t) { + return Jet<T, N>{lerp(a.a, b.a, t.a), + (T(1) - t.a) * a.v + t.a * b.v + (b.a - a.a) * t.v}; +} + +// Computes the midpoint a + (b - a) / 2. +// +// Differentiating midpoint(a, b) with respect to a and b gives: +// +// d/da midpoint(a, b) = 1/2 +// d/db midpoint(a, b) = 1/2 +// +// with the dual representation given by +// +// midpoint(a + da, b + db) ~= midpoint(a, b) + (da + db) / 2 . +template <typename T, int N> +inline Jet<T, N> midpoint(const Jet<T, N>& a, const Jet<T, N>& b) { + Jet<T, N> result{midpoint(a.a, b.a)}; + // To avoid overflow in the differential, compute + // (da + db) / 2 using midpoint. + for (int i = 0; i < N; ++i) { + result.v[i] = midpoint(a.v[i], b.v[i]); + } + return result; +} +#endif // defined(CERES_HAS_CPP20) + // atan2(b + db, a + da) ~= atan2(b, a) + (- b da + a db) / (a^2 + b^2) // // In words: the rate of change of theta is 1/r times the rate of @@ -737,6 +1098,22 @@ inline Jet<T, N> atan2(const Jet<T, N>& g, const Jet<T, N>& f) { return Jet<T, N>(atan2(g.a, f.a), tmp * (-g.a * f.v + f.a * g.v)); } +// Computes the square x^2 of a real number x (not the Euclidean L^2 norm as +// the name might suggest). +// +// NOTE: While std::norm is primarily intended for computing the squared +// magnitude of a std::complex<> number, the current Jet implementation does not +// support mixing a scalar T in its real part and std::complex<T> and in the +// infinitesimal. Mixed Jet support is necessary for the type decay from +// std::complex<T> to T (the squared magnitude of a complex number is always +// real) performed by std::norm. +// +// norm(x + h) ~= norm(x) + 2x h +template <typename T, int N> +inline Jet<T, N> norm(const Jet<T, N>& f) { + return Jet<T, N>(norm(f.a), T(2) * f.a * f.v); +} + // pow -- base is a differentiable function, exponent is a constant. // (a+da)^p ~= a^p + p*a^(p-1) da template <typename T, int N> @@ -760,14 +1137,14 @@ template <typename T, int N> inline Jet<T, N> pow(T f, const Jet<T, N>& g) { Jet<T, N> result; - if (f == T(0) && g.a > T(0)) { + if (fpclassify(f) == FP_ZERO && g > 0) { // Handle case 2. result = Jet<T, N>(T(0.0)); } else { - if (f < 0 && g.a == floor(g.a)) { // Handle case 3. + if (f < 0 && g == floor(g.a)) { // Handle case 3. result = Jet<T, N>(pow(f, g.a)); for (int i = 0; i < N; i++) { - if (g.v[i] != T(0.0)) { + if (fpclassify(g.v[i]) != FP_ZERO) { // Return a NaN when g.v != 0. result.v[i] = std::numeric_limits<T>::quiet_NaN(); } @@ -822,21 +1199,21 @@ template <typename T, int N> inline Jet<T, N> pow(const Jet<T, N>& f, const Jet<T, N>& g) { Jet<T, N> result; - if (f.a == T(0) && g.a >= T(1)) { + if (fpclassify(f) == FP_ZERO && g >= 1) { // Handle cases 2 and 3. - if (g.a > T(1)) { + if (g > 1) { result = Jet<T, N>(T(0.0)); } else { result = f; } } else { - if (f.a < T(0) && g.a == floor(g.a)) { + if (f < 0 && g == floor(g.a)) { // Handle cases 7 and 8. T const tmp = g.a * pow(f.a, g.a - T(1.0)); result = Jet<T, N>(pow(f.a, g.a), tmp * f.v); for (int i = 0; i < N; i++) { - if (g.v[i] != T(0.0)) { + if (fpclassify(g.v[i]) != FP_ZERO) { // Return a NaN when g.v != 0. result.v[i] = T(std::numeric_limits<double>::quiet_NaN()); } @@ -904,8 +1281,9 @@ struct numeric_limits<ceres::Jet<T, N>> { static constexpr bool tinyness_before = std::numeric_limits<T>::tinyness_before; - static constexpr ceres::Jet<T, N> min() noexcept { - return ceres::Jet<T, N>(std::numeric_limits<T>::min()); + static constexpr ceres::Jet<T, N> min + CERES_PREVENT_MACRO_SUBSTITUTION() noexcept { + return ceres::Jet<T, N>((std::numeric_limits<T>::min)()); } static constexpr ceres::Jet<T, N> lowest() noexcept { return ceres::Jet<T, N>(std::numeric_limits<T>::lowest()); @@ -929,8 +1307,9 @@ struct numeric_limits<ceres::Jet<T, N>> { return ceres::Jet<T, N>(std::numeric_limits<T>::denorm_min()); } - static constexpr ceres::Jet<T, N> max() noexcept { - return ceres::Jet<T, N>(std::numeric_limits<T>::max()); + static constexpr ceres::Jet<T, N> max + CERES_PREVENT_MACRO_SUBSTITUTION() noexcept { + return ceres::Jet<T, N>((std::numeric_limits<T>::max)()); } }; @@ -942,10 +1321,10 @@ namespace Eigen { // Eigen arrays, getting all the goodness of Eigen combined with autodiff. template <typename T, int N> struct NumTraits<ceres::Jet<T, N>> { - typedef ceres::Jet<T, N> Real; - typedef ceres::Jet<T, N> NonInteger; - typedef ceres::Jet<T, N> Nested; - typedef ceres::Jet<T, N> Literal; + using Real = ceres::Jet<T, N>; + using NonInteger = ceres::Jet<T, N>; + using Nested = ceres::Jet<T, N>; + using Literal = ceres::Jet<T, N>; static typename ceres::Jet<T, N> dummy_precision() { return ceres::Jet<T, N>(1e-12); @@ -984,8 +1363,8 @@ struct NumTraits<ceres::Jet<T, N>> { }; }; - static inline Real highest() { return Real(std::numeric_limits<T>::max()); } - static inline Real lowest() { return Real(-std::numeric_limits<T>::max()); } + static inline Real highest() { return Real((std::numeric_limits<T>::max)()); } + static inline Real lowest() { return Real(-(std::numeric_limits<T>::max)()); } }; // Specifying the return type of binary operations between Jets and scalar types @@ -996,11 +1375,11 @@ struct NumTraits<ceres::Jet<T, N>> { // is only available on Eigen versions >= 3.3 template <typename BinaryOp, typename T, int N> struct ScalarBinaryOpTraits<ceres::Jet<T, N>, T, BinaryOp> { - typedef ceres::Jet<T, N> ReturnType; + using ReturnType = ceres::Jet<T, N>; }; template <typename BinaryOp, typename T, int N> struct ScalarBinaryOpTraits<T, ceres::Jet<T, N>, BinaryOp> { - typedef ceres::Jet<T, N> ReturnType; + using ReturnType = ceres::Jet<T, N>; }; } // namespace Eigen diff --git a/extern/ceres/internal/ceres/split.h b/extern/ceres/include/ceres/jet_fwd.h index f513023ec69..fbb6286958c 100644 --- a/extern/ceres/internal/ceres/split.h +++ b/extern/ceres/include/ceres/jet_fwd.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -26,27 +26,19 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -// Author: keir@google.com (Keir Mierle) - -#ifndef CERES_INTERNAL_SPLIT_H_ -#define CERES_INTERNAL_SPLIT_H_ - -#include <string> -#include <vector> +// Author: sergiu.deitsch@gmail.com (Sergiu Deitsch) +// -#include "ceres/internal/port.h" +#ifndef CERES_PUBLIC_JET_FWD_H_ +#define CERES_PUBLIC_JET_FWD_H_ namespace ceres { -namespace internal { -// Split a string using one or more character delimiters, presented as a -// nul-terminated c string. Append the components to 'result'. If there are -// consecutive delimiters, this function skips over all of them. -void SplitStringUsing(const std::string& full, - const char* delim, - std::vector<std::string>* res); +// Jet forward declaration necessary for the following partial specialization of +// std::common_type and type traits. +template <typename T, int N> +struct Jet; -} // namespace internal } // namespace ceres -#endif // CERES_INTERNAL_SPLIT_H_ +#endif // CERES_PUBLIC_JET_FWD_H_ diff --git a/extern/ceres/include/ceres/line_manifold.h b/extern/ceres/include/ceres/line_manifold.h new file mode 100644 index 00000000000..f8f1b235220 --- /dev/null +++ b/extern/ceres/include/ceres/line_manifold.h @@ -0,0 +1,304 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: jodebo_beck@gmx.de (Johannes Beck) +// + +#ifndef CERES_PUBLIC_LINE_MANIFOLD_H_ +#define CERES_PUBLIC_LINE_MANIFOLD_H_ + +#include <Eigen/Core> +#include <algorithm> +#include <array> +#include <memory> +#include <vector> + +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/internal/householder_vector.h" +#include "ceres/internal/sphere_manifold_functions.h" +#include "ceres/manifold.h" +#include "ceres/types.h" +#include "glog/logging.h" + +namespace ceres { +// This provides a manifold for lines, where the line is +// over-parameterized by an origin point and a direction vector. So the +// parameter vector size needs to be two times the ambient space dimension, +// where the first half is interpreted as the origin point and the second half +// as the direction. +// +// The plus operator for the line direction is the same as for the +// SphereManifold. The update of the origin point is +// perpendicular to the line direction before the update. +// +// This manifold is a special case of the affine Grassmannian +// manifold (see https://en.wikipedia.org/wiki/Affine_Grassmannian_(manifold)) +// for the case Graff_1(R^n). +// +// The class works with dynamic and static ambient space dimensions. If the +// ambient space dimensions is known at compile time use +// +// LineManifold<3> manifold; +// +// If the ambient space dimensions is not known at compile time the template +// parameter needs to be set to ceres::DYNAMIC and the actual dimension needs +// to be provided as a constructor argument: +// +// LineManifold<ceres::DYNAMIC> manifold(ambient_dim); +// +template <int AmbientSpaceDimension> +class LineManifold final : public Manifold { + public: + static_assert(AmbientSpaceDimension == DYNAMIC || AmbientSpaceDimension >= 2, + "The ambient space must be at least 2."); + static_assert(ceres::DYNAMIC == Eigen::Dynamic, + "ceres::DYNAMIC needs to be the same as Eigen::Dynamic."); + + LineManifold(); + explicit LineManifold(int size); + + int AmbientSize() const override { return 2 * size_; } + int TangentSize() const override { return 2 * (size_ - 1); } + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; + + private: + static constexpr bool IsDynamic = (AmbientSpaceDimension == ceres::DYNAMIC); + static constexpr int TangentSpaceDimension = + IsDynamic ? ceres::DYNAMIC : AmbientSpaceDimension - 1; + + static constexpr int DAmbientSpaceDimension = + IsDynamic ? ceres::DYNAMIC : 2 * AmbientSpaceDimension; + static constexpr int DTangentSpaceDimension = + IsDynamic ? ceres::DYNAMIC : 2 * TangentSpaceDimension; + + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDimension, 1>; + using TangentVector = Eigen::Matrix<double, TangentSpaceDimension, 1>; + using MatrixPlusJacobian = Eigen::Matrix<double, + DAmbientSpaceDimension, + DTangentSpaceDimension, + Eigen::RowMajor>; + using MatrixMinusJacobian = Eigen::Matrix<double, + DTangentSpaceDimension, + DAmbientSpaceDimension, + Eigen::RowMajor>; + + const int size_{AmbientSpaceDimension}; +}; + +template <int AmbientSpaceDimension> +LineManifold<AmbientSpaceDimension>::LineManifold() + : size_{AmbientSpaceDimension} { + static_assert( + AmbientSpaceDimension != Eigen::Dynamic, + "The size is set to dynamic. Please call the constructor with a size."); +} + +template <int AmbientSpaceDimension> +LineManifold<AmbientSpaceDimension>::LineManifold(int size) : size_{size} { + if (AmbientSpaceDimension != Eigen::Dynamic) { + CHECK_EQ(AmbientSpaceDimension, size) + << "Specified size by template parameter differs from the supplied " + "one."; + } else { + CHECK_GT(size_, 1) + << "The size of the manifold needs to be greater than 1."; + } +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::Plus(const double* x_ptr, + const double* delta_ptr, + double* x_plus_delta_ptr) const { + // We seek a box plus operator of the form + // + // [o*, d*] = Plus([o, d], [delta_o, delta_d]) + // + // where o is the origin point, d is the direction vector, delta_o is + // the delta of the origin point and delta_d the delta of the direction and + // o* and d* is the updated origin point and direction. + // + // We separate the Plus operator into the origin point and directional part + // d* = Plus_d(d, delta_d) + // o* = Plus_o(o, d, delta_o) + // + // The direction update function Plus_d is the same as as the SphereManifold: + // + // d* = H_{v(d)} [0.5 sinc(0.5 |delta_d|) delta_d, cos(0.5 |delta_d|)]^T + // + // where H is the householder matrix + // H_{v} = I - (2 / |v|^2) v v^T + // and + // v(d) = d - sign(d_n) |d| e_n. + // + // The origin point update function Plus_o is defined as + // + // o* = o + H_{v(d)} [0.5 delta_o, 0]^T. + + Eigen::Map<const AmbientVector> o(x_ptr, size_); + Eigen::Map<const AmbientVector> d(x_ptr + size_, size_); + + Eigen::Map<const TangentVector> delta_o(delta_ptr, size_ - 1); + Eigen::Map<const TangentVector> delta_d(delta_ptr + size_ - 1, size_ - 1); + Eigen::Map<AmbientVector> o_plus_delta(x_plus_delta_ptr, size_); + Eigen::Map<AmbientVector> d_plus_delta(x_plus_delta_ptr + size_, size_); + + const double norm_delta_d = delta_d.norm(); + + o_plus_delta = o; + + // Shortcut for zero delta direction. + if (norm_delta_d == 0.0) { + d_plus_delta = d; + + if (delta_o.isZero(0.0)) { + return true; + } + } + + // Calculate the householder transformation which is needed for f_d and f_o. + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(d, &v, &beta); + + if (norm_delta_d != 0.0) { + internal::ComputeSphereManifoldPlus( + v, beta, d, delta_d, norm_delta_d, &d_plus_delta); + } + + // The null space is in the direction of the line, so the tangent space is + // perpendicular to the line direction. This is achieved by using the + // householder matrix of the direction and allow only movements + // perpendicular to e_n. + // + // The factor of 0.5 is used to be consistent with the line direction + // update. + AmbientVector y(size_); + y << 0.5 * delta_o, 0; + o_plus_delta += internal::ApplyHouseholderVector(y, v, beta); + + return true; +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::PlusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> d(x_ptr + size_, size_); + Eigen::Map<MatrixPlusJacobian> jacobian( + jacobian_ptr, 2 * size_, 2 * (size_ - 1)); + + // Clear the Jacobian as only half of the matrix is not zero. + jacobian.setZero(); + + auto jacobian_d = + jacobian + .template topLeftCorner<AmbientSpaceDimension, TangentSpaceDimension>( + size_, size_ - 1); + auto jacobian_o = jacobian.template bottomRightCorner<AmbientSpaceDimension, + TangentSpaceDimension>( + size_, size_ - 1); + internal::ComputeSphereManifoldPlusJacobian(d, &jacobian_d); + jacobian_o = jacobian_d; + return true; +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::Minus(const double* y_ptr, + const double* x_ptr, + double* y_minus_x) const { + Eigen::Map<const AmbientVector> y_o(y_ptr, size_); + Eigen::Map<const AmbientVector> y_d(y_ptr + size_, size_); + Eigen::Map<const AmbientVector> x_o(x_ptr, size_); + Eigen::Map<const AmbientVector> x_d(x_ptr + size_, size_); + + Eigen::Map<TangentVector> y_minus_x_o(y_minus_x, size_ - 1); + Eigen::Map<TangentVector> y_minus_x_d(y_minus_x + size_ - 1, size_ - 1); + + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(x_d, &v, &beta); + + internal::ComputeSphereManifoldMinus(v, beta, x_d, y_d, &y_minus_x_d); + + AmbientVector delta_o = y_o - x_o; + const AmbientVector h_delta_o = + 2.0 * internal::ApplyHouseholderVector(delta_o, v, beta); + y_minus_x_o = h_delta_o.template head<TangentSpaceDimension>(size_ - 1); + + return true; +} + +template <int AmbientSpaceDimension> +bool LineManifold<AmbientSpaceDimension>::MinusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> d(x_ptr + size_, size_); + Eigen::Map<MatrixMinusJacobian> jacobian( + jacobian_ptr, 2 * (size_ - 1), 2 * size_); + + // Clear the Jacobian as only half of the matrix is not zero. + jacobian.setZero(); + + auto jacobian_d = + jacobian + .template topLeftCorner<TangentSpaceDimension, AmbientSpaceDimension>( + size_ - 1, size_); + auto jacobian_o = jacobian.template bottomRightCorner<TangentSpaceDimension, + AmbientSpaceDimension>( + size_ - 1, size_); + internal::ComputeSphereManifoldMinusJacobian(d, &jacobian_d); + jacobian_o = jacobian_d; + + return true; +} + +} // namespace ceres + +// clang-format off +#include "ceres/internal/reenable_warnings.h" +// clang-format on + +#endif // CERES_PUBLIC_LINE_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/local_parameterization.h b/extern/ceres/include/ceres/local_parameterization.h index ba7579deca0..5815dd17d15 100644 --- a/extern/ceres/include/ceres/local_parameterization.h +++ b/extern/ceres/include/ceres/local_parameterization.h @@ -37,10 +37,14 @@ #include <vector> #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" namespace ceres { +// WARNING: LocalParameterizations are deprecated. They will be removed from +// Ceres Solver in version 2.2.0. Please use Manifolds instead. + // Purpose: Sometimes parameter blocks x can overparameterize a problem // // min f(x) @@ -111,7 +115,10 @@ namespace ceres { // // The class LocalParameterization defines the function Plus and its // Jacobian which is needed to compute the Jacobian of f w.r.t delta. -class CERES_EXPORT LocalParameterization { +class CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations will be removed from the Ceres Solver API in " + "version 2.2.0. Use Manifolds instead.") + CERES_EXPORT LocalParameterization { public: virtual ~LocalParameterization(); @@ -120,6 +127,7 @@ class CERES_EXPORT LocalParameterization { // x_plus_delta = Plus(x, delta) // // with the condition that Plus(x, 0) = x. + // virtual bool Plus(const double* x, const double* delta, double* x_plus_delta) const = 0; @@ -152,10 +160,10 @@ class CERES_EXPORT LocalParameterization { // Some basic parameterizations // Identity Parameterization: Plus(x, delta) = x + delta -class CERES_EXPORT IdentityParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use EuclideanManifold instead.") + CERES_EXPORT IdentityParameterization : public LocalParameterization { public: explicit IdentityParameterization(int size); - virtual ~IdentityParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -172,11 +180,11 @@ class CERES_EXPORT IdentityParameterization : public LocalParameterization { }; // Hold a subset of the parameters inside a parameter block constant. -class CERES_EXPORT SubsetParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use SubsetManifold instead.") + CERES_EXPORT SubsetParameterization : public LocalParameterization { public: explicit SubsetParameterization(int size, const std::vector<int>& constant_parameters); - virtual ~SubsetParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -199,9 +207,9 @@ class CERES_EXPORT SubsetParameterization : public LocalParameterization { // with * being the quaternion multiplication operator. Here we assume // that the first element of the quaternion vector is the real (cos // theta) part. -class CERES_EXPORT QuaternionParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use QuaternionManifold instead.") + CERES_EXPORT QuaternionParameterization : public LocalParameterization { public: - virtual ~QuaternionParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -221,10 +229,10 @@ class CERES_EXPORT QuaternionParameterization : public LocalParameterization { // // Plus(x, delta) = [sin(|delta|) delta / |delta|, cos(|delta|)] * x // with * being the quaternion multiplication operator. -class CERES_EXPORT EigenQuaternionParameterization +class CERES_DEPRECATED_WITH_MSG("Use EigenQuaternionManifold instead.") + CERES_EXPORT EigenQuaternionParameterization : public ceres::LocalParameterization { public: - virtual ~EigenQuaternionParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -234,23 +242,23 @@ class CERES_EXPORT EigenQuaternionParameterization }; // This provides a parameterization for homogeneous vectors which are commonly -// used in Structure for Motion problems. One example where they are used is -// in representing points whose triangulation is ill-conditioned. Here -// it is advantageous to use an over-parameterization since homogeneous vectors -// can represent points at infinity. +// used in Structure from Motion problems. One example where they are used is +// in representing points whose triangulation is ill-conditioned. Here it is +// advantageous to use an over-parameterization since homogeneous vectors can +// represent points at infinity. // // The plus operator is defined as // Plus(x, delta) = // [sin(0.5 * |delta|) * delta / |delta|, cos(0.5 * |delta|)] * x +// // with * defined as an operator which applies the update orthogonal to x to // remain on the sphere. We assume that the last element of x is the scalar // component. The size of the homogeneous vector is required to be greater than // 1. -class CERES_EXPORT HomogeneousVectorParameterization - : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use SphereManifold instead.") CERES_EXPORT + HomogeneousVectorParameterization : public LocalParameterization { public: explicit HomogeneousVectorParameterization(int size); - virtual ~HomogeneousVectorParameterization() {} bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; @@ -276,7 +284,8 @@ class CERES_EXPORT HomogeneousVectorParameterization // manifold (see https://en.wikipedia.org/wiki/Affine_Grassmannian_(manifold)) // for the case Graff_1(R^n). template <int AmbientSpaceDimension> -class LineParameterization : public LocalParameterization { +class CERES_DEPRECATED_WITH_MSG("Use LineManifold instead.") + LineParameterization : public LocalParameterization { public: static_assert(AmbientSpaceDimension >= 2, "The ambient space must be at least 2"); @@ -302,21 +311,19 @@ class LineParameterization : public LocalParameterization { // // is the local parameterization for a rigid transformation, where the // rotation is represented using a quaternion. -class CERES_EXPORT ProductParameterization : public LocalParameterization { +// +class CERES_DEPRECATED_WITH_MSG("Use ProductManifold instead.") + CERES_EXPORT ProductParameterization : public LocalParameterization { public: ProductParameterization(const ProductParameterization&) = delete; ProductParameterization& operator=(const ProductParameterization&) = delete; - virtual ~ProductParameterization() {} // // NOTE: The constructor takes ownership of the input local // parameterizations. // template <typename... LocalParams> - ProductParameterization(LocalParams*... local_params) - : local_params_(sizeof...(LocalParams)), - local_size_{0}, - global_size_{0}, - buffer_size_{0} { + explicit ProductParameterization(LocalParams*... local_params) + : local_params_(sizeof...(LocalParams)) { constexpr int kNumLocalParams = sizeof...(LocalParams); static_assert(kNumLocalParams >= 2, "At least two local parameterizations must be specified."); @@ -342,22 +349,23 @@ class CERES_EXPORT ProductParameterization : public LocalParameterization { bool Plus(const double* x, const double* delta, double* x_plus_delta) const override; - bool ComputeJacobian(const double* x, - double* jacobian) const override; + bool ComputeJacobian(const double* x, double* jacobian) const override; int GlobalSize() const override { return global_size_; } int LocalSize() const override { return local_size_; } private: std::vector<std::unique_ptr<LocalParameterization>> local_params_; - int local_size_; - int global_size_; - int buffer_size_; + int local_size_{0}; + int global_size_{0}; + int buffer_size_{0}; }; } // namespace ceres // clang-format off #include "ceres/internal/reenable_warnings.h" +// clang-format on + #include "ceres/internal/line_parameterization.h" #endif // CERES_PUBLIC_LOCAL_PARAMETERIZATION_H_ diff --git a/extern/ceres/include/ceres/loss_function.h b/extern/ceres/include/ceres/loss_function.h index 7aabf7dfce1..8a5a37ff665 100644 --- a/extern/ceres/include/ceres/loss_function.h +++ b/extern/ceres/include/ceres/loss_function.h @@ -35,7 +35,7 @@ // // For least squares problem where there are no outliers and standard // squared loss is expected, it is not necessary to create a loss -// function; instead passing a NULL to the problem when adding +// function; instead passing a nullptr to the problem when adding // residuals implies a standard squared loss. // // For least squares problems where the minimization may encounter @@ -78,6 +78,7 @@ #include <memory> #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/types.h" #include "glog/logging.h" @@ -85,7 +86,7 @@ namespace ceres { class CERES_EXPORT LossFunction { public: - virtual ~LossFunction() {} + virtual ~LossFunction(); // For a residual vector with squared 2-norm 'sq_norm', this method // is required to fill in the value and derivatives of the loss @@ -125,10 +126,10 @@ class CERES_EXPORT LossFunction { // // At s = 0: rho = [0, 1, 0]. // -// It is not normally necessary to use this, as passing NULL for the +// It is not normally necessary to use this, as passing nullptr for the // loss function when building the problem accomplishes the same // thing. -class CERES_EXPORT TrivialLoss : public LossFunction { +class CERES_EXPORT TrivialLoss final : public LossFunction { public: void Evaluate(double, double*) const override; }; @@ -171,7 +172,7 @@ class CERES_EXPORT TrivialLoss : public LossFunction { // // The scaling parameter 'a' corresponds to 'delta' on this page: // http://en.wikipedia.org/wiki/Huber_Loss_Function -class CERES_EXPORT HuberLoss : public LossFunction { +class CERES_EXPORT HuberLoss final : public LossFunction { public: explicit HuberLoss(double a) : a_(a), b_(a * a) {} void Evaluate(double, double*) const override; @@ -187,7 +188,7 @@ class CERES_EXPORT HuberLoss : public LossFunction { // rho(s) = 2 (sqrt(1 + s) - 1). // // At s = 0: rho = [0, 1, -1 / (2 * a^2)]. -class CERES_EXPORT SoftLOneLoss : public LossFunction { +class CERES_EXPORT SoftLOneLoss final : public LossFunction { public: explicit SoftLOneLoss(double a) : b_(a * a), c_(1 / b_) {} void Evaluate(double, double*) const override; @@ -204,7 +205,7 @@ class CERES_EXPORT SoftLOneLoss : public LossFunction { // rho(s) = log(1 + s). // // At s = 0: rho = [0, 1, -1 / a^2]. -class CERES_EXPORT CauchyLoss : public LossFunction { +class CERES_EXPORT CauchyLoss final : public LossFunction { public: explicit CauchyLoss(double a) : b_(a * a), c_(1 / b_) {} void Evaluate(double, double*) const override; @@ -225,7 +226,7 @@ class CERES_EXPORT CauchyLoss : public LossFunction { // rho(s) = a atan(s / a). // // At s = 0: rho = [0, 1, 0]. -class CERES_EXPORT ArctanLoss : public LossFunction { +class CERES_EXPORT ArctanLoss final : public LossFunction { public: explicit ArctanLoss(double a) : a_(a), b_(1 / (a * a)) {} void Evaluate(double, double*) const override; @@ -264,7 +265,7 @@ class CERES_EXPORT ArctanLoss : public LossFunction { // concentrated in the range a - b to a + b. // // At s = 0: rho = [0, ~0, ~0]. -class CERES_EXPORT TolerantLoss : public LossFunction { +class CERES_EXPORT TolerantLoss final : public LossFunction { public: explicit TolerantLoss(double a, double b); void Evaluate(double, double*) const override; @@ -283,7 +284,7 @@ class CERES_EXPORT TolerantLoss : public LossFunction { // rho(s) = a^2 / 3 for s > a^2. // // At s = 0: rho = [0, 1, -2 / a^2] -class CERES_EXPORT TukeyLoss : public ceres::LossFunction { +class CERES_EXPORT TukeyLoss final : public ceres::LossFunction { public: explicit TukeyLoss(double a) : a_squared_(a * a) {} void Evaluate(double, double*) const override; @@ -294,14 +295,14 @@ class CERES_EXPORT TukeyLoss : public ceres::LossFunction { // Composition of two loss functions. The error is the result of first // evaluating g followed by f to yield the composition f(g(s)). -// The loss functions must not be NULL. -class CERES_EXPORT ComposedLoss : public LossFunction { +// The loss functions must not be nullptr. +class CERES_EXPORT ComposedLoss final : public LossFunction { public: explicit ComposedLoss(const LossFunction* f, Ownership ownership_f, const LossFunction* g, Ownership ownership_g); - virtual ~ComposedLoss(); + ~ComposedLoss() override; void Evaluate(double, double*) const override; private: @@ -322,11 +323,11 @@ class CERES_EXPORT ComposedLoss : public LossFunction { // s -> a * rho'(s) // s -> a * rho''(s) // -// Since we treat the a NULL Loss function as the Identity loss -// function, rho = NULL is a valid input and will result in the input +// Since we treat the a nullptr Loss function as the Identity loss +// function, rho = nullptr is a valid input and will result in the input // being scaled by a. This provides a simple way of implementing a // scaled ResidualBlock. -class CERES_EXPORT ScaledLoss : public LossFunction { +class CERES_EXPORT ScaledLoss final : public LossFunction { public: // Constructs a ScaledLoss wrapping another loss function. Takes // ownership of the wrapped loss function or not depending on the @@ -336,7 +337,7 @@ class CERES_EXPORT ScaledLoss : public LossFunction { ScaledLoss(const ScaledLoss&) = delete; void operator=(const ScaledLoss&) = delete; - virtual ~ScaledLoss() { + ~ScaledLoss() override { if (ownership_ == DO_NOT_TAKE_OWNERSHIP) { rho_.release(); } @@ -361,8 +362,8 @@ class CERES_EXPORT ScaledLoss : public LossFunction { // whose scale can be mutated after an optimization problem has been // constructed. // -// Since we treat the a NULL Loss function as the Identity loss -// function, rho = NULL is a valid input. +// Since we treat the a nullptr Loss function as the Identity loss +// function, rho = nullptr is a valid input. // // Example usage // @@ -374,7 +375,8 @@ class CERES_EXPORT ScaledLoss : public LossFunction { // new AutoDiffCostFunction < UW_Camera_Mapper, 2, 9, 3>( // new UW_Camera_Mapper(feature_x, feature_y)); // -// LossFunctionWrapper* loss_function(new HuberLoss(1.0), TAKE_OWNERSHIP); +// LossFunctionWrapper* loss_function = new LossFunctionWrapper( +// new HuberLoss(1.0), TAKE_OWNERSHIP); // // problem.AddResidualBlock(cost_function, loss_function, parameters); // @@ -387,7 +389,7 @@ class CERES_EXPORT ScaledLoss : public LossFunction { // // Solve(options, &problem, &summary) // -class CERES_EXPORT LossFunctionWrapper : public LossFunction { +class CERES_EXPORT LossFunctionWrapper final : public LossFunction { public: LossFunctionWrapper(LossFunction* rho, Ownership ownership) : rho_(rho), ownership_(ownership) {} @@ -395,14 +397,14 @@ class CERES_EXPORT LossFunctionWrapper : public LossFunction { LossFunctionWrapper(const LossFunctionWrapper&) = delete; void operator=(const LossFunctionWrapper&) = delete; - virtual ~LossFunctionWrapper() { + ~LossFunctionWrapper() override { if (ownership_ == DO_NOT_TAKE_OWNERSHIP) { rho_.release(); } } void Evaluate(double sq_norm, double out[3]) const override { - if (rho_.get() == NULL) { + if (rho_.get() == nullptr) { out[0] = sq_norm; out[1] = 1.0; out[2] = 0.0; diff --git a/extern/ceres/include/ceres/manifold.h b/extern/ceres/include/ceres/manifold.h new file mode 100644 index 00000000000..4d6e9fa0f59 --- /dev/null +++ b/extern/ceres/include/ceres/manifold.h @@ -0,0 +1,411 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_PUBLIC_MANIFOLD_H_ +#define CERES_PUBLIC_MANIFOLD_H_ + +#include <Eigen/Core> +#include <algorithm> +#include <array> +#include <memory> +#include <utility> +#include <vector> + +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/types.h" +#include "glog/logging.h" + +namespace ceres { + +// In sensor fusion problems, often we have to model quantities that live in +// spaces known as Manifolds, for example the rotation/orientation of a sensor +// that is represented by a quaternion. +// +// Manifolds are spaces which locally look like Euclidean spaces. More +// precisely, at each point on the manifold there is a linear space that is +// tangent to the manifold. It has dimension equal to the intrinsic dimension of +// the manifold itself, which is less than or equal to the ambient space in +// which the manifold is embedded. +// +// For example, the tangent space to a point on a sphere in three dimensions is +// the two dimensional plane that is tangent to the sphere at that point. There +// are two reasons tangent spaces are interesting: +// +// 1. They are Eucliean spaces so the usual vector space operations apply there, +// which makes numerical operations easy. +// 2. Movement in the tangent space translate into movements along the manifold. +// Movements perpendicular to the tangent space do not translate into +// movements on the manifold. +// +// Returning to our sphere example, moving in the 2 dimensional plane +// tangent to the sphere and projecting back onto the sphere will move you away +// from the point you started from but moving along the normal at the same point +// and the projecting back onto the sphere brings you back to the point. +// +// The Manifold interface defines two operations (and their derivatives) +// involving the tangent space, allowing filtering and optimization to be +// performed on said manifold: +// +// 1. x_plus_delta = Plus(x, delta) +// 2. delta = Minus(x_plus_delta, x) +// +// "Plus" computes the result of moving along delta in the tangent space at x, +// and then projecting back onto the manifold that x belongs to. In Differential +// Geometry this is known as a "Retraction". It is a generalization of vector +// addition in Euclidean spaces. +// +// Given two points on the manifold, "Minus" computes the change delta to x in +// the tangent space at x, that will take it to x_plus_delta. +// +// Let us now consider two examples. +// +// The Euclidean space R^n is the simplest example of a manifold. It has +// dimension n (and so does its tangent space) and Plus and Minus are the +// familiar vector sum and difference operations. +// +// Plus(x, delta) = x + delta = y, +// Minus(y, x) = y - x = delta. +// +// A more interesting case is SO(3), the special orthogonal group in three +// dimensions - the space of 3x3 rotation matrices. SO(3) is a three dimensional +// manifold embedded in R^9 or R^(3x3). So points on SO(3) are represented using +// 9 dimensional vectors or 3x3 matrices, and points in its tangent spaces are +// represented by 3 dimensional vectors. +// +// Defining Plus and Minus are defined in terms of the matrix Exp and Log +// operations as follows: +// +// Let Exp(p, q, r) = [cos(theta) + cp^2, -sr + cpq , sq + cpr ] +// [sr + cpq , cos(theta) + cq^2, -sp + cqr ] +// [-sq + cpr , sp + cqr , cos(theta) + cr^2] +// +// where: theta = sqrt(p^2 + q^2 + r^2) +// s = sinc(theta) +// c = (1 - cos(theta))/theta^2 +// +// and Log(x) = 1/(2 sinc(theta))[x_32 - x_23, x_13 - x_31, x_21 - x_12] +// +// where: theta = acos((Trace(x) - 1)/2) +// +// Then, +// +// Plus(x, delta) = x Exp(delta) +// Minus(y, x) = Log(x^T y) +// +// For Plus and Minus to be mathematically consistent, the following identities +// must be satisfied at all points x on the manifold: +// +// 1. Plus(x, 0) = x. +// 2. For all y, Plus(x, Minus(y, x)) = y. +// 3. For all delta, Minus(Plus(x, delta), x) = delta. +// 4. For all delta_1, delta_2 +// |Minus(Plus(x, delta_1), Plus(x, delta_2)) <= |delta_1 - delta_2| +// +// Briefly: +// (1) Ensures that the tangent space is "centered" at x, and the zero vector is +// the identity element. +// (2) Ensures that any y can be reached from x. +// (3) Ensures that Plus is an injective (one-to-one) map. +// (4) Allows us to define a metric on the manifold. +// +// Additionally we require that Plus and Minus be sufficiently smooth. In +// particular they need to be differentiable everywhere on the manifold. +// +// For more details, please see +// +// "Integrating Generic Sensor Fusion Algorithms with Sound State +// Representations through Encapsulation of Manifolds" +// By C. Hertzberg, R. Wagner, U. Frese and L. Schroder +// https://arxiv.org/pdf/1107.1119.pdf +class CERES_EXPORT Manifold { + public: + virtual ~Manifold(); + + // Dimension of the ambient space in which the manifold is embedded. + virtual int AmbientSize() const = 0; + + // Dimension of the manifold/tangent space. + virtual int TangentSize() const = 0; + + // x_plus_delta = Plus(x, delta), + // + // A generalization of vector addition in Euclidean space, Plus computes the + // result of moving along delta in the tangent space at x, and then projecting + // back onto the manifold that x belongs to. + // + // x and x_plus_delta are AmbientSize() vectors. + // delta is a TangentSize() vector. + // + // Return value indicates if the operation was successful or not. + virtual bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const = 0; + + // Compute the derivative of Plus(x, delta) w.r.t delta at delta = 0, i.e. + // + // (D_2 Plus)(x, 0) + // + // jacobian is a row-major AmbientSize() x TangentSize() matrix. + // + // Return value indicates whether the operation was successful or not. + virtual bool PlusJacobian(const double* x, double* jacobian) const = 0; + + // tangent_matrix = ambient_matrix * (D_2 Plus)(x, 0) + // + // ambient_matrix is a row-major num_rows x AmbientSize() matrix. + // tangent_matrix is a row-major num_rows x TangentSize() matrix. + // + // Return value indicates whether the operation was successful or not. + // + // This function is only used by the GradientProblemSolver, where the + // dimension of the parameter block can be large and it may be more efficient + // to compute this product directly rather than first evaluating the Jacobian + // into a matrix and then doing a matrix vector product. + // + // Because this is not an often used function, we provide a default + // implementation for convenience. If performance becomes an issue then the + // user should consider implementing a specialization. + virtual bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const; + + // y_minus_x = Minus(y, x) + // + // Given two points on the manifold, Minus computes the change to x in the + // tangent space at x, that will take it to y. + // + // x and y are AmbientSize() vectors. + // y_minus_x is a TangentSize() vector. + // + // Return value indicates if the operation was successful or not. + virtual bool Minus(const double* y, + const double* x, + double* y_minus_x) const = 0; + + // Compute the derivative of Minus(y, x) w.r.t y at y = x, i.e + // + // (D_1 Minus) (x, x) + // + // Jacobian is a row-major TangentSize() x AmbientSize() matrix. + // + // Return value indicates whether the operation was successful or not. + virtual bool MinusJacobian(const double* x, double* jacobian) const = 0; +}; + +// The Euclidean manifold is another name for the ordinary vector space R^size, +// where the plus and minus operations are the usual vector addition and +// subtraction: +// Plus(x, delta) = x + delta +// Minus(y, x) = y - x. +// +// The class works with dynamic and static ambient space dimensions. If the +// ambient space dimensions is know at compile time use +// +// EuclideanManifold<3> manifold; +// +// If the ambient space dimensions is not known at compile time the template +// parameter needs to be set to ceres::DYNAMIC and the actual dimension needs +// to be provided as a constructor argument: +// +// EuclideanManifold<ceres::DYNAMIC> manifold(ambient_dim); +template <int Size> +class EuclideanManifold final : public Manifold { + public: + static_assert(Size == ceres::DYNAMIC || Size >= 0, + "The size of the manifold needs to be non-negative."); + static_assert(ceres::DYNAMIC == Eigen::Dynamic, + "ceres::DYNAMIC needs to be the same as Eigen::Dynamic."); + + EuclideanManifold() : size_{Size} { + static_assert( + Size != ceres::DYNAMIC, + "The size is set to dynamic. Please call the constructor with a size."); + } + + explicit EuclideanManifold(int size) : size_(size) { + if (Size != ceres::DYNAMIC) { + CHECK_EQ(Size, size) + << "Specified size by template parameter differs from the supplied " + "one."; + } else { + CHECK_GE(size_, 0) + << "The size of the manifold needs to be non-negative."; + } + } + + int AmbientSize() const override { return size_; } + int TangentSize() const override { return size_; } + + bool Plus(const double* x_ptr, + const double* delta_ptr, + double* x_plus_delta_ptr) const override { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<const AmbientVector> delta(delta_ptr, size_); + Eigen::Map<AmbientVector> x_plus_delta(x_plus_delta_ptr, size_); + x_plus_delta = x + delta; + return true; + } + + bool PlusJacobian(const double* x_ptr, double* jacobian_ptr) const override { + Eigen::Map<MatrixJacobian> jacobian(jacobian_ptr, size_, size_); + jacobian.setIdentity(); + return true; + } + + bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const override { + std::copy_n(ambient_matrix, num_rows * size_, tangent_matrix); + return true; + } + + bool Minus(const double* y_ptr, + const double* x_ptr, + double* y_minus_x_ptr) const override { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<const AmbientVector> y(y_ptr, size_); + Eigen::Map<AmbientVector> y_minus_x(y_minus_x_ptr, size_); + y_minus_x = y - x; + return true; + } + + bool MinusJacobian(const double* x_ptr, double* jacobian_ptr) const override { + Eigen::Map<MatrixJacobian> jacobian(jacobian_ptr, size_, size_); + jacobian.setIdentity(); + return true; + } + + private: + static constexpr bool IsDynamic = (Size == ceres::DYNAMIC); + using AmbientVector = Eigen::Matrix<double, Size, 1>; + using MatrixJacobian = Eigen::Matrix<double, Size, Size, Eigen::RowMajor>; + + int size_{}; +}; + +// Hold a subset of the parameters inside a parameter block constant. +class CERES_EXPORT SubsetManifold final : public Manifold { + public: + SubsetManifold(int size, const std::vector<int>& constant_parameters); + int AmbientSize() const override; + int TangentSize() const override; + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; + + private: + const int tangent_size_ = 0; + std::vector<bool> constancy_mask_; +}; + +// Implements the manifold for a Hamilton quaternion as defined in +// https://en.wikipedia.org/wiki/Quaternion. Quaternions are represented as +// unit norm 4-vectors, i.e. +// +// q = [q0; q1; q2; q3], |q| = 1 +// +// is the ambient space representation. +// +// q0 scalar part. +// q1 coefficient of i. +// q2 coefficient of j. +// q3 coefficient of k. +// +// where: i*i = j*j = k*k = -1 and i*j = k, j*k = i, k*i = j. +// +// The tangent space is R^3, which relates to the ambient space through the +// Plus and Minus operations defined as: +// +// Plus(x, delta) = [cos(|delta|); sin(|delta|) * delta / |delta|] * x +// Minus(y, x) = to_delta(y * x^{-1}) +// +// where "*" is the quaternion product and because q is a unit quaternion +// (|q|=1), q^-1 = [q0; -q1; -q2; -q3] +// +// and to_delta( [q0; u_{3x1}] ) = u / |u| * atan2(|u|, q0) +class CERES_EXPORT QuaternionManifold final : public Manifold { + public: + int AmbientSize() const override { return 4; } + int TangentSize() const override { return 3; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; +}; + +// Implements the quaternion manifold for Eigen's representation of the +// Hamilton quaternion. Geometrically it is exactly the same as the +// QuaternionManifold defined above. However, Eigen uses a different internal +// memory layout for the elements of the quaternion than what is commonly +// used. It stores the quaternion in memory as [q1, q2, q3, q0] or +// [x, y, z, w] where the real (scalar) part is last. +// +// Since Ceres operates on parameter blocks which are raw double pointers this +// difference is important and requires a different manifold. +class CERES_EXPORT EigenQuaternionManifold final : public Manifold { + public: + int AmbientSize() const override { return 4; } + int TangentSize() const override { return 3; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; +}; + +} // namespace ceres + +// clang-format off +#include "ceres/internal/reenable_warnings.h" +// clang-format on + +#endif // CERES_PUBLIC_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/manifold_test_utils.h b/extern/ceres/include/ceres/manifold_test_utils.h new file mode 100644 index 00000000000..3f9fb21e8f3 --- /dev/null +++ b/extern/ceres/include/ceres/manifold_test_utils.h @@ -0,0 +1,328 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include <cmath> +#include <limits> +#include <memory> + +#include "ceres/dynamic_numeric_diff_cost_function.h" +#include "ceres/internal/eigen.h" +#include "ceres/manifold.h" +#include "ceres/numeric_diff_options.h" +#include "ceres/types.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace ceres { + +// Matchers and macros for help with testing Manifold objects. +// +// Testing a Manifold has two parts. +// +// 1. Checking that Manifold::Plus is correctly defined. This requires per +// manifold tests. +// +// 2. The other methods of the manifold have mathematical properties that make +// it compatible with Plus, as described in: +// +// "Integrating Generic Sensor Fusion Algorithms with Sound State +// Representations through Encapsulation of Manifolds" +// By C. Hertzberg, R. Wagner, U. Frese and L. Schroder +// https://arxiv.org/pdf/1107.1119.pdf +// +// These tests are implemented using generic matchers defined below which can +// all be called by the macro EXPECT_THAT_MANIFOLD_INVARIANTS_HOLD(manifold, x, +// delta, y, tolerance). See manifold_test.cc for example usage. + +// Checks that the invariant Plus(x, 0) == x holds. +MATCHER_P2(XPlusZeroIsXAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Vector actual = Vector::Zero(ambient_size); + Vector zero = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Plus(x.data(), zero.data(), actual.data())); + const double n = (actual - x).norm(); + const double d = x.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nexpected (x): " << x.transpose() + << "\nactual: " << actual.transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Checks that the invariant Minus(x, x) == 0 holds. +MATCHER_P2(XMinusXIsZeroAt, x, tolerance, "") { + const int tangent_size = arg.TangentSize(); + Vector actual = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Minus(x.data(), x.data(), actual.data())); + const double diffnorm = actual.norm(); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() // + << "\nexpected: 0 0 0" + << "\nactual: " << actual.transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Helper struct to curry Plus(x, .) so that it can be numerically +// differentiated. +struct PlusFunctor { + PlusFunctor(const Manifold& manifold, const double* x) + : manifold(manifold), x(x) {} + bool operator()(double const* const* parameters, double* x_plus_delta) const { + return manifold.Plus(x, parameters[0], x_plus_delta); + } + + const Manifold& manifold; + const double* x; +}; + +// Checks that the output of PlusJacobian matches the one obtained by +// numerically evaluating D_2 Plus(x,0). +MATCHER_P2(HasCorrectPlusJacobianAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + NumericDiffOptions options; + options.ridders_relative_initial_step_size = 1e-4; + + DynamicNumericDiffCostFunction<PlusFunctor, RIDDERS> cost_function( + new PlusFunctor(arg, x.data()), TAKE_OWNERSHIP, options); + cost_function.AddParameterBlock(tangent_size); + cost_function.SetNumResiduals(ambient_size); + + Vector zero = Vector::Zero(tangent_size); + double* parameters[1] = {zero.data()}; + + Vector x_plus_zero = Vector::Zero(ambient_size); + Matrix expected = Matrix::Zero(ambient_size, tangent_size); + double* jacobians[1] = {expected.data()}; + + EXPECT_TRUE( + cost_function.Evaluate(parameters, x_plus_zero.data(), jacobians)); + + Matrix actual = Matrix::Random(ambient_size, tangent_size); + EXPECT_TRUE(arg.PlusJacobian(x.data(), actual.data())); + + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = (d == 0.0) ? n : n / d; + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm : " << diffnorm; + return false; + } + return true; +} + +// Checks that the invariant Minus(Plus(x, delta), x) == delta holds. +MATCHER_P3(MinusPlusIsIdentityAt, x, delta, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + Vector x_plus_delta = Vector::Zero(ambient_size); + EXPECT_TRUE(arg.Plus(x.data(), delta.data(), x_plus_delta.data())); + Vector actual = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Minus(x_plus_delta.data(), x.data(), actual.data())); + + const double n = (actual - delta).norm(); + const double d = delta.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() + << "\nexpected: " << delta.transpose() + << "\nactual:" << actual.transpose() + << "\ndiff:" << (delta - actual).transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Checks that the invariant Plus(Minus(y, x), x) == y holds. +MATCHER_P3(PlusMinusIsIdentityAt, x, y, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Vector y_minus_x = Vector::Zero(tangent_size); + EXPECT_TRUE(arg.Minus(y.data(), x.data(), y_minus_x.data())); + + Vector actual = Vector::Zero(ambient_size); + EXPECT_TRUE(arg.Plus(x.data(), y_minus_x.data(), actual.data())); + + const double n = (actual - y).norm(); + const double d = y.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() + << "\nexpected: " << y.transpose() + << "\nactual:" << actual.transpose() + << "\ndiff:" << (y - actual).transpose() + << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Helper struct to curry Minus(., x) so that it can be numerically +// differentiated. +struct MinusFunctor { + MinusFunctor(const Manifold& manifold, const double* x) + : manifold(manifold), x(x) {} + bool operator()(double const* const* parameters, double* y_minus_x) const { + return manifold.Minus(parameters[0], x, y_minus_x); + } + + const Manifold& manifold; + const double* x; +}; + +// Checks that the output of MinusJacobian matches the one obtained by +// numerically evaluating D_1 Minus(x,x). +MATCHER_P2(HasCorrectMinusJacobianAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Vector y = x; + Vector y_minus_x = Vector::Zero(tangent_size); + + NumericDiffOptions options; + options.ridders_relative_initial_step_size = 1e-4; + DynamicNumericDiffCostFunction<MinusFunctor, RIDDERS> cost_function( + new MinusFunctor(arg, x.data()), TAKE_OWNERSHIP, options); + cost_function.AddParameterBlock(ambient_size); + cost_function.SetNumResiduals(tangent_size); + + double* parameters[1] = {y.data()}; + + Matrix expected = Matrix::Zero(tangent_size, ambient_size); + double* jacobians[1] = {expected.data()}; + + EXPECT_TRUE(cost_function.Evaluate(parameters, y_minus_x.data(), jacobians)); + + Matrix actual = Matrix::Random(tangent_size, ambient_size); + EXPECT_TRUE(arg.MinusJacobian(x.data(), actual.data())); + + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm: " << diffnorm; + return false; + } + return true; +} + +// Checks that D_delta Minus(Plus(x, delta), x) at delta = 0 is an identity +// matrix. +MATCHER_P2(MinusPlusJacobianIsIdentityAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + Matrix plus_jacobian(ambient_size, tangent_size); + EXPECT_TRUE(arg.PlusJacobian(x.data(), plus_jacobian.data())); + Matrix minus_jacobian(tangent_size, ambient_size); + EXPECT_TRUE(arg.MinusJacobian(x.data(), minus_jacobian.data())); + + const Matrix actual = minus_jacobian * plus_jacobian; + const Matrix expected = Matrix::Identity(tangent_size, tangent_size); + + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = n / d; + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm: " << diffnorm; + + return false; + } + return true; +} + +// Verify that the output of RightMultiplyByPlusJacobian is ambient_matrix * +// plus_jacobian. +MATCHER_P2(HasCorrectRightMultiplyByPlusJacobianAt, x, tolerance, "") { + const int ambient_size = arg.AmbientSize(); + const int tangent_size = arg.TangentSize(); + + constexpr int kMinNumRows = 0; + constexpr int kMaxNumRows = 3; + for (int num_rows = kMinNumRows; num_rows <= kMaxNumRows; ++num_rows) { + Matrix plus_jacobian = Matrix::Random(ambient_size, tangent_size); + EXPECT_TRUE(arg.PlusJacobian(x.data(), plus_jacobian.data())); + + Matrix ambient_matrix = Matrix::Random(num_rows, ambient_size); + Matrix expected = ambient_matrix * plus_jacobian; + + Matrix actual = Matrix::Random(num_rows, tangent_size); + EXPECT_TRUE(arg.RightMultiplyByPlusJacobian( + x.data(), num_rows, ambient_matrix.data(), actual.data())); + const double n = (actual - expected).norm(); + const double d = expected.norm(); + const double diffnorm = (d == 0.0) ? n : (n / d); + if (diffnorm > tolerance) { + *result_listener << "\nx: " << x.transpose() << "\nambient_matrix : \n" + << ambient_matrix << "\nplus_jacobian : \n" + << plus_jacobian << "\nexpected: \n" + << expected << "\nactual:\n" + << actual << "\ndiff:\n" + << expected - actual << "\ndiffnorm : " << diffnorm; + return false; + } + } + return true; +} + +#define EXPECT_THAT_MANIFOLD_INVARIANTS_HOLD(manifold, x, delta, y, tolerance) \ + Vector zero_tangent = Vector::Zero(manifold.TangentSize()); \ + EXPECT_THAT(manifold, XPlusZeroIsXAt(x, tolerance)); \ + EXPECT_THAT(manifold, XMinusXIsZeroAt(x, tolerance)); \ + EXPECT_THAT(manifold, MinusPlusIsIdentityAt(x, delta, tolerance)); \ + EXPECT_THAT(manifold, MinusPlusIsIdentityAt(x, zero_tangent, tolerance)); \ + EXPECT_THAT(manifold, PlusMinusIsIdentityAt(x, x, tolerance)); \ + EXPECT_THAT(manifold, PlusMinusIsIdentityAt(x, y, tolerance)); \ + EXPECT_THAT(manifold, HasCorrectPlusJacobianAt(x, tolerance)); \ + EXPECT_THAT(manifold, HasCorrectMinusJacobianAt(x, tolerance)); \ + EXPECT_THAT(manifold, MinusPlusJacobianIsIdentityAt(x, tolerance)); \ + EXPECT_THAT(manifold, HasCorrectRightMultiplyByPlusJacobianAt(x, tolerance)); + +} // namespace ceres diff --git a/extern/ceres/include/ceres/normal_prior.h b/extern/ceres/include/ceres/normal_prior.h index 14ab379f4af..c5c7f3e623e 100644 --- a/extern/ceres/include/ceres/normal_prior.h +++ b/extern/ceres/include/ceres/normal_prior.h @@ -57,7 +57,7 @@ namespace ceres { // which would be the case if the covariance matrix S is rank // deficient. -class CERES_EXPORT NormalPrior : public CostFunction { +class CERES_EXPORT NormalPrior final : public CostFunction { public: // Check that the number of rows in the vector b are the same as the // number of columns in the matrix A, crash otherwise. diff --git a/extern/ceres/include/ceres/numeric_diff_cost_function.h b/extern/ceres/include/ceres/numeric_diff_cost_function.h index cf7971cde79..6ec53175030 100644 --- a/extern/ceres/include/ceres/numeric_diff_cost_function.h +++ b/extern/ceres/include/ceres/numeric_diff_cost_function.h @@ -179,9 +179,10 @@ template <typename CostFunctor, NumericDiffMethodType method = CENTRAL, int kNumResiduals = 0, // Number of residuals, or ceres::DYNAMIC int... Ns> // Parameters dimensions for each block. -class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { +class NumericDiffCostFunction final + : public SizedCostFunction<kNumResiduals, Ns...> { public: - NumericDiffCostFunction( + explicit NumericDiffCostFunction( CostFunctor* functor, Ownership ownership = TAKE_OWNERSHIP, int num_residuals = kNumResiduals, @@ -192,7 +193,7 @@ class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { } } - explicit NumericDiffCostFunction(NumericDiffCostFunction&& other) + NumericDiffCostFunction(NumericDiffCostFunction&& other) : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} virtual ~NumericDiffCostFunction() { @@ -219,7 +220,7 @@ class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { return false; } - if (jacobians == NULL) { + if (jacobians == nullptr) { return true; } @@ -246,6 +247,8 @@ class NumericDiffCostFunction : public SizedCostFunction<kNumResiduals, Ns...> { return true; } + const CostFunctor& functor() const { return *functor_; } + private: std::unique_ptr<CostFunctor> functor_; Ownership ownership_; diff --git a/extern/ceres/include/ceres/numeric_diff_first_order_function.h b/extern/ceres/include/ceres/numeric_diff_first_order_function.h new file mode 100644 index 00000000000..f5bb005be58 --- /dev/null +++ b/extern/ceres/include/ceres/numeric_diff_first_order_function.h @@ -0,0 +1,163 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2019 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ +#define CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ + +#include <algorithm> +#include <memory> + +#include "ceres/first_order_function.h" +#include "ceres/internal/eigen.h" +#include "ceres/internal/fixed_array.h" +#include "ceres/internal/numeric_diff.h" +#include "ceres/internal/parameter_dims.h" +#include "ceres/internal/variadic_evaluate.h" +#include "ceres/numeric_diff_options.h" +#include "ceres/types.h" + +namespace ceres { + +// Creates FirstOrderFunctions as needed by the GradientProblem +// framework, with gradients computed via numeric differentiation. For +// more information on numeric differentiation, see the wikipedia +// article at https://en.wikipedia.org/wiki/Numerical_differentiation +// +// To get an numerically differentiated cost function, you must define +// a class with an operator() (a functor) that computes the cost. +// +// The function must write the computed value in the last argument +// (the only non-const one) and return true to indicate success. +// +// For example, consider a scalar error e = x'y - a, where both x and y are +// two-dimensional column vector parameters, the prime sign indicates +// transposition, and a is a constant. +// +// To write an numerically-differentiable cost function for the above model, +// first define the object +// +// class QuadraticCostFunctor { +// public: +// explicit QuadraticCostFunctor(double a) : a_(a) {} +// bool operator()(const double* const xy, double* cost) const { +// constexpr int kInputVectorLength = 2; +// const double* const x = xy; +// const double* const y = xy + kInputVectorLength; +// *cost = x[0] * y[0] + x[1] * y[1] - a_; +// return true; +// } +// +// private: +// double a_; +// }; +// +// +// Note that in the declaration of operator() the input parameters xy +// come first, and are passed as const pointers to array of +// doubles. The output cost is the last parameter. +// +// Then given this class definition, the numerically differentiated +// first order function with central differences used for computing the +// derivative can be constructed as follows. +// +// FirstOrderFunction* function +// = new NumericDiffFirstOrderFunction<MyScalarCostFunctor, CENTRAL, 4>( +// new QuadraticCostFunctor(1.0)); ^ ^ ^ +// | | | +// Finite Differencing Scheme -+ | | +// Dimension of xy ------------------------+ +// +// +// In the instantiation above, the template parameters following +// "QuadraticCostFunctor", "CENTRAL, 4", describe the finite +// differencing scheme as "central differencing" and the functor as +// computing its cost from a 4 dimensional input. +template <typename FirstOrderFunctor, + NumericDiffMethodType method, + int kNumParameters> +class NumericDiffFirstOrderFunction final : public FirstOrderFunction { + public: + explicit NumericDiffFirstOrderFunction( + FirstOrderFunctor* functor, + Ownership ownership = TAKE_OWNERSHIP, + const NumericDiffOptions& options = NumericDiffOptions()) + : functor_(functor), ownership_(ownership), options_(options) { + static_assert(kNumParameters > 0, "kNumParameters must be positive"); + } + + ~NumericDiffFirstOrderFunction() override { + if (ownership_ != TAKE_OWNERSHIP) { + functor_.release(); + } + } + + bool Evaluate(const double* const parameters, + double* cost, + double* gradient) const override { + using ParameterDims = internal::StaticParameterDims<kNumParameters>; + constexpr int kNumResiduals = 1; + + // Get the function value (cost) at the the point to evaluate. + if (!internal::VariadicEvaluate<ParameterDims>( + *functor_, ¶meters, cost)) { + return false; + } + + if (gradient == nullptr) { + return true; + } + + // Create a copy of the parameters which will get mutated. + internal::FixedArray<double, 32> parameters_copy(kNumParameters); + std::copy_n(parameters, kNumParameters, parameters_copy.data()); + double* parameters_ptr = parameters_copy.data(); + internal::EvaluateJacobianForParameterBlocks< + ParameterDims>::template Apply<method, kNumResiduals>(functor_.get(), + cost, + options_, + kNumResiduals, + ¶meters_ptr, + &gradient); + return true; + } + + int NumParameters() const override { return kNumParameters; } + + const FirstOrderFunctor& functor() const { return *functor_; } + + private: + std::unique_ptr<FirstOrderFunctor> functor_; + Ownership ownership_; + NumericDiffOptions options_; +}; + +} // namespace ceres + +#endif // CERES_PUBLIC_NUMERIC_DIFF_FIRST_ORDER_FUNCTION_H_ diff --git a/extern/ceres/include/ceres/numeric_diff_options.h b/extern/ceres/include/ceres/numeric_diff_options.h index 64919ed5ab1..b025b51d938 100644 --- a/extern/ceres/include/ceres/numeric_diff_options.h +++ b/extern/ceres/include/ceres/numeric_diff_options.h @@ -32,7 +32,8 @@ #ifndef CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_ #define CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { @@ -70,4 +71,6 @@ struct CERES_EXPORT NumericDiffOptions { } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_NUMERIC_DIFF_OPTIONS_H_ diff --git a/extern/ceres/include/ceres/ordered_groups.h b/extern/ceres/include/ceres/ordered_groups.h index 954663c97e6..c1531cce65f 100644 --- a/extern/ceres/include/ceres/ordered_groups.h +++ b/extern/ceres/include/ceres/ordered_groups.h @@ -36,7 +36,7 @@ #include <unordered_map> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -190,7 +190,7 @@ class OrderedGroups { }; // Typedef for the most commonly used version of OrderedGroups. -typedef OrderedGroups<double*> ParameterBlockOrdering; +using ParameterBlockOrdering = OrderedGroups<double*>; } // namespace ceres diff --git a/extern/ceres/include/ceres/problem.h b/extern/ceres/include/ceres/problem.h index add12ea401d..819fa454b21 100644 --- a/extern/ceres/include/ceres/problem.h +++ b/extern/ceres/include/ceres/problem.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include "ceres/context.h" #include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" #include "ceres/types.h" #include "glog/logging.h" @@ -53,6 +54,7 @@ class CostFunction; class EvaluationCallback; class LossFunction; class LocalParameterization; +class Manifold; class Solver; struct CRSMatrix; @@ -65,7 +67,7 @@ class ResidualBlock; // A ResidualBlockId is an opaque handle clients can use to remove residual // blocks from a Problem after adding them. -typedef internal::ResidualBlock* ResidualBlockId; +using ResidualBlockId = internal::ResidualBlock*; // A class to represent non-linear least squares problems. Such // problems have a cost function that is a sum of error terms (known @@ -78,31 +80,28 @@ typedef internal::ResidualBlock* ResidualBlockId; // // where // -// r_ij is residual number i, component j; the residual is a -// function of some subset of the parameters x1...xk. For -// example, in a structure from motion problem a residual -// might be the difference between a measured point in an -// image and the reprojected position for the matching -// camera, point pair. The residual would have two -// components, error in x and error in y. +// r_ij is residual number i, component j; the residual is a function of some +// subset of the parameters x1...xk. For example, in a structure from +// motion problem a residual might be the difference between a measured +// point in an image and the reprojected position for the matching +// camera, point pair. The residual would have two components, error in x +// and error in y. // -// loss(y) is the loss function; for example, squared error or -// Huber L1 loss. If loss(y) = y, then the cost function is -// non-robustified least squares. +// loss(y) is the loss function; for example, squared error or Huber L1 +// loss. If loss(y) = y, then the cost function is non-robustified +// least squares. // -// This class is specifically designed to address the important subset -// of "sparse" least squares problems, where each component of the -// residual depends only on a small number number of parameters, even -// though the total number of residuals and parameters may be very -// large. This property affords tremendous gains in scale, allowing -// efficient solving of large problems that are otherwise -// inaccessible. +// This class is specifically designed to address the important subset of +// "sparse" least squares problems, where each component of the residual depends +// only on a small number number of parameters, even though the total number of +// residuals and parameters may be very large. This property affords tremendous +// gains in scale, allowing efficient solving of large problems that are +// otherwise inaccessible. // // The canonical example of a sparse least squares problem is -// "structure-from-motion" (SFM), where the parameters are points and -// cameras, and residuals are reprojection errors. Typically a single -// residual will depend only on 9 parameters (3 for the point, 6 for -// the camera). +// "structure-from-motion" (SFM), where the parameters are points and cameras, +// and residuals are reprojection errors. Typically a single residual will +// depend only on 9 parameters (3 for the point, 6 for the camera). // // To create a least squares problem, use the AddResidualBlock() and // AddParameterBlock() methods, documented below. Here is an example least @@ -119,41 +118,52 @@ typedef internal::ResidualBlock* ResidualBlockId; // problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x3); // // Please see cost_function.h for details of the CostFunction object. +// +// NOTE: We are currently in the process of transitioning from +// LocalParameterization to Manifolds in the Ceres API. During this period, +// Problem will support using both Manifold and LocalParameterization objects +// interchangably. In particular, adding a LocalParameterization to a parameter +// block is the same as adding a Manifold to that parameter block. For methods +// in the API affected by this change, see their documentation below. class CERES_EXPORT Problem { public: struct CERES_EXPORT Options { - // These flags control whether the Problem object owns the cost - // functions, loss functions, and parameterizations passed into - // the Problem. If set to TAKE_OWNERSHIP, then the problem object - // will delete the corresponding cost or loss functions on - // destruction. The destructor is careful to delete the pointers - // only once, since sharing cost/loss/parameterizations is - // allowed. + // These flags control whether the Problem object owns the CostFunctions, + // LossFunctions, LocalParameterizations, and Manifolds passed into the + // Problem. + // + // If set to TAKE_OWNERSHIP, then the problem object will delete the + // corresponding object on destruction. The destructor is careful to delete + // the pointers only once, since sharing objects is allowed. Ownership cost_function_ownership = TAKE_OWNERSHIP; Ownership loss_function_ownership = TAKE_OWNERSHIP; + CERES_DEPRECATED_WITH_MSG( + "Local Parameterizations are deprecated. Use Manifold and " + "manifold_ownership instead.") Ownership local_parameterization_ownership = TAKE_OWNERSHIP; + Ownership manifold_ownership = TAKE_OWNERSHIP; // If true, trades memory for faster RemoveResidualBlock() and // RemoveParameterBlock() operations. // // By default, RemoveParameterBlock() and RemoveResidualBlock() take time - // proportional to the size of the entire problem. If you only ever remove + // proportional to the size of the entire problem. If you only ever remove // parameters or residuals from the problem occasionally, this might be - // acceptable. However, if you have memory to spare, enable this option to + // acceptable. However, if you have memory to spare, enable this option to // make RemoveParameterBlock() take time proportional to the number of // residual blocks that depend on it, and RemoveResidualBlock() take (on // average) constant time. // - // The increase in memory usage is twofold: an additional hash set per + // The increase in memory usage is two-fold: an additional hash set per // parameter block containing all the residuals that depend on the parameter // block; and a hash set in the problem containing all residuals. bool enable_fast_removal = false; // By default, Ceres performs a variety of safety checks when constructing - // the problem. There is a small but measurable performance penalty to - // these checks, typically around 5% of construction time. If you are sure - // your problem construction is correct, and 5% of the problem construction - // time is truly an overhead you want to avoid, then you can set + // the problem. There is a small but measurable performance penalty to these + // checks, typically around 5% of construction time. If you are sure your + // problem construction is correct, and 5% of the problem construction time + // is truly an overhead you want to avoid, then you can set // disable_all_safety_checks to true. // // WARNING: Do not set this to true, unless you are absolutely sure of what @@ -167,26 +177,23 @@ class CERES_EXPORT Problem { // Ceres does NOT take ownership of the pointer. Context* context = nullptr; - // Using this callback interface, Ceres can notify you when it is - // about to evaluate the residuals or jacobians. With the - // callback, you can share computation between residual blocks by - // doing the shared computation in + // Using this callback interface, Ceres can notify you when it is about to + // evaluate the residuals or jacobians. With the callback, you can share + // computation between residual blocks by doing the shared computation in // EvaluationCallback::PrepareForEvaluation() before Ceres calls - // CostFunction::Evaluate(). It also enables caching results - // between a pure residual evaluation and a residual & jacobian - // evaluation. + // CostFunction::Evaluate(). It also enables caching results between a pure + // residual evaluation and a residual & jacobian evaluation. // // Problem DOES NOT take ownership of the callback. // - // NOTE: Evaluation callbacks are incompatible with inner - // iterations. So calling Solve with - // Solver::Options::use_inner_iterations = true on a Problem with - // a non-null evaluation callback is an error. + // NOTE: Evaluation callbacks are incompatible with inner iterations. So + // calling Solve with Solver::Options::use_inner_iterations = true on a + // Problem with a non-null evaluation callback is an error. EvaluationCallback* evaluation_callback = nullptr; }; - // The default constructor is equivalent to the - // invocation Problem(Problem::Options()). + // The default constructor is equivalent to the invocation + // Problem(Problem::Options()). Problem(); explicit Problem(const Options& options); Problem(Problem&&); @@ -197,31 +204,29 @@ class CERES_EXPORT Problem { ~Problem(); - // Add a residual block to the overall cost function. The cost - // function carries with its information about the sizes of the - // parameter blocks it expects. The function checks that these match - // the sizes of the parameter blocks listed in parameter_blocks. The - // program aborts if a mismatch is detected. loss_function can be - // nullptr, in which case the cost of the term is just the squared norm - // of the residuals. - // - // The user has the option of explicitly adding the parameter blocks - // using AddParameterBlock. This causes additional correctness - // checking; however, AddResidualBlock implicitly adds the parameter - // blocks if they are not present, so calling AddParameterBlock - // explicitly is not required. - // - // The Problem object by default takes ownership of the - // cost_function and loss_function pointers. These objects remain - // live for the life of the Problem object. If the user wishes to - // keep control over the destruction of these objects, then they can + // Add a residual block to the overall cost function. The cost function + // carries with its information about the sizes of the parameter blocks it + // expects. The function checks that these match the sizes of the parameter + // blocks listed in parameter_blocks. The program aborts if a mismatch is + // detected. loss_function can be nullptr, in which case the cost of the term + // is just the squared norm of the residuals. + // + // The user has the option of explicitly adding the parameter blocks using + // AddParameterBlock. This causes additional correctness checking; however, + // AddResidualBlock implicitly adds the parameter blocks if they are not + // present, so calling AddParameterBlock explicitly is not required. + // + // The Problem object by default takes ownership of the cost_function and + // loss_function pointers (See Problem::Options to override this behaviour). + // These objects remain live for the life of the Problem object. If the user + // wishes to keep control over the destruction of these objects, then they can // do this by setting the corresponding enums in the Options struct. // - // Note: Even though the Problem takes ownership of cost_function - // and loss_function, it does not preclude the user from re-using - // them in another residual block. The destructor takes care to call - // delete on each cost_function or loss_function pointer only once, - // regardless of how many residual blocks refer to them. + // Note: Even though the Problem takes ownership of cost_function and + // loss_function, it does not preclude the user from re-using them in another + // residual block. The destructor takes care to call delete on each + // cost_function or loss_function pointer only once, regardless of how many + // residual blocks refer to them. // // Example usage: // @@ -234,8 +239,8 @@ class CERES_EXPORT Problem { // problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, x1); // problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x1); // - // Add a residual block by listing the parameter block pointers - // directly instead of wapping them in a container. + // Add a residual block by listing the parameter block pointers directly + // instead of wapping them in a container. template <typename... Ts> ResidualBlockId AddResidualBlock(CostFunction* cost_function, LossFunction* loss_function, @@ -261,29 +266,75 @@ class CERES_EXPORT Problem { double* const* const parameter_blocks, int num_parameter_blocks); - // Add a parameter block with appropriate size to the problem. - // Repeated calls with the same arguments are ignored. Repeated - // calls with the same double pointer but a different size results - // in undefined behaviour. + // Add a parameter block with appropriate size to the problem. Repeated calls + // with the same arguments are ignored. Repeated calls with the same double + // pointer but a different size will result in a crash. void AddParameterBlock(double* values, int size); - // Add a parameter block with appropriate size and parameterization - // to the problem. Repeated calls with the same arguments are - // ignored. Repeated calls with the same double pointer but a - // different size results in undefined behaviour. + // Add a parameter block with appropriate size and parameterization to the + // problem. It is okay for local_parameterization to be nullptr. + // + // Repeated calls with the same arguments are ignored. Repeated calls + // with the same double pointer but a different size results in a crash + // (unless Solver::Options::diable_all_safety_checks is set to true). + // + // Repeated calls with the same double pointer and size but different + // LocalParameterization is equivalent to calling + // SetParameterization(local_parameterization), i.e., any previously + // associated LocalParameterization or Manifold object will be replaced with + // the local_parameterization. + // + // NOTE: + // ---- + // + // This method is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using the Manifold based version of + // AddParameterBlock. + // + // During the transition from LocalParameterization to Manifold, internally + // the LocalParameterization is treated as a Manifold by wrapping it using a + // ManifoldAdapter object. So HasManifold() will return true, GetManifold() + // will return the wrapped object and ParameterBlockTangentSize() will return + // the LocalSize of the LocalParameterization. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use the version with Manifolds " + "instead.") void AddParameterBlock(double* values, int size, LocalParameterization* local_parameterization); - // Remove a parameter block from the problem. The parameterization of the - // parameter block, if it exists, will persist until the deletion of the - // problem (similar to cost/loss functions in residual block removal). Any - // residual blocks that depend on the parameter are also removed, as - // described above in RemoveResidualBlock(). + // Add a parameter block with appropriate size and Manifold to the + // problem. It is okay for manifold to be nullptr. // - // If Problem::Options::enable_fast_removal is true, then the - // removal is fast (almost constant time). Otherwise, removing a parameter - // block will incur a scan of the entire Problem object. + // Repeated calls with the same arguments are ignored. Repeated calls + // with the same double pointer but a different size results in a crash + // (unless Solver::Options::diable_all_safety_checks is set to true). + // + // Repeated calls with the same double pointer and size but different Manifold + // is equivalent to calling SetManifold(manifold), i.e., any previously + // associated LocalParameterization or Manifold object will be replaced with + // the manifold. + // + // Note: + // ---- + // + // During the transition from LocalParameterization to Manifold, calling + // AddParameterBlock with a Manifold when a LocalParameterization is already + // associated with the parameter block is okay. It is equivalent to calling + // SetManifold(manifold), i.e., any previously associated + // LocalParameterization or Manifold object will be replaced with the + // manifold. + void AddParameterBlock(double* values, int size, Manifold* manifold); + + // Remove a parameter block from the problem. The LocalParameterization or + // Manifold of the parameter block, if it exists, will persist until the + // deletion of the problem (similar to cost/loss functions in residual block + // removal). Any residual blocks that depend on the parameter are also + // removed, as described above in RemoveResidualBlock(). + // + // If Problem::Options::enable_fast_removal is true, then the removal is fast + // (almost constant time). Otherwise, removing a parameter block will incur a + // scan of the entire Problem object. // // WARNING: Removing a residual or parameter block will destroy the implicit // ordering, rendering the jacobian or residuals returned from the solver @@ -308,35 +359,109 @@ class CERES_EXPORT Problem { // Allow the indicated parameter block to vary during optimization. void SetParameterBlockVariable(double* values); - // Returns true if a parameter block is set constant, and false - // otherwise. A parameter block may be set constant in two ways: - // either by calling SetParameterBlockConstant or by associating a - // LocalParameterization with a zero dimensional tangent space with - // it. + // Returns true if a parameter block is set constant, and false otherwise. A + // parameter block may be set constant in two ways: either by calling + // SetParameterBlockConstant or by associating a LocalParameterization or + // Manifold with a zero dimensional tangent space with it. bool IsParameterBlockConstant(const double* values) const; - // Set the local parameterization for one of the parameter blocks. - // The local_parameterization is owned by the Problem by default. It - // is acceptable to set the same parameterization for multiple - // parameters; the destructor is careful to delete local - // parameterizations only once. Calling SetParameterization with - // nullptr will clear any previously set parameterization. + // Set the LocalParameterization for the parameter block. Calling + // SetParameterization with nullptr will clear any previously set + // LocalParameterization or Manifold for the parameter block. + // + // Repeated calls will cause any previously associated LocalParameterization + // or Manifold object to be replaced with the local_parameterization. + // + // The local_parameterization is owned by the Problem by default (See + // Problem::Options to override this behaviour). + // + // It is acceptable to set the same LocalParameterization for multiple + // parameter blocks; the destructor is careful to delete + // LocalParamaterizations only once. + // + // NOTE: + // ---- + // + // This method is deprecated and will be removed in the next public + // release of Ceres Solver. Please move to using the SetManifold instead. + // + // During the transition from LocalParameterization to Manifold, internally + // the LocalParameterization is treated as a Manifold by wrapping it using a + // ManifoldAdapter object. So HasManifold() will return true, GetManifold() + // will return the wrapped object and ParameterBlockTangentSize will return + // the same value of ParameterBlockLocalSize. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use SetManifold instead.") void SetParameterization(double* values, LocalParameterization* local_parameterization); - // Get the local parameterization object associated with this - // parameter block. If there is no parameterization object - // associated then nullptr is returned. + // Get the LocalParameterization object associated with this parameter block. + // If there is no LocalParameterization associated then nullptr is returned. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. Use GetManifold instead. + // + // Note also that if a LocalParameterization is associated with a parameter + // block, HasManifold will return true and GetManifold will return the + // LocalParameterization wrapped in a ManifoldAdapter. + // + // The converse is NOT true, i.e., if a Manifold is associated with a + // parameter block, HasParameterization will return false and + // GetParameterization will return a nullptr. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use GetManifold " + "instead.") const LocalParameterization* GetParameterization(const double* values) const; + // Returns true if a LocalParameterization is associated with this parameter + // block, false otherwise. + // + // NOTE: This method is deprecated and will be removed in the next public + // release of Ceres Solver. Use HasManifold instead. + // + // Note also that if a Manifold is associated with the parameter block, this + // method will return false. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use HasManifold instead.") + bool HasParameterization(const double* values) const; + + // Set the Manifold for the parameter block. Calling SetManifold with nullptr + // will clear any previously set LocalParameterization or Manifold for the + // parameter block. + // + // Repeated calls will result in any previously associated + // LocalParameterization or Manifold object to be replaced with the manifold. + // + // The manifold is owned by the Problem by default (See Problem::Options to + // override this behaviour). + // + // It is acceptable to set the same Manifold for multiple parameter blocks. + void SetManifold(double* values, Manifold* manifold); + + // Get the Manifold object associated with this parameter block. + // + // If there is no Manifold Or LocalParameterization object associated then + // nullptr is returned. + // + // NOTE: During the transition from LocalParameterization to Manifold, + // internally the LocalParameterization is treated as a Manifold by wrapping + // it using a ManifoldAdapter object. So calling GetManifold on a parameter + // block with a LocalParameterization associated with it will return the + // LocalParameterization wrapped in a ManifoldAdapter + const Manifold* GetManifold(const double* values) const; + + // Returns true if a Manifold or a LocalParameterization is associated with + // this parameter block, false otherwise. + bool HasManifold(const double* values) const; + // Set the lower/upper bound for the parameter at position "index". void SetParameterLowerBound(double* values, int index, double lower_bound); void SetParameterUpperBound(double* values, int index, double upper_bound); - // Get the lower/upper bound for the parameter at position - // "index". If the parameter is not bounded by the user, then its - // lower bound is -std::numeric_limits<double>::max() and upper - // bound is std::numeric_limits<double>::max(). + // Get the lower/upper bound for the parameter at position "index". If the + // parameter is not bounded by the user, then its lower bound is + // -std::numeric_limits<double>::max() and upper bound is + // std::numeric_limits<double>::max(). double GetParameterLowerBound(const double* values, int index) const; double GetParameterUpperBound(const double* values, int index) const; @@ -344,37 +469,47 @@ class CERES_EXPORT Problem { // parameter_blocks().size() and parameter_block_sizes().size(). int NumParameterBlocks() const; - // The size of the parameter vector obtained by summing over the - // sizes of all the parameter blocks. + // The size of the parameter vector obtained by summing over the sizes of all + // the parameter blocks. int NumParameters() const; // Number of residual blocks in the problem. Always equals // residual_blocks().size(). int NumResidualBlocks() const; - // The size of the residual vector obtained by summing over the - // sizes of all of the residual blocks. + // The size of the residual vector obtained by summing over the sizes of all + // of the residual blocks. int NumResiduals() const; // The size of the parameter block. int ParameterBlockSize(const double* values) const; - // The size of local parameterization for the parameter block. If - // there is no local parameterization associated with this parameter - // block, then ParameterBlockLocalSize = ParameterBlockSize. + // The dimension of the tangent space of the LocalParameterization or Manifold + // for the parameter block. If there is no LocalParameterization or Manifold + // associated with this parameter block, then ParameterBlockLocalSize = + // ParameterBlockSize. + CERES_DEPRECATED_WITH_MSG( + "LocalParameterizations are deprecated. Use ParameterBlockTangentSize " + "instead.") int ParameterBlockLocalSize(const double* values) const; + // The dimenion of the tangent space of the LocalParameterization or Manifold + // for the parameter block. If there is no LocalParameterization or Manifold + // associated with this parameter block, then ParameterBlockTangentSize = + // ParameterBlockSize. + int ParameterBlockTangentSize(const double* values) const; + // Is the given parameter block present in this problem or not? bool HasParameterBlock(const double* values) const; - // Fills the passed parameter_blocks vector with pointers to the - // parameter blocks currently in the problem. After this call, - // parameter_block.size() == NumParameterBlocks. + // Fills the passed parameter_blocks vector with pointers to the parameter + // blocks currently in the problem. After this call, parameter_block.size() == + // NumParameterBlocks. void GetParameterBlocks(std::vector<double*>* parameter_blocks) const; - // Fills the passed residual_blocks vector with pointers to the - // residual blocks currently in the problem. After this call, - // residual_blocks.size() == NumResidualBlocks. + // Fills the passed residual_blocks vector with pointers to the residual + // blocks currently in the problem. After this call, residual_blocks.size() == + // NumResidualBlocks. void GetResidualBlocks(std::vector<ResidualBlockId>* residual_blocks) const; // Get all the parameter blocks that depend on the given residual block. @@ -393,10 +528,10 @@ class CERES_EXPORT Problem { // Get all the residual blocks that depend on the given parameter block. // - // If Problem::Options::enable_fast_removal is true, then - // getting the residual blocks is fast and depends only on the number of - // residual blocks. Otherwise, getting the residual blocks for a parameter - // block will incur a scan of the entire Problem object. + // If Problem::Options::enable_fast_removal is true, then getting the residual + // blocks is fast and depends only on the number of residual + // blocks. Otherwise, getting the residual blocks for a parameter block will + // incur a scan of the entire Problem object. void GetResidualBlocksForParameterBlock( const double* values, std::vector<ResidualBlockId>* residual_blocks) const; @@ -404,49 +539,45 @@ class CERES_EXPORT Problem { // Options struct to control Problem::Evaluate. struct EvaluateOptions { // The set of parameter blocks for which evaluation should be - // performed. This vector determines the order that parameter - // blocks occur in the gradient vector and in the columns of the - // jacobian matrix. If parameter_blocks is empty, then it is - // assumed to be equal to vector containing ALL the parameter - // blocks. Generally speaking the parameter blocks will occur in - // the order in which they were added to the problem. But, this - // may change if the user removes any parameter blocks from the - // problem. + // performed. This vector determines the order that parameter blocks occur + // in the gradient vector and in the columns of the jacobian matrix. If + // parameter_blocks is empty, then it is assumed to be equal to vector + // containing ALL the parameter blocks. Generally speaking the parameter + // blocks will occur in the order in which they were added to the + // problem. But, this may change if the user removes any parameter blocks + // from the problem. // - // NOTE: This vector should contain the same pointers as the ones - // used to add parameter blocks to the Problem. These parameter - // block should NOT point to new memory locations. Bad things will - // happen otherwise. + // NOTE: This vector should contain the same pointers as the ones used to + // add parameter blocks to the Problem. These parameter block should NOT + // point to new memory locations. Bad things will happen otherwise. std::vector<double*> parameter_blocks; - // The set of residual blocks to evaluate. This vector determines - // the order in which the residuals occur, and how the rows of the - // jacobian are ordered. If residual_blocks is empty, then it is - // assumed to be equal to the vector containing ALL the residual - // blocks. Generally speaking the residual blocks will occur in - // the order in which they were added to the problem. But, this - // may change if the user removes any residual blocks from the - // problem. + // The set of residual blocks to evaluate. This vector determines the order + // in which the residuals occur, and how the rows of the jacobian are + // ordered. If residual_blocks is empty, then it is assumed to be equal to + // the vector containing ALL the residual blocks. Generally speaking the + // residual blocks will occur in the order in which they were added to the + // problem. But, this may change if the user removes any residual blocks + // from the problem. std::vector<ResidualBlockId> residual_blocks; // Even though the residual blocks in the problem may contain loss - // functions, setting apply_loss_function to false will turn off - // the application of the loss function to the output of the cost - // function. This is of use for example if the user wishes to - // analyse the solution quality by studying the distribution of - // residuals before and after the solve. + // functions, setting apply_loss_function to false will turn off the + // application of the loss function to the output of the cost function. This + // is of use for example if the user wishes to analyse the solution quality + // by studying the distribution of residuals before and after the solve. bool apply_loss_function = true; int num_threads = 1; }; - // Evaluate Problem. Any of the output pointers can be nullptr. Which - // residual blocks and parameter blocks are used is controlled by - // the EvaluateOptions struct above. + // Evaluate Problem. Any of the output pointers can be nullptr. Which residual + // blocks and parameter blocks are used is controlled by the EvaluateOptions + // struct above. // - // Note 1: The evaluation will use the values stored in the memory - // locations pointed to by the parameter block pointers used at the - // time of the construction of the problem. i.e., + // Note 1: The evaluation will use the values stored in the memory locations + // pointed to by the parameter block pointers used at the time of the + // construction of the problem. i.e., // // Problem problem; // double x = 1; @@ -456,8 +587,8 @@ class CERES_EXPORT Problem { // problem.Evaluate(Problem::EvaluateOptions(), &cost, // nullptr, nullptr, nullptr); // - // The cost is evaluated at x = 1. If you wish to evaluate the - // problem at x = 2, then + // The cost is evaluated at x = 1. If you wish to evaluate the problem at x = + // 2, then // // x = 2; // problem.Evaluate(Problem::EvaluateOptions(), &cost, @@ -465,80 +596,75 @@ class CERES_EXPORT Problem { // // is the way to do so. // - // Note 2: If no local parameterizations are used, then the size of - // the gradient vector (and the number of columns in the jacobian) - // is the sum of the sizes of all the parameter blocks. If a - // parameter block has a local parameterization, then it contributes - // "LocalSize" entries to the gradient vector (and the number of - // columns in the jacobian). + // Note 2: If no LocalParameterizations or Manifolds are used, then the size + // of the gradient vector (and the number of columns in the jacobian) is the + // sum of the sizes of all the parameter blocks. If a parameter block has a + // LocalParameterization or Manifold, then it contributes "TangentSize" + // entries to the gradient vector (and the number of columns in the jacobian). // - // Note 3: This function cannot be called while the problem is being - // solved, for example it cannot be called from an IterationCallback - // at the end of an iteration during a solve. + // Note 3: This function cannot be called while the problem is being solved, + // for example it cannot be called from an IterationCallback at the end of an + // iteration during a solve. // - // Note 4: If an EvaluationCallback is associated with the problem, - // then its PrepareForEvaluation method will be called every time - // this method is called with new_point = true. + // Note 4: If an EvaluationCallback is associated with the problem, then its + // PrepareForEvaluation method will be called every time this method is called + // with new_point = true. bool Evaluate(const EvaluateOptions& options, double* cost, std::vector<double>* residuals, std::vector<double>* gradient, CRSMatrix* jacobian); - // Evaluates the residual block, storing the scalar cost in *cost, - // the residual components in *residuals, and the jacobians between - // the parameters and residuals in jacobians[i], in row-major order. + // Evaluates the residual block, storing the scalar cost in *cost, the + // residual components in *residuals, and the jacobians between the parameters + // and residuals in jacobians[i], in row-major order. // // If residuals is nullptr, the residuals are not computed. // - // If jacobians is nullptr, no Jacobians are computed. If - // jacobians[i] is nullptr, then the Jacobian for that parameter - // block is not computed. + // If jacobians is nullptr, no Jacobians are computed. If jacobians[i] is + // nullptr, then the Jacobian for that parameter block is not computed. // - // It is not okay to request the Jacobian w.r.t a parameter block - // that is constant. + // It is not okay to request the Jacobian w.r.t a parameter block that is + // constant. // - // The return value indicates the success or failure. Even if the - // function returns false, the caller should expect the output - // memory locations to have been modified. + // The return value indicates the success or failure. Even if the function + // returns false, the caller should expect the output memory locations to have + // been modified. // // The returned cost and jacobians have had robustification and - // local parameterizations applied already; for example, the - // jacobian for a 4-dimensional quaternion parameter using the - // "QuaternionParameterization" is num_residuals by 3 instead of - // num_residuals by 4. + // LocalParameterization/Manifold applied already; for example, the jacobian + // for a 4-dimensional quaternion parameter using the + // "QuaternionParameterization" is num_residuals by 3 instead of num_residuals + // by 4. // - // apply_loss_function as the name implies allows the user to switch - // the application of the loss function on and off. + // apply_loss_function as the name implies allows the user to switch the + // application of the loss function on and off. // // If an EvaluationCallback is associated with the problem, then its - // PrepareForEvaluation method will be called every time this method - // is called with new_point = true. This conservatively assumes that - // the user may have changed the parameter values since the previous - // call to evaluate / solve. For improved efficiency, and only if - // you know that the parameter values have not changed between - // calls, see EvaluateResidualBlockAssumingParametersUnchanged(). + // PrepareForEvaluation method will be called every time this method is called + // with new_point = true. This conservatively assumes that the user may have + // changed the parameter values since the previous call to evaluate / solve. + // For improved efficiency, and only if you know that the parameter values + // have not changed between calls, see + // EvaluateResidualBlockAssumingParametersUnchanged(). bool EvaluateResidualBlock(ResidualBlockId residual_block_id, bool apply_loss_function, double* cost, double* residuals, double** jacobians) const; - // Same as EvaluateResidualBlock except that if an - // EvaluationCallback is associated with the problem, then its - // PrepareForEvaluation method will be called every time this method - // is called with new_point = false. - // - // This means, if an EvaluationCallback is associated with the - // problem then it is the user's responsibility to call - // PrepareForEvaluation before calling this method if necessary, - // i.e. iff the parameter values have been changed since the last - // call to evaluate / solve.' - // - // This is because, as the name implies, we assume that the - // parameter blocks did not change since the last time - // PrepareForEvaluation was called (via Solve, Evaluate or - // EvaluateResidualBlock). + // Same as EvaluateResidualBlock except that if an EvaluationCallback is + // associated with the problem, then its PrepareForEvaluation method will be + // called every time this method is called with new_point = false. + // + // This means, if an EvaluationCallback is associated with the problem then it + // is the user's responsibility to call PrepareForEvaluation before calling + // this method if necessary, i.e. iff the parameter values have been changed + // since the last call to evaluate / solve.' + // + // This is because, as the name implies, we assume that the parameter blocks + // did not change since the last time PrepareForEvaluation was called (via + // Solve, Evaluate or EvaluateResidualBlock). bool EvaluateResidualBlockAssumingParametersUnchanged( ResidualBlockId residual_block_id, bool apply_loss_function, diff --git a/extern/ceres/include/ceres/product_manifold.h b/extern/ceres/include/ceres/product_manifold.h new file mode 100644 index 00000000000..33f046da24e --- /dev/null +++ b/extern/ceres/include/ceres/product_manifold.h @@ -0,0 +1,328 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) +// sergiu.deitsch@gmail.com (Sergiu Deitsch) +// + +#ifndef CERES_PUBLIC_PRODUCT_MANIFOLD_H_ +#define CERES_PUBLIC_PRODUCT_MANIFOLD_H_ + +#include <algorithm> +#include <array> +#include <cassert> +#include <cstddef> +#include <numeric> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "ceres/internal/eigen.h" +#include "ceres/internal/fixed_array.h" +#include "ceres/internal/port.h" +#include "ceres/manifold.h" + +namespace ceres { + +// Construct a manifold by taking the Cartesian product of a number of other +// manifolds. This is useful, when a parameter block is the Cartesian product +// of two or more manifolds. For example the parameters of a camera consist of +// a rotation and a translation, i.e., SO(3) x R^3. +// +// Example usage: +// +// ProductManifold<QuaternionManifold, EuclideanManifold<3>> se3; +// +// is the manifold for a rigid transformation, where the rotation is +// represented using a quaternion. +// +// Manifolds can be copied and moved to ProductManifold: +// +// SubsetManifold manifold1(5, {2}); +// SubsetManifold manifold2(3, {0, 1}); +// ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1, +// manifold2); +// +// In advanced use cases, manifolds can be dynamically allocated and passed as +// (smart) pointers: +// +// ProductManifold<std::unique_ptr<QuaternionManifold>, EuclideanManifold<3>> +// se3{std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}}; +// +// In C++17, the template parameters can be left out as they are automatically +// deduced making the initialization much simpler: +// +// ProductManifold se3{QuaternionManifold{}, EuclideanManifold<3>{}}; +// +// The manifold implementations must be either default constructible, copyable +// or moveable to be usable in a ProductManifold. +template <typename Manifold0, typename Manifold1, typename... ManifoldN> +class ProductManifold final : public Manifold { + public: + // ProductManifold constructor perfect forwards arguments to store manifolds. + // + // Either use default construction or if you need to copy or move-construct a + // manifold instance, you need to pass an instance as an argument for all + // types given as class template parameters. + template <typename... Args, + std::enable_if_t<std::is_constructible< + std::tuple<Manifold0, Manifold1, ManifoldN...>, + Args...>::value>* = nullptr> + explicit ProductManifold(Args&&... manifolds) + : ProductManifold{std::make_index_sequence<kNumManifolds>{}, + std::forward<Args>(manifolds)...} {} + + int AmbientSize() const override { return ambient_size_; } + int TangentSize() const override { return tangent_size_; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override { + return PlusImpl( + x, delta, x_plus_delta, std::make_index_sequence<kNumManifolds>{}); + } + + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override { + return MinusImpl( + y, x, y_minus_x, std::make_index_sequence<kNumManifolds>{}); + } + + bool PlusJacobian(const double* x, double* jacobian_ptr) const override { + MatrixRef jacobian(jacobian_ptr, AmbientSize(), TangentSize()); + jacobian.setZero(); + internal::FixedArray<double> buffer(buffer_size_); + + return PlusJacobianImpl( + x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{}); + } + + bool MinusJacobian(const double* x, double* jacobian_ptr) const override { + MatrixRef jacobian(jacobian_ptr, TangentSize(), AmbientSize()); + jacobian.setZero(); + internal::FixedArray<double> buffer(buffer_size_); + + return MinusJacobianImpl( + x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{}); + } + + private: + static constexpr std::size_t kNumManifolds = 2 + sizeof...(ManifoldN); + + template <std::size_t... Indices, typename... Args> + explicit ProductManifold(std::index_sequence<Indices...>, Args&&... manifolds) + : manifolds_{std::forward<Args>(manifolds)...}, + buffer_size_{(std::max)( + {(Dereference(std::get<Indices>(manifolds_)).TangentSize() * + Dereference(std::get<Indices>(manifolds_)).AmbientSize())...})}, + ambient_sizes_{ + Dereference(std::get<Indices>(manifolds_)).AmbientSize()...}, + tangent_sizes_{ + Dereference(std::get<Indices>(manifolds_)).TangentSize()...}, + ambient_offsets_{ExclusiveScan(ambient_sizes_)}, + tangent_offsets_{ExclusiveScan(tangent_sizes_)}, + ambient_size_{ + std::accumulate(ambient_sizes_.begin(), ambient_sizes_.end(), 0)}, + tangent_size_{ + std::accumulate(tangent_sizes_.begin(), tangent_sizes_.end(), 0)} {} + + template <std::size_t Index0, std::size_t... Indices> + bool PlusImpl(const double* x, + const double* delta, + double* x_plus_delta, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .Plus(x + ambient_offsets_[Index0], + delta + tangent_offsets_[Index0], + x_plus_delta + ambient_offsets_[Index0])) { + return false; + } + + return PlusImpl(x, delta, x_plus_delta, std::index_sequence<Indices...>{}); + } + + static constexpr bool PlusImpl(const double* /*x*/, + const double* /*delta*/, + double* /*x_plus_delta*/, + std::index_sequence<>) noexcept { + return true; + } + + template <std::size_t Index0, std::size_t... Indices> + bool MinusImpl(const double* y, + const double* x, + double* y_minus_x, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .Minus(y + ambient_offsets_[Index0], + x + ambient_offsets_[Index0], + y_minus_x + tangent_offsets_[Index0])) { + return false; + } + + return MinusImpl(y, x, y_minus_x, std::index_sequence<Indices...>{}); + } + + static constexpr bool MinusImpl(const double* /*y*/, + const double* /*x*/, + double* /*y_minus_x*/, + std::index_sequence<>) noexcept { + return true; + } + + template <std::size_t Index0, std::size_t... Indices> + bool PlusJacobianImpl(const double* x, + MatrixRef& jacobian, + internal::FixedArray<double>& buffer, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .PlusJacobian(x + ambient_offsets_[Index0], buffer.data())) { + return false; + } + + jacobian.block(ambient_offsets_[Index0], + tangent_offsets_[Index0], + ambient_sizes_[Index0], + tangent_sizes_[Index0]) = + MatrixRef( + buffer.data(), ambient_sizes_[Index0], tangent_sizes_[Index0]); + + return PlusJacobianImpl( + x, jacobian, buffer, std::index_sequence<Indices...>{}); + } + + static constexpr bool PlusJacobianImpl( + const double* /*x*/, + MatrixRef& /*jacobian*/, + internal::FixedArray<double>& /*buffer*/, + std::index_sequence<>) noexcept { + return true; + } + + template <std::size_t Index0, std::size_t... Indices> + bool MinusJacobianImpl(const double* x, + MatrixRef& jacobian, + internal::FixedArray<double>& buffer, + std::index_sequence<Index0, Indices...>) const { + if (!Dereference(std::get<Index0>(manifolds_)) + .MinusJacobian(x + ambient_offsets_[Index0], buffer.data())) { + return false; + } + + jacobian.block(tangent_offsets_[Index0], + ambient_offsets_[Index0], + tangent_sizes_[Index0], + ambient_sizes_[Index0]) = + MatrixRef( + buffer.data(), tangent_sizes_[Index0], ambient_sizes_[Index0]); + + return MinusJacobianImpl( + x, jacobian, buffer, std::index_sequence<Indices...>{}); + } + + static constexpr bool MinusJacobianImpl( + const double* /*x*/, + MatrixRef& /*jacobian*/, + internal::FixedArray<double>& /*buffer*/, + std::index_sequence<>) noexcept { + return true; + } + + template <typename T, std::size_t N> + static std::array<T, N> ExclusiveScan(const std::array<T, N>& values) { + std::array<T, N> result; + T init = 0; + + // TODO Replace by std::exclusive_scan once C++17 is available + for (std::size_t i = 0; i != N; ++i) { + result[i] = init; + init += values[i]; + } + + return result; + } + + // TODO Replace by std::void_t once C++17 is available + template <typename... Types> + struct Void { + using type = void; + }; + + template <typename T, typename E = void> + struct IsDereferenceable : std::false_type {}; + + template <typename T> + struct IsDereferenceable<T, typename Void<decltype(*std::declval<T>())>::type> + : std::true_type {}; + + template <typename T, + std::enable_if_t<!IsDereferenceable<T>::value>* = nullptr> + static constexpr decltype(auto) Dereference(T& value) { + return value; + } + + // Support dereferenceable types such as std::unique_ptr, std::shared_ptr, raw + // pointers etc. + template <typename T, + std::enable_if_t<IsDereferenceable<T>::value>* = nullptr> + static constexpr decltype(auto) Dereference(T& value) { + return *value; + } + + template <typename T> + static constexpr decltype(auto) Dereference(T* p) { + assert(p != nullptr); + return *p; + } + + std::tuple<Manifold0, Manifold1, ManifoldN...> manifolds_; + int buffer_size_; + std::array<int, kNumManifolds> ambient_sizes_; + std::array<int, kNumManifolds> tangent_sizes_; + std::array<int, kNumManifolds> ambient_offsets_; + std::array<int, kNumManifolds> tangent_offsets_; + int ambient_size_; + int tangent_size_; +}; + +#ifdef CERES_HAS_CPP17 +// C++17 deduction guide that allows the user to avoid explicitly specifying +// the template parameters of ProductManifold. The class can instead be +// instantiated as follows: +// +// ProductManifold manifold{QuaternionManifold{}, EuclideanManifold<3>{}}; +// +template <typename Manifold0, typename Manifold1, typename... Manifolds> +ProductManifold(Manifold0&&, Manifold1&&, Manifolds&&...) + -> ProductManifold<Manifold0, Manifold1, Manifolds...>; +#endif + +} // namespace ceres + +#endif // CERES_PUBLIC_PRODUCT_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/rotation.h b/extern/ceres/include/ceres/rotation.h index 0c82a417a2c..51079901aaf 100644 --- a/extern/ceres/include/ceres/rotation.h +++ b/extern/ceres/include/ceres/rotation.h @@ -521,18 +521,18 @@ inline void UnitQuaternionRotatePoint(const T q[4], DCHECK_NE(pt, result) << "Inplace rotation is not supported."; // clang-format off - const T t2 = q[0] * q[1]; - const T t3 = q[0] * q[2]; - const T t4 = q[0] * q[3]; - const T t5 = -q[1] * q[1]; - const T t6 = q[1] * q[2]; - const T t7 = q[1] * q[3]; - const T t8 = -q[2] * q[2]; - const T t9 = q[2] * q[3]; - const T t1 = -q[3] * q[3]; - result[0] = T(2) * ((t8 + t1) * pt[0] + (t6 - t4) * pt[1] + (t3 + t7) * pt[2]) + pt[0]; // NOLINT - result[1] = T(2) * ((t4 + t6) * pt[0] + (t5 + t1) * pt[1] + (t9 - t2) * pt[2]) + pt[1]; // NOLINT - result[2] = T(2) * ((t7 - t3) * pt[0] + (t2 + t9) * pt[1] + (t5 + t8) * pt[2]) + pt[2]; // NOLINT + T uv0 = q[2] * pt[2] - q[3] * pt[1]; + T uv1 = q[3] * pt[0] - q[1] * pt[2]; + T uv2 = q[1] * pt[1] - q[2] * pt[0]; + uv0 += uv0; + uv1 += uv1; + uv2 += uv2; + result[0] = pt[0] + q[0] * uv0; + result[1] = pt[1] + q[0] * uv1; + result[2] = pt[2] + q[0] * uv2; + result[0] += q[2] * uv2 - q[3] * uv1; + result[1] += q[3] * uv0 - q[1] * uv2; + result[2] += q[1] * uv1 - q[2] * uv0; // clang-format on } @@ -624,16 +624,16 @@ inline void AngleAxisRotatePoint(const T angle_axis[3], result[2] = pt[2] * costheta + w_cross_pt[2] * sintheta + w[2] * tmp; } else { // Near zero, the first order Taylor approximation of the rotation - // matrix R corresponding to a vector w and angle w is + // matrix R corresponding to a vector w and angle theta is // // R = I + hat(w) * sin(theta) // // But sintheta ~ theta and theta * w = angle_axis, which gives us // - // R = I + hat(w) + // R = I + hat(angle_axis) // // and actually performing multiplication with the point pt, gives us - // R * pt = pt + w x pt. + // R * pt = pt + angle_axis x pt. // // Switching to the Taylor expansion near zero provides meaningful // derivatives when evaluated using Jets. diff --git a/extern/ceres/include/ceres/sized_cost_function.h b/extern/ceres/include/ceres/sized_cost_function.h index 8e92f1b796c..d76b5c26b4c 100644 --- a/extern/ceres/include/ceres/sized_cost_function.h +++ b/extern/ceres/include/ceres/sized_cost_function.h @@ -61,8 +61,6 @@ class SizedCostFunction : public CostFunction { *mutable_parameter_block_sizes() = std::vector<int32_t>{Ns...}; } - virtual ~SizedCostFunction() {} - // Subclasses must implement Evaluate(). }; diff --git a/extern/ceres/include/ceres/solver.h b/extern/ceres/include/ceres/solver.h index 61b8dd53eb3..026fc1c0830 100644 --- a/extern/ceres/include/ceres/solver.h +++ b/extern/ceres/include/ceres/solver.h @@ -38,8 +38,9 @@ #include <vector> #include "ceres/crs_matrix.h" +#include "ceres/internal/config.h" #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/ordered_groups.h" #include "ceres/problem.h" @@ -363,23 +364,23 @@ class CERES_EXPORT Solver { std::unordered_set<ResidualBlockId> residual_blocks_for_subset_preconditioner; - // Ceres supports using multiple dense linear algebra libraries - // for dense matrix factorizations. Currently EIGEN and LAPACK are - // the valid choices. EIGEN is always available, LAPACK refers to - // the system BLAS + LAPACK library which may or may not be + // Ceres supports using multiple dense linear algebra libraries for dense + // matrix factorizations. Currently EIGEN, LAPACK and CUDA are the valid + // choices. EIGEN is always available, LAPACK refers to the system BLAS + + // LAPACK library which may or may not be available. CUDA refers to Nvidia's + // GPU based dense linear algebra library, which may or may not be // available. // - // This setting affects the DENSE_QR, DENSE_NORMAL_CHOLESKY and - // DENSE_SCHUR solvers. For small to moderate sized problem EIGEN - // is a fine choice but for large problems, an optimized LAPACK + - // BLAS implementation can make a substantial difference in - // performance. + // This setting affects the DENSE_QR, DENSE_NORMAL_CHOLESKY and DENSE_SCHUR + // solvers. For small to moderate sized problem EIGEN is a fine choice but + // for large problems, an optimized LAPACK + BLAS or CUDA implementation can + // make a substantial difference in performance. DenseLinearAlgebraLibraryType dense_linear_algebra_library_type = EIGEN; - // Ceres supports using multiple sparse linear algebra libraries - // for sparse matrix ordering and factorizations. Currently, - // SUITE_SPARSE and CX_SPARSE are the valid choices, depending on - // whether they are linked into Ceres at build time. + // Ceres supports using multiple sparse linear algebra libraries for sparse + // matrix ordering and factorizations. Currently, SUITE_SPARSE and CX_SPARSE + // are the valid choices, depending on whether they are linked into Ceres at + // build time. SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type = #if !defined(CERES_NO_SUITESPARSE) SUITE_SPARSE; @@ -423,7 +424,7 @@ class CERES_EXPORT Solver { // each group, Ceres is free to order the parameter blocks as it // chooses. // - // If NULL, then all parameter blocks are assumed to be in the + // If nullptr, then all parameter blocks are assumed to be in the // same group and the solver is free to decide the best // ordering. // @@ -536,8 +537,9 @@ class CERES_EXPORT Solver { // max_num_refinement_iterations to 2-3. // // NOTE2: The following two options are currently only applicable - // if sparse_linear_algebra_library_type is EIGEN_SPARSE and - // linear_solver_type is SPARSE_NORMAL_CHOLESKY, or SPARSE_SCHUR. + // if sparse_linear_algebra_library_type is EIGEN_SPARSE or + // ACCELERATE_SPARSE, and linear_solver_type is SPARSE_NORMAL_CHOLESKY + // or SPARSE_SCHUR. bool use_mixed_precision_solves = false; // Number steps of the iterative refinement process to run when @@ -882,7 +884,7 @@ class CERES_EXPORT Solver { // Dimension of the tangent space of the problem (or the number of // columns in the Jacobian for the problem). This is different // from num_parameters if a parameter block is associated with a - // LocalParameterization + // LocalParameterization/Manifold. int num_effective_parameters = -1; // Number of residual blocks in the problem. @@ -903,7 +905,7 @@ class CERES_EXPORT Solver { // number of columns in the Jacobian for the reduced // problem). This is different from num_parameters_reduced if a // parameter block in the reduced problem is associated with a - // LocalParameterization. + // LocalParameterization/Manifold. int num_effective_parameters_reduced = -1; // Number of residual blocks in the reduced problem. diff --git a/extern/ceres/include/ceres/sphere_manifold.h b/extern/ceres/include/ceres/sphere_manifold.h new file mode 100644 index 00000000000..5d71cbbca9a --- /dev/null +++ b/extern/ceres/include/ceres/sphere_manifold.h @@ -0,0 +1,231 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Mike Vitus) +// jodebo_beck@gmx.de (Johannes Beck) + +#ifndef CERES_PUBLIC_SPHERE_MANIFOLD_H_ +#define CERES_PUBLIC_SPHERE_MANIFOLD_H_ + +#include <Eigen/Core> +#include <algorithm> +#include <array> +#include <memory> +#include <vector> + +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/internal/householder_vector.h" +#include "ceres/internal/sphere_manifold_functions.h" +#include "ceres/manifold.h" +#include "ceres/types.h" +#include "glog/logging.h" + +namespace ceres { + +// This provides a manifold on a sphere meaning that the norm of the vector +// stays the same. Such cases often arises in Structure for Motion +// problems. One example where they are used is in representing points whose +// triangulation is ill-conditioned. Here it is advantageous to use an +// over-parameterization since homogeneous vectors can represent points at +// infinity. +// +// The plus operator is defined as +// Plus(x, delta) = +// [sin(0.5 * |delta|) * delta / |delta|, cos(0.5 * |delta|)] * x +// +// The minus operator is defined as +// Minus(x, y) = 2 atan2(nhy, y[-1]) / nhy * hy[0 : size_ - 1] +// with nhy = norm(hy[0 : size_ - 1]) +// +// with * defined as an operator which applies the update orthogonal to x to +// remain on the sphere. The ambient space dimension is required to be greater +// than 1. +// +// The class works with dynamic and static ambient space dimensions. If the +// ambient space dimensions is known at compile time use +// +// SphereManifold<3> manifold; +// +// If the ambient space dimensions is not known at compile time the template +// parameter needs to be set to ceres::DYNAMIC and the actual dimension needs +// to be provided as a constructor argument: +// +// SphereManifold<ceres::DYNAMIC> manifold(ambient_dim); +// +// See section B.2 (p.25) in "Integrating Generic Sensor Fusion Algorithms +// with Sound State Representations through Encapsulation of Manifolds" by C. +// Hertzberg, R. Wagner, U. Frese and L. Schroder for more details +// (https://arxiv.org/pdf/1107.1119.pdf) +template <int AmbientSpaceDimension> +class SphereManifold final : public Manifold { + public: + static_assert( + AmbientSpaceDimension == ceres::DYNAMIC || AmbientSpaceDimension > 1, + "The size of the homogeneous vector needs to be greater than 1."); + static_assert(ceres::DYNAMIC == Eigen::Dynamic, + "ceres::DYNAMIC needs to be the same as Eigen::Dynamic."); + + SphereManifold(); + explicit SphereManifold(int size); + + int AmbientSize() const override { + return AmbientSpaceDimension == ceres::DYNAMIC ? size_ + : AmbientSpaceDimension; + } + int TangentSize() const override { return AmbientSize() - 1; } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override; + bool PlusJacobian(const double* x, double* jacobian) const override; + + bool Minus(const double* y, + const double* x, + double* y_minus_x) const override; + bool MinusJacobian(const double* x, double* jacobian) const override; + + private: + static constexpr int TangentSpaceDimension = + AmbientSpaceDimension > 0 ? AmbientSpaceDimension - 1 : Eigen::Dynamic; + + using AmbientVector = Eigen::Matrix<double, AmbientSpaceDimension, 1>; + using TangentVector = Eigen::Matrix<double, TangentSpaceDimension, 1>; + using MatrixPlusJacobian = Eigen::Matrix<double, + AmbientSpaceDimension, + TangentSpaceDimension, + Eigen::RowMajor>; + using MatrixMinusJacobian = Eigen::Matrix<double, + TangentSpaceDimension, + AmbientSpaceDimension, + Eigen::RowMajor>; + + const int size_{}; +}; + +template <int AmbientSpaceDimension> +SphereManifold<AmbientSpaceDimension>::SphereManifold() + : size_{AmbientSpaceDimension} { + static_assert( + AmbientSpaceDimension != Eigen::Dynamic, + "The size is set to dynamic. Please call the constructor with a size."); +} + +template <int AmbientSpaceDimension> +SphereManifold<AmbientSpaceDimension>::SphereManifold(int size) : size_{size} { + if (AmbientSpaceDimension != Eigen::Dynamic) { + CHECK_EQ(AmbientSpaceDimension, size) + << "Specified size by template parameter differs from the supplied " + "one."; + } else { + CHECK_GT(size_, 1) + << "The size of the manifold needs to be greater than 1."; + } +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::Plus( + const double* x_ptr, + const double* delta_ptr, + double* x_plus_delta_ptr) const { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<const TangentVector> delta(delta_ptr, size_ - 1); + Eigen::Map<AmbientVector> x_plus_delta(x_plus_delta_ptr, size_); + + const double norm_delta = delta.norm(); + + if (norm_delta == 0.0) { + x_plus_delta = x; + return true; + } + + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(x, &v, &beta); + + internal::ComputeSphereManifoldPlus( + v, beta, x, delta, norm_delta, &x_plus_delta); + + return true; +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::PlusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<MatrixPlusJacobian> jacobian(jacobian_ptr, size_, size_ - 1); + internal::ComputeSphereManifoldPlusJacobian(x, &jacobian); + + return true; +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::Minus(const double* y_ptr, + const double* x_ptr, + double* y_minus_x_ptr) const { + AmbientVector y = Eigen::Map<const AmbientVector>(y_ptr, size_); + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<TangentVector> y_minus_x(y_minus_x_ptr, size_ - 1); + + // Apply hoseholder transformation. + AmbientVector v(size_); + double beta; + + // NOTE: The explicit template arguments are needed here because + // ComputeHouseholderVector is templated and some versions of MSVC + // have trouble deducing the type of v automatically. + internal::ComputeHouseholderVector<Eigen::Map<const AmbientVector>, + double, + AmbientSpaceDimension>(x, &v, &beta); + internal::ComputeSphereManifoldMinus(v, beta, x, y, &y_minus_x); + return true; +} + +template <int AmbientSpaceDimension> +bool SphereManifold<AmbientSpaceDimension>::MinusJacobian( + const double* x_ptr, double* jacobian_ptr) const { + Eigen::Map<const AmbientVector> x(x_ptr, size_); + Eigen::Map<MatrixMinusJacobian> jacobian(jacobian_ptr, size_ - 1, size_); + + internal::ComputeSphereManifoldMinusJacobian(x, &jacobian); + return true; +} + +} // namespace ceres + +// clang-format off +#include "ceres/internal/reenable_warnings.h" +// clang-format on + +#endif // CERES_PUBLIC_SPHERE_MANIFOLD_H_ diff --git a/extern/ceres/include/ceres/tiny_solver.h b/extern/ceres/include/ceres/tiny_solver.h index 47db5824dc5..86a655dc07d 100644 --- a/extern/ceres/include/ceres/tiny_solver.h +++ b/extern/ceres/include/ceres/tiny_solver.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -84,7 +84,8 @@ namespace ceres { // double* parameters -- NUM_PARAMETERS or NumParameters() // double* residuals -- NUM_RESIDUALS or NumResiduals() // double* jacobian -- NUM_RESIDUALS * NUM_PARAMETERS in column-major format -// (Eigen's default); or NULL if no jacobian requested. +// (Eigen's default); or nullptr if no jacobian +// requested. // // An example (fully statically sized): // @@ -126,8 +127,8 @@ namespace ceres { // template <typename Function, typename LinearSolver = - Eigen::LDLT<Eigen::Matrix<typename Function::Scalar, - Function::NUM_PARAMETERS, + Eigen::LDLT<Eigen::Matrix<typename Function::Scalar, // + Function::NUM_PARAMETERS, // Function::NUM_PARAMETERS>>> class TinySolver { public: @@ -139,41 +140,59 @@ class TinySolver { NUM_RESIDUALS = Function::NUM_RESIDUALS, NUM_PARAMETERS = Function::NUM_PARAMETERS }; - typedef typename Function::Scalar Scalar; - typedef typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1> Parameters; + using Scalar = typename Function::Scalar; + using Parameters = typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1>; enum Status { - GRADIENT_TOO_SMALL, // eps > max(J'*f(x)) - RELATIVE_STEP_SIZE_TOO_SMALL, // eps > ||dx|| / (||x|| + eps) - COST_TOO_SMALL, // eps > ||f(x)||^2 / 2 + // max_norm |J'(x) * f(x)| < gradient_tolerance + GRADIENT_TOO_SMALL, + // ||dx|| <= parameter_tolerance * (||x|| + parameter_tolerance) + RELATIVE_STEP_SIZE_TOO_SMALL, + // cost_threshold > ||f(x)||^2 / 2 + COST_TOO_SMALL, + // num_iterations >= max_num_iterations HIT_MAX_ITERATIONS, + // (new_cost - old_cost) < function_tolerance * old_cost + COST_CHANGE_TOO_SMALL, // TODO(sameeragarwal): Deal with numerical failures. }; struct Options { - Scalar gradient_tolerance = 1e-10; // eps > max(J'*f(x)) - Scalar parameter_tolerance = 1e-8; // eps > ||dx|| / ||x|| - Scalar cost_threshold = // eps > ||f(x)|| - std::numeric_limits<Scalar>::epsilon(); - Scalar initial_trust_region_radius = 1e4; int max_num_iterations = 50; + + // max_norm |J'(x) * f(x)| < gradient_tolerance + Scalar gradient_tolerance = 1e-10; + + // ||dx|| <= parameter_tolerance * (||x|| + parameter_tolerance) + Scalar parameter_tolerance = 1e-8; + + // (new_cost - old_cost) < function_tolerance * old_cost + Scalar function_tolerance = 1e-6; + + // cost_threshold > ||f(x)||^2 / 2 + Scalar cost_threshold = std::numeric_limits<Scalar>::epsilon(); + + Scalar initial_trust_region_radius = 1e4; }; struct Summary { - Scalar initial_cost = -1; // 1/2 ||f(x)||^2 - Scalar final_cost = -1; // 1/2 ||f(x)||^2 - Scalar gradient_max_norm = -1; // max(J'f(x)) + // 1/2 ||f(x_0)||^2 + Scalar initial_cost = -1; + // 1/2 ||f(x)||^2 + Scalar final_cost = -1; + // max_norm(J'f(x)) + Scalar gradient_max_norm = -1; int iterations = -1; Status status = HIT_MAX_ITERATIONS; }; bool Update(const Function& function, const Parameters& x) { - if (!function(x.data(), error_.data(), jacobian_.data())) { + if (!function(x.data(), residuals_.data(), jacobian_.data())) { return false; } - error_ = -error_; + residuals_ = -residuals_; // On the first iteration, compute a diagonal (Jacobi) scaling // matrix, which we store as a vector. @@ -192,9 +211,9 @@ class TinySolver { // factorization. jacobian_ = jacobian_ * jacobi_scaling_.asDiagonal(); jtj_ = jacobian_.transpose() * jacobian_; - g_ = jacobian_.transpose() * error_; + g_ = jacobian_.transpose() * residuals_; summary.gradient_max_norm = g_.array().abs().maxCoeff(); - cost_ = error_.squaredNorm() / 2; + cost_ = residuals_.squaredNorm() / 2; return true; } @@ -231,7 +250,7 @@ class TinySolver { const Scalar max_diagonal = 1e32; for (int i = 0; i < lm_diagonal_.rows(); ++i) { lm_diagonal_[i] = std::sqrt( - u * std::min(std::max(jtj_(i, i), min_diagonal), max_diagonal)); + u * (std::min)((std::max)(jtj_(i, i), min_diagonal), max_diagonal)); jtj_regularized_(i, i) += lm_diagonal_[i] * lm_diagonal_[i]; } @@ -253,10 +272,9 @@ class TinySolver { // TODO(keir): Add proper handling of errors from user eval of cost // functions. - function(&x_new_[0], &f_x_new_[0], NULL); + function(&x_new_[0], &f_x_new_[0], nullptr); const Scalar cost_change = (2 * cost_ - f_x_new_.squaredNorm()); - // TODO(sameeragarwal): Better more numerically stable evaluation. const Scalar model_cost_change = lm_step_.dot(2 * g_ - jtj_ * lm_step_); @@ -269,6 +287,12 @@ class TinySolver { // model fits well. x = x_new_; + if (std::abs(cost_change) < options.function_tolerance) { + cost_ = f_x_new_.squaredNorm() / 2; + summary.status = COST_CHANGE_TOO_SMALL; + break; + } + // TODO(sameeragarwal): Deal with failure. Update(function, x); if (summary.gradient_max_norm < options.gradient_tolerance) { @@ -282,16 +306,24 @@ class TinySolver { } Scalar tmp = Scalar(2 * rho - 1); - u = u * std::max(1 / 3., 1 - tmp * tmp * tmp); + u = u * (std::max)(Scalar(1 / 3.), Scalar(1) - tmp * tmp * tmp); v = 2; - continue; - } - // Reject the update because either the normal equations failed to solve - // or the local linear model was not good (rho < 0). Instead, increase u - // to move closer to gradient descent. - u *= v; - v *= 2; + } else { + // Reject the update because either the normal equations failed to solve + // or the local linear model was not good (rho < 0). + + // Additionally if the cost change is too small, then terminate. + if (std::abs(cost_change) < options.function_tolerance) { + // Terminate + summary.status = COST_CHANGE_TOO_SMALL; + break; + } + + // Reduce the size of the trust region. + u *= v; + v *= 2; + } } summary.final_cost = cost_; @@ -307,7 +339,7 @@ class TinySolver { LinearSolver linear_solver_; Scalar cost_; Parameters dx_, x_new_, g_, jacobi_scaling_, lm_diagonal_, lm_step_; - Eigen::Matrix<Scalar, NUM_RESIDUALS, 1> error_, f_x_new_; + Eigen::Matrix<Scalar, NUM_RESIDUALS, 1> residuals_, f_x_new_; Eigen::Matrix<Scalar, NUM_RESIDUALS, NUM_PARAMETERS> jacobian_; Eigen::Matrix<Scalar, NUM_PARAMETERS, NUM_PARAMETERS> jtj_, jtj_regularized_; @@ -317,7 +349,7 @@ class TinySolver { template <typename T> struct enable_if<true, T> { - typedef T type; + using type = T; }; // The number of parameters and residuals are dynamically sized. @@ -355,7 +387,7 @@ class TinySolver { jacobi_scaling_.resize(num_parameters); lm_diagonal_.resize(num_parameters); lm_step_.resize(num_parameters); - error_.resize(num_residuals); + residuals_.resize(num_residuals); f_x_new_.resize(num_residuals); jacobian_.resize(num_residuals, num_parameters); jtj_.resize(num_parameters, num_parameters); diff --git a/extern/ceres/include/ceres/tiny_solver_autodiff_function.h b/extern/ceres/include/ceres/tiny_solver_autodiff_function.h index b782f549cc1..3e3675ff070 100644 --- a/extern/ceres/include/ceres/tiny_solver_autodiff_function.h +++ b/extern/ceres/include/ceres/tiny_solver_autodiff_function.h @@ -113,12 +113,12 @@ class TinySolverAutoDiffFunction { // as a member a Jet type, which itself has a fixed-size Eigen type as member. EIGEN_MAKE_ALIGNED_OPERATOR_NEW - TinySolverAutoDiffFunction(const CostFunctor& cost_functor) + explicit TinySolverAutoDiffFunction(const CostFunctor& cost_functor) : cost_functor_(cost_functor) { Initialize<kNumResiduals>(cost_functor); } - typedef T Scalar; + using Scalar = T; enum { NUM_PARAMETERS = kNumParameters, NUM_RESIDUALS = kNumResiduals, @@ -127,7 +127,7 @@ class TinySolverAutoDiffFunction { // This is similar to AutoDifferentiate(), but since there is only one // parameter block it is easier to inline to avoid overhead. bool operator()(const T* parameters, T* residuals, T* jacobian) const { - if (jacobian == NULL) { + if (jacobian == nullptr) { // No jacobian requested, so just directly call the cost function with // doubles, skipping jets and derivatives. return cost_functor_(parameters, residuals); diff --git a/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h b/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h index 18ccb398f90..cc5ca16af5d 100644 --- a/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h +++ b/extern/ceres/include/ceres/tiny_solver_cost_function_adapter.h @@ -75,7 +75,7 @@ template <int kNumResiduals = Eigen::Dynamic, int kNumParameters = Eigen::Dynamic> class TinySolverCostFunctionAdapter { public: - typedef double Scalar; + using Scalar = double; enum ComponentSizeType { NUM_PARAMETERS = kNumParameters, NUM_RESIDUALS = kNumResiduals @@ -85,7 +85,7 @@ class TinySolverCostFunctionAdapter { // fixed-size Eigen types. EIGEN_MAKE_ALIGNED_OPERATOR_NEW - TinySolverCostFunctionAdapter(const CostFunction& cost_function) + explicit TinySolverCostFunctionAdapter(const CostFunction& cost_function) : cost_function_(cost_function) { CHECK_EQ(cost_function_.parameter_block_sizes().size(), 1) << "Only CostFunctions with exactly one parameter blocks are allowed."; @@ -108,7 +108,7 @@ class TinySolverCostFunctionAdapter { double* residuals, double* jacobian) const { if (!jacobian) { - return cost_function_.Evaluate(¶meters, residuals, NULL); + return cost_function_.Evaluate(¶meters, residuals, nullptr); } double* jacobians[1] = {row_major_jacobian_.data()}; diff --git a/extern/ceres/include/ceres/types.h b/extern/ceres/include/ceres/types.h index 5ee6fdca576..e5224238129 100644 --- a/extern/ceres/include/ceres/types.h +++ b/extern/ceres/include/ceres/types.h @@ -40,7 +40,7 @@ #include <string> #include "ceres/internal/disable_warnings.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -186,6 +186,7 @@ enum SparseLinearAlgebraLibraryType { enum DenseLinearAlgebraLibraryType { EIGEN, LAPACK, + CUDA, }; // Logging options diff --git a/extern/ceres/include/ceres/version.h b/extern/ceres/include/ceres/version.h index a76cc1099c5..e0d61972896 100644 --- a/extern/ceres/include/ceres/version.h +++ b/extern/ceres/include/ceres/version.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ #define CERES_PUBLIC_VERSION_H_ #define CERES_VERSION_MAJOR 2 -#define CERES_VERSION_MINOR 0 +#define CERES_VERSION_MINOR 1 #define CERES_VERSION_REVISION 0 // Classic CPP stringifcation; the extra level of indirection allows the diff --git a/extern/ceres/internal/ceres/accelerate_sparse.cc b/extern/ceres/internal/ceres/accelerate_sparse.cc index d2b642bf5dc..74adfaf9afc 100644 --- a/extern/ceres/internal/ceres/accelerate_sparse.cc +++ b/extern/ceres/internal/ceres/accelerate_sparse.cc @@ -29,11 +29,12 @@ // Author: alexs.mac@gmail.com (Alex Stewart) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_ACCELERATE_SPARSE #include <algorithm> +#include <memory> #include <string> #include <vector> @@ -196,17 +197,17 @@ template <typename Scalar> LinearSolverTerminationType AppleAccelerateCholesky<Scalar>::Factorize( CompressedRowSparseMatrix* lhs, std::string* message) { CHECK_EQ(lhs->storage_type(), StorageType()); - if (lhs == NULL) { - *message = "Failure: Input lhs is NULL."; + if (lhs == nullptr) { + *message = "Failure: Input lhs is nullptr."; return LINEAR_SOLVER_FATAL_ERROR; } typename SparseTypesTrait<Scalar>::SparseMatrix as_lhs = as_.CreateSparseMatrixTransposeView(lhs); if (!symbolic_factor_) { - symbolic_factor_.reset( - new typename SparseTypesTrait<Scalar>::SymbolicFactorization( - as_.AnalyzeCholesky(&as_lhs))); + symbolic_factor_ = std::make_unique< + typename SparseTypesTrait<Scalar>::SymbolicFactorization>( + as_.AnalyzeCholesky(&as_lhs)); if (symbolic_factor_->status != SparseStatusOK) { *message = StringPrintf( "Apple Accelerate Failure : Symbolic factorisation failed: %s", @@ -217,9 +218,9 @@ LinearSolverTerminationType AppleAccelerateCholesky<Scalar>::Factorize( } if (!numeric_factor_) { - numeric_factor_.reset( - new typename SparseTypesTrait<Scalar>::NumericFactorization( - as_.Cholesky(&as_lhs, symbolic_factor_.get()))); + numeric_factor_ = std::make_unique< + typename SparseTypesTrait<Scalar>::NumericFactorization>( + as_.Cholesky(&as_lhs, symbolic_factor_.get())); } else { // Recycle memory from previous numeric factorization. as_.Cholesky(&as_lhs, numeric_factor_.get()); @@ -265,7 +266,7 @@ template <typename Scalar> void AppleAccelerateCholesky<Scalar>::FreeSymbolicFactorization() { if (symbolic_factor_) { SparseCleanup(*symbolic_factor_); - symbolic_factor_.reset(); + symbolic_factor_ = nullptr; } } @@ -273,7 +274,7 @@ template <typename Scalar> void AppleAccelerateCholesky<Scalar>::FreeNumericFactorization() { if (numeric_factor_) { SparseCleanup(*numeric_factor_); - numeric_factor_.reset(); + numeric_factor_ = nullptr; } } diff --git a/extern/ceres/internal/ceres/accelerate_sparse.h b/extern/ceres/internal/ceres/accelerate_sparse.h index e53758dfa15..29d78e8c261 100644 --- a/extern/ceres/internal/ceres/accelerate_sparse.h +++ b/extern/ceres/internal/ceres/accelerate_sparse.h @@ -32,7 +32,7 @@ #define CERES_INTERNAL_ACCELERATE_SPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_ACCELERATE_SPARSE @@ -111,7 +111,7 @@ class AccelerateSparse { // An implementation of SparseCholesky interface using Apple's Accelerate // framework. template <typename Scalar> -class AppleAccelerateCholesky : public SparseCholesky { +class AppleAccelerateCholesky final : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); diff --git a/extern/ceres/internal/ceres/array_utils.cc b/extern/ceres/internal/ceres/array_utils.cc index 6bffd840f4b..113d41c927e 100644 --- a/extern/ceres/internal/ceres/array_utils.cc +++ b/extern/ceres/internal/ceres/array_utils.cc @@ -44,7 +44,7 @@ namespace internal { using std::string; bool IsArrayValid(const int size, const double* x) { - if (x != NULL) { + if (x != nullptr) { for (int i = 0; i < size; ++i) { if (!std::isfinite(x[i]) || (x[i] == kImpossibleValue)) { return false; @@ -55,7 +55,7 @@ bool IsArrayValid(const int size, const double* x) { } int FindInvalidValue(const int size, const double* x) { - if (x == NULL) { + if (x == nullptr) { return size; } @@ -69,7 +69,7 @@ int FindInvalidValue(const int size, const double* x) { } void InvalidateArray(const int size, double* x) { - if (x != NULL) { + if (x != nullptr) { for (int i = 0; i < size; ++i) { x[i] = kImpossibleValue; } @@ -78,7 +78,7 @@ void InvalidateArray(const int size, double* x) { void AppendArrayToString(const int size, const double* x, string* result) { for (int i = 0; i < size; ++i) { - if (x == NULL) { + if (x == nullptr) { StringAppendF(result, "Not Computed "); } else { if (x[i] == kImpossibleValue) { diff --git a/extern/ceres/internal/ceres/array_utils.h b/extern/ceres/internal/ceres/array_utils.h index 68feca5e792..d2fc7914e1b 100644 --- a/extern/ceres/internal/ceres/array_utils.h +++ b/extern/ceres/internal/ceres/array_utils.h @@ -45,29 +45,30 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { // Fill the array x with an impossible value that the user code is // never expected to compute. -CERES_EXPORT_INTERNAL void InvalidateArray(int size, double* x); +CERES_NO_EXPORT void InvalidateArray(int size, double* x); // Check if all the entries of the array x are valid, i.e. all the // values in the array should be finite and none of them should be // equal to the "impossible" value used by InvalidateArray. -CERES_EXPORT_INTERNAL bool IsArrayValid(int size, const double* x); +CERES_NO_EXPORT bool IsArrayValid(int size, const double* x); // If the array contains an invalid value, return the index for it, // otherwise return size. -CERES_EXPORT_INTERNAL int FindInvalidValue(const int size, const double* x); +CERES_NO_EXPORT int FindInvalidValue(const int size, const double* x); // Utility routine to print an array of doubles to a string. If the -// array pointer is NULL, it is treated as an array of zeros. -CERES_EXPORT_INTERNAL void AppendArrayToString(const int size, - const double* x, - std::string* result); +// array pointer is nullptr, it is treated as an array of zeros. +CERES_NO_EXPORT void AppendArrayToString(const int size, + const double* x, + std::string* result); // This routine takes an array of integer values, sorts and uniques // them and then maps each value in the array to its position in the @@ -82,9 +83,11 @@ CERES_EXPORT_INTERNAL void AppendArrayToString(const int size, // gets mapped to // // [1 0 2 3 0 1 3] -CERES_EXPORT_INTERNAL void MapValuesToContiguousRange(int size, int* array); +CERES_NO_EXPORT void MapValuesToContiguousRange(int size, int* array); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_ARRAY_UTILS_H_ diff --git a/extern/ceres/internal/ceres/block_evaluate_preparer.cc b/extern/ceres/internal/ceres/block_evaluate_preparer.cc index 7db96d94e0a..56c97b60cc4 100644 --- a/extern/ceres/internal/ceres/block_evaluate_preparer.cc +++ b/extern/ceres/internal/ceres/block_evaluate_preparer.cc @@ -53,7 +53,7 @@ void BlockEvaluatePreparer::Prepare(const ResidualBlock* residual_block, SparseMatrix* jacobian, double** jacobians) { // If the overall jacobian is not available, use the scratch space. - if (jacobian == NULL) { + if (jacobian == nullptr) { scratch_evaluate_preparer_.Prepare( residual_block, residual_block_index, jacobian, jacobians); return; @@ -73,7 +73,7 @@ void BlockEvaluatePreparer::Prepare(const ResidualBlock* residual_block, // parameters. Instead, bump the pointer for active parameters only. jacobian_block_offset++; } else { - jacobians[j] = NULL; + jacobians[j] = nullptr; } } } diff --git a/extern/ceres/internal/ceres/block_evaluate_preparer.h b/extern/ceres/internal/ceres/block_evaluate_preparer.h index 4378689729f..d72e41ba3e4 100644 --- a/extern/ceres/internal/ceres/block_evaluate_preparer.h +++ b/extern/ceres/internal/ceres/block_evaluate_preparer.h @@ -36,6 +36,7 @@ #ifndef CERES_INTERNAL_BLOCK_EVALUATE_PREPARER_H_ #define CERES_INTERNAL_BLOCK_EVALUATE_PREPARER_H_ +#include "ceres/internal/export.h" #include "ceres/scratch_evaluate_preparer.h" namespace ceres { @@ -44,7 +45,7 @@ namespace internal { class ResidualBlock; class SparseMatrix; -class BlockEvaluatePreparer { +class CERES_NO_EXPORT BlockEvaluatePreparer { public: // Using Init() instead of a constructor allows for allocating this structure // with new[]. This is because C++ doesn't allow passing arguments to objects diff --git a/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc b/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc index 6f37aca553c..6e979dea93b 100644 --- a/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc +++ b/extern/ceres/internal/ceres/block_jacobi_preconditioner.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -47,10 +47,10 @@ BlockJacobiPreconditioner::BlockJacobiPreconditioner( blocks[i] = bs->cols[i].size; } - m_.reset(new BlockRandomAccessDiagonalMatrix(blocks)); + m_ = std::make_unique<BlockRandomAccessDiagonalMatrix>(blocks); } -BlockJacobiPreconditioner::~BlockJacobiPreconditioner() {} +BlockJacobiPreconditioner::~BlockJacobiPreconditioner() = default; bool BlockJacobiPreconditioner::UpdateImpl(const BlockSparseMatrix& A, const double* D) { @@ -60,21 +60,20 @@ bool BlockJacobiPreconditioner::UpdateImpl(const BlockSparseMatrix& A, for (int i = 0; i < bs->rows.size(); ++i) { const int row_block_size = bs->rows[i].block.size; const std::vector<Cell>& cells = bs->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - const int block_id = cells[j].block_id; + for (const auto& cell : cells) { + const int block_id = cell.block_id; const int col_block_size = bs->cols[block_id].size; int r, c, row_stride, col_stride; CellInfo* cell_info = m_->GetCell(block_id, block_id, &r, &c, &row_stride, &col_stride); MatrixRef m(cell_info->values, row_stride, col_stride); - ConstMatrixRef b( - values + cells[j].position, row_block_size, col_block_size); + ConstMatrixRef b(values + cell.position, row_block_size, col_block_size); m.block(r, c, col_block_size, col_block_size) += b.transpose() * b; } } - if (D != NULL) { + if (D != nullptr) { // Add the diagonal. int position = 0; for (int i = 0; i < bs->cols.size(); ++i) { diff --git a/extern/ceres/internal/ceres/block_jacobi_preconditioner.h b/extern/ceres/internal/ceres/block_jacobi_preconditioner.h index 18f749533e0..e0a512a1469 100644 --- a/extern/ceres/internal/ceres/block_jacobi_preconditioner.h +++ b/extern/ceres/internal/ceres/block_jacobi_preconditioner.h @@ -34,7 +34,8 @@ #include <memory> #include "ceres/block_random_access_diagonal_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preconditioner.h" namespace ceres { @@ -53,7 +54,7 @@ struct CompressedRowBlockStructure; // update the matrix by running Update(A, D). The values of the matrix A are // inspected to construct the preconditioner. The vector D is applied as the // D^TD diagonal term. -class CERES_EXPORT_INTERNAL BlockJacobiPreconditioner +class CERES_NO_EXPORT BlockJacobiPreconditioner : public BlockSparseMatrixPreconditioner { public: // A must remain valid while the BlockJacobiPreconditioner is. @@ -61,7 +62,7 @@ class CERES_EXPORT_INTERNAL BlockJacobiPreconditioner BlockJacobiPreconditioner(const BlockJacobiPreconditioner&) = delete; void operator=(const BlockJacobiPreconditioner&) = delete; - virtual ~BlockJacobiPreconditioner(); + ~BlockJacobiPreconditioner() override; // Preconditioner interface void RightMultiply(const double* x, double* y) const final; @@ -78,4 +79,6 @@ class CERES_EXPORT_INTERNAL BlockJacobiPreconditioner } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/block_jacobian_writer.cc b/extern/ceres/internal/ceres/block_jacobian_writer.cc index 17c157b47f9..a70660f860a 100644 --- a/extern/ceres/internal/ceres/block_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/block_jacobian_writer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,10 +30,13 @@ #include "ceres/block_jacobian_writer.h" +#include <algorithm> +#include <memory> + #include "ceres/block_evaluate_preparer.h" #include "ceres/block_sparse_matrix.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block.h" #include "ceres/program.h" #include "ceres/residual_block.h" @@ -66,8 +69,7 @@ void BuildJacobianLayout(const Program& program, // matrix. Also compute the number of jacobian blocks. int f_block_pos = 0; int num_jacobian_blocks = 0; - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_residuals = residual_block->NumResiduals(); const int num_parameter_blocks = residual_block->NumParameterBlocks(); @@ -78,7 +80,7 @@ void BuildJacobianLayout(const Program& program, // Only count blocks for active parameters. num_jacobian_blocks++; if (parameter_block->index() < num_eliminate_blocks) { - f_block_pos += num_residuals * parameter_block->LocalSize(); + f_block_pos += num_residuals * parameter_block->TangentSize(); } } } @@ -107,7 +109,7 @@ void BuildJacobianLayout(const Program& program, continue; } const int jacobian_block_size = - num_residuals * parameter_block->LocalSize(); + num_residuals * parameter_block->TangentSize(); if (parameter_block_index < num_eliminate_blocks) { *jacobian_pos = e_block_pos; e_block_pos += jacobian_block_size; @@ -136,20 +138,20 @@ BlockJacobianWriter::BlockJacobianWriter(const Evaluator::Options& options, // Create evaluate prepareres that point directly into the final jacobian. This // makes the final Write() a nop. -BlockEvaluatePreparer* BlockJacobianWriter::CreateEvaluatePreparers( - int num_threads) { +std::unique_ptr<BlockEvaluatePreparer[]> +BlockJacobianWriter::CreateEvaluatePreparers(int num_threads) { int max_derivatives_per_residual_block = program_->MaxDerivativesPerResidualBlock(); - BlockEvaluatePreparer* preparers = new BlockEvaluatePreparer[num_threads]; + auto preparers = std::make_unique<BlockEvaluatePreparer[]>(num_threads); for (int i = 0; i < num_threads; i++) { preparers[i].Init(&jacobian_layout_[0], max_derivatives_per_residual_block); } return preparers; } -SparseMatrix* BlockJacobianWriter::CreateJacobian() const { - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; +std::unique_ptr<SparseMatrix> BlockJacobianWriter::CreateJacobian() const { + auto* bs = new CompressedRowBlockStructure; const vector<ParameterBlock*>& parameter_blocks = program_->parameter_blocks(); @@ -159,7 +161,7 @@ SparseMatrix* BlockJacobianWriter::CreateJacobian() const { for (int i = 0, cursor = 0; i < parameter_blocks.size(); ++i) { CHECK_NE(parameter_blocks[i]->index(), -1); CHECK(!parameter_blocks[i]->IsConstant()); - bs->cols[i].size = parameter_blocks[i]->LocalSize(); + bs->cols[i].size = parameter_blocks[i]->TangentSize(); bs->cols[i].position = cursor; cursor += bs->cols[i].size; } @@ -201,12 +203,10 @@ SparseMatrix* BlockJacobianWriter::CreateJacobian() const { } } - sort(row->cells.begin(), row->cells.end(), CellLessThan); + std::sort(row->cells.begin(), row->cells.end(), CellLessThan); } - BlockSparseMatrix* jacobian = new BlockSparseMatrix(bs); - CHECK(jacobian != nullptr); - return jacobian; + return std::make_unique<BlockSparseMatrix>(bs); } } // namespace internal diff --git a/extern/ceres/internal/ceres/block_jacobian_writer.h b/extern/ceres/internal/ceres/block_jacobian_writer.h index 8054d7b33aa..b2d0aaa3b73 100644 --- a/extern/ceres/internal/ceres/block_jacobian_writer.h +++ b/extern/ceres/internal/ceres/block_jacobian_writer.h @@ -38,10 +38,11 @@ #ifndef CERES_INTERNAL_BLOCK_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_BLOCK_JACOBIAN_WRITER_H_ +#include <memory> #include <vector> #include "ceres/evaluator.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -51,7 +52,7 @@ class Program; class SparseMatrix; // TODO(sameeragarwal): This class needs documemtation. -class BlockJacobianWriter { +class CERES_NO_EXPORT BlockJacobianWriter { public: BlockJacobianWriter(const Evaluator::Options& options, Program* program); @@ -59,9 +60,10 @@ class BlockJacobianWriter { // Create evaluate prepareres that point directly into the final jacobian. // This makes the final Write() a nop. - BlockEvaluatePreparer* CreateEvaluatePreparers(int num_threads); + std::unique_ptr<BlockEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads); - SparseMatrix* CreateJacobian() const; + std::unique_ptr<SparseMatrix> CreateJacobian() const; void Write(int /* residual_id */, int /* residual_offset */, diff --git a/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc b/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc index 386f81eae77..ed172de1d82 100644 --- a/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_dense_matrix.cc @@ -48,9 +48,9 @@ BlockRandomAccessDenseMatrix::BlockRandomAccessDenseMatrix( num_rows_ += blocks[i]; } - values_.reset(new double[num_rows_ * num_rows_]); + values_ = std::make_unique<double[]>(num_rows_ * num_rows_); - cell_infos_.reset(new CellInfo[num_blocks * num_blocks]); + cell_infos_ = std::make_unique<CellInfo[]>(num_blocks * num_blocks); for (int i = 0; i < num_blocks * num_blocks; ++i) { cell_infos_[i].values = values_.get(); } @@ -60,7 +60,7 @@ BlockRandomAccessDenseMatrix::BlockRandomAccessDenseMatrix( // Assume that the user does not hold any locks on any cell blocks // when they are calling SetZero. -BlockRandomAccessDenseMatrix::~BlockRandomAccessDenseMatrix() {} +BlockRandomAccessDenseMatrix::~BlockRandomAccessDenseMatrix() = default; CellInfo* BlockRandomAccessDenseMatrix::GetCell(const int row_block_id, const int col_block_id, diff --git a/extern/ceres/internal/ceres/block_random_access_dense_matrix.h b/extern/ceres/internal/ceres/block_random_access_dense_matrix.h index 9e555242994..171a6d694b5 100644 --- a/extern/ceres/internal/ceres/block_random_access_dense_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_dense_matrix.h @@ -35,7 +35,8 @@ #include <vector> #include "ceres/block_random_access_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -46,11 +47,11 @@ namespace internal { // num_rows x num_cols. // // This class is NOT thread safe. Since all n^2 cells are stored, -// GetCell never returns NULL for any (row_block_id, col_block_id) +// GetCell never returns nullptr for any (row_block_id, col_block_id) // pair. // // ReturnCell is a nop. -class CERES_EXPORT_INTERNAL BlockRandomAccessDenseMatrix +class CERES_NO_EXPORT BlockRandomAccessDenseMatrix : public BlockRandomAccessMatrix { public: // blocks is a vector of block sizes. The resulting matrix has @@ -61,7 +62,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDenseMatrix // The destructor is not thread safe. It assumes that no one is // modifying any cells when the matrix is being destroyed. - virtual ~BlockRandomAccessDenseMatrix(); + ~BlockRandomAccessDenseMatrix() override; // BlockRandomAccessMatrix interface. CellInfo* GetCell(int row_block_id, @@ -94,4 +95,6 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDenseMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_RANDOM_ACCESS_DENSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc index 08f6d7f1750..f55f3b30c61 100644 --- a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,12 +31,13 @@ #include "ceres/block_random_access_diagonal_matrix.h" #include <algorithm> +#include <memory> #include <set> #include <utility> #include <vector> #include "Eigen/Dense" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/stl_util.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" @@ -57,16 +58,17 @@ BlockRandomAccessDiagonalMatrix::BlockRandomAccessDiagonalMatrix( int num_cols = 0; int num_nonzeros = 0; vector<int> block_positions; - for (int i = 0; i < blocks_.size(); ++i) { + for (int block_size : blocks_) { block_positions.push_back(num_cols); - num_cols += blocks_[i]; - num_nonzeros += blocks_[i] * blocks_[i]; + num_cols += block_size; + num_nonzeros += block_size * block_size; } VLOG(1) << "Matrix Size [" << num_cols << "," << num_cols << "] " << num_nonzeros; - tsm_.reset(new TripletSparseMatrix(num_cols, num_cols, num_nonzeros)); + tsm_ = + std::make_unique<TripletSparseMatrix>(num_cols, num_cols, num_nonzeros); tsm_->set_num_nonzeros(num_nonzeros); int* rows = tsm_->mutable_rows(); int* cols = tsm_->mutable_cols(); @@ -99,7 +101,7 @@ CellInfo* BlockRandomAccessDiagonalMatrix::GetCell(int row_block_id, int* row_stride, int* col_stride) { if (row_block_id != col_block_id) { - return NULL; + return nullptr; } const int stride = blocks_[row_block_id]; @@ -121,8 +123,7 @@ void BlockRandomAccessDiagonalMatrix::SetZero() { void BlockRandomAccessDiagonalMatrix::Invert() { double* values = tsm_->mutable_values(); - for (int i = 0; i < blocks_.size(); ++i) { - const int block_size = blocks_[i]; + for (int block_size : blocks_) { MatrixRef block(values, block_size, block_size); block = block.selfadjointView<Eigen::Upper>().llt().solve( Matrix::Identity(block_size, block_size)); @@ -135,8 +136,7 @@ void BlockRandomAccessDiagonalMatrix::RightMultiply(const double* x, CHECK(x != nullptr); CHECK(y != nullptr); const double* values = tsm_->values(); - for (int i = 0; i < blocks_.size(); ++i) { - const int block_size = blocks_[i]; + for (int block_size : blocks_) { ConstMatrixRef block(values, block_size, block_size); VectorRef(y, block_size).noalias() += block * ConstVectorRef(x, block_size); x += block_size; diff --git a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h index 3fe7c1e5b22..3d36c378320 100644 --- a/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_diagonal_matrix.h @@ -37,7 +37,8 @@ #include <vector> #include "ceres/block_random_access_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" @@ -46,7 +47,7 @@ namespace internal { // A thread safe block diagonal matrix implementation of // BlockRandomAccessMatrix. -class CERES_EXPORT_INTERNAL BlockRandomAccessDiagonalMatrix +class CERES_NO_EXPORT BlockRandomAccessDiagonalMatrix : public BlockRandomAccessMatrix { public: // blocks is an array of block sizes. @@ -57,7 +58,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDiagonalMatrix // The destructor is not thread safe. It assumes that no one is // modifying any cells when the matrix is being destroyed. - virtual ~BlockRandomAccessDiagonalMatrix(); + ~BlockRandomAccessDiagonalMatrix() override; // BlockRandomAccessMatrix Interface. CellInfo* GetCell(int row_block_id, @@ -98,4 +99,6 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessDiagonalMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_RANDOM_ACCESS_DIAGONAL_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_random_access_matrix.cc b/extern/ceres/internal/ceres/block_random_access_matrix.cc index ea88855b59a..8e70c049796 100644 --- a/extern/ceres/internal/ceres/block_random_access_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_matrix.cc @@ -33,7 +33,7 @@ namespace ceres { namespace internal { -BlockRandomAccessMatrix::~BlockRandomAccessMatrix() {} +BlockRandomAccessMatrix::~BlockRandomAccessMatrix() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/block_random_access_matrix.h b/extern/ceres/internal/ceres/block_random_access_matrix.h index f190622eafe..48759b79a18 100644 --- a/extern/ceres/internal/ceres/block_random_access_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_matrix.h @@ -35,7 +35,7 @@ #include <mutex> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -62,7 +62,7 @@ namespace internal { // // There is no requirement that all cells be present, i.e. the matrix // itself can be block sparse. When a cell is not present, the GetCell -// method will return a NULL pointer. +// method will return a nullptr pointer. // // There is no requirement about how the cells are stored beyond that // form a dense submatrix of a larger dense matrix. Like everywhere @@ -77,7 +77,7 @@ namespace internal { // &row, &col, // &row_stride, &col_stride); // -// if (cell != NULL) { +// if (cell != nullptr) { // MatrixRef m(cell->values, row_stride, col_stride); // std::lock_guard<std::mutex> l(&cell->m); // m.block(row, col, row_block_size, col_block_size) = ... @@ -85,21 +85,21 @@ namespace internal { // Structure to carry a pointer to the array containing a cell and the // mutex guarding it. -struct CellInfo { - CellInfo() : values(nullptr) {} +struct CERES_NO_EXPORT CellInfo { + CellInfo() = default; explicit CellInfo(double* values) : values(values) {} - double* values; + double* values{nullptr}; std::mutex m; }; -class CERES_EXPORT_INTERNAL BlockRandomAccessMatrix { +class CERES_NO_EXPORT BlockRandomAccessMatrix { public: virtual ~BlockRandomAccessMatrix(); // If the cell (row_block_id, col_block_id) is present, then return // a CellInfo with a pointer to the dense matrix containing it, - // otherwise return NULL. The dense matrix containing this cell has + // otherwise return nullptr. The dense matrix containing this cell has // size row_stride, col_stride and the cell is located at position // (row, col) within this matrix. // diff --git a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc index c28b7cef3f4..a026daa5dac 100644 --- a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -36,7 +36,7 @@ #include <utility> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" #include "glog/logging.h" @@ -58,9 +58,9 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix( // rows/columns. int num_cols = 0; block_positions_.reserve(blocks_.size()); - for (int i = 0; i < blocks_.size(); ++i) { + for (int block_size : blocks_) { block_positions_.push_back(num_cols); - num_cols += blocks_[i]; + num_cols += block_size; } // Count the number of scalar non-zero entries and build the layout @@ -76,7 +76,8 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix( VLOG(1) << "Matrix Size [" << num_cols << "," << num_cols << "] " << num_nonzeros; - tsm_.reset(new TripletSparseMatrix(num_cols, num_cols, num_nonzeros)); + tsm_ = + std::make_unique<TripletSparseMatrix>(num_cols, num_cols, num_nonzeros); tsm_->set_num_nonzeros(num_nonzeros); int* rows = tsm_->mutable_rows(); int* cols = tsm_->mutable_cols(); @@ -86,7 +87,7 @@ BlockRandomAccessSparseMatrix::BlockRandomAccessSparseMatrix( for (const auto& block_pair : block_pairs) { const int row_block_size = blocks_[block_pair.first]; const int col_block_size = blocks_[block_pair.second]; - cell_values_.push_back(make_pair(block_pair, values + pos)); + cell_values_.emplace_back(block_pair, values + pos); layout_[IntPairToLong(block_pair.first, block_pair.second)] = new CellInfo(values + pos); pos += row_block_size * col_block_size; @@ -129,7 +130,7 @@ CellInfo* BlockRandomAccessSparseMatrix::GetCell(int row_block_id, const LayoutType::iterator it = layout_.find(IntPairToLong(row_block_id, col_block_id)); if (it == layout_.end()) { - return NULL; + return nullptr; } // Each cell is stored contiguously as its own little dense matrix. diff --git a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h index 0e58bbb6b42..b31a2ade843 100644 --- a/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h +++ b/extern/ceres/internal/ceres/block_random_access_sparse_matrix.h @@ -39,7 +39,8 @@ #include <vector> #include "ceres/block_random_access_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/small_blas.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" @@ -51,7 +52,7 @@ namespace internal { // BlockRandomAccessMatrix. Internally a TripletSparseMatrix is used // for doing the actual storage. This class augments this matrix with // an unordered_map that allows random read/write access. -class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix +class CERES_NO_EXPORT BlockRandomAccessSparseMatrix : public BlockRandomAccessMatrix { public: // blocks is an array of block sizes. block_pairs is a set of @@ -65,7 +66,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix // The destructor is not thread safe. It assumes that no one is // modifying any cells when the matrix is being destroyed. - virtual ~BlockRandomAccessSparseMatrix(); + ~BlockRandomAccessSparseMatrix() override; // BlockRandomAccessMatrix Interface. CellInfo* GetCell(int row_block_id, @@ -111,7 +112,7 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix // A mapping from <row_block_id, col_block_id> to the position in // the values array of tsm_ where the block is stored. - typedef std::unordered_map<long int, CellInfo*> LayoutType; + using LayoutType = std::unordered_map<long, CellInfo*>; LayoutType layout_; // In order traversal of contents of the matrix. This allows us to @@ -127,4 +128,6 @@ class CERES_EXPORT_INTERNAL BlockRandomAccessSparseMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_RANDOM_ACCESS_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_sparse_matrix.cc b/extern/ceres/internal/ceres/block_sparse_matrix.cc index 5efd2e1ecfb..31ea39daeea 100644 --- a/extern/ceres/internal/ceres/block_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/block_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ #include <algorithm> #include <cstddef> +#include <memory> #include <vector> #include "ceres/block_structure.h" @@ -46,8 +47,6 @@ namespace internal { using std::vector; -BlockSparseMatrix::~BlockSparseMatrix() {} - BlockSparseMatrix::BlockSparseMatrix( CompressedRowBlockStructure* block_structure) : num_rows_(0), @@ -57,8 +56,8 @@ BlockSparseMatrix::BlockSparseMatrix( CHECK(block_structure_ != nullptr); // Count the number of columns in the matrix. - for (int i = 0; i < block_structure_->cols.size(); ++i) { - num_cols_ += block_structure_->cols[i].size; + for (auto& col : block_structure_->cols) { + num_cols_ += col.size; } // Count the number of non-zero entries and the number of rows in @@ -68,8 +67,8 @@ BlockSparseMatrix::BlockSparseMatrix( num_rows_ += row_block_size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; num_nonzeros_ += col_block_size * row_block_size; } @@ -80,7 +79,7 @@ BlockSparseMatrix::BlockSparseMatrix( CHECK_GE(num_nonzeros_, 0); VLOG(2) << "Allocating values array with " << num_nonzeros_ * sizeof(double) << " bytes."; // NOLINT - values_.reset(new double[num_nonzeros_]); + values_ = std::make_unique<double[]>(num_nonzeros_); max_num_nonzeros_ = num_nonzeros_; CHECK(values_ != nullptr); } @@ -97,12 +96,12 @@ void BlockSparseMatrix::RightMultiply(const double* x, double* y) const { int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values_.get() + cells[j].position, + values_.get() + cell.position, row_block_size, col_block_size, x + col_block_pos, @@ -119,12 +118,12 @@ void BlockSparseMatrix::LeftMultiply(const double* x, double* y) const { int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values_.get() + cells[j].position, + values_.get() + cell.position, row_block_size, col_block_size, x + row_block_pos, @@ -139,12 +138,12 @@ void BlockSparseMatrix::SquaredColumnNorm(double* x) const { for (int i = 0; i < block_structure_->rows.size(); ++i) { int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; const MatrixRef m( - values_.get() + cells[j].position, row_block_size, col_block_size); + values_.get() + cell.position, row_block_size, col_block_size); VectorRef(x + col_block_pos, col_block_size) += m.colwise().squaredNorm(); } } @@ -156,12 +155,12 @@ void BlockSparseMatrix::ScaleColumns(const double* scale) { for (int i = 0; i < block_structure_->rows.size(); ++i) { int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; MatrixRef m( - values_.get() + cells[j].position, row_block_size, col_block_size); + values_.get() + cell.position, row_block_size, col_block_size); m *= ConstVectorRef(scale + col_block_pos, col_block_size).asDiagonal(); } } @@ -178,11 +177,11 @@ void BlockSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const { int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; - int jac_pos = cells[j].position; + int jac_pos = cell.position; m.block(row_block_pos, col_block_pos, row_block_size, col_block_size) += MatrixRef(values_.get() + jac_pos, row_block_size, col_block_size); } @@ -201,11 +200,11 @@ void BlockSparseMatrix::ToTripletSparseMatrix( int row_block_pos = block_structure_->rows[i].block.position; int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + int col_block_id = cell.block_id; int col_block_size = block_structure_->cols[col_block_id].size; int col_block_pos = block_structure_->cols[col_block_id].position; - int jac_pos = cells[j].position; + int jac_pos = cell.position; for (int r = 0; r < row_block_size; ++r) { for (int c = 0; c < col_block_size; ++c, ++jac_pos) { matrix->mutable_rows()[jac_pos] = row_block_pos + r; @@ -230,11 +229,11 @@ void BlockSparseMatrix::ToTextFile(FILE* file) const { const int row_block_pos = block_structure_->rows[i].block.position; const int row_block_size = block_structure_->rows[i].block.size; const vector<Cell>& cells = block_structure_->rows[i].cells; - for (int j = 0; j < cells.size(); ++j) { - const int col_block_id = cells[j].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_size = block_structure_->cols[col_block_id].size; const int col_block_pos = block_structure_->cols[col_block_id].position; - int jac_pos = cells[j].position; + int jac_pos = cell.position; for (int r = 0; r < row_block_size; ++r) { for (int c = 0; c < col_block_size; ++c) { fprintf(file, @@ -248,10 +247,10 @@ void BlockSparseMatrix::ToTextFile(FILE* file) const { } } -BlockSparseMatrix* BlockSparseMatrix::CreateDiagonalMatrix( +std::unique_ptr<BlockSparseMatrix> BlockSparseMatrix::CreateDiagonalMatrix( const double* diagonal, const std::vector<Block>& column_blocks) { // Create the block structure for the diagonal matrix. - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure(); + auto* bs = new CompressedRowBlockStructure(); bs->cols = column_blocks; int position = 0; bs->rows.resize(column_blocks.size(), CompressedRow(1)); @@ -265,13 +264,13 @@ BlockSparseMatrix* BlockSparseMatrix::CreateDiagonalMatrix( } // Create the BlockSparseMatrix with the given block structure. - BlockSparseMatrix* matrix = new BlockSparseMatrix(bs); + auto matrix = std::make_unique<BlockSparseMatrix>(bs); matrix->SetZero(); // Fill the values array of the block sparse matrix. double* values = matrix->mutable_values(); - for (int i = 0; i < column_blocks.size(); ++i) { - const int size = column_blocks[i].size; + for (const auto& column_block : column_blocks) { + const int size = column_block.size; for (int j = 0; j < size; ++j) { // (j + 1) * size is compact way of accessing the (j,j) entry. values[j * (size + 1)] = diagonal[j]; @@ -308,9 +307,10 @@ void BlockSparseMatrix::AppendRows(const BlockSparseMatrix& m) { } if (num_nonzeros_ > max_num_nonzeros_) { - double* new_values = new double[num_nonzeros_]; - std::copy(values_.get(), values_.get() + old_num_nonzeros, new_values); - values_.reset(new_values); + std::unique_ptr<double[]> new_values = + std::make_unique<double[]>(num_nonzeros_); + std::copy_n(values_.get(), old_num_nonzeros, new_values.get()); + values_ = std::move(new_values); max_num_nonzeros_ = num_nonzeros_; } @@ -337,7 +337,7 @@ void BlockSparseMatrix::DeleteRowBlocks(const int delta_row_blocks) { block_structure_->rows.resize(num_row_blocks - delta_row_blocks); } -BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( +std::unique_ptr<BlockSparseMatrix> BlockSparseMatrix::CreateRandomMatrix( const BlockSparseMatrix::RandomMatrixOptions& options) { CHECK_GT(options.num_row_blocks, 0); CHECK_GT(options.min_row_block_size, 0); @@ -346,7 +346,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( CHECK_GT(options.block_density, 0.0); CHECK_LE(options.block_density, 1.0); - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure(); + auto* bs = new CompressedRowBlockStructure(); if (options.col_blocks.empty()) { CHECK_GT(options.num_col_blocks, 0); CHECK_GT(options.min_col_block_size, 0); @@ -360,7 +360,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( const int delta_block_size = Uniform(options.max_col_block_size - options.min_col_block_size); const int col_block_size = options.min_col_block_size + delta_block_size; - bs->cols.push_back(Block(col_block_size, col_block_position)); + bs->cols.emplace_back(col_block_size, col_block_position); col_block_position += col_block_size; } } else { @@ -377,7 +377,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( const int delta_block_size = Uniform(options.max_row_block_size - options.min_row_block_size); const int row_block_size = options.min_row_block_size + delta_block_size; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = row_block_size; row.block.position = row_block_position; @@ -385,7 +385,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( for (int c = 0; c < bs->cols.size(); ++c) { if (RandDouble() > options.block_density) continue; - row.cells.push_back(Cell()); + row.cells.emplace_back(); Cell& cell = row.cells.back(); cell.block_id = c; cell.position = value_position; @@ -395,7 +395,7 @@ BlockSparseMatrix* BlockSparseMatrix::CreateRandomMatrix( } } - BlockSparseMatrix* matrix = new BlockSparseMatrix(bs); + auto matrix = std::make_unique<BlockSparseMatrix>(bs); double* values = matrix->mutable_values(); for (int i = 0; i < matrix->num_nonzeros(); ++i) { values[i] = RandNormal(); diff --git a/extern/ceres/internal/ceres/block_sparse_matrix.h b/extern/ceres/internal/ceres/block_sparse_matrix.h index e5b3634c3cc..75b0deb59e6 100644 --- a/extern/ceres/internal/ceres/block_sparse_matrix.h +++ b/extern/ceres/internal/ceres/block_sparse_matrix.h @@ -37,8 +37,9 @@ #include <memory> #include "ceres/block_structure.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" namespace ceres { @@ -54,7 +55,7 @@ class TripletSparseMatrix; // // internal/ceres/block_structure.h // -class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT BlockSparseMatrix final : public SparseMatrix { public: // Construct a block sparse matrix with a fully initialized // CompressedRowBlockStructure objected. The matrix takes over @@ -68,8 +69,6 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { BlockSparseMatrix(const BlockSparseMatrix&) = delete; void operator=(const BlockSparseMatrix&) = delete; - virtual ~BlockSparseMatrix(); - // Implementation of SparseMatrix interface. void SetZero() final; void RightMultiply(const double* x, double* y) const final; @@ -97,7 +96,7 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { // Delete the bottom delta_rows_blocks. void DeleteRowBlocks(int delta_row_blocks); - static BlockSparseMatrix* CreateDiagonalMatrix( + static std::unique_ptr<BlockSparseMatrix> CreateDiagonalMatrix( const double* diagonal, const std::vector<Block>& column_blocks); struct RandomMatrixOptions { @@ -122,9 +121,7 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { // Create a random BlockSparseMatrix whose entries are normally // distributed and whose structure is determined by // RandomMatrixOptions. - // - // Caller owns the result. - static BlockSparseMatrix* CreateRandomMatrix( + static std::unique_ptr<BlockSparseMatrix> CreateRandomMatrix( const RandomMatrixOptions& options); private: @@ -142,9 +139,9 @@ class CERES_EXPORT_INTERNAL BlockSparseMatrix : public SparseMatrix { // // BlockSparseDataMatrix a struct that carries these two bits of // information -class BlockSparseMatrixData { +class CERES_NO_EXPORT BlockSparseMatrixData { public: - BlockSparseMatrixData(const BlockSparseMatrix& m) + explicit BlockSparseMatrixData(const BlockSparseMatrix& m) : block_structure_(m.block_structure()), values_(m.values()){}; BlockSparseMatrixData(const CompressedRowBlockStructure* block_structure, @@ -164,4 +161,6 @@ class BlockSparseMatrixData { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_BLOCK_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/block_structure.h b/extern/ceres/internal/ceres/block_structure.h index d49d7d3f3a4..fe7574c6817 100644 --- a/extern/ceres/internal/ceres/block_structure.h +++ b/extern/ceres/internal/ceres/block_structure.h @@ -41,54 +41,54 @@ #include <cstdint> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { -typedef int32_t BlockSize; +using BlockSize = int32_t; -struct Block { - Block() : size(-1), position(-1) {} +struct CERES_NO_EXPORT Block { + Block() = default; Block(int size_, int position_) : size(size_), position(position_) {} - BlockSize size; - int position; // Position along the row/column. + BlockSize size{-1}; + int position{-1}; // Position along the row/column. }; -struct Cell { - Cell() : block_id(-1), position(-1) {} +struct CERES_NO_EXPORT Cell { + Cell() = default; Cell(int block_id_, int position_) : block_id(block_id_), position(position_) {} // Column or row block id as the case maybe. - int block_id; + int block_id{-1}; // Where in the values array of the jacobian is this cell located. - int position; + int position{-1}; }; // Order cell by their block_id; -bool CellLessThan(const Cell& lhs, const Cell& rhs); +CERES_NO_EXPORT bool CellLessThan(const Cell& lhs, const Cell& rhs); -struct CompressedList { - CompressedList() {} +struct CERES_NO_EXPORT CompressedList { + CompressedList() = default; // Construct a CompressedList with the cells containing num_cells // entries. - CompressedList(int num_cells) : cells(num_cells) {} + explicit CompressedList(int num_cells) : cells(num_cells) {} Block block; std::vector<Cell> cells; }; -typedef CompressedList CompressedRow; -typedef CompressedList CompressedColumn; +using CompressedRow = CompressedList; +using CompressedColumn = CompressedList; -struct CompressedRowBlockStructure { +struct CERES_NO_EXPORT CompressedRowBlockStructure { std::vector<Block> cols; std::vector<CompressedRow> rows; }; -struct CompressedColumnBlockStructure { +struct CERES_NO_EXPORT CompressedColumnBlockStructure { std::vector<Block> rows; std::vector<CompressedColumn> cols; }; diff --git a/extern/ceres/internal/ceres/c_api.cc b/extern/ceres/internal/ceres/c_api.cc index 251cde42101..8ea344dd54a 100644 --- a/extern/ceres/internal/ceres/c_api.cc +++ b/extern/ceres/internal/ceres/c_api.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ #include "ceres/c_api.h" #include <iostream> +#include <memory> #include <string> #include <vector> @@ -64,7 +65,7 @@ void ceres_free_problem(ceres_problem_t* problem) { // This cost function wraps a C-level function pointer from the user, to bridge // between C and C++. -class CallbackCostFunction : public ceres::CostFunction { +class CERES_NO_EXPORT CallbackCostFunction final : public ceres::CostFunction { public: CallbackCostFunction(ceres_cost_function_t cost_function, void* user_data, @@ -78,8 +79,6 @@ class CallbackCostFunction : public ceres::CostFunction { } } - virtual ~CallbackCostFunction() {} - bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const final { @@ -94,7 +93,7 @@ class CallbackCostFunction : public ceres::CostFunction { // This loss function wraps a C-level function pointer from the user, to bridge // between C and C++. -class CallbackLossFunction : public ceres::LossFunction { +class CallbackLossFunction final : public ceres::LossFunction { public: explicit CallbackLossFunction(ceres_loss_function_t loss_function, void* user_data) @@ -146,30 +145,31 @@ ceres_residual_block_id_t* ceres_problem_add_residual_block( int num_parameter_blocks, int* parameter_block_sizes, double** parameters) { - Problem* ceres_problem = reinterpret_cast<Problem*>(problem); - - ceres::CostFunction* callback_cost_function = - new CallbackCostFunction(cost_function, - cost_function_data, - num_residuals, - num_parameter_blocks, - parameter_block_sizes); - - ceres::LossFunction* callback_loss_function = NULL; - if (loss_function != NULL) { - callback_loss_function = - new CallbackLossFunction(loss_function, loss_function_data); + auto* ceres_problem = reinterpret_cast<Problem*>(problem); + + auto callback_cost_function = + std::make_unique<CallbackCostFunction>(cost_function, + cost_function_data, + num_residuals, + num_parameter_blocks, + parameter_block_sizes); + + std::unique_ptr<ceres::LossFunction> callback_loss_function; + if (loss_function != nullptr) { + callback_loss_function = std::make_unique<CallbackLossFunction>( + loss_function, loss_function_data); } std::vector<double*> parameter_blocks(parameters, parameters + num_parameter_blocks); return reinterpret_cast<ceres_residual_block_id_t*>( - ceres_problem->AddResidualBlock( - callback_cost_function, callback_loss_function, parameter_blocks)); + ceres_problem->AddResidualBlock(callback_cost_function.release(), + callback_loss_function.release(), + parameter_blocks)); } void ceres_solve(ceres_problem_t* c_problem) { - Problem* problem = reinterpret_cast<Problem*>(c_problem); + auto* problem = reinterpret_cast<Problem*>(c_problem); // TODO(keir): Obviously, this way of setting options won't scale or last. // Instead, figure out a way to specify some of the options without diff --git a/extern/ceres/internal/ceres/callbacks.cc b/extern/ceres/internal/ceres/callbacks.cc index 0e0df9d91b1..7a4381c293f 100644 --- a/extern/ceres/internal/ceres/callbacks.cc +++ b/extern/ceres/internal/ceres/callbacks.cc @@ -30,6 +30,7 @@ #include "ceres/callbacks.h" +#include <algorithm> #include <iostream> // NO LINT #include "ceres/program.h" @@ -45,7 +46,7 @@ StateUpdatingCallback::StateUpdatingCallback(Program* program, double* parameters) : program_(program), parameters_(parameters) {} -StateUpdatingCallback::~StateUpdatingCallback() {} +StateUpdatingCallback::~StateUpdatingCallback() = default; CallbackReturnType StateUpdatingCallback::operator()( const IterationSummary& summary) { @@ -64,14 +65,12 @@ GradientProblemSolverStateUpdatingCallback:: user_parameters_(user_parameters) {} GradientProblemSolverStateUpdatingCallback:: - ~GradientProblemSolverStateUpdatingCallback() {} + ~GradientProblemSolverStateUpdatingCallback() = default; CallbackReturnType GradientProblemSolverStateUpdatingCallback::operator()( const IterationSummary& summary) { if (summary.step_is_successful) { - std::copy(internal_parameters_, - internal_parameters_ + num_parameters_, - user_parameters_); + std::copy_n(internal_parameters_, num_parameters_, user_parameters_); } return SOLVER_CONTINUE; } @@ -80,44 +79,42 @@ LoggingCallback::LoggingCallback(const MinimizerType minimizer_type, const bool log_to_stdout) : minimizer_type(minimizer_type), log_to_stdout_(log_to_stdout) {} -LoggingCallback::~LoggingCallback() {} +LoggingCallback::~LoggingCallback() = default; CallbackReturnType LoggingCallback::operator()( const IterationSummary& summary) { string output; if (minimizer_type == LINE_SEARCH) { - const char* kReportRowFormat = - "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e " - "s:% 3.2e e:% 3d it:% 3.2e tt:% 3.2e"; - output = StringPrintf(kReportRowFormat, - summary.iteration, - summary.cost, - summary.cost_change, - summary.gradient_max_norm, - summary.step_norm, - summary.step_size, - summary.line_search_function_evaluations, - summary.iteration_time_in_seconds, - summary.cumulative_time_in_seconds); + output = StringPrintf( + "% 4d: f:% 8e d:% 3.2e g:% 3.2e h:% 3.2e s:% 3.2e e:% 3d it:% 3.2e " + "tt:% 3.2e", + summary.iteration, + summary.cost, + summary.cost_change, + summary.gradient_max_norm, + summary.step_norm, + summary.step_size, + summary.line_search_function_evaluations, + summary.iteration_time_in_seconds, + summary.cumulative_time_in_seconds); } else if (minimizer_type == TRUST_REGION) { // clang-format off if (summary.iteration == 0) { output = "iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time\n"; // NOLINT } - const char* kReportRowFormat = - "% 4d % 8e % 3.2e % 3.2e % 3.2e % 3.2e % 3.2e % 4d % 3.2e % 3.2e"; // NOLINT - // clang-format on - output += StringPrintf(kReportRowFormat, - summary.iteration, - summary.cost, - summary.cost_change, - summary.gradient_max_norm, - summary.step_norm, - summary.relative_decrease, - summary.trust_region_radius, - summary.linear_solver_iterations, - summary.iteration_time_in_seconds, - summary.cumulative_time_in_seconds); + output += StringPrintf( + "% 4d % 8e % 3.2e % 3.2e % 3.2e % 3.2e % 3.2e % 4d % 3.2e % 3.2e", // NOLINT + // clang-format on + summary.iteration, + summary.cost, + summary.cost_change, + summary.gradient_max_norm, + summary.step_norm, + summary.relative_decrease, + summary.trust_region_radius, + summary.linear_solver_iterations, + summary.iteration_time_in_seconds, + summary.cumulative_time_in_seconds); } else { LOG(FATAL) << "Unknown minimizer type."; } diff --git a/extern/ceres/internal/ceres/callbacks.h b/extern/ceres/internal/ceres/callbacks.h index 47112b88fd8..3b1d10cfa7f 100644 --- a/extern/ceres/internal/ceres/callbacks.h +++ b/extern/ceres/internal/ceres/callbacks.h @@ -33,7 +33,7 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" namespace ceres { @@ -43,10 +43,10 @@ class Program; // Callback for updating the externally visible state of parameter // blocks. -class StateUpdatingCallback : public IterationCallback { +class CERES_NO_EXPORT StateUpdatingCallback final : public IterationCallback { public: StateUpdatingCallback(Program* program, double* parameters); - virtual ~StateUpdatingCallback(); + ~StateUpdatingCallback() override; CallbackReturnType operator()(const IterationSummary& summary) final; private: @@ -56,12 +56,13 @@ class StateUpdatingCallback : public IterationCallback { // Callback for updating the externally visible state of the // parameters vector for GradientProblemSolver. -class GradientProblemSolverStateUpdatingCallback : public IterationCallback { +class CERES_NO_EXPORT GradientProblemSolverStateUpdatingCallback final + : public IterationCallback { public: GradientProblemSolverStateUpdatingCallback(int num_parameters, const double* internal_parameters, double* user_parameters); - virtual ~GradientProblemSolverStateUpdatingCallback(); + ~GradientProblemSolverStateUpdatingCallback() override; CallbackReturnType operator()(const IterationSummary& summary) final; private: @@ -72,10 +73,10 @@ class GradientProblemSolverStateUpdatingCallback : public IterationCallback { // Callback for logging the state of the minimizer to STDERR or // STDOUT depending on the user's preferences and logging level. -class LoggingCallback : public IterationCallback { +class CERES_NO_EXPORT LoggingCallback final : public IterationCallback { public: LoggingCallback(MinimizerType minimizer_type, bool log_to_stdout); - virtual ~LoggingCallback(); + ~LoggingCallback() override; CallbackReturnType operator()(const IterationSummary& summary) final; private: diff --git a/extern/ceres/internal/ceres/canonical_views_clustering.cc b/extern/ceres/internal/ceres/canonical_views_clustering.cc index c193735f106..01b8ad38ae0 100644 --- a/extern/ceres/internal/ceres/canonical_views_clustering.cc +++ b/extern/ceres/internal/ceres/canonical_views_clustering.cc @@ -35,6 +35,7 @@ #include <unordered_set> #include "ceres/graph.h" +#include "ceres/internal/export.h" #include "ceres/map_util.h" #include "glog/logging.h" @@ -43,13 +44,11 @@ namespace internal { using std::vector; -typedef std::unordered_map<int, int> IntMap; -typedef std::unordered_set<int> IntSet; +using IntMap = std::unordered_map<int, int>; +using IntSet = std::unordered_set<int>; -class CanonicalViewsClustering { +class CERES_NO_EXPORT CanonicalViewsClustering { public: - CanonicalViewsClustering() {} - // Compute the canonical views clustering of the vertices of the // graph. centers will contain the vertices that are the identified // as the canonical views/cluster centers, and membership is a map @@ -85,11 +84,11 @@ void ComputeCanonicalViewsClustering( const WeightedGraph<int>& graph, vector<int>* centers, IntMap* membership) { - time_t start_time = time(NULL); + time_t start_time = time(nullptr); CanonicalViewsClustering cv; cv.ComputeClustering(options, graph, centers, membership); VLOG(2) << "Canonical views clustering time (secs): " - << time(NULL) - start_time; + << time(nullptr) - start_time; } // Implementation of CanonicalViewsClustering @@ -107,7 +106,7 @@ void CanonicalViewsClustering::ComputeClustering( IntSet valid_views; FindValidViews(&valid_views); - while (valid_views.size() > 0) { + while (!valid_views.empty()) { // Find the next best canonical view. double best_difference = -std::numeric_limits<double>::max(); int best_view = 0; @@ -174,9 +173,9 @@ double CanonicalViewsClustering::ComputeClusteringQualityDifference( difference -= options_.size_penalty_weight; // Orthogonality. - for (int i = 0; i < centers.size(); ++i) { + for (int center : centers) { difference -= options_.similarity_penalty_weight * - graph_->EdgeWeight(centers[i], candidate); + graph_->EdgeWeight(center, candidate); } return difference; diff --git a/extern/ceres/internal/ceres/canonical_views_clustering.h b/extern/ceres/internal/ceres/canonical_views_clustering.h index 465233ddfcd..00a6a739d29 100644 --- a/extern/ceres/internal/ceres/canonical_views_clustering.h +++ b/extern/ceres/internal/ceres/canonical_views_clustering.h @@ -45,7 +45,8 @@ #include <vector> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -95,13 +96,13 @@ struct CanonicalViewsClusteringOptions; // It is possible depending on the configuration of the clustering // algorithm that some of the vertices may not be assigned to any // cluster. In this case they are assigned to a cluster with id = -1; -CERES_EXPORT_INTERNAL void ComputeCanonicalViewsClustering( +CERES_NO_EXPORT void ComputeCanonicalViewsClustering( const CanonicalViewsClusteringOptions& options, const WeightedGraph<int>& graph, std::vector<int>* centers, std::unordered_map<int, int>* membership); -struct CERES_EXPORT_INTERNAL CanonicalViewsClusteringOptions { +struct CERES_NO_EXPORT CanonicalViewsClusteringOptions { // The minimum number of canonical views to compute. int min_views = 3; @@ -122,4 +123,6 @@ struct CERES_EXPORT_INTERNAL CanonicalViewsClusteringOptions { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_ diff --git a/extern/ceres/internal/ceres/casts.h b/extern/ceres/internal/ceres/casts.h index d13707131c2..04d8ba4fe33 100644 --- a/extern/ceres/internal/ceres/casts.h +++ b/extern/ceres/internal/ceres/casts.h @@ -32,14 +32,13 @@ #define CERES_INTERNAL_CASTS_H_ #include <cassert> -#include <cstddef> // For NULL. namespace ceres { // Identity metafunction. template <class T> struct identity_ { - typedef T type; + using type = T; }; // Use implicit_cast as a safe version of static_cast or const_cast @@ -86,6 +85,7 @@ inline To implicit_cast(typename identity_<From>::type const& f) { // if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo); // You should design the code some other way not to need this. +// TODO(sameeragarwal): Modernize this. template <typename To, typename From> // use like this: down_cast<T*>(foo); inline To down_cast(From* f) { // so we only accept pointers // Ensures that To is a sub-type of From *. This test is here only @@ -95,11 +95,11 @@ inline To down_cast(From* f) { // so we only accept pointers // TODO(csilvers): This should use COMPILE_ASSERT. if (false) { - implicit_cast<From*, To>(NULL); + implicit_cast<From*, To>(nullptr); } // uses RTTI in dbg and fastbuild. asserts are disabled in opt builds. - assert(f == NULL || dynamic_cast<To>(f) != NULL); // NOLINT + assert(f == nullptr || dynamic_cast<To>(f) != nullptr); // NOLINT return static_cast<To>(f); } diff --git a/extern/ceres/internal/ceres/cgnr_linear_operator.h b/extern/ceres/internal/ceres/cgnr_linear_operator.h index beb8bbc2c2a..d708efca24c 100644 --- a/extern/ceres/internal/ceres/cgnr_linear_operator.h +++ b/extern/ceres/internal/ceres/cgnr_linear_operator.h @@ -34,7 +34,9 @@ #include <algorithm> #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" namespace ceres { @@ -78,11 +80,10 @@ class SparseMatrix; // and z = A^T b // // Note: This class is not thread safe, since it uses some temporary storage. -class CgnrLinearOperator : public LinearOperator { +class CERES_NO_EXPORT CgnrLinearOperator final : public LinearOperator { public: CgnrLinearOperator(const LinearOperator& A, const double* D) : A_(A), D_(D), z_(new double[A.num_rows()]) {} - virtual ~CgnrLinearOperator() {} void RightMultiply(const double* x, double* y) const final { std::fill(z_.get(), z_.get() + A_.num_rows(), 0.0); @@ -94,7 +95,7 @@ class CgnrLinearOperator : public LinearOperator { A_.LeftMultiply(z_.get(), y); // y = y + DtDx - if (D_ != NULL) { + if (D_ != nullptr) { int n = A_.num_cols(); VectorRef(y, n).array() += ConstVectorRef(D_, n).array().square() * ConstVectorRef(x, n).array(); @@ -117,4 +118,6 @@ class CgnrLinearOperator : public LinearOperator { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CGNR_LINEAR_OPERATOR_H_ diff --git a/extern/ceres/internal/ceres/cgnr_solver.cc b/extern/ceres/internal/ceres/cgnr_solver.cc index 9dba1cfb4a8..cca72bca988 100644 --- a/extern/ceres/internal/ceres/cgnr_solver.cc +++ b/extern/ceres/internal/ceres/cgnr_solver.cc @@ -30,6 +30,9 @@ #include "ceres/cgnr_solver.h" +#include <memory> +#include <utility> + #include "ceres/block_jacobi_preconditioner.h" #include "ceres/cgnr_linear_operator.h" #include "ceres/conjugate_gradients_solver.h" @@ -42,8 +45,8 @@ namespace ceres { namespace internal { -CgnrSolver::CgnrSolver(const LinearSolver::Options& options) - : options_(options) { +CgnrSolver::CgnrSolver(LinearSolver::Options options) + : options_(std::move(options)) { if (options_.preconditioner_type != JACOBI && options_.preconditioner_type != IDENTITY && options_.preconditioner_type != SUBSET) { @@ -54,7 +57,7 @@ CgnrSolver::CgnrSolver(const LinearSolver::Options& options) } } -CgnrSolver::~CgnrSolver() {} +CgnrSolver::~CgnrSolver() = default; LinearSolver::Summary CgnrSolver::SolveImpl( BlockSparseMatrix* A, @@ -70,7 +73,7 @@ LinearSolver::Summary CgnrSolver::SolveImpl( if (!preconditioner_) { if (options_.preconditioner_type == JACOBI) { - preconditioner_.reset(new BlockJacobiPreconditioner(*A)); + preconditioner_ = std::make_unique<BlockJacobiPreconditioner>(*A); } else if (options_.preconditioner_type == SUBSET) { Preconditioner::Options preconditioner_options; preconditioner_options.type = SUBSET; @@ -81,8 +84,8 @@ LinearSolver::Summary CgnrSolver::SolveImpl( preconditioner_options.use_postordering = options_.use_postordering; preconditioner_options.num_threads = options_.num_threads; preconditioner_options.context = options_.context; - preconditioner_.reset( - new SubsetPreconditioner(preconditioner_options, *A)); + preconditioner_ = + std::make_unique<SubsetPreconditioner>(preconditioner_options, *A); } } diff --git a/extern/ceres/internal/ceres/cgnr_solver.h b/extern/ceres/internal/ceres/cgnr_solver.h index bc701c0e9ed..25e62e9abd9 100644 --- a/extern/ceres/internal/ceres/cgnr_solver.h +++ b/extern/ceres/internal/ceres/cgnr_solver.h @@ -33,6 +33,7 @@ #include <memory> +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -49,12 +50,12 @@ class BlockJacobiPreconditioner; // // as required for solving for x in the least squares sense. Currently only // block diagonal preconditioning is supported. -class CgnrSolver : public BlockSparseMatrixSolver { +class CERES_NO_EXPORT CgnrSolver final : public BlockSparseMatrixSolver { public: - explicit CgnrSolver(const LinearSolver::Options& options); + explicit CgnrSolver(LinearSolver::Options options); CgnrSolver(const CgnrSolver&) = delete; void operator=(const CgnrSolver&) = delete; - virtual ~CgnrSolver(); + ~CgnrSolver() override; Summary SolveImpl(BlockSparseMatrix* A, const double* b, diff --git a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc index e1f6bb8ff9d..94e7e9aa446 100644 --- a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc +++ b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.cc @@ -33,7 +33,7 @@ #include <algorithm> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { diff --git a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h index d442e1a9bb8..f88a5bd9588 100644 --- a/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h +++ b/extern/ceres/internal/ceres/compressed_col_sparse_matrix_utils.h @@ -31,9 +31,11 @@ #ifndef CERES_INTERNAL_COMPRESSED_COL_SPARSE_MATRIX_UTILS_H_ #define CERES_INTERNAL_COMPRESSED_COL_SPARSE_MATRIX_UTILS_H_ +#include <algorithm> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -48,7 +50,7 @@ namespace internal { // and column block j, then it is expected that A contains at least // one non-zero entry corresponding to the top left entry of c_ij, // as that entry is used to detect the presence of a non-zero c_ij. -CERES_EXPORT_INTERNAL void CompressedColumnScalarMatrixToBlockMatrix( +CERES_NO_EXPORT void CompressedColumnScalarMatrixToBlockMatrix( const int* scalar_rows, const int* scalar_cols, const std::vector<int>& row_blocks, @@ -59,7 +61,7 @@ CERES_EXPORT_INTERNAL void CompressedColumnScalarMatrixToBlockMatrix( // Given a set of blocks and a permutation of these blocks, compute // the corresponding "scalar" ordering, where the scalar ordering of // size sum(blocks). -CERES_EXPORT_INTERNAL void BlockOrderingToScalarOrdering( +CERES_NO_EXPORT void BlockOrderingToScalarOrdering( const std::vector<int>& blocks, const std::vector<int>& block_ordering, std::vector<int>* scalar_ordering); @@ -142,4 +144,6 @@ void SolveRTRWithSparseRHS(IntegerType num_cols, } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_COMPRESSED_COL_SPARSE_MATRIX_UTILS_H_ diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc index 8e7e3e7e7e6..55b30a290f9 100644 --- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,7 +30,10 @@ #include "ceres/compressed_row_jacobian_writer.h" +#include <algorithm> #include <iterator> +#include <memory> +#include <string> #include <utility> #include <vector> @@ -55,7 +58,7 @@ void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors( vector<int>& col_blocks = *(jacobian->mutable_col_blocks()); col_blocks.resize(parameter_blocks.size()); for (int i = 0; i < parameter_blocks.size(); ++i) { - col_blocks[i] = parameter_blocks[i]->LocalSize(); + col_blocks[i] = parameter_blocks[i]->TangentSize(); } const vector<ResidualBlock*>& residual_blocks = program->residual_blocks(); @@ -81,10 +84,12 @@ void CompressedRowJacobianWriter::GetOrderedParameterBlocks( make_pair(parameter_block->index(), j)); } } - sort(evaluated_jacobian_blocks->begin(), evaluated_jacobian_blocks->end()); + std::sort(evaluated_jacobian_blocks->begin(), + evaluated_jacobian_blocks->end()); } -SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { +std::unique_ptr<SparseMatrix> CompressedRowJacobianWriter::CreateJacobian() + const { const vector<ResidualBlock*>& residual_blocks = program_->residual_blocks(); int total_num_residuals = program_->NumResiduals(); @@ -92,14 +97,13 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // Count the number of jacobian nonzeros. int num_jacobian_nonzeros = 0; - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_residuals = residual_block->NumResiduals(); const int num_parameter_blocks = residual_block->NumParameterBlocks(); for (int j = 0; j < num_parameter_blocks; ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; if (!parameter_block->IsConstant()) { - num_jacobian_nonzeros += num_residuals * parameter_block->LocalSize(); + num_jacobian_nonzeros += num_residuals * parameter_block->TangentSize(); } } } @@ -108,10 +112,11 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // Allocate more space than needed to store the jacobian so that when the LM // algorithm adds the diagonal, no reallocation is necessary. This reduces // peak memory usage significantly. - CompressedRowSparseMatrix* jacobian = new CompressedRowSparseMatrix( - total_num_residuals, - total_num_effective_parameters, - num_jacobian_nonzeros + total_num_effective_parameters); + std::unique_ptr<CompressedRowSparseMatrix> jacobian = + std::make_unique<CompressedRowSparseMatrix>( + total_num_residuals, + total_num_effective_parameters, + num_jacobian_nonzeros + total_num_effective_parameters); // At this stage, the CompressedRowSparseMatrix is an invalid state. But this // seems to be the only way to construct it without doing a memory copy. @@ -120,8 +125,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { int row_pos = 0; rows[0] = 0; - for (int i = 0; i < residual_blocks.size(); ++i) { - const ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_parameter_blocks = residual_block->NumParameterBlocks(); // Count the number of derivatives for a row of this residual block and @@ -132,7 +136,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; if (!parameter_block->IsConstant()) { parameter_indices.push_back(parameter_block->index()); - num_derivatives += parameter_block->LocalSize(); + num_derivatives += parameter_block->TangentSize(); } } @@ -163,10 +167,10 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // parameter vector. This code mirrors that in Write(), where jacobian // values are updated. int col_pos = 0; - for (int j = 0; j < parameter_indices.size(); ++j) { + for (int parameter_index : parameter_indices) { ParameterBlock* parameter_block = - program_->parameter_blocks()[parameter_indices[j]]; - const int parameter_block_size = parameter_block->LocalSize(); + program_->parameter_blocks()[parameter_index]; + const int parameter_block_size = parameter_block->TangentSize(); for (int r = 0; r < num_residuals; ++r) { // This is the position in the values array of the jacobian where this @@ -183,7 +187,7 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { } CHECK_EQ(num_jacobian_nonzeros, rows[total_num_residuals]); - PopulateJacobianRowAndColumnBlockVectors(program_, jacobian); + PopulateJacobianRowAndColumnBlockVectors(program_, jacobian.get()); return jacobian; } @@ -192,8 +196,7 @@ void CompressedRowJacobianWriter::Write(int residual_id, int residual_offset, double** jacobians, SparseMatrix* base_jacobian) { - CompressedRowSparseMatrix* jacobian = - down_cast<CompressedRowSparseMatrix*>(base_jacobian); + auto* jacobian = down_cast<CompressedRowSparseMatrix*>(base_jacobian); double* jacobian_values = jacobian->mutable_values(); const int* jacobian_rows = jacobian->rows(); @@ -210,11 +213,11 @@ void CompressedRowJacobianWriter::Write(int residual_id, // Iterate over the jacobian blocks in increasing order of their // positions in the reduced parameter vector. - for (int i = 0; i < evaluated_jacobian_blocks.size(); ++i) { + for (auto& evaluated_jacobian_block : evaluated_jacobian_blocks) { const ParameterBlock* parameter_block = - program_->parameter_blocks()[evaluated_jacobian_blocks[i].first]; - const int argument = evaluated_jacobian_blocks[i].second; - const int parameter_block_size = parameter_block->LocalSize(); + program_->parameter_blocks()[evaluated_jacobian_block.first]; + const int argument = evaluated_jacobian_block.second; + const int parameter_block_size = parameter_block->TangentSize(); // Copy one row of the jacobian block at a time. for (int r = 0; r < num_residuals; ++r) { diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h index b1251ca5cf5..7badab71b04 100644 --- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h +++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.h @@ -33,10 +33,12 @@ #ifndef CERES_INTERNAL_COMPRESSED_ROW_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_COMPRESSED_ROW_JACOBIAN_WRITER_H_ +#include <memory> #include <utility> #include <vector> #include "ceres/evaluator.h" +#include "ceres/internal/export.h" #include "ceres/scratch_evaluate_preparer.h" namespace ceres { @@ -46,7 +48,7 @@ class CompressedRowSparseMatrix; class Program; class SparseMatrix; -class CompressedRowJacobianWriter { +class CERES_NO_EXPORT CompressedRowJacobianWriter { public: CompressedRowJacobianWriter(Evaluator::Options /* ignored */, Program* program) @@ -89,11 +91,12 @@ class CompressedRowJacobianWriter { // assumed by the cost functions, use scratch space to store the // jacobians temporarily then copy them over to the larger jacobian // in the Write() function. - ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads) { + std::unique_ptr<ScratchEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads) { return ScratchEvaluatePreparer::Create(*program_, num_threads); } - SparseMatrix* CreateJacobian() const; + std::unique_ptr<SparseMatrix> CreateJacobian() const; void Write(int residual_id, int residual_offset, diff --git a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc index 900586c2c45..db103d9c0fa 100644 --- a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,11 +31,12 @@ #include "ceres/compressed_row_sparse_matrix.h" #include <algorithm> +#include <memory> #include <numeric> #include <vector> #include "ceres/crs_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/random.h" #include "ceres/triplet_sparse_matrix.h" #include "glog/logging.h" @@ -104,7 +105,7 @@ void TransposeForCompressedRowSparseStructure(const int num_rows, const int c = cols[idx]; const int transpose_idx = transpose_rows[c]++; transpose_cols[transpose_idx] = r; - if (values != NULL && transpose_values != NULL) { + if (values != nullptr && transpose_values != nullptr) { transpose_values[transpose_idx] = values[idx]; } } @@ -174,18 +175,20 @@ CompressedRowSparseMatrix::CompressedRowSparseMatrix(int num_rows, cols_.size() * sizeof(double); // NOLINT } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::FromTripletSparseMatrix( const TripletSparseMatrix& input) { return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, false); } -CompressedRowSparseMatrix* +std::unique_ptr<CompressedRowSparseMatrix> CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed( const TripletSparseMatrix& input) { return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, true); } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::FromTripletSparseMatrix( const TripletSparseMatrix& input, bool transpose) { int num_rows = input.num_rows(); int num_cols = input.num_cols(); @@ -214,8 +217,9 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix( input.num_nonzeros() * sizeof(int) + // NOLINT input.num_nonzeros() * sizeof(double)); // NOLINT - CompressedRowSparseMatrix* output = - new CompressedRowSparseMatrix(num_rows, num_cols, input.num_nonzeros()); + std::unique_ptr<CompressedRowSparseMatrix> output = + std::make_unique<CompressedRowSparseMatrix>( + num_rows, num_cols, input.num_nonzeros()); if (num_rows == 0) { // No data to copy. @@ -266,7 +270,7 @@ CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal, CHECK_EQ(num_nonzeros(), num_rows); } -CompressedRowSparseMatrix::~CompressedRowSparseMatrix() {} +CompressedRowSparseMatrix::~CompressedRowSparseMatrix() = default; void CompressedRowSparseMatrix::SetZero() { std::fill(values_.begin(), values_.end(), 0); @@ -533,17 +537,19 @@ void CompressedRowSparseMatrix::SetMaxNumNonZeros(int num_nonzeros) { values_.resize(num_nonzeros); } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( const double* diagonal, const vector<int>& blocks) { int num_rows = 0; int num_nonzeros = 0; - for (int i = 0; i < blocks.size(); ++i) { - num_rows += blocks[i]; - num_nonzeros += blocks[i] * blocks[i]; + for (int block_size : blocks) { + num_rows += block_size; + num_nonzeros += block_size * block_size; } - CompressedRowSparseMatrix* matrix = - new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros); + std::unique_ptr<CompressedRowSparseMatrix> matrix = + std::make_unique<CompressedRowSparseMatrix>( + num_rows, num_rows, num_nonzeros); int* rows = matrix->mutable_rows(); int* cols = matrix->mutable_cols(); @@ -552,8 +558,7 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( int idx_cursor = 0; int col_cursor = 0; - for (int i = 0; i < blocks.size(); ++i) { - const int block_size = blocks[i]; + for (int block_size : blocks) { for (int r = 0; r < block_size; ++r) { *(rows++) = idx_cursor; values[idx_cursor + r] = diagonal[col_cursor + r]; @@ -573,9 +578,11 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( return matrix; } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const { - CompressedRowSparseMatrix* transpose = - new CompressedRowSparseMatrix(num_cols_, num_rows_, num_nonzeros()); +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::Transpose() const { + std::unique_ptr<CompressedRowSparseMatrix> transpose = + std::make_unique<CompressedRowSparseMatrix>( + num_cols_, num_rows_, num_nonzeros()); switch (storage_type_) { case UNSYMMETRIC: @@ -612,7 +619,8 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::Transpose() const { return transpose; } -CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateRandomMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +CompressedRowSparseMatrix::CreateRandomMatrix( CompressedRowSparseMatrix::RandomMatrixOptions options) { CHECK_GT(options.num_row_blocks, 0); CHECK_GT(options.min_row_block_size, 0); @@ -714,7 +722,7 @@ CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateRandomMatrix( const int num_rows = std::accumulate(row_blocks.begin(), row_blocks.end(), 0); const int num_cols = std::accumulate(col_blocks.begin(), col_blocks.end(), 0); const bool kDoNotTranspose = false; - CompressedRowSparseMatrix* matrix = + std::unique_ptr<CompressedRowSparseMatrix> matrix = CompressedRowSparseMatrix::FromTripletSparseMatrix( TripletSparseMatrix( num_rows, num_cols, tsm_rows, tsm_cols, tsm_values), diff --git a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h index 0a1b945193d..3d7d385b185 100644 --- a/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h +++ b/extern/ceres/internal/ceres/compressed_row_sparse_matrix.h @@ -31,9 +31,11 @@ #ifndef CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_ #define CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_ +#include <memory> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" #include "glog/logging.h" @@ -46,7 +48,7 @@ namespace internal { class TripletSparseMatrix; -class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT CompressedRowSparseMatrix : public SparseMatrix { public: enum StorageType { UNSYMMETRIC, @@ -63,9 +65,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // entries. // // The storage type of the matrix is set to UNSYMMETRIC. - // - // Caller owns the result. - static CompressedRowSparseMatrix* FromTripletSparseMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> FromTripletSparseMatrix( const TripletSparseMatrix& input); // Create a matrix with the same content as the TripletSparseMatrix @@ -73,10 +73,8 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // entries. // // The storage type of the matrix is set to UNSYMMETRIC. - // - // Caller owns the result. - static CompressedRowSparseMatrix* FromTripletSparseMatrixTransposed( - const TripletSparseMatrix& input); + static std::unique_ptr<CompressedRowSparseMatrix> + FromTripletSparseMatrixTransposed(const TripletSparseMatrix& input); // Use this constructor only if you know what you are doing. This // creates a "blank" matrix with the appropriate amount of memory @@ -100,7 +98,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { CompressedRowSparseMatrix(const double* diagonal, int num_rows); // SparseMatrix interface. - virtual ~CompressedRowSparseMatrix(); + ~CompressedRowSparseMatrix() override; void SetZero() final; void RightMultiply(const double* x, double* y) const final; void LeftMultiply(const double* x, double* y) const final; @@ -124,7 +122,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { void ToCRSMatrix(CRSMatrix* matrix) const; - CompressedRowSparseMatrix* Transpose() const; + std::unique_ptr<CompressedRowSparseMatrix> Transpose() const; // Destructive array resizing method. void SetMaxNumNonZeros(int num_nonzeros); @@ -140,7 +138,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { const int* rows() const { return &rows_[0]; } int* mutable_rows() { return &rows_[0]; } - const StorageType storage_type() const { return storage_type_; } + StorageType storage_type() const { return storage_type_; } void set_storage_type(const StorageType storage_type) { storage_type_ = storage_type; } @@ -154,9 +152,7 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // Create a block diagonal CompressedRowSparseMatrix with the given // block structure. The individual blocks are assumed to be laid out // contiguously in the diagonal array, one block at a time. - // - // Caller owns the result. - static CompressedRowSparseMatrix* CreateBlockDiagonalMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> CreateBlockDiagonalMatrix( const double* diagonal, const std::vector<int>& blocks); // Options struct to control the generation of random block sparse @@ -198,13 +194,11 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { // Create a random CompressedRowSparseMatrix whose entries are // normally distributed and whose structure is determined by // RandomMatrixOptions. - // - // Caller owns the result. - static CompressedRowSparseMatrix* CreateRandomMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> CreateRandomMatrix( RandomMatrixOptions options); private: - static CompressedRowSparseMatrix* FromTripletSparseMatrix( + static std::unique_ptr<CompressedRowSparseMatrix> FromTripletSparseMatrix( const TripletSparseMatrix& input, bool transpose); int num_rows_; @@ -226,4 +220,6 @@ class CERES_EXPORT_INTERNAL CompressedRowSparseMatrix : public SparseMatrix { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_COMPRESSED_ROW_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/concurrent_queue.h b/extern/ceres/internal/ceres/concurrent_queue.h index a04d147c5c0..1e74153566a 100644 --- a/extern/ceres/internal/ceres/concurrent_queue.h +++ b/extern/ceres/internal/ceres/concurrent_queue.h @@ -78,7 +78,7 @@ template <typename T> class ConcurrentQueue { public: // Defaults the queue to blocking on Wait calls. - ConcurrentQueue() : wait_(true) {} + ConcurrentQueue() = default; // Atomically push an element onto the queue. If a thread was waiting for an // element, wake it up. @@ -149,7 +149,7 @@ class ConcurrentQueue { std::queue<T> queue_; // If true, signals that callers of Wait will block waiting to pop an // element off the queue. - bool wait_; + bool wait_{true}; }; } // namespace internal diff --git a/extern/ceres/internal/ceres/conditioned_cost_function.cc b/extern/ceres/internal/ceres/conditioned_cost_function.cc index fb4c52af084..a9013a23d0a 100644 --- a/extern/ceres/internal/ceres/conditioned_cost_function.cc +++ b/extern/ceres/internal/ceres/conditioned_cost_function.cc @@ -98,7 +98,7 @@ bool ConditionedCostFunction::Evaluate(double const* const* parameters, double** conditioner_derivative_pointer2 = &conditioner_derivative_pointer; if (!jacobians) { - conditioner_derivative_pointer2 = NULL; + conditioner_derivative_pointer2 = nullptr; } double unconditioned_residual = residuals[r]; diff --git a/extern/ceres/internal/ceres/conjugate_gradients_solver.cc b/extern/ceres/internal/ceres/conjugate_gradients_solver.cc index 3019628a16c..62ae9201cb5 100644 --- a/extern/ceres/internal/ceres/conjugate_gradients_solver.cc +++ b/extern/ceres/internal/ceres/conjugate_gradients_solver.cc @@ -41,6 +41,7 @@ #include <cmath> #include <cstddef> +#include <utility> #include "ceres/internal/eigen.h" #include "ceres/linear_operator.h" @@ -57,8 +58,8 @@ bool IsZeroOrInfinity(double x) { return ((x == 0.0) || std::isinf(x)); } } // namespace ConjugateGradientsSolver::ConjugateGradientsSolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)) {} LinearSolver::Summary ConjugateGradientsSolver::Solve( LinearOperator* A, @@ -112,7 +113,7 @@ LinearSolver::Summary ConjugateGradientsSolver::Solve( for (summary.num_iterations = 1;; ++summary.num_iterations) { // Apply preconditioner - if (per_solve_options.preconditioner != NULL) { + if (per_solve_options.preconditioner != nullptr) { z.setZero(); per_solve_options.preconditioner->RightMultiply(r.data(), z.data()); } else { diff --git a/extern/ceres/internal/ceres/conjugate_gradients_solver.h b/extern/ceres/internal/ceres/conjugate_gradients_solver.h index f79ca496531..99ddb5d485b 100644 --- a/extern/ceres/internal/ceres/conjugate_gradients_solver.h +++ b/extern/ceres/internal/ceres/conjugate_gradients_solver.h @@ -34,7 +34,8 @@ #ifndef CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_ #define CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -55,9 +56,9 @@ class LinearOperator; // For more details see the documentation for // LinearSolver::PerSolveOptions::r_tolerance and // LinearSolver::PerSolveOptions::q_tolerance in linear_solver.h. -class CERES_EXPORT_INTERNAL ConjugateGradientsSolver : public LinearSolver { +class CERES_NO_EXPORT ConjugateGradientsSolver final : public LinearSolver { public: - explicit ConjugateGradientsSolver(const LinearSolver::Options& options); + explicit ConjugateGradientsSolver(LinearSolver::Options options); Summary Solve(LinearOperator* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, @@ -70,4 +71,6 @@ class CERES_EXPORT_INTERNAL ConjugateGradientsSolver : public LinearSolver { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CONJUGATE_GRADIENTS_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/context.cc b/extern/ceres/internal/ceres/context.cc index 55e76351219..fde16b84bca 100644 --- a/extern/ceres/internal/ceres/context.cc +++ b/extern/ceres/internal/ceres/context.cc @@ -34,6 +34,8 @@ namespace ceres { +Context::Context() = default; Context* Context::Create() { return new internal::ContextImpl(); } +Context::~Context() = default; } // namespace ceres diff --git a/extern/ceres/internal/ceres/context_impl.cc b/extern/ceres/internal/ceres/context_impl.cc index 20fe5cbab2a..a4b3c842da1 100644 --- a/extern/ceres/internal/ceres/context_impl.cc +++ b/extern/ceres/internal/ceres/context_impl.cc @@ -30,9 +30,75 @@ #include "ceres/context_impl.h" +#include <string> + +#include "ceres/internal/config.h" + +#ifndef CERES_NO_CUDA +#include "cublas_v2.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + namespace ceres { namespace internal { +ContextImpl::ContextImpl() = default; + +#ifndef CERES_NO_CUDA +bool ContextImpl::InitCUDA(std::string* message) { + if (cuda_initialized_) { + return true; + } + if (cublasCreate(&cublas_handle_) != CUBLAS_STATUS_SUCCESS) { + *message = "cuBLAS::cublasCreate failed."; + cublas_handle_ = nullptr; + return false; + } + if (cusolverDnCreate(&cusolver_handle_) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnCreate failed."; + cusolver_handle_ = nullptr; + cublasDestroy(cublas_handle_); + cublas_handle_ = nullptr; + return false; + } + if (cudaStreamCreateWithFlags(&stream_, cudaStreamNonBlocking) != + cudaSuccess) { + *message = "CUDA::cudaStreamCreateWithFlags failed."; + cusolverDnDestroy(cusolver_handle_); + cublasDestroy(cublas_handle_); + cusolver_handle_ = nullptr; + cublas_handle_ = nullptr; + stream_ = nullptr; + return false; + } + if (cusolverDnSetStream(cusolver_handle_, stream_) != + CUSOLVER_STATUS_SUCCESS || + cublasSetStream(cublas_handle_, stream_) != CUBLAS_STATUS_SUCCESS) { + *message = + "cuSolverDN::cusolverDnSetStream or cuBLAS::cublasSetStream failed."; + cusolverDnDestroy(cusolver_handle_); + cublasDestroy(cublas_handle_); + cudaStreamDestroy(stream_); + cusolver_handle_ = nullptr; + cublas_handle_ = nullptr; + stream_ = nullptr; + return false; + } + cuda_initialized_ = true; + return true; +} +#endif // CERES_NO_CUDA + +ContextImpl::~ContextImpl() { +#ifndef CERES_NO_CUDA + if (cuda_initialized_) { + cusolverDnDestroy(cusolver_handle_); + cublasDestroy(cublas_handle_); + cudaStreamDestroy(stream_); + } +#endif // CERES_NO_CUDA +} void ContextImpl::EnsureMinimumThreads(int num_threads) { #ifdef CERES_USE_CXX_THREADS thread_pool.Resize(num_threads); diff --git a/extern/ceres/internal/ceres/context_impl.h b/extern/ceres/internal/ceres/context_impl.h index 574d1efcc6d..8e9a03fb4ae 100644 --- a/extern/ceres/internal/ceres/context_impl.h +++ b/extern/ceres/internal/ceres/context_impl.h @@ -33,10 +33,20 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" -// clanf-format on +#include "ceres/internal/config.h" +// clang-format on + +#include <string> #include "ceres/context.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" + +#ifndef CERES_NO_CUDA +#include "cublas_v2.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA #ifdef CERES_USE_CXX_THREADS #include "ceres/thread_pool.h" @@ -45,14 +55,13 @@ namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL ContextImpl : public Context { +class CERES_NO_EXPORT ContextImpl final : public Context { public: - ContextImpl() {} + ContextImpl(); + ~ContextImpl() override; ContextImpl(const ContextImpl&) = delete; void operator=(const ContextImpl&) = delete; - virtual ~ContextImpl() {} - // When compiled with C++ threading support, resize the thread pool to have // at min(num_thread, num_hardware_threads) where num_hardware_threads is // defined by the hardware. Otherwise this call is a no-op. @@ -61,9 +70,28 @@ class CERES_EXPORT_INTERNAL ContextImpl : public Context { #ifdef CERES_USE_CXX_THREADS ThreadPool thread_pool; #endif // CERES_USE_CXX_THREADS + +#ifndef CERES_NO_CUDA + // Initializes the cuSolverDN context, creates an asynchronous stream, and + // associates the stream with cuSolverDN. Returns true iff initialization was + // successful, else it returns false and a human-readable error message is + // returned. + bool InitCUDA(std::string* message); + + // Handle to the cuSOLVER context. + cusolverDnHandle_t cusolver_handle_ = nullptr; + // Handle to cuBLAS context. + cublasHandle_t cublas_handle_ = nullptr; + // CUDA device stream. + cudaStream_t stream_ = nullptr; + // Indicates whether all the CUDA resources have been initialized. + bool cuda_initialized_ = false; +#endif // CERES_NO_CUDA }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CONTEXT_IMPL_H_ diff --git a/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc b/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc index 93096ac0728..a6e149d1cee 100644 --- a/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc +++ b/extern/ceres/internal/ceres/coordinate_descent_minimizer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -64,7 +64,7 @@ CoordinateDescentMinimizer::CoordinateDescentMinimizer(ContextImpl* context) CHECK(context_ != nullptr); } -CoordinateDescentMinimizer::~CoordinateDescentMinimizer() {} +CoordinateDescentMinimizer::~CoordinateDescentMinimizer() = default; bool CoordinateDescentMinimizer::Init( const Program& program, @@ -94,9 +94,9 @@ bool CoordinateDescentMinimizer::Init( // assign zero offsets/empty independent sets to these parameter // blocks. const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - if (!ordering.IsMember(parameter_blocks[i]->mutable_user_state())) { - parameter_blocks_.push_back(parameter_blocks[i]); + for (auto* parameter_block : parameter_blocks) { + if (!ordering.IsMember(parameter_block->mutable_user_state())) { + parameter_blocks_.push_back(parameter_block); independent_set_offsets_.push_back(independent_set_offsets_.back()); } } @@ -105,8 +105,7 @@ bool CoordinateDescentMinimizer::Init( // block. residual_blocks_.resize(parameter_block_index.size()); const vector<ResidualBlock*>& residual_blocks = program.residual_blocks(); - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_parameter_blocks = residual_block->NumParameterBlocks(); for (int j = 0; j < num_parameter_blocks; ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; @@ -129,14 +128,15 @@ void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options, double* parameters, Solver::Summary* summary) { // Set the state and mark all parameter blocks constant. - for (int i = 0; i < parameter_blocks_.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { parameter_block->SetState(parameters + parameter_block->state_offset()); parameter_block->SetConstant(); } - std::unique_ptr<LinearSolver*[]> linear_solvers( - new LinearSolver*[options.num_threads]); + std::vector<std::unique_ptr<LinearSolver>> linear_solvers( + options.num_threads); + // std::unique_ptr<LinearSolver*[]> linear_solvers( + // new LinearSolver*[options.num_threads]); LinearSolver::Options linear_solver_options; linear_solver_options.type = DENSE_QR; @@ -188,7 +188,7 @@ void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options, // we are fine. Solver::Summary inner_summary; Solve(&inner_program, - linear_solvers[thread_id], + linear_solvers[thread_id].get(), parameters + parameter_block->state_offset(), &inner_summary); @@ -200,13 +200,13 @@ void CoordinateDescentMinimizer::Minimize(const Minimizer::Options& options, }); } - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->SetVarying(); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->SetVarying(); } - for (int i = 0; i < options.num_threads; ++i) { - delete linear_solvers[i]; - } + // for (int i = 0; i < options.num_threads; ++i) { + // delete linear_solvers[i]; + //} } // Solve the optimization problem for one parameter block. @@ -221,17 +221,16 @@ void CoordinateDescentMinimizer::Solve(Program* program, string error; Minimizer::Options minimizer_options; - minimizer_options.evaluator.reset( - Evaluator::Create(evaluator_options_, program, &error)); + minimizer_options.evaluator = + Evaluator::Create(evaluator_options_, program, &error); CHECK(minimizer_options.evaluator != nullptr); - minimizer_options.jacobian.reset( - minimizer_options.evaluator->CreateJacobian()); + minimizer_options.jacobian = minimizer_options.evaluator->CreateJacobian(); CHECK(minimizer_options.jacobian != nullptr); TrustRegionStrategy::Options trs_options; trs_options.linear_solver = linear_solver; - minimizer_options.trust_region_strategy.reset( - TrustRegionStrategy::Create(trs_options)); + minimizer_options.trust_region_strategy = + TrustRegionStrategy::Create(trs_options); CHECK(minimizer_options.trust_region_strategy != nullptr); minimizer_options.is_silent = true; @@ -263,12 +262,12 @@ bool CoordinateDescentMinimizer::IsOrderingValid( // of independent sets of decreasing size and invert it. This // seems to work better in practice, i.e., Cameras before // points. -ParameterBlockOrdering* CoordinateDescentMinimizer::CreateOrdering( - const Program& program) { - std::unique_ptr<ParameterBlockOrdering> ordering(new ParameterBlockOrdering); +std::shared_ptr<ParameterBlockOrdering> +CoordinateDescentMinimizer::CreateOrdering(const Program& program) { + auto ordering = std::make_shared<ParameterBlockOrdering>(); ComputeRecursiveIndependentSetOrdering(program, ordering.get()); ordering->Reverse(); - return ordering.release(); + return ordering; } } // namespace internal diff --git a/extern/ceres/internal/ceres/coordinate_descent_minimizer.h b/extern/ceres/internal/ceres/coordinate_descent_minimizer.h index 7d17d53eb0f..75f26480c88 100644 --- a/extern/ceres/internal/ceres/coordinate_descent_minimizer.h +++ b/extern/ceres/internal/ceres/coordinate_descent_minimizer.h @@ -56,7 +56,7 @@ class LinearSolver; // // The minimizer assumes that none of the parameter blocks in the // program are constant. -class CoordinateDescentMinimizer : public Minimizer { +class CERES_NO_EXPORT CoordinateDescentMinimizer final : public Minimizer { public: explicit CoordinateDescentMinimizer(ContextImpl* context); @@ -66,7 +66,7 @@ class CoordinateDescentMinimizer : public Minimizer { std::string* error); // Minimizer interface. - virtual ~CoordinateDescentMinimizer(); + ~CoordinateDescentMinimizer() override; void Minimize(const Minimizer::Options& options, double* parameters, @@ -81,7 +81,8 @@ class CoordinateDescentMinimizer : public Minimizer { // of independent sets of decreasing size and invert it. This // seems to work better in practice, i.e., Cameras before // points. - static ParameterBlockOrdering* CreateOrdering(const Program& program); + static std::shared_ptr<ParameterBlockOrdering> CreateOrdering( + const Program& program); private: void Solve(Program* program, diff --git a/extern/ceres/internal/ceres/corrector.cc b/extern/ceres/internal/ceres/corrector.cc index 6a79a06a544..bf3ba9c5714 100644 --- a/extern/ceres/internal/ceres/corrector.cc +++ b/extern/ceres/internal/ceres/corrector.cc @@ -111,7 +111,7 @@ Corrector::Corrector(const double sq_norm, const double rho[3]) { } void Corrector::CorrectResiduals(const int num_rows, double* residuals) { - DCHECK(residuals != NULL); + DCHECK(residuals != nullptr); // Equation 11 in BANS. VectorRef(residuals, num_rows) *= residual_scaling_; } @@ -120,8 +120,8 @@ void Corrector::CorrectJacobian(const int num_rows, const int num_cols, double* residuals, double* jacobian) { - DCHECK(residuals != NULL); - DCHECK(jacobian != NULL); + DCHECK(residuals != nullptr); + DCHECK(jacobian != nullptr); // The common case (rho[2] <= 0). if (alpha_sq_norm_ == 0.0) { diff --git a/extern/ceres/internal/ceres/corrector.h b/extern/ceres/internal/ceres/corrector.h index 3e11cdce1ae..44379a3ea7a 100644 --- a/extern/ceres/internal/ceres/corrector.h +++ b/extern/ceres/internal/ceres/corrector.h @@ -35,7 +35,8 @@ #ifndef CERES_INTERNAL_CORRECTOR_H_ #define CERES_INTERNAL_CORRECTOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -48,7 +49,7 @@ namespace internal { // gauss newton approximation and then take its square root to get the // corresponding corrections to the residual and jacobian. For the // full expressions see Eq. 10 and 11 in BANS by Triggs et al. -class CERES_EXPORT_INTERNAL Corrector { +class CERES_NO_EXPORT Corrector { public: // The constructor takes the squared norm, the value, the first and // second derivatives of the LossFunction. It precalculates some of @@ -89,4 +90,6 @@ class CERES_EXPORT_INTERNAL Corrector { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_CORRECTOR_H_ diff --git a/extern/ceres/internal/ceres/blas.h b/extern/ceres/internal/ceres/cost_function.cc index a43301c5d18..7597b431ec9 100644 --- a/extern/ceres/internal/ceres/blas.h +++ b/extern/ceres/internal/ceres/cost_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -27,31 +27,13 @@ // POSSIBILITY OF SUCH DAMAGE. // // Author: sameeragarwal@google.com (Sameer Agarwal) -// -// Wrapper functions around BLAS functions. +// keir@google.m (Keir Mierle) -#ifndef CERES_INTERNAL_BLAS_H_ -#define CERES_INTERNAL_BLAS_H_ +#include "ceres/cost_function.h" namespace ceres { -namespace internal { -class BLAS { - public: - // transpose = true : c = alpha * a'a + beta * c; - // transpose = false : c = alpha * aa' + beta * c; - // - // Assumes column major matrices. - static void SymmetricRankKUpdate(int num_rows, - int num_cols, - const double* a, - bool transpose, - double alpha, - double beta, - double* c); -}; +CostFunction::CostFunction() : num_residuals_(0) {} +CostFunction::~CostFunction() = default; -} // namespace internal } // namespace ceres - -#endif // CERES_INTERNAL_BLAS_H_ diff --git a/extern/ceres/internal/ceres/covariance.cc b/extern/ceres/internal/ceres/covariance.cc index 8e240ff317c..d63dd3789c3 100644 --- a/extern/ceres/internal/ceres/covariance.cc +++ b/extern/ceres/internal/ceres/covariance.cc @@ -39,15 +39,14 @@ namespace ceres { -using std::make_pair; using std::pair; using std::vector; Covariance::Covariance(const Covariance::Options& options) { - impl_.reset(new internal::CovarianceImpl(options)); + impl_ = std::make_unique<internal::CovarianceImpl>(options); } -Covariance::~Covariance() {} +Covariance::~Covariance() = default; bool Covariance::Compute( const vector<pair<const double*, const double*>>& covariance_blocks, diff --git a/extern/ceres/internal/ceres/covariance_impl.cc b/extern/ceres/internal/ceres/covariance_impl.cc index 1f86707f5a7..324b5531a04 100644 --- a/extern/ceres/internal/ceres/covariance_impl.cc +++ b/extern/ceres/internal/ceres/covariance_impl.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -79,13 +79,12 @@ CovarianceImpl::CovarianceImpl(const Covariance::Options& options) evaluate_options_.apply_loss_function = options_.apply_loss_function; } -CovarianceImpl::~CovarianceImpl() {} +CovarianceImpl::~CovarianceImpl() = default; template <typename T> void CheckForDuplicates(std::vector<T> blocks) { - sort(blocks.begin(), blocks.end()); - typename std::vector<T>::iterator it = - std::adjacent_find(blocks.begin(), blocks.end()); + std::sort(blocks.begin(), blocks.end()); + auto it = std::adjacent_find(blocks.begin(), blocks.end()); if (it != blocks.end()) { // In case there are duplicates, we search for their location. std::map<T, std::vector<int>> blocks_map; @@ -117,7 +116,7 @@ bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks, covariance_blocks); problem_ = problem; parameter_block_to_row_index_.clear(); - covariance_matrix_.reset(NULL); + covariance_matrix_ = nullptr; is_valid_ = (ComputeCovarianceSparsity(covariance_blocks, problem) && ComputeCovarianceValues()); is_computed_ = true; @@ -162,10 +161,10 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( const int block1_size = block1->Size(); const int block2_size = block2->Size(); - const int block1_local_size = block1->LocalSize(); - const int block2_local_size = block2->LocalSize(); + const int block1_tangent_size = block1->TangentSize(); + const int block2_tangent_size = block2->TangentSize(); if (!lift_covariance_to_ambient_space) { - MatrixRef(covariance_block, block1_local_size, block2_local_size) + MatrixRef(covariance_block, block1_tangent_size, block2_tangent_size) .setZero(); } else { MatrixRef(covariance_block, block1_size, block2_size).setZero(); @@ -209,34 +208,34 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( FindOrDie(parameter_map, const_cast<double*>(parameter_block1)); ParameterBlock* block2 = FindOrDie(parameter_map, const_cast<double*>(parameter_block2)); - const LocalParameterization* local_param1 = block1->local_parameterization(); - const LocalParameterization* local_param2 = block2->local_parameterization(); + const Manifold* manifold1 = block1->manifold(); + const Manifold* manifold2 = block2->manifold(); const int block1_size = block1->Size(); - const int block1_local_size = block1->LocalSize(); + const int block1_tangent_size = block1->TangentSize(); const int block2_size = block2->Size(); - const int block2_local_size = block2->LocalSize(); + const int block2_tangent_size = block2->TangentSize(); - ConstMatrixRef cov( - covariance_matrix_->values() + rows[row_begin], block1_size, row_size); + ConstMatrixRef cov(covariance_matrix_->values() + rows[row_begin], + block1_tangent_size, + row_size); - // Fast path when there are no local parameterizations or if the - // user does not want it lifted to the ambient space. - if ((local_param1 == NULL && local_param2 == NULL) || + // Fast path when there are no manifolds or if the user does not want it + // lifted to the ambient space. + if ((manifold1 == nullptr && manifold2 == nullptr) || !lift_covariance_to_ambient_space) { if (transpose) { - MatrixRef(covariance_block, block2_local_size, block1_local_size) = - cov.block(0, offset, block1_local_size, block2_local_size) + MatrixRef(covariance_block, block2_tangent_size, block1_tangent_size) = + cov.block(0, offset, block1_tangent_size, block2_tangent_size) .transpose(); } else { - MatrixRef(covariance_block, block1_local_size, block2_local_size) = - cov.block(0, offset, block1_local_size, block2_local_size); + MatrixRef(covariance_block, block1_tangent_size, block2_tangent_size) = + cov.block(0, offset, block1_tangent_size, block2_tangent_size); } return true; } - // If local parameterizations are used then the covariance that has - // been computed is in the tangent space and it needs to be lifted - // back to the ambient space. + // If manifolds are used then the covariance that has been computed is in the + // tangent space and it needs to be lifted back to the ambient space. // // This is given by the formula // @@ -249,36 +248,37 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( // See Result 5.11 on page 142 of Hartley & Zisserman (2nd Edition) // for a proof. // - // TODO(sameeragarwal): Add caching of local parameterization, so - // that they are computed just once per parameter block. - Matrix block1_jacobian(block1_size, block1_local_size); - if (local_param1 == NULL) { + // TODO(sameeragarwal): Add caching the manifold plus_jacobian, so that they + // are computed just once per parameter block. + Matrix block1_jacobian(block1_size, block1_tangent_size); + if (manifold1 == nullptr) { block1_jacobian.setIdentity(); } else { - local_param1->ComputeJacobian(parameter_block1, block1_jacobian.data()); + manifold1->PlusJacobian(parameter_block1, block1_jacobian.data()); } - Matrix block2_jacobian(block2_size, block2_local_size); + Matrix block2_jacobian(block2_size, block2_tangent_size); // Fast path if the user is requesting a diagonal block. if (parameter_block1 == parameter_block2) { block2_jacobian = block1_jacobian; } else { - if (local_param2 == NULL) { + if (manifold2 == nullptr) { block2_jacobian.setIdentity(); } else { - local_param2->ComputeJacobian(parameter_block2, block2_jacobian.data()); + manifold2->PlusJacobian(parameter_block2, block2_jacobian.data()); } } if (transpose) { MatrixRef(covariance_block, block2_size, block1_size) = block2_jacobian * - cov.block(0, offset, block1_local_size, block2_local_size).transpose() * + cov.block(0, offset, block1_tangent_size, block2_tangent_size) + .transpose() * block1_jacobian.transpose(); } else { MatrixRef(covariance_block, block1_size, block2_size) = block1_jacobian * - cov.block(0, offset, block1_local_size, block2_local_size) * + cov.block(0, offset, block1_tangent_size, block2_tangent_size) * block2_jacobian.transpose(); } @@ -309,7 +309,7 @@ bool CovarianceImpl::GetCovarianceMatrixInTangentOrAmbientSpace( if (lift_covariance_to_ambient_space) { parameter_sizes.push_back(block->Size()); } else { - parameter_sizes.push_back(block->LocalSize()); + parameter_sizes.push_back(block->TangentSize()); } } std::partial_sum(parameter_sizes.begin(), @@ -383,8 +383,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( std::vector<ResidualBlock*> residual_blocks; problem->GetResidualBlocks(&residual_blocks); - for (int i = 0; i < residual_blocks.size(); ++i) { - ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { parameter_blocks_in_use.insert(residual_block->parameter_blocks(), residual_block->parameter_blocks() + residual_block->NumParameterBlocks()); @@ -394,8 +393,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( std::vector<double*>& active_parameter_blocks = evaluate_options_.parameter_blocks; active_parameter_blocks.clear(); - for (int i = 0; i < all_parameter_blocks.size(); ++i) { - double* parameter_block = all_parameter_blocks[i]; + for (auto* parameter_block : all_parameter_blocks) { ParameterBlock* block = FindOrDie(parameter_map, parameter_block); if (!block->IsConstant() && (parameter_blocks_in_use.count(block) > 0)) { active_parameter_blocks.push_back(parameter_block); @@ -411,10 +409,9 @@ bool CovarianceImpl::ComputeCovarianceSparsity( // ordering of parameter blocks just constructed. int num_rows = 0; parameter_block_to_row_index_.clear(); - for (int i = 0; i < active_parameter_blocks.size(); ++i) { - double* parameter_block = active_parameter_blocks[i]; + for (auto* parameter_block : active_parameter_blocks) { const int parameter_block_size = - problem->ParameterBlockLocalSize(parameter_block); + problem->ParameterBlockTangentSize(parameter_block); parameter_block_to_row_index_[parameter_block] = num_rows; num_rows += parameter_block_size; } @@ -424,9 +421,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( // triangular part of the matrix. int num_nonzeros = 0; CovarianceBlocks covariance_blocks; - for (int i = 0; i < original_covariance_blocks.size(); ++i) { - const std::pair<const double*, const double*>& block_pair = - original_covariance_blocks[i]; + for (const auto& block_pair : original_covariance_blocks) { if (constant_parameter_blocks_.count(block_pair.first) > 0 || constant_parameter_blocks_.count(block_pair.second) > 0) { continue; @@ -434,8 +429,8 @@ bool CovarianceImpl::ComputeCovarianceSparsity( int index1 = FindOrDie(parameter_block_to_row_index_, block_pair.first); int index2 = FindOrDie(parameter_block_to_row_index_, block_pair.second); - const int size1 = problem->ParameterBlockLocalSize(block_pair.first); - const int size2 = problem->ParameterBlockLocalSize(block_pair.second); + const int size1 = problem->ParameterBlockTangentSize(block_pair.first); + const int size2 = problem->ParameterBlockTangentSize(block_pair.second); num_nonzeros += size1 * size2; // Make sure we are constructing a block upper triangular matrix. @@ -447,9 +442,9 @@ bool CovarianceImpl::ComputeCovarianceSparsity( } } - if (covariance_blocks.size() == 0) { + if (covariance_blocks.empty()) { VLOG(2) << "No non-zero covariance blocks found"; - covariance_matrix_.reset(NULL); + covariance_matrix_ = nullptr; return true; } @@ -459,8 +454,8 @@ bool CovarianceImpl::ComputeCovarianceSparsity( std::sort(covariance_blocks.begin(), covariance_blocks.end()); // Fill the sparsity pattern of the covariance matrix. - covariance_matrix_.reset( - new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros)); + covariance_matrix_ = std::make_unique<CompressedRowSparseMatrix>( + num_rows, num_rows, num_nonzeros); int* rows = covariance_matrix_->mutable_rows(); int* cols = covariance_matrix_->mutable_cols(); @@ -480,7 +475,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( int cursor = 0; // index into the covariance matrix. for (const auto& entry : parameter_block_to_row_index_) { const double* row_block = entry.first; - const int row_block_size = problem->ParameterBlockLocalSize(row_block); + const int row_block_size = problem->ParameterBlockTangentSize(row_block); int row_begin = entry.second; // Iterate over the covariance blocks contained in this row block @@ -493,7 +488,7 @@ bool CovarianceImpl::ComputeCovarianceSparsity( if (block_pair.first != row_block) { break; } - num_columns += problem->ParameterBlockLocalSize(block_pair.second); + num_columns += problem->ParameterBlockTangentSize(block_pair.second); } // Fill out all the compressed rows for this parameter block. @@ -501,7 +496,8 @@ bool CovarianceImpl::ComputeCovarianceSparsity( rows[row_begin + r] = cursor; for (int c = 0; c < num_col_blocks; ++c) { const double* col_block = covariance_blocks[i + c].second; - const int col_block_size = problem->ParameterBlockLocalSize(col_block); + const int col_block_size = + problem->ParameterBlockTangentSize(col_block); int col_begin = FindOrDie(parameter_block_to_row_index_, col_block); for (int k = 0; k < col_block_size; ++k) { cols[cursor++] = col_begin++; @@ -556,13 +552,13 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { "CovarianceImpl::ComputeCovarianceValuesUsingSparseQR"); #ifndef CERES_NO_SUITESPARSE - if (covariance_matrix_.get() == NULL) { + if (covariance_matrix_ == nullptr) { // Nothing to do, all zeros covariance matrix. return true; } CRSMatrix jacobian; - problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian); + problem_->Evaluate(evaluate_options_, nullptr, nullptr, nullptr, &jacobian); event_logger.AddEvent("Evaluate"); // Construct a compressed column form of the Jacobian. @@ -601,11 +597,11 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { cholmod_jacobian.nrow = num_rows; cholmod_jacobian.ncol = num_cols; cholmod_jacobian.nzmax = num_nonzeros; - cholmod_jacobian.nz = NULL; + cholmod_jacobian.nz = nullptr; cholmod_jacobian.p = reinterpret_cast<void*>(&transpose_rows[0]); cholmod_jacobian.i = reinterpret_cast<void*>(&transpose_cols[0]); cholmod_jacobian.x = reinterpret_cast<void*>(&transpose_values[0]); - cholmod_jacobian.z = NULL; + cholmod_jacobian.z = nullptr; cholmod_jacobian.stype = 0; // Matrix is not symmetric. cholmod_jacobian.itype = CHOLMOD_LONG; cholmod_jacobian.xtype = CHOLMOD_REAL; @@ -616,8 +612,8 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { cholmod_common cc; cholmod_l_start(&cc); - cholmod_sparse* R = NULL; - SuiteSparse_long* permutation = NULL; + cholmod_sparse* R = nullptr; + SuiteSparse_long* permutation = nullptr; // Compute a Q-less QR factorization of the Jacobian. Since we are // only interested in inverting J'J = R'R, we do not need Q. This @@ -648,9 +644,9 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { } if (rank < cholmod_jacobian.ncol) { - LOG(ERROR) << "Jacobian matrix is rank deficient. " - << "Number of columns: " << cholmod_jacobian.ncol - << " rank: " << rank; + LOG(WARNING) << "Jacobian matrix is rank deficient. " + << "Number of columns: " << cholmod_jacobian.ncol + << " rank: " << rank; free(permutation); cholmod_l_free_sparse(&R, &cc); cholmod_l_finish(&cc); @@ -721,13 +717,13 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() { bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() { EventLogger event_logger( "CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD"); - if (covariance_matrix_.get() == NULL) { + if (covariance_matrix_ == nullptr) { // Nothing to do, all zeros covariance matrix. return true; } CRSMatrix jacobian; - problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian); + problem_->Evaluate(evaluate_options_, nullptr, nullptr, nullptr, &jacobian); event_logger.AddEvent("Evaluate"); Matrix dense_jacobian(jacobian.num_rows, jacobian.num_cols); @@ -812,20 +808,20 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() { bool CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR() { EventLogger event_logger( "CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR"); - if (covariance_matrix_.get() == NULL) { + if (covariance_matrix_ == nullptr) { // Nothing to do, all zeros covariance matrix. return true; } CRSMatrix jacobian; - problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian); + problem_->Evaluate(evaluate_options_, nullptr, nullptr, nullptr, &jacobian); event_logger.AddEvent("Evaluate"); - typedef Eigen::SparseMatrix<double, Eigen::ColMajor> EigenSparseMatrix; + using EigenSparseMatrix = Eigen::SparseMatrix<double, Eigen::ColMajor>; // Convert the matrix to column major order as required by SparseQR. EigenSparseMatrix sparse_jacobian = - Eigen::MappedSparseMatrix<double, Eigen::RowMajor>( + Eigen::Map<Eigen::SparseMatrix<double, Eigen::RowMajor>>( jacobian.num_rows, jacobian.num_cols, static_cast<int>(jacobian.values.size()), diff --git a/extern/ceres/internal/ceres/covariance_impl.h b/extern/ceres/internal/ceres/covariance_impl.h index 394a04bbc78..fc029ce25b7 100644 --- a/extern/ceres/internal/ceres/covariance_impl.h +++ b/extern/ceres/internal/ceres/covariance_impl.h @@ -38,7 +38,8 @@ #include <vector> #include "ceres/covariance.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/problem_impl.h" #include "ceres/suitesparse.h" @@ -47,7 +48,7 @@ namespace internal { class CompressedRowSparseMatrix; -class CERES_EXPORT_INTERNAL CovarianceImpl { +class CERES_NO_EXPORT CovarianceImpl { public: explicit CovarianceImpl(const Covariance::Options& options); ~CovarianceImpl(); @@ -98,4 +99,6 @@ class CERES_EXPORT_INTERNAL CovarianceImpl { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_COVARIANCE_IMPL_H_ diff --git a/extern/ceres/internal/ceres/cuda_buffer.h b/extern/ceres/internal/ceres/cuda_buffer.h new file mode 100644 index 00000000000..a1cf78420d5 --- /dev/null +++ b/extern/ceres/internal/ceres/cuda_buffer.h @@ -0,0 +1,107 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: joydeepb@cs.utexas.edu (Joydeep Biswas) + +#ifndef CERES_INTERNAL_CUDA_BUFFER_H_ +#define CERES_INTERNAL_CUDA_BUFFER_H_ + +#include "ceres/internal/config.h" + +#ifndef CERES_NO_CUDA + +#include <vector> + +#include "cuda_runtime.h" +#include "glog/logging.h" + +// An encapsulated buffer to maintain GPU memory, and handle transfers between +// GPU and system memory. It is the responsibility of the user to ensure that +// the appropriate GPU device is selected before each subroutine is called. This +// is particularly important when using multiple GPU devices on different CPU +// threads, since active Cuda devices are determined by the cuda runtime on a +// per-thread basis. Note that unless otherwise specified, all methods use the +// default stream, and are synchronous. +template <typename T> +class CudaBuffer { + public: + CudaBuffer() = default; + CudaBuffer(const CudaBuffer&) = delete; + CudaBuffer& operator=(const CudaBuffer&) = delete; + + ~CudaBuffer() { + if (data_ != nullptr) { + CHECK_EQ(cudaFree(data_), cudaSuccess); + } + } + + // Grow the GPU memory buffer if needed to accommodate data of the specified + // size + void Reserve(const size_t size) { + if (size > size_) { + if (data_ != nullptr) { + CHECK_EQ(cudaFree(data_), cudaSuccess); + } + CHECK_EQ(cudaMalloc(&data_, size * sizeof(T)), cudaSuccess); + size_ = size; + } + } + + // Perform an asynchronous copy from CPU memory to GPU memory using the stream + // provided. + void CopyToGpuAsync(const T* data, const size_t size, cudaStream_t stream) { + Reserve(size); + CHECK_EQ(cudaMemcpyAsync( + data_, data, size * sizeof(T), cudaMemcpyHostToDevice, stream), + cudaSuccess); + } + + // Copy data from the GPU to CPU memory. This is necessarily synchronous since + // any potential GPU kernels that may be writing to the buffer must finish + // before the transfer happens. + void CopyToHost(T* data, const size_t size) { + CHECK(data_ != nullptr); + CHECK_EQ(cudaMemcpy(data, data_, size * sizeof(T), cudaMemcpyDeviceToHost), + cudaSuccess); + } + + void CopyToGpu(const std::vector<T>& data) { + CopyToGpu(data.data(), data.size()); + } + + T* data() { return data_; } + size_t size() const { return size_; } + + private: + T* data_ = nullptr; + size_t size_ = 0; +}; + +#endif // CERES_NO_CUDA + +#endif // CERES_INTERNAL_CUDA_BUFFER_H_
\ No newline at end of file diff --git a/extern/ceres/internal/ceres/cxsparse.cc b/extern/ceres/internal/ceres/cxsparse.cc index 0167f988648..b1eb2055e35 100644 --- a/extern/ceres/internal/ceres/cxsparse.cc +++ b/extern/ceres/internal/ceres/cxsparse.cc @@ -29,10 +29,11 @@ // Author: strandmark@google.com (Petter Strandmark) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_CXSPARSE +#include <memory> #include <string> #include <vector> @@ -47,7 +48,7 @@ namespace internal { using std::vector; -CXSparse::CXSparse() : scratch_(NULL), scratch_size_(0) {} +CXSparse::CXSparse() : scratch_(nullptr), scratch_size_(0) {} CXSparse::~CXSparse() { if (scratch_size_ > 0) { @@ -116,7 +117,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, block_matrix.nzmax = block_rows.size(); block_matrix.p = &block_cols[0]; block_matrix.i = &block_rows[0]; - block_matrix.x = NULL; + block_matrix.x = nullptr; int* ordering = cs_amd(1, &block_matrix); vector<int> block_ordering(num_row_blocks, -1); @@ -126,7 +127,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, vector<int> scalar_ordering; BlockOrderingToScalarOrdering(row_blocks, block_ordering, &scalar_ordering); - cs_dis* symbolic_factor = + auto* symbolic_factor = reinterpret_cast<cs_dis*>(cs_calloc(1, sizeof(cs_dis))); symbolic_factor->pinv = cs_pinv(&scalar_ordering[0], A->n); cs* permuted_A = cs_symperm(A, symbolic_factor->pinv, 0); @@ -138,7 +139,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, cs_free(postordering); cs_spfree(permuted_A); - symbolic_factor->cp = (int*)cs_malloc(A->n + 1, sizeof(int)); + symbolic_factor->cp = static_cast<int*>(cs_malloc(A->n + 1, sizeof(int))); symbolic_factor->lnz = cs_cumsum(symbolic_factor->cp, column_counts, A->n); symbolic_factor->unz = symbolic_factor->lnz; @@ -146,7 +147,7 @@ cs_dis* CXSparse::BlockAnalyzeCholesky(cs_di* A, if (symbolic_factor->lnz < 0) { cs_sfree(symbolic_factor); - symbolic_factor = NULL; + symbolic_factor = nullptr; } return symbolic_factor; @@ -206,8 +207,8 @@ CompressedRowSparseMatrix::StorageType CXSparseCholesky::StorageType() const { CXSparseCholesky::CXSparseCholesky(const OrderingType ordering_type) : ordering_type_(ordering_type), - symbolic_factor_(NULL), - numeric_factor_(NULL) {} + symbolic_factor_(nullptr), + numeric_factor_(nullptr) {} CXSparseCholesky::~CXSparseCholesky() { FreeSymbolicFactorization(); @@ -217,14 +218,14 @@ CXSparseCholesky::~CXSparseCholesky() { LinearSolverTerminationType CXSparseCholesky::Factorize( CompressedRowSparseMatrix* lhs, std::string* message) { CHECK_EQ(lhs->storage_type(), StorageType()); - if (lhs == NULL) { - *message = "Failure: Input lhs is NULL."; + if (lhs == nullptr) { + *message = "Failure: Input lhs is nullptr."; return LINEAR_SOLVER_FATAL_ERROR; } cs_di cs_lhs = cs_.CreateSparseMatrixTransposeView(lhs); - if (symbolic_factor_ == NULL) { + if (symbolic_factor_ == nullptr) { if (ordering_type_ == NATURAL) { symbolic_factor_ = cs_.AnalyzeCholeskyWithNaturalOrdering(&cs_lhs); } else { @@ -236,7 +237,7 @@ LinearSolverTerminationType CXSparseCholesky::Factorize( } } - if (symbolic_factor_ == NULL) { + if (symbolic_factor_ == nullptr) { *message = "CXSparse Failure : Symbolic factorization failed."; return LINEAR_SOLVER_FATAL_ERROR; } @@ -244,7 +245,7 @@ LinearSolverTerminationType CXSparseCholesky::Factorize( FreeNumericFactorization(); numeric_factor_ = cs_.Cholesky(&cs_lhs, symbolic_factor_); - if (numeric_factor_ == NULL) { + if (numeric_factor_ == nullptr) { *message = "CXSparse Failure : Numeric factorization failed."; return LINEAR_SOLVER_FAILURE; } @@ -255,7 +256,7 @@ LinearSolverTerminationType CXSparseCholesky::Factorize( LinearSolverTerminationType CXSparseCholesky::Solve(const double* rhs, double* solution, std::string* message) { - CHECK(numeric_factor_ != NULL) + CHECK(numeric_factor_ != nullptr) << "Solve called without a call to Factorize first."; const int num_cols = numeric_factor_->L->n; memcpy(solution, rhs, num_cols * sizeof(*solution)); @@ -264,16 +265,16 @@ LinearSolverTerminationType CXSparseCholesky::Solve(const double* rhs, } void CXSparseCholesky::FreeSymbolicFactorization() { - if (symbolic_factor_ != NULL) { + if (symbolic_factor_ != nullptr) { cs_.Free(symbolic_factor_); - symbolic_factor_ = NULL; + symbolic_factor_ = nullptr; } } void CXSparseCholesky::FreeNumericFactorization() { - if (numeric_factor_ != NULL) { + if (numeric_factor_ != nullptr) { cs_.Free(numeric_factor_); - numeric_factor_ = NULL; + numeric_factor_ = nullptr; } } diff --git a/extern/ceres/internal/ceres/cxsparse.h b/extern/ceres/internal/ceres/cxsparse.h index d3f76e0575e..97fc0459464 100644 --- a/extern/ceres/internal/ceres/cxsparse.h +++ b/extern/ceres/internal/ceres/cxsparse.h @@ -32,7 +32,7 @@ #define CERES_INTERNAL_CXSPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_CXSPARSE @@ -40,6 +40,7 @@ #include <string> #include <vector> +#include "ceres/internal/disable_warnings.h" #include "ceres/linear_solver.h" #include "ceres/sparse_cholesky.h" #include "cs.h" @@ -54,7 +55,7 @@ class TripletSparseMatrix; // factorization with a known symbolic factorization. This features does not // explicitly exist in CXSparse. The methods in the class are nonstatic because // the class manages internal scratch space. -class CXSparse { +class CERES_NO_EXPORT CXSparse { public: CXSparse(); ~CXSparse(); @@ -80,7 +81,7 @@ class CXSparse { cs_di CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A); // Creates a new matrix from a triplet form. Deallocate the returned matrix - // with Free. May return NULL if the compression or allocation fails. + // with Free. May return nullptr if the compression or allocation fails. cs_di* CreateSparseMatrix(TripletSparseMatrix* A); // B = A' @@ -122,7 +123,7 @@ class CXSparse { const std::vector<int>& col_blocks); // Compute an fill-reducing approximate minimum degree ordering of - // the matrix A. ordering should be non-NULL and should point to + // the matrix A. ordering should be non-nullptr and should point to // enough memory to hold the ordering for the rows of A. void ApproximateMinimumDegreeOrdering(cs_di* A, int* ordering); @@ -138,13 +139,13 @@ class CXSparse { // An implementation of SparseCholesky interface using the CXSparse // library. -class CXSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT CXSparseCholesky final : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); // SparseCholesky interface. - virtual ~CXSparseCholesky(); + ~CXSparseCholesky() override; CompressedRowSparseMatrix::StorageType StorageType() const final; LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, std::string* message) final; @@ -153,7 +154,7 @@ class CXSparseCholesky : public SparseCholesky { std::string* message) final; private: - CXSparseCholesky(const OrderingType ordering_type); + explicit CXSparseCholesky(const OrderingType ordering_type); void FreeSymbolicFactorization(); void FreeNumericFactorization(); @@ -166,6 +167,8 @@ class CXSparseCholesky : public SparseCholesky { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #else typedef void cs_dis; diff --git a/extern/ceres/internal/ceres/dense_cholesky.cc b/extern/ceres/internal/ceres/dense_cholesky.cc new file mode 100644 index 00000000000..0e0bba7873b --- /dev/null +++ b/extern/ceres/internal/ceres/dense_cholesky.cc @@ -0,0 +1,327 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/dense_cholesky.h" + +#include <algorithm> +#include <memory> +#include <string> +#include <vector> + +#include "ceres/internal/config.h" + +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +#ifndef CERES_NO_LAPACK + +// C interface to the LAPACK Cholesky factorization and triangular solve. +extern "C" void dpotrf_( + const char* uplo, const int* n, double* a, const int* lda, int* info); + +extern "C" void dpotrs_(const char* uplo, + const int* n, + const int* nrhs, + const double* a, + const int* lda, + double* b, + const int* ldb, + int* info); +#endif + +namespace ceres { +namespace internal { + +DenseCholesky::~DenseCholesky() = default; + +std::unique_ptr<DenseCholesky> DenseCholesky::Create( + const LinearSolver::Options& options) { + std::unique_ptr<DenseCholesky> dense_cholesky; + + switch (options.dense_linear_algebra_library_type) { + case EIGEN: + dense_cholesky = std::make_unique<EigenDenseCholesky>(); + break; + + case LAPACK: +#ifndef CERES_NO_LAPACK + dense_cholesky = std::make_unique<LAPACKDenseCholesky>(); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for LAPACK."; +#endif + + case CUDA: +#ifndef CERES_NO_CUDA + dense_cholesky = CUDADenseCholesky::Create(options); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for CUDA."; +#endif + + default: + LOG(FATAL) << "Unknown dense linear algebra library type : " + << DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type); + } + return dense_cholesky; +} + +LinearSolverTerminationType DenseCholesky::FactorAndSolve( + int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message) { + LinearSolverTerminationType termination_type = + Factorize(num_cols, lhs, message); + if (termination_type == LINEAR_SOLVER_SUCCESS) { + termination_type = Solve(rhs, solution, message); + } + return termination_type; +} + +LinearSolverTerminationType EigenDenseCholesky::Factorize( + int num_cols, double* lhs, std::string* message) { + Eigen::Map<Eigen::MatrixXd> m(lhs, num_cols, num_cols); + llt_ = std::make_unique<LLTType>(m); + if (llt_->info() != Eigen::Success) { + *message = "Eigen failure. Unable to perform dense Cholesky factorization."; + return LINEAR_SOLVER_FAILURE; + } + + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType EigenDenseCholesky::Solve(const double* rhs, + double* solution, + std::string* message) { + if (llt_->info() != Eigen::Success) { + *message = "Eigen failure. Unable to perform dense Cholesky factorization."; + return LINEAR_SOLVER_FAILURE; + } + + VectorRef(solution, llt_->cols()) = + llt_->solve(ConstVectorRef(rhs, llt_->cols())); + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +#ifndef CERES_NO_LAPACK +LinearSolverTerminationType LAPACKDenseCholesky::Factorize( + int num_cols, double* lhs, std::string* message) { + lhs_ = lhs; + num_cols_ = num_cols; + + const char uplo = 'L'; + int info = 0; + dpotrf_(&uplo, &num_cols_, lhs_, &num_cols_, &info); + + if (info < 0) { + termination_type_ = LINEAR_SOLVER_FATAL_ERROR; + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it. " + << "LAPACK::dpotrf fatal error. " + << "Argument: " << -info << " is invalid."; + } else if (info > 0) { + termination_type_ = LINEAR_SOLVER_FAILURE; + *message = StringPrintf( + "LAPACK::dpotrf numerical failure. " + "The leading minor of order %d is not positive definite.", + info); + } else { + termination_type_ = LINEAR_SOLVER_SUCCESS; + *message = "Success."; + } + return termination_type_; +} + +LinearSolverTerminationType LAPACKDenseCholesky::Solve(const double* rhs, + double* solution, + std::string* message) { + const char uplo = 'L'; + const int nrhs = 1; + int info = 0; + + std::copy_n(rhs, num_cols_, solution); + dpotrs_( + &uplo, &num_cols_, &nrhs, lhs_, &num_cols_, solution, &num_cols_, &info); + + if (info < 0) { + termination_type_ = LINEAR_SOLVER_FATAL_ERROR; + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it. " + << "LAPACK::dpotrs fatal error. " + << "Argument: " << -info << " is invalid."; + } + + *message = "Success"; + termination_type_ = LINEAR_SOLVER_SUCCESS; + + return termination_type_; +} + +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA + +bool CUDADenseCholesky::Init(ContextImpl* context, std::string* message) { + if (!context->InitCUDA(message)) { + return false; + } + cusolver_handle_ = context->cusolver_handle_; + stream_ = context->stream_; + error_.Reserve(1); + *message = "CUDADenseCholesky::Init Success."; + return true; +} + +LinearSolverTerminationType CUDADenseCholesky::Factorize(int num_cols, + double* lhs, + std::string* message) { + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + lhs_.Reserve(num_cols * num_cols); + num_cols_ = num_cols; + lhs_.CopyToGpuAsync(lhs, num_cols * num_cols, stream_); + int device_workspace_size = 0; + if (cusolverDnDpotrf_bufferSize(cusolver_handle_, + CUBLAS_FILL_MODE_LOWER, + num_cols, + lhs_.data(), + num_cols, + &device_workspace_size) != + CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDpotrf_bufferSize failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + device_workspace_.Reserve(device_workspace_size); + if (cusolverDnDpotrf(cusolver_handle_, + CUBLAS_FILL_MODE_LOWER, + num_cols, + lhs_.data(), + num_cols, + reinterpret_cast<double*>(device_workspace_.data()), + device_workspace_.size(), + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDpotrf failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres - " + << "please report it. " + << "cuSolverDN::cusolverDnXpotrf fatal error. " + << "Argument: " << -error << " is invalid."; + // The following line is unreachable, but return failure just to be + // pedantic, since the compiler does not know that. + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } else if (error > 0) { + *message = StringPrintf( + "cuSolverDN::cusolverDnDpotrf numerical failure. " + "The leading minor of order %d is not positive definite.", + error); + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_FAILURE; + return LinearSolverTerminationType::LINEAR_SOLVER_FAILURE; + } + *message = "Success"; + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType CUDADenseCholesky::Solve(const double* rhs, + double* solution, + std::string* message) { + if (factorize_result_ != LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS) { + *message = "Factorize did not complete succesfully previously."; + return factorize_result_; + } + rhs_.CopyToGpuAsync(rhs, num_cols_, stream_); + if (cusolverDnDpotrs(cusolver_handle_, + CUBLAS_FILL_MODE_LOWER, + num_cols_, + 1, + lhs_.data(), + num_cols_, + rhs_.data(), + num_cols_, + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDpotrs failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error != 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it." + << "cuSolverDN::cusolverDnDpotrs fatal error. " + << "Argument: " << -error << " is invalid."; + } + rhs_.CopyToHost(solution, num_cols_); + *message = "Success"; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +std::unique_ptr<CUDADenseCholesky> CUDADenseCholesky::Create( + const LinearSolver::Options& options) { + if (options.dense_linear_algebra_library_type != CUDA) { + // The user called the wrong factory method. + return nullptr; + } + auto cuda_dense_cholesky = + std::unique_ptr<CUDADenseCholesky>(new CUDADenseCholesky()); + std::string cuda_error; + if (cuda_dense_cholesky->Init(options.context, &cuda_error)) { + return cuda_dense_cholesky; + } + // Initialization failed, destroy the object (done automatically) and return a + // nullptr. + LOG(ERROR) << "CUDADenseCholesky::Init failed: " << cuda_error; + return nullptr; +} + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/dense_cholesky.h b/extern/ceres/internal/ceres/dense_cholesky.h new file mode 100644 index 00000000000..655a2f815ee --- /dev/null +++ b/extern/ceres/internal/ceres/dense_cholesky.h @@ -0,0 +1,183 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_INTERNAL_DENSE_CHOLESKY_H_ +#define CERES_INTERNAL_DENSE_CHOLESKY_H_ + +// This include must come before any #ifndef check on Ceres compile options. +// clang-format off +#include "ceres/internal/config.h" +// clang-format on + +#include <memory> +#include <vector> + +#include "Eigen/Dense" +#include "ceres/cuda_buffer.h" +#include "ceres/linear_solver.h" +#include "glog/logging.h" +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +namespace ceres { +namespace internal { + +// An interface that abstracts away the internal details of various dense linear +// algebra libraries and offers a simple API for solving dense symmetric +// positive definite linear systems using a Cholesky factorization. +class CERES_NO_EXPORT DenseCholesky { + public: + static std::unique_ptr<DenseCholesky> Create( + const LinearSolver::Options& options); + + virtual ~DenseCholesky(); + + // Computes the Cholesky factorization of the given matrix. + // + // The input matrix lhs is assumed to be a column-major num_cols x num_cols + // matrix, that is symmetric positive definite with its lower triangular part + // containing the left hand side of the linear system being solved. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the factorization succeeds or not. + // As a result it is the user's responsibility to ensure that lhs is valid + // when Solve is called. + virtual LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) = 0; + + // Computes the solution to the equation + // + // lhs * solution = rhs + // + // Calling Solve without calling Factorize is undefined behaviour. It is the + // user's responsibility to ensure that the input matrix lhs passed to + // Factorize has not been freed/modified when Solve is called. + virtual LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) = 0; + + // Convenience method which combines a call to Factorize and Solve. Solve is + // only called if Factorize returns LINEAR_SOLVER_SUCCESS. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the method succeeds or not. It is + // the user's responsibility to ensure that lhs is valid if and when Solve is + // called again after this call. + LinearSolverTerminationType FactorAndSolve(int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message); +}; + +class CERES_NO_EXPORT EigenDenseCholesky final : public DenseCholesky { + public: + LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + using LLTType = Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>, Eigen::Lower>; + std::unique_ptr<LLTType> llt_; +}; + +#ifndef CERES_NO_LAPACK +class CERES_NO_EXPORT LAPACKDenseCholesky final : public DenseCholesky { + public: + LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + double* lhs_ = nullptr; + int num_cols_ = -1; + LinearSolverTerminationType termination_type_ = LINEAR_SOLVER_FATAL_ERROR; +}; +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA +// CUDA implementation of DenseCholesky using the cuSolverDN library using the +// 32-bit legacy interface for maximum compatibility. +class CERES_NO_EXPORT CUDADenseCholesky final : public DenseCholesky { + public: + static std::unique_ptr<CUDADenseCholesky> Create( + const LinearSolver::Options& options); + CUDADenseCholesky(const CUDADenseCholesky&) = delete; + CUDADenseCholesky& operator=(const CUDADenseCholesky&) = delete; + LinearSolverTerminationType Factorize(int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + CUDADenseCholesky() = default; + // Picks up the cuSolverDN and cuStream handles from the context. If + // the context is unable to initialize CUDA, returns false with a + // human-readable message indicating the reason. + bool Init(ContextImpl* context, std::string* message); + + // Handle to the cuSOLVER context. + cusolverDnHandle_t cusolver_handle_ = nullptr; + // CUDA device stream. + cudaStream_t stream_ = nullptr; + // Number of columns in the A matrix, to be cached between calls to *Factorize + // and *Solve. + size_t num_cols_ = 0; + // GPU memory allocated for the A matrix (lhs matrix). + CudaBuffer<double> lhs_; + // GPU memory allocated for the B matrix (rhs vector). + CudaBuffer<double> rhs_; + // Scratch space for cuSOLVER on the GPU. + CudaBuffer<double> device_workspace_; + // Required for error handling with cuSOLVER. + CudaBuffer<int> error_; + // Cache the result of Factorize to ensure that when Solve is called, the + // factiorization of lhs is valid. + LinearSolverTerminationType factorize_result_ = LINEAR_SOLVER_FATAL_ERROR; +}; + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres + +#endif // CERES_INTERNAL_DENSE_CHOLESKY_H_ diff --git a/extern/ceres/internal/ceres/dense_jacobian_writer.h b/extern/ceres/internal/ceres/dense_jacobian_writer.h index 28c60e20a1b..0020937124e 100644 --- a/extern/ceres/internal/ceres/dense_jacobian_writer.h +++ b/extern/ceres/internal/ceres/dense_jacobian_writer.h @@ -33,9 +33,13 @@ #ifndef CERES_INTERNAL_DENSE_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_DENSE_JACOBIAN_WRITER_H_ +#include <memory> + #include "ceres/casts.h" #include "ceres/dense_sparse_matrix.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block.h" #include "ceres/program.h" #include "ceres/residual_block.h" @@ -44,7 +48,7 @@ namespace ceres { namespace internal { -class DenseJacobianWriter { +class CERES_NO_EXPORT DenseJacobianWriter { public: DenseJacobianWriter(Evaluator::Options /* ignored */, Program* program) : program_(program) {} @@ -54,13 +58,14 @@ class DenseJacobianWriter { // Since the dense matrix has different layout than that assumed by the cost // functions, use scratch space to store the jacobians temporarily then copy // them over to the larger jacobian later. - ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads) { + std::unique_ptr<ScratchEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads) { return ScratchEvaluatePreparer::Create(*program_, num_threads); } - SparseMatrix* CreateJacobian() const { - return new DenseSparseMatrix( - program_->NumResiduals(), program_->NumEffectiveParameters(), true); + std::unique_ptr<SparseMatrix> CreateJacobian() const { + return std::make_unique<DenseSparseMatrix>( + program_->NumResiduals(), program_->NumEffectiveParameters()); } void Write(int residual_id, @@ -82,14 +87,14 @@ class DenseJacobianWriter { continue; } - const int parameter_block_size = parameter_block->LocalSize(); + const int parameter_block_size = parameter_block->TangentSize(); ConstMatrixRef parameter_jacobian( jacobians[j], num_residuals, parameter_block_size); - dense_jacobian->mutable_matrix().block(residual_offset, - parameter_block->delta_offset(), - num_residuals, - parameter_block_size) = + dense_jacobian->mutable_matrix()->block(residual_offset, + parameter_block->delta_offset(), + num_residuals, + parameter_block_size) = parameter_jacobian; } } @@ -101,4 +106,6 @@ class DenseJacobianWriter { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_JACOBIAN_WRITER_H_ diff --git a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc index 51c639097b6..30a0c023f51 100644 --- a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.cc @@ -30,13 +30,11 @@ #include "ceres/dense_normal_cholesky_solver.h" -#include <cstddef> +#include <utility> #include "Eigen/Dense" -#include "ceres/blas.h" #include "ceres/dense_sparse_matrix.h" #include "ceres/internal/eigen.h" -#include "ceres/lapack.h" #include "ceres/linear_solver.h" #include "ceres/types.h" #include "ceres/wall_time.h" @@ -45,32 +43,20 @@ namespace ceres { namespace internal { DenseNormalCholeskySolver::DenseNormalCholeskySolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)), + cholesky_(DenseCholesky::Create(options_)) {} LinearSolver::Summary DenseNormalCholeskySolver::SolveImpl( DenseSparseMatrix* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, double* x) { - if (options_.dense_linear_algebra_library_type == EIGEN) { - return SolveUsingEigen(A, b, per_solve_options, x); - } else { - return SolveUsingLAPACK(A, b, per_solve_options, x); - } -} - -LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingEigen( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { EventLogger event_logger("DenseNormalCholeskySolver::Solve"); const int num_rows = A->num_rows(); const int num_cols = A->num_cols(); - ConstColMajorMatrixRef Aref = A->matrix(); Matrix lhs(num_cols, num_cols); lhs.setZero(); @@ -81,12 +67,12 @@ LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingEigen( // Using rankUpdate instead of GEMM, exposes the fact that its the // same matrix being multiplied with itself and that the product is // symmetric. - lhs.selfadjointView<Eigen::Upper>().rankUpdate(Aref.transpose()); + lhs.selfadjointView<Eigen::Upper>().rankUpdate(A->matrix().transpose()); // rhs = A'b - Vector rhs = Aref.transpose() * ConstVectorRef(b, num_rows); + Vector rhs = A->matrix().transpose() * ConstVectorRef(b, num_rows); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { ConstVectorRef D(per_solve_options.D, num_cols); lhs += D.array().square().matrix().asDiagonal(); } @@ -94,64 +80,12 @@ LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingEigen( LinearSolver::Summary summary; summary.num_iterations = 1; - summary.termination_type = LINEAR_SOLVER_SUCCESS; - Eigen::LLT<Matrix, Eigen::Upper> llt = - lhs.selfadjointView<Eigen::Upper>().llt(); - - if (llt.info() != Eigen::Success) { - summary.termination_type = LINEAR_SOLVER_FAILURE; - summary.message = "Eigen LLT decomposition failed."; - } else { - summary.termination_type = LINEAR_SOLVER_SUCCESS; - summary.message = "Success."; - } + summary.termination_type = cholesky_->FactorAndSolve( + num_cols, lhs.data(), rhs.data(), x, &summary.message); + event_logger.AddEvent("FactorAndSolve"); - VectorRef(x, num_cols) = llt.solve(rhs); - event_logger.AddEvent("Solve"); return summary; } -LinearSolver::Summary DenseNormalCholeskySolver::SolveUsingLAPACK( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { - EventLogger event_logger("DenseNormalCholeskySolver::Solve"); - - if (per_solve_options.D != NULL) { - // Temporarily append a diagonal block to the A matrix, but undo - // it before returning the matrix to the user. - A->AppendDiagonal(per_solve_options.D); - } - - const int num_cols = A->num_cols(); - Matrix lhs(num_cols, num_cols); - event_logger.AddEvent("Setup"); - - // lhs = A'A - // - // Note: This is a bit delicate, it assumes that the stride on this - // matrix is the same as the number of rows. - BLAS::SymmetricRankKUpdate( - A->num_rows(), num_cols, A->values(), true, 1.0, 0.0, lhs.data()); - - if (per_solve_options.D != NULL) { - // Undo the modifications to the matrix A. - A->RemoveDiagonal(); - } - - // TODO(sameeragarwal): Replace this with a gemv call for true blasness. - // rhs = A'b - VectorRef(x, num_cols) = - A->matrix().transpose() * ConstVectorRef(b, A->num_rows()); - event_logger.AddEvent("Product"); - - LinearSolver::Summary summary; - summary.num_iterations = 1; - summary.termination_type = LAPACK::SolveInPlaceUsingCholesky( - num_cols, lhs.data(), x, &summary.message); - event_logger.AddEvent("Solve"); - return summary; -} } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h index 68ea611299f..5b3c74069f0 100644 --- a/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h +++ b/extern/ceres/internal/ceres/dense_normal_cholesky_solver.h @@ -34,6 +34,11 @@ #ifndef CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_ #define CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_ +#include <memory> + +#include "ceres/dense_cholesky.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -73,9 +78,10 @@ class DenseSparseMatrix; // library. This solver always returns a solution, it is the user's // responsibility to judge if the solution is good enough for their // purposes. -class DenseNormalCholeskySolver : public DenseSparseMatrixSolver { +class CERES_NO_EXPORT DenseNormalCholeskySolver + : public DenseSparseMatrixSolver { public: - explicit DenseNormalCholeskySolver(const LinearSolver::Options& options); + explicit DenseNormalCholeskySolver(LinearSolver::Options options); private: LinearSolver::Summary SolveImpl( @@ -84,22 +90,13 @@ class DenseNormalCholeskySolver : public DenseSparseMatrixSolver { const LinearSolver::PerSolveOptions& per_solve_options, double* x) final; - LinearSolver::Summary SolveUsingLAPACK( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x); - - LinearSolver::Summary SolveUsingEigen( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x); - const LinearSolver::Options options_; + std::unique_ptr<DenseCholesky> cholesky_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_NORMAL_CHOLESKY_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/dense_qr.cc b/extern/ceres/internal/ceres/dense_qr.cc new file mode 100644 index 00000000000..4b9c8a4a035 --- /dev/null +++ b/extern/ceres/internal/ceres/dense_qr.cc @@ -0,0 +1,481 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/dense_qr.h" + +#include <algorithm> +#include <memory> +#include <string> +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "cublas_v2.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +#ifndef CERES_NO_LAPACK + +// LAPACK routines for solving a linear least squares problem using QR +// factorization. This is done in three stages: +// +// A * x = b +// Q * R * x = b (dgeqrf) +// R * x = Q' * b (dormqr) +// x = R^{-1} * Q'* b (dtrtrs) + +// clang-format off + +// Compute the QR factorization of a. +// +// a is an m x n column major matrix (Denoted by "A" in the above description) +// lda is the leading dimension of a. lda >= max(1, num_rows) +// tau is an array of size min(m,n). It contains the scalar factors of the +// elementary reflectors. +// work is an array of size max(1,lwork). On exit, if info=0, work[0] contains +// the optimal size of work. +// +// if lwork >= 1 it is the size of work. If lwork = -1, then a workspace query is assumed. +// dgeqrf computes the optimal size of the work array and returns it as work[0]. +// +// info = 0, successful exit. +// info < 0, if info = -i, then the i^th argument had illegal value. +extern "C" void dgeqrf_(const int* m, const int* n, double* a, const int* lda, + double* tau, double* work, const int* lwork, int* info); + +// Apply Q or Q' to b. +// +// b is a m times n column major matrix. +// size = 'L' applies Q or Q' on the left, size = 'R' applies Q or Q' on the right. +// trans = 'N', applies Q, trans = 'T', applies Q'. +// k is the number of elementary reflectors whose product defines the matrix Q. +// If size = 'L', m >= k >= 0 and if side = 'R', n >= k >= 0. +// a is an lda x k column major matrix containing the reflectors as returned by dgeqrf. +// ldb is the leading dimension of b. +// work is an array of size max(1, lwork) +// lwork if positive is the size of work. If lwork = -1, then a +// workspace query is assumed. +// +// info = 0, successful exit. +// info < 0, if info = -i, then the i^th argument had illegal value. +extern "C" void dormqr_(const char* side, const char* trans, const int* m, + const int* n ,const int* k, double* a, const int* lda, + double* tau, double* b, const int* ldb, double* work, + const int* lwork, int* info); + +// Solve a triangular system of the form A * x = b +// +// uplo = 'U', A is upper triangular. uplo = 'L' is lower triangular. +// trans = 'N', 'T', 'C' specifies the form - A, A^T, A^H. +// DIAG = 'N', A is not unit triangular. 'U' is unit triangular. +// n is the order of the matrix A. +// nrhs number of columns of b. +// a is a column major lda x n. +// b is a column major matrix of ldb x nrhs +// +// info = 0 succesful. +// = -i < 0 i^th argument is an illegal value. +// = i > 0, i^th diagonal element of A is zero. +extern "C" void dtrtrs_(const char* uplo, const char* trans, const char* diag, + const int* n, const int* nrhs, double* a, const int* lda, + double* b, const int* ldb, int* info); +// clang-format on + +#endif + +namespace ceres { +namespace internal { + +DenseQR::~DenseQR() = default; + +std::unique_ptr<DenseQR> DenseQR::Create(const LinearSolver::Options& options) { + std::unique_ptr<DenseQR> dense_qr; + + switch (options.dense_linear_algebra_library_type) { + case EIGEN: + dense_qr = std::make_unique<EigenDenseQR>(); + break; + + case LAPACK: +#ifndef CERES_NO_LAPACK + dense_qr = std::make_unique<LAPACKDenseQR>(); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for LAPACK."; +#endif + + case CUDA: +#ifndef CERES_NO_CUDA + dense_qr = CUDADenseQR::Create(options); + break; +#else + LOG(FATAL) << "Ceres was compiled without support for CUDA."; +#endif + + default: + LOG(FATAL) << "Unknown dense linear algebra library type : " + << DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type); + } + return dense_qr; +} + +LinearSolverTerminationType DenseQR::FactorAndSolve(int num_rows, + int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message) { + LinearSolverTerminationType termination_type = + Factorize(num_rows, num_cols, lhs, message); + if (termination_type == LINEAR_SOLVER_SUCCESS) { + termination_type = Solve(rhs, solution, message); + } + return termination_type; +} + +LinearSolverTerminationType EigenDenseQR::Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) { + Eigen::Map<ColMajorMatrix> m(lhs, num_rows, num_cols); + qr_ = std::make_unique<QRType>(m); + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType EigenDenseQR::Solve(const double* rhs, + double* solution, + std::string* message) { + VectorRef(solution, qr_->cols()) = + qr_->solve(ConstVectorRef(rhs, qr_->rows())); + *message = "Success."; + return LINEAR_SOLVER_SUCCESS; +} + +#ifndef CERES_NO_LAPACK +LinearSolverTerminationType LAPACKDenseQR::Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) { + int lwork = -1; + double work_size; + int info = 0; + + // Compute the size of the temporary workspace needed to compute the QR + // factorization in the dgeqrf call below. + dgeqrf_(&num_rows, + &num_cols, + lhs_, + &num_rows, + tau_.data(), + &work_size, + &lwork, + &info); + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it." + << "LAPACK::dgels fatal error." + << "Argument: " << -info << " is invalid."; + } + + lhs_ = lhs; + num_rows_ = num_rows; + num_cols_ = num_cols; + + lwork = static_cast<int>(work_size); + + if (work_.size() < lwork) { + work_.resize(lwork); + } + if (tau_.size() < num_cols) { + tau_.resize(num_cols); + } + + if (q_transpose_rhs_.size() < num_rows) { + q_transpose_rhs_.resize(num_rows); + } + + // Factorize the lhs_ using the workspace that we just constructed above. + dgeqrf_(&num_rows, + &num_cols, + lhs_, + &num_rows, + tau_.data(), + work_.data(), + &lwork, + &info); + + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it. dgeqrf fatal error." + << "Argument: " << -info << " is invalid."; + } + + termination_type_ = LINEAR_SOLVER_SUCCESS; + *message = "Success."; + return termination_type_; +} + +LinearSolverTerminationType LAPACKDenseQR::Solve(const double* rhs, + double* solution, + std::string* message) { + if (termination_type_ != LINEAR_SOLVER_SUCCESS) { + *message = "QR factorization failed and solve called."; + return termination_type_; + } + + std::copy_n(rhs, num_rows_, q_transpose_rhs_.data()); + + const char side = 'L'; + char trans = 'T'; + const int num_c_cols = 1; + const int lwork = work_.size(); + int info = 0; + dormqr_(&side, + &trans, + &num_rows_, + &num_c_cols, + &num_cols_, + lhs_, + &num_rows_, + tau_.data(), + q_transpose_rhs_.data(), + &num_rows_, + work_.data(), + &lwork, + &info); + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it. dormr fatal error." + << "Argument: " << -info << " is invalid."; + } + + const char uplo = 'U'; + trans = 'N'; + const char diag = 'N'; + dtrtrs_(&uplo, + &trans, + &diag, + &num_cols_, + &num_c_cols, + lhs_, + &num_rows_, + q_transpose_rhs_.data(), + &num_rows_, + &info); + + if (info < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres." + << "Please report it. dormr fatal error." + << "Argument: " << -info << " is invalid."; + } else if (info > 0) { + *message = + "QR factorization failure. The factorization is not full rank. R has " + "zeros on the diagonal."; + termination_type_ = LINEAR_SOLVER_FAILURE; + } else { + std::copy_n(q_transpose_rhs_.data(), num_cols_, solution); + termination_type_ = LINEAR_SOLVER_SUCCESS; + } + + return termination_type_; +} + +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA + +bool CUDADenseQR::Init(ContextImpl* context, std::string* message) { + if (!context->InitCUDA(message)) { + return false; + } + cublas_handle_ = context->cublas_handle_; + cusolver_handle_ = context->cusolver_handle_; + stream_ = context->stream_; + error_.Reserve(1); + *message = "CUDADenseQR::Init Success."; + return true; +} + +LinearSolverTerminationType CUDADenseQR::Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) { + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + lhs_.Reserve(num_rows * num_cols); + tau_.Reserve(std::min(num_rows, num_cols)); + num_rows_ = num_rows; + num_cols_ = num_cols; + lhs_.CopyToGpuAsync(lhs, num_rows * num_cols, stream_); + int device_workspace_size = 0; + if (cusolverDnDgeqrf_bufferSize(cusolver_handle_, + num_rows, + num_cols, + lhs_.data(), + num_rows, + &device_workspace_size) != + CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDgeqrf_bufferSize failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + device_workspace_.Reserve(device_workspace_size); + if (cusolverDnDgeqrf(cusolver_handle_, + num_rows, + num_cols, + lhs_.data(), + num_rows, + tau_.data(), + reinterpret_cast<double*>(device_workspace_.data()), + device_workspace_.size(), + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDgeqrf failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres - " + << "please report it. " + << "cuSolverDN::cusolverDnDgeqrf fatal error. " + << "Argument: " << -error << " is invalid."; + // The following line is unreachable, but return failure just to be + // pedantic, since the compiler does not know that. + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + + *message = "Success"; + factorize_result_ = LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +LinearSolverTerminationType CUDADenseQR::Solve(const double* rhs, + double* solution, + std::string* message) { + if (factorize_result_ != LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS) { + *message = "Factorize did not complete succesfully previously."; + return factorize_result_; + } + rhs_.CopyToGpuAsync(rhs, num_rows_, stream_); + int device_workspace_size = 0; + if (cusolverDnDormqr_bufferSize(cusolver_handle_, + CUBLAS_SIDE_LEFT, + CUBLAS_OP_T, + num_rows_, + 1, + num_cols_, + lhs_.data(), + num_rows_, + tau_.data(), + rhs_.data(), + num_rows_, + &device_workspace_size) != + CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDormqr_bufferSize failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + device_workspace_.Reserve(device_workspace_size); + // Compute rhs = Q^T * rhs, assuming that lhs has already been factorized. + // The result of factorization would have stored Q in a packed form in lhs_. + if (cusolverDnDormqr(cusolver_handle_, + CUBLAS_SIDE_LEFT, + CUBLAS_OP_T, + num_rows_, + 1, + num_cols_, + lhs_.data(), + num_rows_, + tau_.data(), + rhs_.data(), + num_rows_, + reinterpret_cast<double*>(device_workspace_.data()), + device_workspace_.size(), + error_.data()) != CUSOLVER_STATUS_SUCCESS) { + *message = "cuSolverDN::cusolverDnDormqr failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + int error = 0; + error_.CopyToHost(&error, 1); + if (error < 0) { + LOG(FATAL) << "Congratulations, you found a bug in Ceres. " + << "Please report it." + << "cuSolverDN::cusolverDnDormqr fatal error. " + << "Argument: " << -error << " is invalid."; + } + // Compute the solution vector as x = R \ (Q^T * rhs). Since the previous step + // replaced rhs by (Q^T * rhs), this is just x = R \ rhs. + if (cublasDtrsv(cublas_handle_, + CUBLAS_FILL_MODE_UPPER, + CUBLAS_OP_N, + CUBLAS_DIAG_NON_UNIT, + num_cols_, + lhs_.data(), + num_rows_, + rhs_.data(), + 1) != CUBLAS_STATUS_SUCCESS) { + *message = "cuBLAS::cublasDtrsv failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + if (cudaDeviceSynchronize() != cudaSuccess || + cudaStreamSynchronize(stream_) != cudaSuccess) { + *message = "Cuda device synchronization failed."; + return LinearSolverTerminationType::LINEAR_SOLVER_FATAL_ERROR; + } + rhs_.CopyToHost(solution, num_cols_); + *message = "Success"; + return LinearSolverTerminationType::LINEAR_SOLVER_SUCCESS; +} + +std::unique_ptr<CUDADenseQR> CUDADenseQR::Create( + const LinearSolver::Options& options) { + if (options.dense_linear_algebra_library_type != CUDA) { + // The user called the wrong factory method. + return nullptr; + } + auto cuda_dense_qr = std::unique_ptr<CUDADenseQR>(new CUDADenseQR()); + std::string cuda_error; + if (cuda_dense_qr->Init(options.context, &cuda_error)) { + return cuda_dense_qr; + } + // Initialization failed, destroy the object (done automatically) and return a + // nullptr. + LOG(ERROR) << "CUDADenseQR::Init failed: " << cuda_error; + return nullptr; +} + +CUDADenseQR::CUDADenseQR() = default; + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/dense_qr.h b/extern/ceres/internal/ceres/dense_qr.h new file mode 100644 index 00000000000..7a2ffb52ae6 --- /dev/null +++ b/extern/ceres/internal/ceres/dense_qr.h @@ -0,0 +1,207 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_INTERNAL_DENSE_QR_H_ +#define CERES_INTERNAL_DENSE_QR_H_ + +// This include must come before any #ifndef check on Ceres compile options. +// clang-format off +#include "ceres/internal/config.h" +// clang-format on + +#include <memory> +#include <vector> + +#include "Eigen/Dense" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" +#include "ceres/linear_solver.h" +#include "glog/logging.h" + +#ifndef CERES_NO_CUDA +#include "ceres/context_impl.h" +#include "ceres/cuda_buffer.h" +#include "cublas_v2.h" +#include "cuda_runtime.h" +#include "cusolverDn.h" +#endif // CERES_NO_CUDA + +namespace ceres { +namespace internal { + +// An interface that abstracts away the internal details of various dense linear +// algebra libraries and offers a simple API for solving dense linear systems +// using a QR factorization. +class CERES_NO_EXPORT DenseQR { + public: + static std::unique_ptr<DenseQR> Create(const LinearSolver::Options& options); + + virtual ~DenseQR(); + + // Computes the QR factorization of the given matrix. + // + // The input matrix lhs is assumed to be a column-major num_rows x num_cols + // matrix. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the factorization succeeds or not. + // As a result it is the user's responsibility to ensure that lhs is valid + // when Solve is called. + virtual LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) = 0; + + // Computes the solution to the equation + // + // lhs * solution = rhs + // + // Calling Solve without calling Factorize is undefined behaviour. It is the + // user's responsibility to ensure that the input matrix lhs passed to + // Factorize has not been freed/modified when Solve is called. + virtual LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) = 0; + + // Convenience method which combines a call to Factorize and Solve. Solve is + // only called if Factorize returns LINEAR_SOLVER_SUCCESS. + // + // The input matrix lhs may be modified by the implementation to store the + // factorization, irrespective of whether the method succeeds or not. It is + // the user's responsibility to ensure that lhs is valid if and when Solve is + // called again after this call. + LinearSolverTerminationType FactorAndSolve(int num_rows, + int num_cols, + double* lhs, + const double* rhs, + double* solution, + std::string* message); +}; + +class CERES_NO_EXPORT EigenDenseQR final : public DenseQR { + public: + LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + using QRType = Eigen::HouseholderQR<Eigen::Ref<ColMajorMatrix>>; + std::unique_ptr<QRType> qr_; +}; + +#ifndef CERES_NO_LAPACK +class CERES_NO_EXPORT LAPACKDenseQR final : public DenseQR { + public: + LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + double* lhs_ = nullptr; + int num_rows_; + int num_cols_; + LinearSolverTerminationType termination_type_ = LINEAR_SOLVER_FATAL_ERROR; + Vector work_; + Vector tau_; + Vector q_transpose_rhs_; +}; +#endif // CERES_NO_LAPACK + +#ifndef CERES_NO_CUDA +// Implementation of DenseQR using the 32-bit cuSolverDn interface. A +// requirement for using this solver is that the lhs must not be rank deficient. +// This is because cuSolverDn does not implement the singularity-checking +// wrapper trtrs, hence this solver directly uses trsv from CUBLAS for the +// backsubstitution. +class CERES_NO_EXPORT CUDADenseQR final : public DenseQR { + public: + static std::unique_ptr<CUDADenseQR> Create( + const LinearSolver::Options& options); + CUDADenseQR(const CUDADenseQR&) = delete; + CUDADenseQR& operator=(const CUDADenseQR&) = delete; + LinearSolverTerminationType Factorize(int num_rows, + int num_cols, + double* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; + + private: + CUDADenseQR(); + // Picks up the cuSolverDN, cuBLAS, and cuStream handles from the context. If + // the context is unable to initialize CUDA, returns false with a + // human-readable message indicating the reason. + bool Init(ContextImpl* context, std::string* message); + + // Handle to the cuSOLVER context. + cusolverDnHandle_t cusolver_handle_ = nullptr; + // Handle to cuBLAS context. + cublasHandle_t cublas_handle_ = nullptr; + // CUDA device stream. + cudaStream_t stream_ = nullptr; + // Number of rowns in the A matrix, to be cached between calls to *Factorize + // and *Solve. + size_t num_rows_ = 0; + // Number of columns in the A matrix, to be cached between calls to *Factorize + // and *Solve. + size_t num_cols_ = 0; + // GPU memory allocated for the A matrix (lhs matrix). + CudaBuffer<double> lhs_; + // GPU memory allocated for the B matrix (rhs vector). + CudaBuffer<double> rhs_; + // GPU memory allocated for the TAU matrix (scaling of householder vectors). + CudaBuffer<double> tau_; + // Scratch space for cuSOLVER on the GPU. + CudaBuffer<double> device_workspace_; + // Required for error handling with cuSOLVER. + CudaBuffer<int> error_; + // Cache the result of Factorize to ensure that when Solve is called, the + // factiorization of lhs is valid. + LinearSolverTerminationType factorize_result_ = LINEAR_SOLVER_FATAL_ERROR; +}; + +#endif // CERES_NO_CUDA + +} // namespace internal +} // namespace ceres + +#include "ceres/internal/reenable_warnings.h" + +#endif // CERES_INTERNAL_DENSE_QR_H_ diff --git a/extern/ceres/internal/ceres/dense_qr_solver.cc b/extern/ceres/internal/ceres/dense_qr_solver.cc index 44388f30aee..24cb25abd8e 100644 --- a/extern/ceres/internal/ceres/dense_qr_solver.cc +++ b/extern/ceres/internal/ceres/dense_qr_solver.cc @@ -33,9 +33,9 @@ #include <cstddef> #include "Eigen/Dense" +#include "ceres/dense_qr.h" #include "ceres/dense_sparse_matrix.h" #include "ceres/internal/eigen.h" -#include "ceres/lapack.h" #include "ceres/linear_solver.h" #include "ceres/types.h" #include "ceres/wall_time.h" @@ -44,124 +44,40 @@ namespace ceres { namespace internal { DenseQRSolver::DenseQRSolver(const LinearSolver::Options& options) - : options_(options) { - work_.resize(1); -} + : options_(options), dense_qr_(DenseQR::Create(options)) {} LinearSolver::Summary DenseQRSolver::SolveImpl( DenseSparseMatrix* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, double* x) { - if (options_.dense_linear_algebra_library_type == EIGEN) { - return SolveUsingEigen(A, b, per_solve_options, x); - } else { - return SolveUsingLAPACK(A, b, per_solve_options, x); - } -} - -LinearSolver::Summary DenseQRSolver::SolveUsingLAPACK( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { EventLogger event_logger("DenseQRSolver::Solve"); const int num_rows = A->num_rows(); const int num_cols = A->num_cols(); + const int num_augmented_rows = + num_rows + ((per_solve_options.D != nullptr) ? num_cols : 0); - if (per_solve_options.D != NULL) { - // Temporarily append a diagonal block to the A matrix, but undo - // it before returning the matrix to the user. - A->AppendDiagonal(per_solve_options.D); + if (lhs_.rows() != num_augmented_rows || lhs_.cols() != num_cols) { + lhs_.resize(num_augmented_rows, num_cols); + rhs_.resize(num_augmented_rows); } - // TODO(sameeragarwal): Since we are copying anyways, the diagonal - // can be appended to the matrix instead of doing it on A. - lhs_ = A->matrix(); - - if (per_solve_options.D != NULL) { - // Undo the modifications to the matrix A. - A->RemoveDiagonal(); - } - - // rhs = [b;0] to account for the additional rows in the lhs. - if (rhs_.rows() != lhs_.rows()) { - rhs_.resize(lhs_.rows()); - } - rhs_.setZero(); + lhs_.topRows(num_rows) = A->matrix(); rhs_.head(num_rows) = ConstVectorRef(b, num_rows); - if (work_.rows() == 1) { - const int work_size = - LAPACK::EstimateWorkSizeForQR(lhs_.rows(), lhs_.cols()); - VLOG(3) << "Working memory for Dense QR factorization: " - << work_size * sizeof(double); - work_.resize(work_size); + if (num_rows != num_augmented_rows) { + lhs_.bottomRows(num_cols) = + ConstVectorRef(per_solve_options.D, num_cols).asDiagonal(); + rhs_.tail(num_cols).setZero(); } LinearSolver::Summary summary; + summary.termination_type = dense_qr_->FactorAndSolve( + lhs_.rows(), lhs_.cols(), lhs_.data(), rhs_.data(), x, &summary.message); summary.num_iterations = 1; - summary.termination_type = LAPACK::SolveInPlaceUsingQR(lhs_.rows(), - lhs_.cols(), - lhs_.data(), - work_.rows(), - work_.data(), - rhs_.data(), - &summary.message); event_logger.AddEvent("Solve"); - if (summary.termination_type == LINEAR_SOLVER_SUCCESS) { - VectorRef(x, num_cols) = rhs_.head(num_cols); - } - - event_logger.AddEvent("TearDown"); - return summary; -} - -LinearSolver::Summary DenseQRSolver::SolveUsingEigen( - DenseSparseMatrix* A, - const double* b, - const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { - EventLogger event_logger("DenseQRSolver::Solve"); - - const int num_rows = A->num_rows(); - const int num_cols = A->num_cols(); - - if (per_solve_options.D != NULL) { - // Temporarily append a diagonal block to the A matrix, but undo - // it before returning the matrix to the user. - A->AppendDiagonal(per_solve_options.D); - } - - // rhs = [b;0] to account for the additional rows in the lhs. - const int augmented_num_rows = - num_rows + ((per_solve_options.D != NULL) ? num_cols : 0); - if (rhs_.rows() != augmented_num_rows) { - rhs_.resize(augmented_num_rows); - rhs_.setZero(); - } - rhs_.head(num_rows) = ConstVectorRef(b, num_rows); - event_logger.AddEvent("Setup"); - - // Solve the system. - VectorRef(x, num_cols) = A->matrix().householderQr().solve(rhs_); - event_logger.AddEvent("Solve"); - - if (per_solve_options.D != NULL) { - // Undo the modifications to the matrix A. - A->RemoveDiagonal(); - } - - // We always succeed, since the QR solver returns the best solution - // it can. It is the job of the caller to determine if the solution - // is good enough or not. - LinearSolver::Summary summary; - summary.num_iterations = 1; - summary.termination_type = LINEAR_SOLVER_SUCCESS; - summary.message = "Success."; - event_logger.AddEvent("TearDown"); return summary; } diff --git a/extern/ceres/internal/ceres/dense_qr_solver.h b/extern/ceres/internal/ceres/dense_qr_solver.h index 980243bd6c6..39922a2692b 100644 --- a/extern/ceres/internal/ceres/dense_qr_solver.h +++ b/extern/ceres/internal/ceres/dense_qr_solver.h @@ -32,8 +32,12 @@ #ifndef CERES_INTERNAL_DENSE_QR_SOLVER_H_ #define CERES_INTERNAL_DENSE_QR_SOLVER_H_ +#include <memory> + +#include "ceres/dense_qr.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -79,7 +83,7 @@ class DenseSparseMatrix; // library. This solver always returns a solution, it is the user's // responsibility to judge if the solution is good enough for their // purposes. -class CERES_EXPORT_INTERNAL DenseQRSolver : public DenseSparseMatrixSolver { +class CERES_NO_EXPORT DenseQRSolver final : public DenseSparseMatrixSolver { public: explicit DenseQRSolver(const LinearSolver::Options& options); @@ -105,10 +109,12 @@ class CERES_EXPORT_INTERNAL DenseQRSolver : public DenseSparseMatrixSolver { const LinearSolver::Options options_; ColMajorMatrix lhs_; Vector rhs_; - Vector work_; + std::unique_ptr<DenseQR> dense_qr_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_QR_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/dense_sparse_matrix.cc b/extern/ceres/internal/ceres/dense_sparse_matrix.cc index 53207fe300e..8b967f2ade7 100644 --- a/extern/ceres/internal/ceres/dense_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/dense_sparse_matrix.cc @@ -31,9 +31,10 @@ #include "ceres/dense_sparse_matrix.h" #include <algorithm> +#include <utility> #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "glog/logging.h" @@ -41,28 +42,10 @@ namespace ceres { namespace internal { DenseSparseMatrix::DenseSparseMatrix(int num_rows, int num_cols) - : has_diagonal_appended_(false), has_diagonal_reserved_(false) { - m_.resize(num_rows, num_cols); - m_.setZero(); -} - -DenseSparseMatrix::DenseSparseMatrix(int num_rows, - int num_cols, - bool reserve_diagonal) - : has_diagonal_appended_(false), has_diagonal_reserved_(reserve_diagonal) { - if (reserve_diagonal) { - // Allocate enough space for the diagonal. - m_.resize(num_rows + num_cols, num_cols); - } else { - m_.resize(num_rows, num_cols); - } - m_.setZero(); -} + : m_(Matrix(num_rows, num_cols)) {} DenseSparseMatrix::DenseSparseMatrix(const TripletSparseMatrix& m) - : m_(Eigen::MatrixXd::Zero(m.num_rows(), m.num_cols())), - has_diagonal_appended_(false), - has_diagonal_reserved_(false) { + : m_(Matrix::Zero(m.num_rows(), m.num_cols())) { const double* values = m.values(); const int* rows = m.rows(); const int* cols = m.cols(); @@ -73,8 +56,7 @@ DenseSparseMatrix::DenseSparseMatrix(const TripletSparseMatrix& m) } } -DenseSparseMatrix::DenseSparseMatrix(const ColMajorMatrix& m) - : m_(m), has_diagonal_appended_(false), has_diagonal_reserved_(false) {} +DenseSparseMatrix::DenseSparseMatrix(Matrix m) : m_(std::move(m)) {} void DenseSparseMatrix::SetZero() { m_.setZero(); } @@ -96,72 +78,22 @@ void DenseSparseMatrix::ScaleColumns(const double* scale) { } void DenseSparseMatrix::ToDenseMatrix(Matrix* dense_matrix) const { - *dense_matrix = m_.block(0, 0, num_rows(), num_cols()); + *dense_matrix = m_; } -void DenseSparseMatrix::AppendDiagonal(double* d) { - CHECK(!has_diagonal_appended_); - if (!has_diagonal_reserved_) { - ColMajorMatrix tmp = m_; - m_.resize(m_.rows() + m_.cols(), m_.cols()); - m_.setZero(); - m_.block(0, 0, tmp.rows(), tmp.cols()) = tmp; - has_diagonal_reserved_ = true; - } - - m_.bottomLeftCorner(m_.cols(), m_.cols()) = - ConstVectorRef(d, m_.cols()).asDiagonal(); - has_diagonal_appended_ = true; -} - -void DenseSparseMatrix::RemoveDiagonal() { - CHECK(has_diagonal_appended_); - has_diagonal_appended_ = false; - // Leave the diagonal reserved. -} - -int DenseSparseMatrix::num_rows() const { - if (has_diagonal_reserved_ && !has_diagonal_appended_) { - return m_.rows() - m_.cols(); - } - return m_.rows(); -} +int DenseSparseMatrix::num_rows() const { return m_.rows(); } int DenseSparseMatrix::num_cols() const { return m_.cols(); } -int DenseSparseMatrix::num_nonzeros() const { - if (has_diagonal_reserved_ && !has_diagonal_appended_) { - return (m_.rows() - m_.cols()) * m_.cols(); - } - return m_.rows() * m_.cols(); -} +int DenseSparseMatrix::num_nonzeros() const { return m_.rows() * m_.cols(); } -ConstColMajorMatrixRef DenseSparseMatrix::matrix() const { - return ConstColMajorMatrixRef( - m_.data(), - ((has_diagonal_reserved_ && !has_diagonal_appended_) - ? m_.rows() - m_.cols() - : m_.rows()), - m_.cols(), - Eigen::Stride<Eigen::Dynamic, 1>(m_.rows(), 1)); -} +const Matrix& DenseSparseMatrix::matrix() const { return m_; } -ColMajorMatrixRef DenseSparseMatrix::mutable_matrix() { - return ColMajorMatrixRef(m_.data(), - ((has_diagonal_reserved_ && !has_diagonal_appended_) - ? m_.rows() - m_.cols() - : m_.rows()), - m_.cols(), - Eigen::Stride<Eigen::Dynamic, 1>(m_.rows(), 1)); -} +Matrix* DenseSparseMatrix::mutable_matrix() { return &m_; } void DenseSparseMatrix::ToTextFile(FILE* file) const { CHECK(file != nullptr); - const int active_rows = (has_diagonal_reserved_ && !has_diagonal_appended_) - ? (m_.rows() - m_.cols()) - : m_.rows(); - - for (int r = 0; r < active_rows; ++r) { + for (int r = 0; r < m_.rows(); ++r) { for (int c = 0; c < m_.cols(); ++c) { fprintf(file, "% 10d % 10d %17f\n", r, c, m_(r, c)); } diff --git a/extern/ceres/internal/ceres/dense_sparse_matrix.h b/extern/ceres/internal/ceres/dense_sparse_matrix.h index 94064b3eddc..655cbb8a3db 100644 --- a/extern/ceres/internal/ceres/dense_sparse_matrix.h +++ b/extern/ceres/internal/ceres/dense_sparse_matrix.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,8 +33,9 @@ #ifndef CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_ #define CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_ +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" @@ -43,17 +44,13 @@ namespace internal { class TripletSparseMatrix; -class CERES_EXPORT_INTERNAL DenseSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT DenseSparseMatrix final : public SparseMatrix { public: // Build a matrix with the same content as the TripletSparseMatrix // m. This assumes that m does not have any repeated entries. explicit DenseSparseMatrix(const TripletSparseMatrix& m); - explicit DenseSparseMatrix(const ColMajorMatrix& m); - + explicit DenseSparseMatrix(Matrix m); DenseSparseMatrix(int num_rows, int num_cols); - DenseSparseMatrix(int num_rows, int num_cols, bool reserve_diagonal); - - virtual ~DenseSparseMatrix() {} // SparseMatrix interface. void SetZero() final; @@ -69,40 +66,16 @@ class CERES_EXPORT_INTERNAL DenseSparseMatrix : public SparseMatrix { const double* values() const final { return m_.data(); } double* mutable_values() final { return m_.data(); } - ConstColMajorMatrixRef matrix() const; - ColMajorMatrixRef mutable_matrix(); - - // Only one diagonal can be appended at a time. The diagonal is appended to - // as a new set of rows, e.g. - // - // Original matrix: - // - // x x x - // x x x - // x x x - // - // After append diagonal (1, 2, 3): - // - // x x x - // x x x - // x x x - // 1 0 0 - // 0 2 0 - // 0 0 3 - // - // Calling RemoveDiagonal removes the block. It is a fatal error to append a - // diagonal to a matrix that already has an appended diagonal, and it is also - // a fatal error to remove a diagonal from a matrix that has none. - void AppendDiagonal(double* d); - void RemoveDiagonal(); + const Matrix& matrix() const; + Matrix* mutable_matrix(); private: - ColMajorMatrix m_; - bool has_diagonal_appended_; - bool has_diagonal_reserved_; + Matrix m_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DENSE_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/detect_structure.h b/extern/ceres/internal/ceres/detect_structure.h index 06242307ca8..6151c042256 100644 --- a/extern/ceres/internal/ceres/detect_structure.h +++ b/extern/ceres/internal/ceres/detect_structure.h @@ -32,7 +32,8 @@ #define CERES_INTERNAL_DETECT_STRUCTURE_H_ #include "ceres/block_structure.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -56,13 +57,15 @@ namespace internal { // Note: The structure of rows without any e-blocks has no effect on // the values returned by this function. It is entirely possible that // the f_block_size and row_blocks_size is not constant in such rows. -void CERES_EXPORT DetectStructure(const CompressedRowBlockStructure& bs, - const int num_eliminate_blocks, - int* row_block_size, - int* e_block_size, - int* f_block_size); +void CERES_NO_EXPORT DetectStructure(const CompressedRowBlockStructure& bs, + const int num_eliminate_blocks, + int* row_block_size, + int* e_block_size, + int* f_block_size); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DETECT_STRUCTURE_H_ diff --git a/extern/ceres/internal/ceres/dogleg_strategy.cc b/extern/ceres/internal/ceres/dogleg_strategy.cc index 03ae22f7e57..65f7ccd8480 100644 --- a/extern/ceres/internal/ceres/dogleg_strategy.cc +++ b/extern/ceres/internal/ceres/dogleg_strategy.cc @@ -480,7 +480,7 @@ bool DoglegStrategy::FindMinimumOnTrustRegionBoundary(Vector2d* minimum) const { // Find the real parts y_i of its roots (not only the real roots). Vector roots_real; - if (!FindPolynomialRoots(polynomial, &roots_real, NULL)) { + if (!FindPolynomialRoots(polynomial, &roots_real, nullptr)) { // Failed to find the roots of the polynomial, i.e. the candidate // solutions of the constrained problem. Report this back to the caller. return false; diff --git a/extern/ceres/internal/ceres/dogleg_strategy.h b/extern/ceres/internal/ceres/dogleg_strategy.h index cc3778ea2a0..1d219afe8bc 100644 --- a/extern/ceres/internal/ceres/dogleg_strategy.h +++ b/extern/ceres/internal/ceres/dogleg_strategy.h @@ -31,7 +31,8 @@ #ifndef CERES_INTERNAL_DOGLEG_STRATEGY_H_ #define CERES_INTERNAL_DOGLEG_STRATEGY_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/trust_region_strategy.h" @@ -53,10 +54,9 @@ namespace internal { // DoglegStrategy follows the approach by Shultz, Schnabel, Byrd. // This finds the exact optimum over the two-dimensional subspace // spanned by the two Dogleg vectors. -class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { +class CERES_NO_EXPORT DoglegStrategy final : public TrustRegionStrategy { public: explicit DoglegStrategy(const TrustRegionStrategy::Options& options); - virtual ~DoglegStrategy() {} // TrustRegionStrategy interface Summary ComputeStep(const PerSolveOptions& per_solve_options, @@ -65,7 +65,7 @@ class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { double* step) final; void StepAccepted(double step_quality) final; void StepRejected(double step_quality) final; - void StepIsInvalid(); + void StepIsInvalid() override; double Radius() const final; // These functions are predominantly for testing. @@ -76,8 +76,8 @@ class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { Matrix subspace_B() const { return subspace_B_; } private: - typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vector2d; - typedef Eigen::Matrix<double, 2, 2, Eigen::DontAlign> Matrix2d; + using Vector2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>; + using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>; LinearSolver::Summary ComputeGaussNewtonStep( const PerSolveOptions& per_solve_options, @@ -162,4 +162,6 @@ class CERES_EXPORT_INTERNAL DoglegStrategy : public TrustRegionStrategy { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DOGLEG_STRATEGY_H_ diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h b/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h index 30c98d86b6f..fedee3b7a83 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_finalizer.h @@ -33,13 +33,14 @@ #include "ceres/casts.h" #include "ceres/dynamic_compressed_row_sparse_matrix.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { -struct DynamicCompressedRowJacobianFinalizer { +struct CERES_NO_EXPORT DynamicCompressedRowJacobianFinalizer { void operator()(SparseMatrix* base_jacobian, int num_parameters) { - DynamicCompressedRowSparseMatrix* jacobian = + auto* jacobian = down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian); jacobian->Finalize(num_parameters); } diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc index 1749449043e..8c254e98f46 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,6 +30,8 @@ #include "ceres/dynamic_compressed_row_jacobian_writer.h" +#include <memory> + #include "ceres/casts.h" #include "ceres/compressed_row_jacobian_writer.h" #include "ceres/dynamic_compressed_row_sparse_matrix.h" @@ -43,25 +45,24 @@ namespace internal { using std::pair; using std::vector; -ScratchEvaluatePreparer* +std::unique_ptr<ScratchEvaluatePreparer[]> DynamicCompressedRowJacobianWriter::CreateEvaluatePreparers(int num_threads) { return ScratchEvaluatePreparer::Create(*program_, num_threads); } -SparseMatrix* DynamicCompressedRowJacobianWriter::CreateJacobian() const { - DynamicCompressedRowSparseMatrix* jacobian = - new DynamicCompressedRowSparseMatrix(program_->NumResiduals(), - program_->NumEffectiveParameters(), - 0 /* max_num_nonzeros */); - return jacobian; +std::unique_ptr<SparseMatrix> +DynamicCompressedRowJacobianWriter::CreateJacobian() const { + return std::make_unique<DynamicCompressedRowSparseMatrix>( + program_->NumResiduals(), + program_->NumEffectiveParameters(), + 0 /* max_num_nonzeros */); } void DynamicCompressedRowJacobianWriter::Write(int residual_id, int residual_offset, double** jacobians, SparseMatrix* base_jacobian) { - DynamicCompressedRowSparseMatrix* jacobian = - down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian); + auto* jacobian = down_cast<DynamicCompressedRowSparseMatrix*>(base_jacobian); // Get the `residual_block` of interest. const ResidualBlock* residual_block = @@ -77,12 +78,11 @@ void DynamicCompressedRowJacobianWriter::Write(int residual_id, jacobian->ClearRows(residual_offset, num_residuals); // Iterate over each parameter block. - for (int i = 0; i < evaluated_jacobian_blocks.size(); ++i) { + for (const auto& evaluated_jacobian_block : evaluated_jacobian_blocks) { const ParameterBlock* parameter_block = - program_->parameter_blocks()[evaluated_jacobian_blocks[i].first]; - const int parameter_block_jacobian_index = - evaluated_jacobian_blocks[i].second; - const int parameter_block_size = parameter_block->LocalSize(); + program_->parameter_blocks()[evaluated_jacobian_block.first]; + const int parameter_block_jacobian_index = evaluated_jacobian_block.second; + const int parameter_block_size = parameter_block->TangentSize(); const double* parameter_jacobian = jacobians[parameter_block_jacobian_index]; diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h index ef8fa25d7d4..794a9b4c1e6 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_jacobian_writer.h @@ -34,7 +34,10 @@ #ifndef CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_ #define CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_JACOBIAN_WRITER_H_ +#include <memory> + #include "ceres/evaluator.h" +#include "ceres/internal/export.h" #include "ceres/scratch_evaluate_preparer.h" namespace ceres { @@ -43,7 +46,7 @@ namespace internal { class Program; class SparseMatrix; -class DynamicCompressedRowJacobianWriter { +class CERES_NO_EXPORT DynamicCompressedRowJacobianWriter { public: DynamicCompressedRowJacobianWriter(Evaluator::Options /* ignored */, Program* program) @@ -55,12 +58,13 @@ class DynamicCompressedRowJacobianWriter { // the cost functions. The scratch space is therefore used to store // the jacobians (including zeros) temporarily before only the non-zero // entries are copied over to the larger jacobian in `Write`. - ScratchEvaluatePreparer* CreateEvaluatePreparers(int num_threads); + std::unique_ptr<ScratchEvaluatePreparer[]> CreateEvaluatePreparers( + int num_threads); // Return a `DynamicCompressedRowSparseMatrix` which is filled by // `Write`. Note that `Finalize` must be called to make the // `CompressedRowSparseMatrix` interface valid. - SparseMatrix* CreateJacobian() const; + std::unique_ptr<SparseMatrix> CreateJacobian() const; // Write only the non-zero jacobian entries for a residual block // (specified by `residual_id`) into `base_jacobian`, starting at the row diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc index 936e682b763..7185e14a411 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -70,8 +70,8 @@ void DynamicCompressedRowSparseMatrix::Finalize(int num_additional_elements) { // Count the number of non-zeros and resize `cols_` and `values_`. int num_jacobian_nonzeros = 0; - for (int i = 0; i < dynamic_cols_.size(); ++i) { - num_jacobian_nonzeros += dynamic_cols_[i].size(); + for (const auto& dynamic_col : dynamic_cols_) { + num_jacobian_nonzeros += dynamic_col.size(); } SetMaxNumNonZeros(num_jacobian_nonzeros + num_additional_elements); diff --git a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h index d06c36ebb94..5b4c402f830 100644 --- a/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h +++ b/extern/ceres/internal/ceres/dynamic_compressed_row_sparse_matrix.h @@ -44,12 +44,13 @@ #include <vector> #include "ceres/compressed_row_sparse_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL DynamicCompressedRowSparseMatrix +class CERES_NO_EXPORT DynamicCompressedRowSparseMatrix final : public CompressedRowSparseMatrix { public: // Set the number of rows and columns for the underlyig @@ -100,4 +101,6 @@ class CERES_EXPORT_INTERNAL DynamicCompressedRowSparseMatrix } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_DYNAMIC_COMPRESSED_ROW_SPARSE_MATRIX_H_ diff --git a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc index d31c4228f05..5e907e18d51 100644 --- a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.cc @@ -35,10 +35,12 @@ #include <ctime> #include <memory> #include <sstream> +#include <utility> #include "Eigen/SparseCore" #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/cxsparse.h" +#include "ceres/internal/config.h" #include "ceres/internal/eigen.h" #include "ceres/linear_solver.h" #include "ceres/suitesparse.h" @@ -54,8 +56,8 @@ namespace ceres { namespace internal { DynamicSparseNormalCholeskySolver::DynamicSparseNormalCholeskySolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)) {} LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImpl( CompressedRowSparseMatrix* A, @@ -71,11 +73,11 @@ LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImpl( // it before returning the matrix to the user. std::unique_ptr<CompressedRowSparseMatrix> regularizer; if (!A->col_blocks().empty()) { - regularizer.reset(CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( - per_solve_options.D, A->col_blocks())); + regularizer = CompressedRowSparseMatrix::CreateBlockDiagonalMatrix( + per_solve_options.D, A->col_blocks()); } else { - regularizer.reset( - new CompressedRowSparseMatrix(per_solve_options.D, num_cols)); + regularizer = std::make_unique<CompressedRowSparseMatrix>( + per_solve_options.D, num_cols); } A->AppendRows(*regularizer); } @@ -123,12 +125,13 @@ LinearSolver::Summary DynamicSparseNormalCholeskySolver::SolveImplUsingEigen( EventLogger event_logger("DynamicSparseNormalCholeskySolver::Eigen::Solve"); - Eigen::MappedSparseMatrix<double, Eigen::RowMajor> a(A->num_rows(), - A->num_cols(), - A->num_nonzeros(), - A->mutable_rows(), - A->mutable_cols(), - A->mutable_values()); + Eigen::Map<Eigen::SparseMatrix<double, Eigen::RowMajor>> a( + A->num_rows(), + A->num_cols(), + A->num_nonzeros(), + A->mutable_rows(), + A->mutable_cols(), + A->mutable_values()); Eigen::SparseMatrix<double> lhs = a.transpose() * a; Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> solver; diff --git a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h index 36118bab1a5..6f73c961212 100644 --- a/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h +++ b/extern/ceres/internal/ceres/dynamic_sparse_normal_cholesky_solver.h @@ -36,9 +36,10 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -53,12 +54,10 @@ class CompressedRowSparseMatrix; // // TODO(alex): Add support for Accelerate sparse solvers: // https://github.com/ceres-solver/ceres-solver/issues/397 -class DynamicSparseNormalCholeskySolver +class CERES_NO_EXPORT DynamicSparseNormalCholeskySolver : public CompressedRowSparseMatrixSolver { public: - explicit DynamicSparseNormalCholeskySolver( - const LinearSolver::Options& options); - virtual ~DynamicSparseNormalCholeskySolver() {} + explicit DynamicSparseNormalCholeskySolver(LinearSolver::Options options); private: LinearSolver::Summary SolveImpl(CompressedRowSparseMatrix* A, diff --git a/extern/ceres/internal/ceres/eigensparse.cc b/extern/ceres/internal/ceres/eigensparse.cc index 22ed2c43b5d..81668c82e67 100644 --- a/extern/ceres/internal/ceres/eigensparse.cc +++ b/extern/ceres/internal/ceres/eigensparse.cc @@ -30,6 +30,8 @@ #include "ceres/eigensparse.h" +#include <memory> + #ifdef CERES_USE_EIGEN_SPARSE #include <sstream> @@ -45,10 +47,9 @@ namespace internal { // TODO(sameeragarwal): Use enable_if to clean up the implementations // for when Scalar == double. template <typename Solver> -class EigenSparseCholeskyTemplate : public SparseCholesky { +class EigenSparseCholeskyTemplate final : public SparseCholesky { public: - EigenSparseCholeskyTemplate() : analyzed_(false) {} - virtual ~EigenSparseCholeskyTemplate() {} + EigenSparseCholeskyTemplate() = default; CompressedRowSparseMatrix::StorageType StorageType() const final { return CompressedRowSparseMatrix::LOWER_TRIANGULAR; } @@ -83,7 +84,7 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { LinearSolverTerminationType Solve(const double* rhs_ptr, double* solution_ptr, - std::string* message) { + std::string* message) override { CHECK(analyzed_) << "Solve called without a call to Factorize first."; scalar_rhs_ = ConstVectorRef(rhs_ptr, solver_.cols()) @@ -109,7 +110,7 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { std::string* message) final { CHECK_EQ(lhs->storage_type(), StorageType()); - typename Solver::Scalar* values_ptr = NULL; + typename Solver::Scalar* values_ptr = nullptr; if (std::is_same<typename Solver::Scalar, double>::value) { values_ptr = reinterpret_cast<typename Solver::Scalar*>(lhs->mutable_values()); @@ -122,7 +123,7 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { values_ptr = values_.data(); } - Eigen::MappedSparseMatrix<typename Solver::Scalar, Eigen::ColMajor> + Eigen::Map<Eigen::SparseMatrix<typename Solver::Scalar, Eigen::ColMajor>> eigen_lhs(lhs->num_rows(), lhs->num_rows(), lhs->num_nonzeros(), @@ -135,54 +136,46 @@ class EigenSparseCholeskyTemplate : public SparseCholesky { private: Eigen::Matrix<typename Solver::Scalar, Eigen::Dynamic, 1> values_, scalar_rhs_, scalar_solution_; - bool analyzed_; + bool analyzed_{false}; Solver solver_; }; std::unique_ptr<SparseCholesky> EigenSparseCholesky::Create( const OrderingType ordering_type) { - std::unique_ptr<SparseCholesky> sparse_cholesky; - - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, - Eigen::Upper, - Eigen::AMDOrdering<int>> - WithAMDOrdering; - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, - Eigen::Upper, - Eigen::NaturalOrdering<int>> - WithNaturalOrdering; + using WithAMDOrdering = Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, + Eigen::Upper, + Eigen::AMDOrdering<int>>; + using WithNaturalOrdering = + Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, + Eigen::Upper, + Eigen::NaturalOrdering<int>>; + if (ordering_type == AMD) { - sparse_cholesky.reset(new EigenSparseCholeskyTemplate<WithAMDOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithAMDOrdering>>(); } else { - sparse_cholesky.reset( - new EigenSparseCholeskyTemplate<WithNaturalOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithNaturalOrdering>>(); } - return sparse_cholesky; } -EigenSparseCholesky::~EigenSparseCholesky() {} +EigenSparseCholesky::~EigenSparseCholesky() = default; std::unique_ptr<SparseCholesky> FloatEigenSparseCholesky::Create( const OrderingType ordering_type) { - std::unique_ptr<SparseCholesky> sparse_cholesky; - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, - Eigen::Upper, - Eigen::AMDOrdering<int>> - WithAMDOrdering; - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, - Eigen::Upper, - Eigen::NaturalOrdering<int>> - WithNaturalOrdering; + using WithAMDOrdering = Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, + Eigen::Upper, + Eigen::AMDOrdering<int>>; + using WithNaturalOrdering = + Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, + Eigen::Upper, + Eigen::NaturalOrdering<int>>; if (ordering_type == AMD) { - sparse_cholesky.reset(new EigenSparseCholeskyTemplate<WithAMDOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithAMDOrdering>>(); } else { - sparse_cholesky.reset( - new EigenSparseCholeskyTemplate<WithNaturalOrdering>()); + return std::make_unique<EigenSparseCholeskyTemplate<WithNaturalOrdering>>(); } - return sparse_cholesky; } -FloatEigenSparseCholesky::~FloatEigenSparseCholesky() {} +FloatEigenSparseCholesky::~FloatEigenSparseCholesky() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/eigensparse.h b/extern/ceres/internal/ceres/eigensparse.h index bb89c2c11ac..c4a4142e586 100644 --- a/extern/ceres/internal/ceres/eigensparse.h +++ b/extern/ceres/internal/ceres/eigensparse.h @@ -34,7 +34,7 @@ #define CERES_INTERNAL_EIGENSPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_USE_EIGEN_SPARSE @@ -42,44 +42,45 @@ #include <string> #include "Eigen/SparseCore" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/sparse_cholesky.h" namespace ceres { namespace internal { -class EigenSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT EigenSparseCholesky : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create( const OrderingType ordering_type); // SparseCholesky interface. - virtual ~EigenSparseCholesky(); - virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, - std::string* message) = 0; - virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0; - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, - std::string* message) = 0; + ~EigenSparseCholesky() override; + LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, + std::string* message) override = 0; + CompressedRowSparseMatrix::StorageType StorageType() const override = 0; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override = 0; }; // Even though the input is double precision linear system, this class // solves it by computing a single precision Cholesky factorization. -class FloatEigenSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT FloatEigenSparseCholesky : public SparseCholesky { public: // Factory static std::unique_ptr<SparseCholesky> Create( const OrderingType ordering_type); // SparseCholesky interface. - virtual ~FloatEigenSparseCholesky(); - virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, - std::string* message) = 0; - virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0; - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, - std::string* message) = 0; + ~FloatEigenSparseCholesky() override; + LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, + std::string* message) override = 0; + CompressedRowSparseMatrix::StorageType StorageType() const override = 0; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override = 0; }; } // namespace internal diff --git a/extern/ceres/internal/ceres/evaluation_callback.cc b/extern/ceres/internal/ceres/evaluation_callback.cc new file mode 100644 index 00000000000..77591a8c621 --- /dev/null +++ b/extern/ceres/internal/ceres/evaluation_callback.cc @@ -0,0 +1,37 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2022 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: mierle@gmail.com (Keir Mierle) + +#include "ceres/evaluation_callback.h" + +namespace ceres { + +EvaluationCallback::~EvaluationCallback() = default; + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/evaluator.cc b/extern/ceres/internal/ceres/evaluator.cc index 516874184d9..52d0f09e5df 100644 --- a/extern/ceres/internal/ceres/evaluator.cc +++ b/extern/ceres/internal/ceres/evaluator.cc @@ -30,6 +30,7 @@ #include "ceres/evaluator.h" +#include <memory> #include <vector> #include "ceres/block_evaluate_preparer.h" @@ -40,7 +41,7 @@ #include "ceres/dense_jacobian_writer.h" #include "ceres/dynamic_compressed_row_finalizer.h" #include "ceres/dynamic_compressed_row_jacobian_writer.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/program_evaluator.h" #include "ceres/scratch_evaluate_preparer.h" #include "glog/logging.h" @@ -48,38 +49,42 @@ namespace ceres { namespace internal { -Evaluator::~Evaluator() {} +Evaluator::~Evaluator() = default; -Evaluator* Evaluator::Create(const Evaluator::Options& options, - Program* program, - std::string* error) { - CHECK(options.context != NULL); +std::unique_ptr<Evaluator> Evaluator::Create(const Evaluator::Options& options, + Program* program, + std::string* error) { + CHECK(options.context != nullptr); switch (options.linear_solver_type) { case DENSE_QR: case DENSE_NORMAL_CHOLESKY: - return new ProgramEvaluator<ScratchEvaluatePreparer, DenseJacobianWriter>( + return std::make_unique< + ProgramEvaluator<ScratchEvaluatePreparer, DenseJacobianWriter>>( options, program); case DENSE_SCHUR: case SPARSE_SCHUR: case ITERATIVE_SCHUR: case CGNR: - return new ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>( + return std::make_unique< + ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>>( options, program); case SPARSE_NORMAL_CHOLESKY: if (options.dynamic_sparsity) { - return new ProgramEvaluator<ScratchEvaluatePreparer, - DynamicCompressedRowJacobianWriter, - DynamicCompressedRowJacobianFinalizer>( - options, program); + return std::make_unique< + ProgramEvaluator<ScratchEvaluatePreparer, + DynamicCompressedRowJacobianWriter, + DynamicCompressedRowJacobianFinalizer>>(options, + program); } else { - return new ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>( + return std::make_unique< + ProgramEvaluator<BlockEvaluatePreparer, BlockJacobianWriter>>( options, program); } default: *error = "Invalid Linear Solver Type. Unable to create evaluator."; - return NULL; + return nullptr; } } diff --git a/extern/ceres/internal/ceres/evaluator.h b/extern/ceres/internal/ceres/evaluator.h index 9cf42593e95..68a4fb28a55 100644 --- a/extern/ceres/internal/ceres/evaluator.h +++ b/extern/ceres/internal/ceres/evaluator.h @@ -33,12 +33,14 @@ #define CERES_INTERNAL_EVALUATOR_H_ #include <map> +#include <memory> #include <string> #include <vector> #include "ceres/context_impl.h" #include "ceres/execution_summary.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -54,8 +56,8 @@ class SparseMatrix; // The Evaluator interface offers a way to interact with a least squares cost // function that is useful for an optimizer that wants to minimize the least // squares objective. This insulates the optimizer from issues like Jacobian -// storage, parameterization, etc. -class CERES_EXPORT_INTERNAL Evaluator { +// storage, manifolds, etc. +class CERES_NO_EXPORT Evaluator { public: virtual ~Evaluator(); @@ -68,9 +70,9 @@ class CERES_EXPORT_INTERNAL Evaluator { EvaluationCallback* evaluation_callback = nullptr; }; - static Evaluator* Create(const Options& options, - Program* program, - std::string* error); + static std::unique_ptr<Evaluator> Create(const Options& options, + Program* program, + std::string* error); // Build and return a sparse matrix for storing and working with the Jacobian // of the objective function. The jacobian has dimensions @@ -88,7 +90,7 @@ class CERES_EXPORT_INTERNAL Evaluator { // the jacobian for use with CHOLMOD, where as BlockOptimizationProblem // creates a BlockSparseMatrix representation of the jacobian for use in the // Schur complement based methods. - virtual SparseMatrix* CreateJacobian() const = 0; + virtual std::unique_ptr<SparseMatrix> CreateJacobian() const = 0; // Options struct to control Evaluator::Evaluate; struct EvaluateOptions { @@ -102,10 +104,10 @@ class CERES_EXPORT_INTERNAL Evaluator { // Evaluate the cost function for the given state. Returns the cost, // residuals, and jacobian in the corresponding arguments. Both residuals and - // jacobian are optional; to avoid computing them, pass NULL. + // jacobian are optional; to avoid computing them, pass nullptr. // - // If non-NULL, the Jacobian must have a suitable sparsity pattern; only the - // values array of the jacobian is modified. + // If non-nullptr, the Jacobian must have a suitable sparsity pattern; only + // the values array of the jacobian is modified. // // state is an array of size NumParameters(), cost is a pointer to a single // double, and residuals is an array of doubles of size NumResiduals(). @@ -131,13 +133,13 @@ class CERES_EXPORT_INTERNAL Evaluator { // Make a change delta (of size NumEffectiveParameters()) to state (of size // NumParameters()) and store the result in state_plus_delta. // - // In the case that there are no parameterizations used, this is equivalent to + // In the case that there are no manifolds used, this is equivalent to // // state_plus_delta[i] = state[i] + delta[i] ; // - // however, the mapping is more complicated in the case of parameterizations + // however, the mapping is more complicated in the case of manifolds // like quaternions. This is the same as the "Plus()" operation in - // local_parameterization.h, but operating over the entire state vector for a + // manifold.h, but operating over the entire state vector for a // problem. virtual bool Plus(const double* state, const double* delta, @@ -147,7 +149,7 @@ class CERES_EXPORT_INTERNAL Evaluator { virtual int NumParameters() const = 0; // This is the effective number of parameters that the optimizer may adjust. - // This applies when there are parameterizations on some of the parameters. + // This applies when there are manifolds on some of the parameters. virtual int NumEffectiveParameters() const = 0; // The number of residuals in the optimization problem. @@ -158,11 +160,13 @@ class CERES_EXPORT_INTERNAL Evaluator { // life time issues. Further, these calls are not expected to be // frequent or performance sensitive. virtual std::map<std::string, CallStatistics> Statistics() const { - return std::map<std::string, CallStatistics>(); + return {}; } }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_EVALUATOR_H_ diff --git a/extern/ceres/internal/ceres/execution_summary.h b/extern/ceres/internal/ceres/execution_summary.h index 17fd882af03..fbee75fc0cb 100644 --- a/extern/ceres/internal/ceres/execution_summary.h +++ b/extern/ceres/internal/ceres/execution_summary.h @@ -34,17 +34,18 @@ #include <map> #include <mutex> #include <string> +#include <utility> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/wall_time.h" namespace ceres { namespace internal { struct CallStatistics { - CallStatistics() : time(0.), calls(0) {} - double time; - int calls; + CallStatistics() = default; + double time{0.}; + int calls{0}; }; // Struct used by various objects to report statistics about their @@ -69,8 +70,10 @@ class ExecutionSummary { class ScopedExecutionTimer { public: - ScopedExecutionTimer(const std::string& name, ExecutionSummary* summary) - : start_time_(WallTimeInSeconds()), name_(name), summary_(summary) {} + ScopedExecutionTimer(std::string name, ExecutionSummary* summary) + : start_time_(WallTimeInSeconds()), + name_(std::move(name)), + summary_(summary) {} ~ScopedExecutionTimer() { summary_->IncrementTimeBy(name_, WallTimeInSeconds() - start_time_); diff --git a/extern/ceres/internal/ceres/file.h b/extern/ceres/internal/ceres/file.h index c0015df60f0..bd13128aedf 100644 --- a/extern/ceres/internal/ceres/file.h +++ b/extern/ceres/internal/ceres/file.h @@ -35,21 +35,26 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { +CERES_NO_EXPORT void WriteStringToFileOrDie(const std::string& data, const std::string& filename); +CERES_NO_EXPORT void ReadFileToStringOrDie(const std::string& filename, std::string* data); // Join two path components, adding a slash if necessary. If basename is an // absolute path then JoinPath ignores dirname and simply returns basename. -CERES_EXPORT_INTERNAL std::string JoinPath(const std::string& dirname, - const std::string& basename); +CERES_NO_EXPORT +std::string JoinPath(const std::string& dirname, const std::string& basename); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_FILE_H_ diff --git a/extern/ceres/internal/ceres/blas.cc b/extern/ceres/internal/ceres/first_order_function.cc index f8d006e3069..26f13488a1d 100644 --- a/extern/ceres/internal/ceres/blas.cc +++ b/extern/ceres/internal/ceres/first_order_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -28,55 +28,10 @@ // // Author: sameeragarwal@google.com (Sameer Agarwal) -#include "ceres/blas.h" - -#include "ceres/internal/port.h" -#include "glog/logging.h" - -#ifndef CERES_NO_LAPACK -extern "C" void dsyrk_(char* uplo, - char* trans, - int* n, - int* k, - double* alpha, - double* a, - int* lda, - double* beta, - double* c, - int* ldc); -#endif +#include "ceres/first_order_function.h" namespace ceres { -namespace internal { -void BLAS::SymmetricRankKUpdate(int num_rows, - int num_cols, - const double* a, - bool transpose, - double alpha, - double beta, - double* c) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a BLAS library."; -#else - char uplo = 'L'; - char trans = transpose ? 'T' : 'N'; - int n = transpose ? num_cols : num_rows; - int k = transpose ? num_rows : num_cols; - int lda = k; - int ldc = n; - dsyrk_(&uplo, - &trans, - &n, - &k, - &alpha, - const_cast<double*>(a), - &lda, - &beta, - c, - &ldc); -#endif -} +FirstOrderFunction::~FirstOrderFunction() = default; -} // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/float_cxsparse.cc b/extern/ceres/internal/ceres/float_cxsparse.cc index 6c688303444..a6d5e811efd 100644 --- a/extern/ceres/internal/ceres/float_cxsparse.cc +++ b/extern/ceres/internal/ceres/float_cxsparse.cc @@ -30,6 +30,8 @@ #include "ceres/float_cxsparse.h" +#include <memory> + #if !defined(CERES_NO_CXSPARSE) namespace ceres { @@ -38,7 +40,7 @@ namespace internal { std::unique_ptr<SparseCholesky> FloatCXSparseCholesky::Create( OrderingType ordering_type) { LOG(FATAL) << "FloatCXSparseCholesky is not available."; - return std::unique_ptr<SparseCholesky>(); + return {}; } } // namespace internal diff --git a/extern/ceres/internal/ceres/float_cxsparse.h b/extern/ceres/internal/ceres/float_cxsparse.h index 9a274c23636..8b4514acb18 100644 --- a/extern/ceres/internal/ceres/float_cxsparse.h +++ b/extern/ceres/internal/ceres/float_cxsparse.h @@ -32,12 +32,13 @@ #define CERES_INTERNAL_FLOAT_CXSPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #if !defined(CERES_NO_CXSPARSE) #include <memory> +#include "ceres/internal/export.h" #include "ceres/sparse_cholesky.h" namespace ceres { @@ -45,7 +46,7 @@ namespace internal { // Fake implementation of a single precision Sparse Cholesky using // CXSparse. -class FloatCXSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT FloatCXSparseCholesky : public SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); }; diff --git a/extern/ceres/internal/ceres/float_suitesparse.cc b/extern/ceres/internal/ceres/float_suitesparse.cc index 03604572b5c..dc1d0e45bdb 100644 --- a/extern/ceres/internal/ceres/float_suitesparse.cc +++ b/extern/ceres/internal/ceres/float_suitesparse.cc @@ -30,6 +30,8 @@ #include "ceres/float_suitesparse.h" +#include <memory> + #if !defined(CERES_NO_SUITESPARSE) namespace ceres { @@ -38,7 +40,7 @@ namespace internal { std::unique_ptr<SparseCholesky> FloatSuiteSparseCholesky::Create( OrderingType ordering_type) { LOG(FATAL) << "FloatSuiteSparseCholesky is not available."; - return std::unique_ptr<SparseCholesky>(); + return {}; } } // namespace internal diff --git a/extern/ceres/internal/ceres/float_suitesparse.h b/extern/ceres/internal/ceres/float_suitesparse.h index c436da43f86..7e76799e241 100644 --- a/extern/ceres/internal/ceres/float_suitesparse.h +++ b/extern/ceres/internal/ceres/float_suitesparse.h @@ -33,11 +33,12 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <memory> +#include "ceres/internal/export.h" #include "ceres/sparse_cholesky.h" #if !defined(CERES_NO_SUITESPARSE) @@ -47,7 +48,7 @@ namespace internal { // Fake implementation of a single precision Sparse Cholesky using // SuiteSparse. -class FloatSuiteSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT FloatSuiteSparseCholesky : public SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); }; diff --git a/extern/ceres/internal/ceres/function_sample.h b/extern/ceres/internal/ceres/function_sample.h index 3bcea1bc5ff..63ffc8ff8fc 100644 --- a/extern/ceres/internal/ceres/function_sample.h +++ b/extern/ceres/internal/ceres/function_sample.h @@ -33,8 +33,9 @@ #include <string> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -47,7 +48,7 @@ namespace internal { // line/direction. FunctionSample contains the information in two // ways. Information in the ambient space and information along the // direction of search. -struct CERES_EXPORT_INTERNAL FunctionSample { +struct CERES_NO_EXPORT FunctionSample { FunctionSample(); FunctionSample(double x, double value); FunctionSample(double x, double value, double gradient); @@ -90,4 +91,6 @@ struct CERES_EXPORT_INTERNAL FunctionSample { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_FUNCTION_SAMPLE_H_ diff --git a/extern/ceres/internal/ceres/generate_template_specializations.py b/extern/ceres/internal/ceres/generate_template_specializations.py deleted file mode 100644 index 74e46c28b78..00000000000 --- a/extern/ceres/internal/ceres/generate_template_specializations.py +++ /dev/null @@ -1,246 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2015 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: sameeragarwal@google.com (Sameer Agarwal) -# -# Script for explicitly generating template specialization of the -# SchurEliminator class. It is a rather large class -# and the number of explicit instantiations is also large. Explicitly -# generating these instantiations in separate .cc files breaks the -# compilation into separate compilation unit rather than one large cc -# file which takes 2+GB of RAM to compile. -# -# This script creates three sets of files. -# -# 1. schur_eliminator_x_x_x.cc and partitioned_matrix_view_x_x_x.cc -# where, the x indicates the template parameters and -# -# 2. schur_eliminator.cc & partitioned_matrix_view.cc -# -# that contains a factory function for instantiating these classes -# based on runtime parameters. -# -# 3. schur_templates.cc -# -# that contains a function which can be queried to determine what -# template specializations are available. -# -# The following list of tuples, specializations indicates the set of -# specializations that is generated. -SPECIALIZATIONS = [(2, 2, 2), - (2, 2, 3), - (2, 2, 4), - (2, 2, "Eigen::Dynamic"), - (2, 3, 3), - (2, 3, 4), - (2, 3, 6), - (2, 3, 9), - (2, 3, "Eigen::Dynamic"), - (2, 4, 3), - (2, 4, 4), - (2, 4, 6), - (2, 4, 8), - (2, 4, 9), - (2, 4, "Eigen::Dynamic"), - (2, "Eigen::Dynamic", "Eigen::Dynamic"), - (3, 3, 3), - (4, 4, 2), - (4, 4, 3), - (4, 4, 4), - (4, 4, "Eigen::Dynamic")] - -import schur_eliminator_template -import partitioned_matrix_view_template -import os -import glob - -def SuffixForSize(size): - if size == "Eigen::Dynamic": - return "d" - return str(size) - -def SpecializationFilename(prefix, row_block_size, e_block_size, f_block_size): - return "_".join([prefix] + map(SuffixForSize, (row_block_size, - e_block_size, - f_block_size))) - -def GenerateFactoryConditional(row_block_size, e_block_size, f_block_size): - conditionals = [] - if (row_block_size != "Eigen::Dynamic"): - conditionals.append("(options.row_block_size == %s)" % row_block_size) - if (e_block_size != "Eigen::Dynamic"): - conditionals.append("(options.e_block_size == %s)" % e_block_size) - if (f_block_size != "Eigen::Dynamic"): - conditionals.append("(options.f_block_size == %s)" % f_block_size) - if (len(conditionals) == 0): - return "%s" - - if (len(conditionals) == 1): - return " if " + conditionals[0] + " {\n %s\n }\n" - - return " if (" + " &&\n ".join(conditionals) + ") {\n %s\n }\n" - -def Specialize(name, data): - """ - Generate specialization code and the conditionals to instantiate it. - """ - - # Specialization files - for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS: - output = SpecializationFilename("generated/" + name, - row_block_size, - e_block_size, - f_block_size) + ".cc" - - with open(output, "w") as f: - f.write(data["HEADER"]) - f.write(data["SPECIALIZATION_FILE"] % - (row_block_size, e_block_size, f_block_size)) - - # Generate the _d_d_d specialization. - output = SpecializationFilename("generated/" + name, - "Eigen::Dynamic", - "Eigen::Dynamic", - "Eigen::Dynamic") + ".cc" - with open(output, "w") as f: - f.write(data["HEADER"]) - f.write(data["DYNAMIC_FILE"] % - ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")) - - # Factory - with open(name + ".cc", "w") as f: - f.write(data["HEADER"]) - f.write(data["FACTORY_FILE_HEADER"]) - for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS: - factory_conditional = GenerateFactoryConditional( - row_block_size, e_block_size, f_block_size) - factory = data["FACTORY"] % (row_block_size, e_block_size, f_block_size) - f.write(factory_conditional % factory); - f.write(data["FACTORY_FOOTER"]) - -QUERY_HEADER = """// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) -// -// What template specializations are available. -// -// ======================================== -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -//========================================= -// -// This file is generated using generate_template_specializations.py. -""" - -QUERY_FILE_HEADER = """ -#include "ceres/internal/eigen.h" -#include "ceres/schur_templates.h" - -namespace ceres { -namespace internal { - -void GetBestSchurTemplateSpecialization(int* row_block_size, - int* e_block_size, - int* f_block_size) { - LinearSolver::Options options; - options.row_block_size = *row_block_size; - options.e_block_size = *e_block_size; - options.f_block_size = *f_block_size; - *row_block_size = Eigen::Dynamic; - *e_block_size = Eigen::Dynamic; - *f_block_size = Eigen::Dynamic; -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -QUERY_FOOTER = """ -#endif - return; -} - -} // namespace internal -} // namespace ceres -""" - -QUERY_ACTION = """ *row_block_size = %s; - *e_block_size = %s; - *f_block_size = %s; - return;""" - -def GenerateQueryFile(): - """ - Generate file that allows querying for available template specializations. - """ - - with open("schur_templates.cc", "w") as f: - f.write(QUERY_HEADER) - f.write(QUERY_FILE_HEADER) - for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS: - factory_conditional = GenerateFactoryConditional( - row_block_size, e_block_size, f_block_size) - action = QUERY_ACTION % (row_block_size, e_block_size, f_block_size) - f.write(factory_conditional % action) - f.write(QUERY_FOOTER) - - -if __name__ == "__main__": - for f in glob.glob("generated/*"): - os.remove(f) - - Specialize("schur_eliminator", - schur_eliminator_template.__dict__) - Specialize("partitioned_matrix_view", - partitioned_matrix_view_template.__dict__) - GenerateQueryFile() diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc index f5753bef544..7b4ed167d05 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc index a7a9b5231cf..0f012515a95 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc index faf6c4a754a..bdbe91c43f6 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc index 92fd4cddf43..71f293b5512 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc index 2df314f137a..a6ea7761c9a 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc index ff1ca3e7f1f..e712678a28a 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc index 5041df9152d..3aff26e657b 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc index c0b72fec8b8..6cd239bfd9a 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc index 8a3c162ab7e..68c50552d42 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc index 0e69ca6404d..88c5e29c6f8 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc index ba9bb615291..b9487834441 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc index 1acdb9b21d5..7f044ef628b 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc index 888ff99557d..7394e7998e7 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc index bd4dde3d207..263f1fb36f1 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc index 6d3516fc6d5..d47634e0f40 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc index 77d22ed6bc2..0944cdcbfda 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc index aeb456c6e2a..23674031bb9 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_3_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc index bb240b9e3f6..d5268cac481 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc index 5d47543644d..67e098fc6f3 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc index e14f980933d..5fe28caee8c 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc index 9ec50563ac8..d87c76d0aa4 100644 --- a/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc +++ b/extern/ceres/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc index 289a809acb7..dc47a2e6d8e 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc index 20311ba843d..e2df6f63d2f 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc index 1f6a8ae4a0e..0b1ae949a09 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc index 08b18d357bd..0f7b6d78c7f 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_2_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc index 115b4c8cce1..e4ab8eb19bf 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc index c7035370424..d73d466b04c 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc index edb9afea969..800ee536bbf 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc index faa5c19f5c0..d38cd566082 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc index 81b6f975e7f..4ac4b8ac8b7 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_3_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc index 2cb2d15ac93..d5f5dbea4b4 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc index a78eff3aa02..d50a6d4002b 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc index e2534f235b6..f79fa4dd2f0 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_6.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc index 296a46273bc..972b000f1ba 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_8.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc index 0d0b04e686c..aa33e479bc5 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_9.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc index 797992660d7..a28ef15a522 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc index 189be043af8..43924279a39 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_2_d_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc index 35c14a8f4bd..7ff2a62341c 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_3_3_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc index 878500a2100..9008b816843 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_2.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc index c4b0959db6a..8e37df51bee 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_3.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc index 20df5343335..09d50813a8a 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_4.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc index 17368dca4f6..089df2d7e3e 100644 --- a/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc +++ b/extern/ceres/internal/ceres/generated/schur_eliminator_4_4_d.cc @@ -40,7 +40,7 @@ // This file is generated using generate_template_specializations.py. // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION diff --git a/extern/ceres/internal/ceres/gradient_checker.cc b/extern/ceres/internal/ceres/gradient_checker.cc index dadaaa08734..777001e013c 100644 --- a/extern/ceres/internal/ceres/gradient_checker.cc +++ b/extern/ceres/internal/ceres/gradient_checker.cc @@ -40,6 +40,7 @@ #include <vector> #include "ceres/is_close.h" +#include "ceres/manifold_adapter.h" #include "ceres/stringprintf.h" #include "ceres/types.h" @@ -53,15 +54,13 @@ using std::vector; namespace { // Evaluate the cost function and transform the returned Jacobians to -// the local space of the respective local parameterizations. -bool EvaluateCostFunction( - const ceres::CostFunction* function, - double const* const* parameters, - const std::vector<const ceres::LocalParameterization*>& - local_parameterizations, - Vector* residuals, - std::vector<Matrix>* jacobians, - std::vector<Matrix>* local_jacobians) { +// the tangent space of the respective local parameterizations. +bool EvaluateCostFunction(const CostFunction* function, + double const* const* parameters, + const std::vector<const Manifold*>& manifolds, + Vector* residuals, + std::vector<Matrix>* jacobians, + std::vector<Matrix>* local_jacobians) { CHECK(residuals != nullptr); CHECK(jacobians != nullptr); CHECK(local_jacobians != nullptr); @@ -69,20 +68,20 @@ bool EvaluateCostFunction( const vector<int32_t>& block_sizes = function->parameter_block_sizes(); const int num_parameter_blocks = block_sizes.size(); - // Allocate Jacobian matrices in local space. + // Allocate Jacobian matrices in tangent space. local_jacobians->resize(num_parameter_blocks); vector<double*> local_jacobian_data(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { int block_size = block_sizes.at(i); - if (local_parameterizations.at(i) != NULL) { - block_size = local_parameterizations.at(i)->LocalSize(); + if (manifolds.at(i) != nullptr) { + block_size = manifolds.at(i)->TangentSize(); } local_jacobians->at(i).resize(function->num_residuals(), block_size); local_jacobians->at(i).setZero(); local_jacobian_data.at(i) = local_jacobians->at(i).data(); } - // Allocate Jacobian matrices in global space. + // Allocate Jacobian matrices in ambient space. jacobians->resize(num_parameter_blocks); vector<double*> jacobian_data(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { @@ -100,18 +99,17 @@ bool EvaluateCostFunction( return false; } - // Convert Jacobians from global to local space. + // Convert Jacobians from ambient to local space. for (size_t i = 0; i < local_jacobians->size(); ++i) { - if (local_parameterizations.at(i) == NULL) { + if (manifolds.at(i) == nullptr) { local_jacobians->at(i) = jacobians->at(i); } else { - int global_size = local_parameterizations.at(i)->GlobalSize(); - int local_size = local_parameterizations.at(i)->LocalSize(); - CHECK_EQ(jacobians->at(i).cols(), global_size); - Matrix global_J_local(global_size, local_size); - local_parameterizations.at(i)->ComputeJacobian(parameters[i], - global_J_local.data()); - local_jacobians->at(i).noalias() = jacobians->at(i) * global_J_local; + int ambient_size = manifolds.at(i)->AmbientSize(); + int tangent_size = manifolds.at(i)->TangentSize(); + CHECK_EQ(jacobians->at(i).cols(), ambient_size); + Matrix ambient_J_tangent(ambient_size, tangent_size); + manifolds.at(i)->PlusJacobian(parameters[i], ambient_J_tangent.data()); + local_jacobians->at(i).noalias() = jacobians->at(i) * ambient_J_tangent; } } return true; @@ -122,20 +120,47 @@ GradientChecker::GradientChecker( const CostFunction* function, const vector<const LocalParameterization*>* local_parameterizations, const NumericDiffOptions& options) + : delete_manifolds_(true), function_(function) { + CHECK(function != nullptr); + manifolds_.resize(function->parameter_block_sizes().size(), nullptr); + + // Wrap the local parameterization into manifold objects using + // ManifoldAdapter. + for (int i = 0; i < manifolds_.size(); ++i) { + const LocalParameterization* local_param = local_parameterizations->at(i); + if (local_param == nullptr) { + continue; + } + manifolds_[i] = new internal::ManifoldAdapter(local_param); + } + + auto finite_diff_cost_function = + std::make_unique<DynamicNumericDiffCostFunction<CostFunction, RIDDERS>>( + function, DO_NOT_TAKE_OWNERSHIP, options); + const vector<int32_t>& parameter_block_sizes = + function->parameter_block_sizes(); + for (int32_t parameter_block_size : parameter_block_sizes) { + finite_diff_cost_function->AddParameterBlock(parameter_block_size); + } + finite_diff_cost_function->SetNumResiduals(function->num_residuals()); + + finite_diff_cost_function_ = std::move(finite_diff_cost_function); +} + +GradientChecker::GradientChecker(const CostFunction* function, + const vector<const Manifold*>* manifolds, + const NumericDiffOptions& options) : function_(function) { CHECK(function != nullptr); - if (local_parameterizations != NULL) { - local_parameterizations_ = *local_parameterizations; + if (manifolds != nullptr) { + manifolds_ = *manifolds; } else { - local_parameterizations_.resize(function->parameter_block_sizes().size(), - NULL); + manifolds_.resize(function->parameter_block_sizes().size(), nullptr); } - DynamicNumericDiffCostFunction<CostFunction, RIDDERS>* - finite_diff_cost_function = - new DynamicNumericDiffCostFunction<CostFunction, RIDDERS>( - function, DO_NOT_TAKE_OWNERSHIP, options); - finite_diff_cost_function_.reset(finite_diff_cost_function); + auto finite_diff_cost_function = + std::make_unique<DynamicNumericDiffCostFunction<CostFunction, RIDDERS>>( + function, DO_NOT_TAKE_OWNERSHIP, options); const vector<int32_t>& parameter_block_sizes = function->parameter_block_sizes(); const int num_parameter_blocks = parameter_block_sizes.size(); @@ -143,6 +168,16 @@ GradientChecker::GradientChecker( finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]); } finite_diff_cost_function->SetNumResiduals(function->num_residuals()); + + finite_diff_cost_function_ = std::move(finite_diff_cost_function); +} + +GradientChecker::~GradientChecker() { + if (delete_manifolds_) { + for (const auto m : manifolds_) { + delete m; + } + } } bool GradientChecker::Probe(double const* const* parameters, @@ -154,7 +189,7 @@ bool GradientChecker::Probe(double const* const* parameters, // provided an output argument. ProbeResults* results; ProbeResults results_local; - if (results_param != NULL) { + if (results_param != nullptr) { results = results_param; results->residuals.resize(0); results->jacobians.clear(); @@ -173,7 +208,7 @@ bool GradientChecker::Probe(double const* const* parameters, vector<Matrix>& local_jacobians = results->local_jacobians; if (!EvaluateCostFunction(function_, parameters, - local_parameterizations_, + manifolds_, &results->residuals, &jacobians, &local_jacobians)) { @@ -187,7 +222,7 @@ bool GradientChecker::Probe(double const* const* parameters, Vector finite_diff_residuals; if (!EvaluateCostFunction(finite_diff_cost_function_.get(), parameters, - local_parameterizations_, + manifolds_, &finite_diff_residuals, &numeric_jacobians, &local_numeric_jacobians)) { @@ -205,8 +240,8 @@ bool GradientChecker::Probe(double const* const* parameters, if (!IsClose(results->residuals[i], finite_diff_residuals[i], relative_precision, - NULL, - NULL)) { + nullptr, + nullptr)) { results->error_log = "Function evaluation with and without Jacobians " "resulted in different residuals."; diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc index 2eb6d627167..1c3b318ed04 100644 --- a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc +++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -34,8 +34,10 @@ #include <algorithm> #include <cmath> #include <cstdint> +#include <memory> #include <numeric> #include <string> +#include <utility> #include <vector> #include "ceres/dynamic_numeric_diff_cost_function.h" @@ -60,19 +62,18 @@ using std::vector; namespace { -class GradientCheckingCostFunction : public CostFunction { +class GradientCheckingCostFunction final : public CostFunction { public: - GradientCheckingCostFunction( - const CostFunction* function, - const std::vector<const LocalParameterization*>* local_parameterizations, - const NumericDiffOptions& options, - double relative_precision, - const string& extra_info, - GradientCheckingIterationCallback* callback) + GradientCheckingCostFunction(const CostFunction* function, + const std::vector<const Manifold*>* manifolds, + const NumericDiffOptions& options, + double relative_precision, + string extra_info, + GradientCheckingIterationCallback* callback) : function_(function), - gradient_checker_(function, local_parameterizations, options), + gradient_checker_(function, manifolds, options), relative_precision_(relative_precision), - extra_info_(extra_info), + extra_info_(std::move(extra_info)), callback_(callback) { CHECK(callback_ != nullptr); const vector<int32_t>& parameter_block_sizes = @@ -81,14 +82,12 @@ class GradientCheckingCostFunction : public CostFunction { set_num_residuals(function->num_residuals()); } - virtual ~GradientCheckingCostFunction() {} - bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const final { if (!jacobians) { // Nothing to check in this case; just forward. - return function_->Evaluate(parameters, residuals, NULL); + return function_->Evaluate(parameters, residuals, nullptr); } GradientChecker::ProbeResults results; @@ -108,7 +107,7 @@ class GradientCheckingCostFunction : public CostFunction { // Copy the original jacobian blocks into the jacobians array. const vector<int32_t>& block_sizes = function_->parameter_block_sizes(); for (int k = 0; k < block_sizes.size(); k++) { - if (jacobians[k] != NULL) { + if (jacobians[k] != nullptr) { MatrixRef(jacobians[k], results.jacobians[k].rows(), results.jacobians[k].cols()) = results.jacobians[k]; @@ -145,6 +144,7 @@ CallbackReturnType GradientCheckingIterationCallback::operator()( } return SOLVER_CONTINUE; } + void GradientCheckingIterationCallback::SetGradientErrorDetected( std::string& error_log) { std::lock_guard<std::mutex> l(mutex_); @@ -152,9 +152,9 @@ void GradientCheckingIterationCallback::SetGradientErrorDetected( error_log_ += "\n" + error_log; } -CostFunction* CreateGradientCheckingCostFunction( +std::unique_ptr<CostFunction> CreateGradientCheckingCostFunction( const CostFunction* cost_function, - const std::vector<const LocalParameterization*>* local_parameterizations, + const std::vector<const Manifold*>* manifolds, double relative_step_size, double relative_precision, const std::string& extra_info, @@ -162,51 +162,48 @@ CostFunction* CreateGradientCheckingCostFunction( NumericDiffOptions numeric_diff_options; numeric_diff_options.relative_step_size = relative_step_size; - return new GradientCheckingCostFunction(cost_function, - local_parameterizations, - numeric_diff_options, - relative_precision, - extra_info, - callback); + return std::make_unique<GradientCheckingCostFunction>(cost_function, + manifolds, + numeric_diff_options, + relative_precision, + extra_info, + callback); } -ProblemImpl* CreateGradientCheckingProblemImpl( +std::unique_ptr<ProblemImpl> CreateGradientCheckingProblemImpl( ProblemImpl* problem_impl, double relative_step_size, double relative_precision, GradientCheckingIterationCallback* callback) { CHECK(callback != nullptr); - // We create new CostFunctions by wrapping the original CostFunction - // in a gradient checking CostFunction. So its okay for the - // ProblemImpl to take ownership of it and destroy it. The - // LossFunctions and LocalParameterizations are reused and since - // they are owned by problem_impl, gradient_checking_problem_impl + // We create new CostFunctions by wrapping the original CostFunction in a + // gradient checking CostFunction. So its okay for the ProblemImpl to take + // ownership of it and destroy it. The LossFunctions and Manifolds are reused + // and since they are owned by problem_impl, gradient_checking_problem_impl // should not take ownership of it. Problem::Options gradient_checking_problem_options; gradient_checking_problem_options.cost_function_ownership = TAKE_OWNERSHIP; gradient_checking_problem_options.loss_function_ownership = DO_NOT_TAKE_OWNERSHIP; - gradient_checking_problem_options.local_parameterization_ownership = - DO_NOT_TAKE_OWNERSHIP; + gradient_checking_problem_options.manifold_ownership = DO_NOT_TAKE_OWNERSHIP; gradient_checking_problem_options.context = problem_impl->context(); NumericDiffOptions numeric_diff_options; numeric_diff_options.relative_step_size = relative_step_size; - ProblemImpl* gradient_checking_problem_impl = - new ProblemImpl(gradient_checking_problem_options); + auto gradient_checking_problem_impl = + std::make_unique<ProblemImpl>(gradient_checking_problem_options); Program* program = problem_impl->mutable_program(); - // For every ParameterBlock in problem_impl, create a new parameter - // block with the same local parameterization and constancy. + // For every ParameterBlock in problem_impl, create a new parameter block with + // the same manifold and constancy. const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { gradient_checking_problem_impl->AddParameterBlock( parameter_block->mutable_user_state(), parameter_block->Size(), - parameter_block->mutable_local_parameterization()); + parameter_block->mutable_manifold()); if (parameter_block->IsConstant()) { gradient_checking_problem_impl->SetParameterBlockConstant( @@ -238,22 +235,22 @@ ProblemImpl* CreateGradientCheckingProblemImpl( string extra_info = StringPrintf("Residual block id %d; depends on parameters [", i); vector<double*> parameter_blocks; - vector<const LocalParameterization*> local_parameterizations; + vector<const Manifold*> manifolds; parameter_blocks.reserve(residual_block->NumParameterBlocks()); - local_parameterizations.reserve(residual_block->NumParameterBlocks()); + manifolds.reserve(residual_block->NumParameterBlocks()); for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; parameter_blocks.push_back(parameter_block->mutable_user_state()); StringAppendF(&extra_info, "%p", parameter_block->mutable_user_state()); extra_info += (j < residual_block->NumParameterBlocks() - 1) ? ", " : "]"; - local_parameterizations.push_back(problem_impl->GetParameterization( - parameter_block->mutable_user_state())); + manifolds.push_back( + problem_impl->GetManifold(parameter_block->mutable_user_state())); } // Wrap the original CostFunction in a GradientCheckingCostFunction. CostFunction* gradient_checking_cost_function = new GradientCheckingCostFunction(residual_block->cost_function(), - &local_parameterizations, + &manifolds, numeric_diff_options, relative_precision, extra_info, diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.h b/extern/ceres/internal/ceres/gradient_checking_cost_function.h index ea6e9b31c8c..0caafafa8fa 100644 --- a/extern/ceres/internal/ceres/gradient_checking_cost_function.h +++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.h @@ -32,13 +32,15 @@ #ifndef CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ #define CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ +#include <memory> #include <mutex> #include <string> #include "ceres/cost_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" -#include "ceres/local_parameterization.h" +#include "ceres/manifold.h" namespace ceres { namespace internal { @@ -47,7 +49,7 @@ class ProblemImpl; // Callback that collects information about gradient checking errors, and // will abort the solve as soon as an error occurs. -class CERES_EXPORT_INTERNAL GradientCheckingIterationCallback +class CERES_NO_EXPORT GradientCheckingIterationCallback : public IterationCallback { public: GradientCheckingIterationCallback(); @@ -73,9 +75,10 @@ class CERES_EXPORT_INTERNAL GradientCheckingIterationCallback // with finite differences. This API is only intended for unit tests that intend // to check the functionality of the GradientCheckingCostFunction // implementation directly. -CERES_EXPORT_INTERNAL CostFunction* CreateGradientCheckingCostFunction( +CERES_NO_EXPORT std::unique_ptr<CostFunction> +CreateGradientCheckingCostFunction( const CostFunction* cost_function, - const std::vector<const LocalParameterization*>* local_parameterizations, + const std::vector<const Manifold*>* manifolds, double relative_step_size, double relative_precision, const std::string& extra_info, @@ -92,8 +95,6 @@ CERES_EXPORT_INTERNAL CostFunction* CreateGradientCheckingCostFunction( // iteration, the respective cost function will notify the // GradientCheckingIterationCallback. // -// The caller owns the returned ProblemImpl object. -// // Note: This is quite inefficient and is intended only for debugging. // // relative_step_size and relative_precision are parameters to control @@ -102,7 +103,7 @@ CERES_EXPORT_INTERNAL CostFunction* CreateGradientCheckingCostFunction( // jacobians obtained by numerically differentiating them. See the // documentation of 'numeric_derivative_relative_step_size' in solver.h for a // better explanation. -CERES_EXPORT_INTERNAL ProblemImpl* CreateGradientCheckingProblemImpl( +CERES_NO_EXPORT std::unique_ptr<ProblemImpl> CreateGradientCheckingProblemImpl( ProblemImpl* problem_impl, double relative_step_size, double relative_precision, @@ -111,4 +112,6 @@ CERES_EXPORT_INTERNAL ProblemImpl* CreateGradientCheckingProblemImpl( } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ diff --git a/extern/ceres/internal/ceres/gradient_problem.cc b/extern/ceres/internal/ceres/gradient_problem.cc index ba33fbc90f8..cdd472fe87f 100644 --- a/extern/ceres/internal/ceres/gradient_problem.cc +++ b/extern/ceres/internal/ceres/gradient_problem.cc @@ -30,49 +30,75 @@ #include "ceres/gradient_problem.h" +#include <memory> + #include "ceres/local_parameterization.h" +#include "ceres/manifold_adapter.h" #include "glog/logging.h" namespace ceres { GradientProblem::GradientProblem(FirstOrderFunction* function) : function_(function), - parameterization_( - new IdentityParameterization(function_->NumParameters())), - scratch_(new double[function_->NumParameters()]) {} + manifold_(std::make_unique<EuclideanManifold<DYNAMIC>>( + function_->NumParameters())), + scratch_(new double[function_->NumParameters()]) { + CHECK(function != nullptr); +} GradientProblem::GradientProblem(FirstOrderFunction* function, LocalParameterization* parameterization) : function_(function), parameterization_(parameterization), scratch_(new double[function_->NumParameters()]) { - CHECK_EQ(function_->NumParameters(), parameterization_->GlobalSize()); + CHECK(function != nullptr); + if (parameterization != nullptr) { + manifold_ = + std::make_unique<internal::ManifoldAdapter>(parameterization_.get()); + } else { + manifold_ = std::make_unique<EuclideanManifold<DYNAMIC>>( + function_->NumParameters()); + } + CHECK_EQ(function_->NumParameters(), manifold_->AmbientSize()); +} + +GradientProblem::GradientProblem(FirstOrderFunction* function, + Manifold* manifold) + : function_(function), scratch_(new double[function_->NumParameters()]) { + CHECK(function != nullptr); + if (manifold != nullptr) { + manifold_.reset(manifold); + } else { + manifold_ = std::make_unique<EuclideanManifold<DYNAMIC>>( + function_->NumParameters()); + } + CHECK_EQ(function_->NumParameters(), manifold_->AmbientSize()); } int GradientProblem::NumParameters() const { return function_->NumParameters(); } -int GradientProblem::NumLocalParameters() const { - return parameterization_->LocalSize(); +int GradientProblem::NumTangentParameters() const { + return manifold_->TangentSize(); } bool GradientProblem::Evaluate(const double* parameters, double* cost, double* gradient) const { - if (gradient == NULL) { - return function_->Evaluate(parameters, cost, NULL); + if (gradient == nullptr) { + return function_->Evaluate(parameters, cost, nullptr); } return (function_->Evaluate(parameters, cost, scratch_.get()) && - parameterization_->MultiplyByJacobian( + manifold_->RightMultiplyByPlusJacobian( parameters, 1, scratch_.get(), gradient)); } bool GradientProblem::Plus(const double* x, const double* delta, double* x_plus_delta) const { - return parameterization_->Plus(x, delta, x_plus_delta); + return manifold_->Plus(x, delta, x_plus_delta); } } // namespace ceres diff --git a/extern/ceres/internal/ceres/gradient_problem_evaluator.h b/extern/ceres/internal/ceres/gradient_problem_evaluator.h index d224dbed0ae..efbb257ec75 100644 --- a/extern/ceres/internal/ceres/gradient_problem_evaluator.h +++ b/extern/ceres/internal/ceres/gradient_problem_evaluator.h @@ -32,30 +32,34 @@ #define CERES_INTERNAL_GRADIENT_PROBLEM_EVALUATOR_H_ #include <map> +#include <memory> #include <string> #include "ceres/evaluator.h" #include "ceres/execution_summary.h" #include "ceres/gradient_problem.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" +#include "ceres/sparse_matrix.h" #include "ceres/wall_time.h" namespace ceres { namespace internal { -class GradientProblemEvaluator : public Evaluator { +class CERES_NO_EXPORT GradientProblemEvaluator final : public Evaluator { public: explicit GradientProblemEvaluator(const GradientProblem& problem) : problem_(problem) {} - virtual ~GradientProblemEvaluator() {} - SparseMatrix* CreateJacobian() const final { return nullptr; } + + std::unique_ptr<SparseMatrix> CreateJacobian() const final { return nullptr; } + bool Evaluate(const EvaluateOptions& evaluate_options, const double* state, double* cost, double* residuals, double* gradient, SparseMatrix* jacobian) final { - CHECK(jacobian == NULL); + CHECK(jacobian == nullptr); ScopedExecutionTimer total_timer("Evaluator::Total", &execution_summary_); // The reason we use Residual and Jacobian here even when we are // only computing the cost and gradient has to do with the fact @@ -65,7 +69,7 @@ class GradientProblemEvaluator : public Evaluator { // to be consistent across the code base for the time accounting // to work. ScopedExecutionTimer call_type_timer( - gradient == NULL ? "Evaluator::Residual" : "Evaluator::Jacobian", + gradient == nullptr ? "Evaluator::Residual" : "Evaluator::Jacobian", &execution_summary_); return problem_.Evaluate(state, cost, gradient); } @@ -96,4 +100,6 @@ class GradientProblemEvaluator : public Evaluator { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_GRADIENT_PROBLEM_EVALUATOR_H_ diff --git a/extern/ceres/internal/ceres/gradient_problem_solver.cc b/extern/ceres/internal/ceres/gradient_problem_solver.cc index b72fad91542..9382556d292 100644 --- a/extern/ceres/internal/ceres/gradient_problem_solver.cc +++ b/extern/ceres/internal/ceres/gradient_problem_solver.cc @@ -36,7 +36,7 @@ #include "ceres/gradient_problem.h" #include "ceres/gradient_problem_evaluator.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/map_util.h" #include "ceres/minimizer.h" #include "ceres/solver.h" @@ -92,7 +92,7 @@ bool GradientProblemSolver::Options::IsValid(std::string* error) const { return solver_options.IsValid(error); } -GradientProblemSolver::~GradientProblemSolver() {} +GradientProblemSolver::~GradientProblemSolver() = default; void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options, const GradientProblem& problem, @@ -135,21 +135,22 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options, // now. Minimizer::Options minimizer_options = Minimizer::Options(GradientProblemSolverOptionsToSolverOptions(options)); - minimizer_options.evaluator.reset(new GradientProblemEvaluator(problem)); + minimizer_options.evaluator = + std::make_unique<GradientProblemEvaluator>(problem); std::unique_ptr<IterationCallback> logging_callback; if (options.logging_type != SILENT) { - logging_callback.reset( - new LoggingCallback(LINE_SEARCH, options.minimizer_progress_to_stdout)); + logging_callback = std::make_unique<LoggingCallback>( + LINE_SEARCH, options.minimizer_progress_to_stdout); minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), logging_callback.get()); } std::unique_ptr<IterationCallback> state_updating_callback; if (options.update_state_every_iteration) { - state_updating_callback.reset( - new GradientProblemSolverStateUpdatingCallback( - problem.NumParameters(), solution.data(), parameters_ptr)); + state_updating_callback = + std::make_unique<GradientProblemSolverStateUpdatingCallback>( + problem.NumParameters(), solution.data(), parameters_ptr); minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), state_updating_callback.get()); } diff --git a/extern/ceres/internal/ceres/graph.h b/extern/ceres/internal/ceres/graph.h index 9b26158753f..6a6f8f01c00 100644 --- a/extern/ceres/internal/ceres/graph.h +++ b/extern/ceres/internal/ceres/graph.h @@ -36,6 +36,7 @@ #include <unordered_set> #include <utility> +#include "ceres/internal/export.h" #include "ceres/map_util.h" #include "ceres/pair_hash.h" #include "ceres/types.h" @@ -47,10 +48,8 @@ namespace internal { // A unweighted undirected graph templated over the vertex ids. Vertex // should be hashable. template <typename Vertex> -class Graph { +class CERES_NO_EXPORT Graph { public: - Graph() {} - // Add a vertex. void AddVertex(const Vertex& vertex) { if (vertices_.insert(vertex).second) { @@ -106,8 +105,6 @@ class Graph { template <typename Vertex> class WeightedGraph { public: - WeightedGraph() {} - // Add a weighted vertex. If the vertex already exists in the graph, // its weight is set to the new weight. void AddVertex(const Vertex& vertex, double weight) { diff --git a/extern/ceres/internal/ceres/graph_algorithms.h b/extern/ceres/internal/ceres/graph_algorithms.h index 7d63b337f68..5299f80d963 100644 --- a/extern/ceres/internal/ceres/graph_algorithms.h +++ b/extern/ceres/internal/ceres/graph_algorithms.h @@ -34,12 +34,14 @@ #define CERES_INTERNAL_GRAPH_ALGORITHMS_H_ #include <algorithm> +#include <memory> #include <unordered_map> #include <unordered_set> #include <utility> #include <vector> #include "ceres/graph.h" +#include "ceres/internal/export.h" #include "ceres/wall_time.h" #include "glog/logging.h" @@ -49,7 +51,7 @@ namespace internal { // Compare two vertices of a graph by their degrees, if the degrees // are equal then order them by their ids. template <typename Vertex> -class VertexTotalOrdering { +class CERES_NO_EXPORT VertexTotalOrdering { public: explicit VertexTotalOrdering(const Graph<Vertex>& graph) : graph_(graph) {} @@ -257,11 +259,11 @@ Vertex FindConnectedComponent(const Vertex& vertex, // spanning forest, or a collection of linear paths that span the // graph G. template <typename Vertex> -WeightedGraph<Vertex>* Degree2MaximumSpanningForest( +std::unique_ptr<WeightedGraph<Vertex>> Degree2MaximumSpanningForest( const WeightedGraph<Vertex>& graph) { // Array of edges sorted in decreasing order of their weights. std::vector<std::pair<double, std::pair<Vertex, Vertex>>> weighted_edges; - WeightedGraph<Vertex>* forest = new WeightedGraph<Vertex>(); + auto forest = std::make_unique<WeightedGraph<Vertex>>(); // Disjoint-set to keep track of the connected components in the // maximum spanning tree. diff --git a/extern/ceres/internal/ceres/implicit_schur_complement.cc b/extern/ceres/internal/ceres/implicit_schur_complement.cc index f2196d4ef9c..677d767fa93 100644 --- a/extern/ceres/internal/ceres/implicit_schur_complement.cc +++ b/extern/ceres/internal/ceres/implicit_schur_complement.cc @@ -43,17 +43,15 @@ namespace internal { ImplicitSchurComplement::ImplicitSchurComplement( const LinearSolver::Options& options) - : options_(options), D_(NULL), b_(NULL) {} - -ImplicitSchurComplement::~ImplicitSchurComplement() {} + : options_(options), D_(nullptr), b_(nullptr) {} void ImplicitSchurComplement::Init(const BlockSparseMatrix& A, const double* D, const double* b) { // Since initialization is reasonably heavy, perhaps we can save on // constructing a new object everytime. - if (A_ == NULL) { - A_.reset(PartitionedMatrixViewBase::Create(options_, A)); + if (A_ == nullptr) { + A_ = PartitionedMatrixViewBase::Create(options_, A); } D_ = D; @@ -61,10 +59,10 @@ void ImplicitSchurComplement::Init(const BlockSparseMatrix& A, // Initialize temporary storage and compute the block diagonals of // E'E and F'E. - if (block_diagonal_EtE_inverse_ == NULL) { - block_diagonal_EtE_inverse_.reset(A_->CreateBlockDiagonalEtE()); + if (block_diagonal_EtE_inverse_ == nullptr) { + block_diagonal_EtE_inverse_ = A_->CreateBlockDiagonalEtE(); if (options_.preconditioner_type == JACOBI) { - block_diagonal_FtF_inverse_.reset(A_->CreateBlockDiagonalFtF()); + block_diagonal_FtF_inverse_ = A_->CreateBlockDiagonalFtF(); } rhs_.resize(A_->num_cols_f()); rhs_.setZero(); @@ -84,7 +82,7 @@ void ImplicitSchurComplement::Init(const BlockSparseMatrix& A, // the block diagonals and invert them. AddDiagonalAndInvert(D_, block_diagonal_EtE_inverse_.get()); if (options_.preconditioner_type == JACOBI) { - AddDiagonalAndInvert((D_ == NULL) ? NULL : D_ + A_->num_cols_e(), + AddDiagonalAndInvert((D_ == nullptr) ? nullptr : D_ + A_->num_cols_e(), block_diagonal_FtF_inverse_.get()); } @@ -118,7 +116,7 @@ void ImplicitSchurComplement::RightMultiply(const double* x, double* y) const { A_->RightMultiplyE(tmp_e_cols_2_.data(), tmp_rows_.data()); // y5 = D * x - if (D_ != NULL) { + if (D_ != nullptr) { ConstVectorRef Dref(D_ + A_->num_cols_e(), num_cols()); VectorRef(y, num_cols()) = (Dref.array().square() * ConstVectorRef(x, num_cols()).array()) @@ -138,15 +136,15 @@ void ImplicitSchurComplement::AddDiagonalAndInvert( const double* D, BlockSparseMatrix* block_diagonal) { const CompressedRowBlockStructure* block_diagonal_structure = block_diagonal->block_structure(); - for (int r = 0; r < block_diagonal_structure->rows.size(); ++r) { - const int row_block_pos = block_diagonal_structure->rows[r].block.position; - const int row_block_size = block_diagonal_structure->rows[r].block.size; - const Cell& cell = block_diagonal_structure->rows[r].cells[0]; + for (const auto& row : block_diagonal_structure->rows) { + const int row_block_pos = row.block.position; + const int row_block_size = row.block.size; + const Cell& cell = row.cells[0]; MatrixRef m(block_diagonal->mutable_values() + cell.position, row_block_size, row_block_size); - if (D != NULL) { + if (D != nullptr) { ConstVectorRef d(D + row_block_pos, row_block_size); m += d.array().square().matrix().asDiagonal(); } diff --git a/extern/ceres/internal/ceres/implicit_schur_complement.h b/extern/ceres/internal/ceres/implicit_schur_complement.h index e83892af017..598d48411aa 100644 --- a/extern/ceres/internal/ceres/implicit_schur_complement.h +++ b/extern/ceres/internal/ceres/implicit_schur_complement.h @@ -36,8 +36,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" #include "ceres/linear_solver.h" #include "ceres/partitioned_matrix_view.h" @@ -88,7 +89,7 @@ class BlockSparseMatrix; // RightMultiply (and the LeftMultiply) methods are not thread safe as // they depend on mutable arrays used for the temporaries needed to // compute the product y += Sx; -class CERES_EXPORT_INTERNAL ImplicitSchurComplement : public LinearOperator { +class CERES_NO_EXPORT ImplicitSchurComplement final : public LinearOperator { public: // num_eliminate_blocks is the number of E blocks in the matrix // A. @@ -100,7 +101,6 @@ class CERES_EXPORT_INTERNAL ImplicitSchurComplement : public LinearOperator { // TODO(sameeragarwal): Get rid of the two bools below and replace // them with enums. explicit ImplicitSchurComplement(const LinearSolver::Options& options); - virtual ~ImplicitSchurComplement(); // Initialize the Schur complement for a linear least squares // problem of the form @@ -166,4 +166,6 @@ class CERES_EXPORT_INTERNAL ImplicitSchurComplement : public LinearOperator { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_IMPLICIT_SCHUR_COMPLEMENT_H_ diff --git a/extern/ceres/internal/ceres/inner_product_computer.cc b/extern/ceres/internal/ceres/inner_product_computer.cc index ef38b7b3ad4..fbc43bfed8b 100644 --- a/extern/ceres/internal/ceres/inner_product_computer.cc +++ b/extern/ceres/internal/ceres/inner_product_computer.cc @@ -31,6 +31,7 @@ #include "ceres/inner_product_computer.h" #include <algorithm> +#include <memory> #include "ceres/small_blas.h" @@ -44,11 +45,12 @@ namespace internal { // or the lower triangular part of the product. // // num_nonzeros is the number of non-zeros in the result matrix. -CompressedRowSparseMatrix* InnerProductComputer::CreateResultMatrix( +std::unique_ptr<CompressedRowSparseMatrix> +InnerProductComputer::CreateResultMatrix( const CompressedRowSparseMatrix::StorageType storage_type, const int num_nonzeros) { - CompressedRowSparseMatrix* matrix = - new CompressedRowSparseMatrix(m_.num_cols(), m_.num_cols(), num_nonzeros); + auto matrix = std::make_unique<CompressedRowSparseMatrix>( + m_.num_cols(), m_.num_cols(), num_nonzeros); matrix->set_storage_type(storage_type); const CompressedRowBlockStructure* bs = m_.block_structure(); @@ -116,14 +118,14 @@ InnerProductComputer::InnerProductComputer(const BlockSparseMatrix& m, // // product_storage_type controls the form of the output matrix. It // can be LOWER_TRIANGULAR or UPPER_TRIANGULAR. -InnerProductComputer* InnerProductComputer::Create( +std::unique_ptr<InnerProductComputer> InnerProductComputer::Create( const BlockSparseMatrix& m, CompressedRowSparseMatrix::StorageType product_storage_type) { return InnerProductComputer::Create( m, 0, m.block_structure()->rows.size(), product_storage_type); } -InnerProductComputer* InnerProductComputer::Create( +std::unique_ptr<InnerProductComputer> InnerProductComputer::Create( const BlockSparseMatrix& m, const int start_row_block, const int end_row_block, @@ -132,8 +134,8 @@ InnerProductComputer* InnerProductComputer::Create( product_storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR); CHECK_GT(m.num_nonzeros(), 0) << "Congratulations, you found a bug in Ceres. Please report it."; - InnerProductComputer* inner_product_computer = - new InnerProductComputer(m, start_row_block, end_row_block); + std::unique_ptr<InnerProductComputer> inner_product_computer( + new InnerProductComputer(m, start_row_block, end_row_block)); inner_product_computer->Init(product_storage_type); return inner_product_computer; } @@ -165,8 +167,8 @@ void InnerProductComputer::Init( for (int c2 = c2_begin; c2 < c2_end; ++c2) { const Cell& cell2 = row.cells[c2]; - product_terms.push_back(InnerProductComputer::ProductTerm( - cell1.block_id, cell2.block_id, product_terms.size())); + product_terms.emplace_back( + cell1.block_id, cell2.block_id, product_terms.size()); } } } @@ -183,7 +185,7 @@ void InnerProductComputer::ComputeOffsetsAndCreateResultMatrix( std::vector<int> row_block_nnz; const int num_nonzeros = ComputeNonzeros(product_terms, &row_block_nnz); - result_.reset(CreateResultMatrix(product_storage_type, num_nonzeros)); + result_ = CreateResultMatrix(product_storage_type, num_nonzeros); // Populate the row non-zero counts in the result matrix. int* crsm_rows = result_->mutable_rows(); diff --git a/extern/ceres/internal/ceres/inner_product_computer.h b/extern/ceres/internal/ceres/inner_product_computer.h index 04ec1d18316..c6ed0b23e87 100644 --- a/extern/ceres/internal/ceres/inner_product_computer.h +++ b/extern/ceres/internal/ceres/inner_product_computer.h @@ -36,7 +36,8 @@ #include "ceres/block_sparse_matrix.h" #include "ceres/compressed_row_sparse_matrix.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -61,7 +62,7 @@ namespace internal { // This is not a problem as sparse linear algebra libraries can ignore // these entries with ease and the space used is minimal/linear in the // size of the matrices. -class CERES_EXPORT_INTERNAL InnerProductComputer { +class CERES_NO_EXPORT InnerProductComputer { public: // Factory // @@ -74,7 +75,7 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { // // The user must ensure that the matrix m is valid for the life time // of this object. - static InnerProductComputer* Create( + static std::unique_ptr<InnerProductComputer> Create( const BlockSparseMatrix& m, CompressedRowSparseMatrix::StorageType storage_type); @@ -83,7 +84,7 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { // // a = m(start_row_block : end_row_block, :); // result = a' * a; - static InnerProductComputer* Create( + static std::unique_ptr<InnerProductComputer> Create( const BlockSparseMatrix& m, int start_row_block, int end_row_block, @@ -127,7 +128,7 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { void Init(CompressedRowSparseMatrix::StorageType storage_type); - CompressedRowSparseMatrix* CreateResultMatrix( + std::unique_ptr<CompressedRowSparseMatrix> CreateResultMatrix( const CompressedRowSparseMatrix::StorageType storage_type, int num_nonzeros); @@ -155,4 +156,6 @@ class CERES_EXPORT_INTERNAL InnerProductComputer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_INNER_PRODUCT_COMPUTER_H_ diff --git a/extern/ceres/internal/ceres/is_close.h b/extern/ceres/internal/ceres/is_close.h index b781a4493ff..a1e4e2f6721 100644 --- a/extern/ceres/internal/ceres/is_close.h +++ b/extern/ceres/internal/ceres/is_close.h @@ -33,21 +33,24 @@ #ifndef CERES_INTERNAL_IS_CLOSE_H_ #define CERES_INTERNAL_IS_CLOSE_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { // Returns true if x and y have a relative (unsigned) difference less than // relative_precision and false otherwise. Stores the relative and absolute -// difference in relative/absolute_error if non-NULL. If one of the two values -// is exactly zero, the absolute difference will be compared, and relative_error -// will be set to the absolute difference. -CERES_EXPORT_INTERNAL bool IsClose(double x, - double y, - double relative_precision, - double* relative_error, - double* absolute_error); +// difference in relative/absolute_error if non-nullptr. If one of the two +// values is exactly zero, the absolute difference will be compared, and +// relative_error will be set to the absolute difference. +CERES_NO_EXPORT bool IsClose(double x, + double y, + double relative_precision, + double* relative_error, + double* absolute_error); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_IS_CLOSE_H_ diff --git a/extern/ceres/internal/ceres/iteration_callback.cc b/extern/ceres/internal/ceres/iteration_callback.cc new file mode 100644 index 00000000000..804811d2807 --- /dev/null +++ b/extern/ceres/internal/ceres/iteration_callback.cc @@ -0,0 +1,37 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2019 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/iteration_callback.h" + +namespace ceres { + +IterationCallback::~IterationCallback() = default; + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/iterative_refiner.cc b/extern/ceres/internal/ceres/iterative_refiner.cc index 5f0bfdd250d..18154690597 100644 --- a/extern/ceres/internal/ceres/iterative_refiner.cc +++ b/extern/ceres/internal/ceres/iterative_refiner.cc @@ -42,7 +42,7 @@ namespace internal { IterativeRefiner::IterativeRefiner(const int max_num_iterations) : max_num_iterations_(max_num_iterations) {} -IterativeRefiner::~IterativeRefiner() {} +IterativeRefiner::~IterativeRefiner() = default; void IterativeRefiner::Allocate(int num_cols) { residual_.resize(num_cols); diff --git a/extern/ceres/internal/ceres/iterative_refiner.h b/extern/ceres/internal/ceres/iterative_refiner.h index 08f8d6762cf..837af178ab4 100644 --- a/extern/ceres/internal/ceres/iterative_refiner.h +++ b/extern/ceres/internal/ceres/iterative_refiner.h @@ -33,10 +33,11 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -57,11 +58,11 @@ class SparseMatrix; // Definite linear systems. // // The above iterative loop is run until max_num_iterations is reached. -class CERES_EXPORT_INTERNAL IterativeRefiner { +class CERES_NO_EXPORT IterativeRefiner { public: // max_num_iterations is the number of refinement iterations to // perform. - IterativeRefiner(int max_num_iterations); + explicit IterativeRefiner(int max_num_iterations); // Needed for mocking. virtual ~IterativeRefiner(); diff --git a/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc b/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc index 143df5e5814..bc22d68bc55 100644 --- a/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc +++ b/extern/ceres/internal/ceres/iterative_schur_complement_solver.cc @@ -32,6 +32,7 @@ #include <algorithm> #include <cstring> +#include <utility> #include <vector> #include "Eigen/Dense" @@ -54,10 +55,10 @@ namespace ceres { namespace internal { IterativeSchurComplementSolver::IterativeSchurComplementSolver( - const LinearSolver::Options& options) - : options_(options) {} + LinearSolver::Options options) + : options_(std::move(options)) {} -IterativeSchurComplementSolver::~IterativeSchurComplementSolver() {} +IterativeSchurComplementSolver::~IterativeSchurComplementSolver() = default; LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( BlockSparseMatrix* A, @@ -69,13 +70,13 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( CHECK(A->block_structure() != nullptr); const int num_eliminate_blocks = options_.elimination_groups[0]; // Initialize a ImplicitSchurComplement object. - if (schur_complement_ == NULL) { + if (schur_complement_ == nullptr) { DetectStructure(*(A->block_structure()), num_eliminate_blocks, &options_.row_block_size, &options_.e_block_size, &options_.f_block_size); - schur_complement_.reset(new ImplicitSchurComplement(options_)); + schur_complement_ = std::make_unique<ImplicitSchurComplement>(options_); } schur_complement_->Init(*A, per_solve_options.D, b); @@ -86,7 +87,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( LinearSolver::Summary summary; summary.num_iterations = 0; summary.termination_type = LINEAR_SOLVER_SUCCESS; - schur_complement_->BackSubstitute(NULL, x); + schur_complement_->BackSubstitute(nullptr, x); return summary; } @@ -104,7 +105,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( cg_per_solve_options.q_tolerance = per_solve_options.q_tolerance; CreatePreconditioner(A); - if (preconditioner_.get() != NULL) { + if (preconditioner_.get() != nullptr) { if (!preconditioner_->Update(*A, per_solve_options.D)) { LinearSolver::Summary summary; summary.num_iterations = 0; @@ -134,7 +135,7 @@ LinearSolver::Summary IterativeSchurComplementSolver::SolveImpl( void IterativeSchurComplementSolver::CreatePreconditioner( BlockSparseMatrix* A) { if (options_.preconditioner_type == IDENTITY || - preconditioner_.get() != NULL) { + preconditioner_.get() != nullptr) { return; } @@ -149,22 +150,22 @@ void IterativeSchurComplementSolver::CreatePreconditioner( preconditioner_options.e_block_size = options_.e_block_size; preconditioner_options.f_block_size = options_.f_block_size; preconditioner_options.elimination_groups = options_.elimination_groups; - CHECK(options_.context != NULL); + CHECK(options_.context != nullptr); preconditioner_options.context = options_.context; switch (options_.preconditioner_type) { case JACOBI: - preconditioner_.reset(new SparseMatrixPreconditionerWrapper( - schur_complement_->block_diagonal_FtF_inverse())); + preconditioner_ = std::make_unique<SparseMatrixPreconditionerWrapper>( + schur_complement_->block_diagonal_FtF_inverse()); break; case SCHUR_JACOBI: - preconditioner_.reset(new SchurJacobiPreconditioner( - *A->block_structure(), preconditioner_options)); + preconditioner_ = std::make_unique<SchurJacobiPreconditioner>( + *A->block_structure(), preconditioner_options); break; case CLUSTER_JACOBI: case CLUSTER_TRIDIAGONAL: - preconditioner_.reset(new VisibilityBasedPreconditioner( - *A->block_structure(), preconditioner_options)); + preconditioner_ = std::make_unique<VisibilityBasedPreconditioner>( + *A->block_structure(), preconditioner_options); break; default: LOG(FATAL) << "Unknown Preconditioner Type"; diff --git a/extern/ceres/internal/ceres/iterative_schur_complement_solver.h b/extern/ceres/internal/ceres/iterative_schur_complement_solver.h index 37606b32d3a..50f469484f8 100644 --- a/extern/ceres/internal/ceres/iterative_schur_complement_solver.h +++ b/extern/ceres/internal/ceres/iterative_schur_complement_solver.h @@ -33,8 +33,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/types.h" @@ -69,15 +70,15 @@ class Preconditioner; // a proof of this fact and others related to this solver please see // the section on Domain Decomposition Methods in Saad's book // "Iterative Methods for Sparse Linear Systems". -class CERES_EXPORT_INTERNAL IterativeSchurComplementSolver +class CERES_NO_EXPORT IterativeSchurComplementSolver final : public BlockSparseMatrixSolver { public: - explicit IterativeSchurComplementSolver(const LinearSolver::Options& options); + explicit IterativeSchurComplementSolver(LinearSolver::Options options); IterativeSchurComplementSolver(const IterativeSchurComplementSolver&) = delete; void operator=(const IterativeSchurComplementSolver&) = delete; - virtual ~IterativeSchurComplementSolver(); + ~IterativeSchurComplementSolver() override; private: LinearSolver::Summary SolveImpl(BlockSparseMatrix* A, @@ -96,4 +97,6 @@ class CERES_EXPORT_INTERNAL IterativeSchurComplementSolver } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_ITERATIVE_SCHUR_COMPLEMENT_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/lapack.cc b/extern/ceres/internal/ceres/lapack.cc deleted file mode 100644 index a159ec70696..00000000000 --- a/extern/ceres/internal/ceres/lapack.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) - -#include "ceres/lapack.h" - -#include "ceres/internal/port.h" -#include "ceres/linear_solver.h" -#include "glog/logging.h" - -#ifndef CERES_NO_LAPACK -// C interface to the LAPACK Cholesky factorization and triangular solve. -extern "C" void dpotrf_(char* uplo, int* n, double* a, int* lda, int* info); - -extern "C" void dpotrs_(char* uplo, - int* n, - int* nrhs, - double* a, - int* lda, - double* b, - int* ldb, - int* info); - -extern "C" void dgels_(char* uplo, - int* m, - int* n, - int* nrhs, - double* a, - int* lda, - double* b, - int* ldb, - double* work, - int* lwork, - int* info); -#endif - -namespace ceres { -namespace internal { - -LinearSolverTerminationType LAPACK::SolveInPlaceUsingCholesky( - int num_rows, - const double* in_lhs, - double* rhs_and_solution, - std::string* message) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a BLAS library."; - return LINEAR_SOLVER_FATAL_ERROR; -#else - char uplo = 'L'; - int n = num_rows; - int info = 0; - int nrhs = 1; - double* lhs = const_cast<double*>(in_lhs); - - dpotrf_(&uplo, &n, lhs, &n, &info); - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dpotrf fatal error." - << "Argument: " << -info << " is invalid."; - return LINEAR_SOLVER_FATAL_ERROR; - } - - if (info > 0) { - *message = StringPrintf( - "LAPACK::dpotrf numerical failure. " - "The leading minor of order %d is not positive definite.", - info); - return LINEAR_SOLVER_FAILURE; - } - - dpotrs_(&uplo, &n, &nrhs, lhs, &n, rhs_and_solution, &n, &info); - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dpotrs fatal error." - << "Argument: " << -info << " is invalid."; - return LINEAR_SOLVER_FATAL_ERROR; - } - - *message = "Success"; - return LINEAR_SOLVER_SUCCESS; -#endif -} - -int LAPACK::EstimateWorkSizeForQR(int num_rows, int num_cols) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a LAPACK library."; - return -1; -#else - char trans = 'N'; - int nrhs = 1; - int lwork = -1; - double work; - int info = 0; - dgels_(&trans, - &num_rows, - &num_cols, - &nrhs, - NULL, - &num_rows, - NULL, - &num_rows, - &work, - &lwork, - &info); - - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dgels fatal error." - << "Argument: " << -info << " is invalid."; - } - return static_cast<int>(work); -#endif -} - -LinearSolverTerminationType LAPACK::SolveInPlaceUsingQR( - int num_rows, - int num_cols, - const double* in_lhs, - int work_size, - double* work, - double* rhs_and_solution, - std::string* message) { -#ifdef CERES_NO_LAPACK - LOG(FATAL) << "Ceres was built without a LAPACK library."; - return LINEAR_SOLVER_FATAL_ERROR; -#else - char trans = 'N'; - int m = num_rows; - int n = num_cols; - int nrhs = 1; - int lda = num_rows; - int ldb = num_rows; - int info = 0; - double* lhs = const_cast<double*>(in_lhs); - - dgels_(&trans, - &m, - &n, - &nrhs, - lhs, - &lda, - rhs_and_solution, - &ldb, - work, - &work_size, - &info); - - if (info < 0) { - LOG(FATAL) << "Congratulations, you found a bug in Ceres." - << "Please report it." - << "LAPACK::dgels fatal error." - << "Argument: " << -info << " is invalid."; - } - - *message = "Success."; - return LINEAR_SOLVER_SUCCESS; -#endif -} - -} // namespace internal -} // namespace ceres diff --git a/extern/ceres/internal/ceres/lapack.h b/extern/ceres/internal/ceres/lapack.h deleted file mode 100644 index 5c5bf8bf8b8..00000000000 --- a/extern/ceres/internal/ceres/lapack.h +++ /dev/null @@ -1,101 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) - -#ifndef CERES_INTERNAL_LAPACK_H_ -#define CERES_INTERNAL_LAPACK_H_ - -#include <string> - -#include "ceres/internal/port.h" -#include "ceres/linear_solver.h" - -namespace ceres { -namespace internal { - -class LAPACK { - public: - // Solve - // - // lhs * solution = rhs - // - // using a Cholesky factorization. Here - // lhs is a symmetric positive definite matrix. It is assumed to be - // column major and only the lower triangular part of the matrix is - // referenced. - // - // This function uses the LAPACK dpotrf and dpotrs routines. - // - // The return value and the message string together describe whether - // the solver terminated successfully or not and if so, what was the - // reason for failure. - static LinearSolverTerminationType SolveInPlaceUsingCholesky( - int num_rows, - const double* lhs, - double* rhs_and_solution, - std::string* message); - - // The SolveUsingQR function requires a buffer for its temporary - // computation. This function given the size of the lhs matrix will - // return the size of the buffer needed. - static int EstimateWorkSizeForQR(int num_rows, int num_cols); - - // Solve - // - // lhs * solution = rhs - // - // using a dense QR factorization. lhs is an arbitrary (possibly - // rectangular) matrix with full column rank. - // - // work is an array of size work_size that this routine uses for its - // temporary storage. The optimal size of this array can be obtained - // by calling EstimateWorkSizeForQR. - // - // When calling, rhs_and_solution contains the rhs, and upon return - // the first num_col entries are the solution. - // - // This function uses the LAPACK dgels routine. - // - // The return value and the message string together describe whether - // the solver terminated successfully or not and if so, what was the - // reason for failure. - static LinearSolverTerminationType SolveInPlaceUsingQR( - int num_rows, - int num_cols, - const double* lhs, - int work_size, - double* work, - double* rhs_and_solution, - std::string* message); -}; - -} // namespace internal -} // namespace ceres - -#endif // CERES_INTERNAL_LAPACK_H_ diff --git a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc index cb0e9371b75..2445f5bb99a 100644 --- a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc +++ b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.cc @@ -61,7 +61,7 @@ LevenbergMarquardtStrategy::LevenbergMarquardtStrategy( CHECK_GT(max_radius_, 0.0); } -LevenbergMarquardtStrategy::~LevenbergMarquardtStrategy() {} +LevenbergMarquardtStrategy::~LevenbergMarquardtStrategy() = default; TrustRegionStrategy::Summary LevenbergMarquardtStrategy::ComputeStep( const TrustRegionStrategy::PerSolveOptions& per_solve_options, diff --git a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h index 12cd463c152..4383a493cde 100644 --- a/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h +++ b/extern/ceres/internal/ceres/levenberg_marquardt_strategy.h @@ -31,8 +31,9 @@ #ifndef CERES_INTERNAL_LEVENBERG_MARQUARDT_STRATEGY_H_ #define CERES_INTERNAL_LEVENBERG_MARQUARDT_STRATEGY_H_ +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/trust_region_strategy.h" namespace ceres { @@ -43,12 +44,12 @@ namespace internal { // K. Madsen, H.B. Nielsen and O. Tingleff. Available to download from // // http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf -class CERES_EXPORT_INTERNAL LevenbergMarquardtStrategy +class CERES_NO_EXPORT LevenbergMarquardtStrategy final : public TrustRegionStrategy { public: explicit LevenbergMarquardtStrategy( const TrustRegionStrategy::Options& options); - virtual ~LevenbergMarquardtStrategy(); + ~LevenbergMarquardtStrategy() override; // TrustRegionStrategy interface TrustRegionStrategy::Summary ComputeStep( @@ -86,4 +87,6 @@ class CERES_EXPORT_INTERNAL LevenbergMarquardtStrategy } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LEVENBERG_MARQUARDT_STRATEGY_H_ diff --git a/extern/ceres/internal/ceres/line_search.cc b/extern/ceres/internal/ceres/line_search.cc index 7e871a20a53..7e7d97f6d93 100644 --- a/extern/ceres/internal/ceres/line_search.cc +++ b/extern/ceres/internal/ceres/line_search.cc @@ -34,6 +34,7 @@ #include <cmath> #include <iomanip> #include <iostream> // NOLINT +#include <memory> #include "ceres/evaluator.h" #include "ceres/function_sample.h" @@ -65,27 +66,26 @@ ostream& operator<<(ostream& os, const FunctionSample& sample) { return os; } +LineSearch::~LineSearch() = default; + LineSearch::LineSearch(const LineSearch::Options& options) : options_(options) {} -LineSearch* LineSearch::Create(const LineSearchType line_search_type, - const LineSearch::Options& options, - string* error) { - LineSearch* line_search = NULL; +std::unique_ptr<LineSearch> LineSearch::Create( + const LineSearchType line_search_type, + const LineSearch::Options& options, + string* error) { switch (line_search_type) { case ceres::ARMIJO: - line_search = new ArmijoLineSearch(options); - break; + return std::make_unique<ArmijoLineSearch>(options); case ceres::WOLFE: - line_search = new WolfeLineSearch(options); - break; + return std::make_unique<WolfeLineSearch>(options); default: *error = string("Invalid line search algorithm type: ") + LineSearchTypeToString(line_search_type) + string(", unable to create line search."); - return NULL; } - return line_search; + return nullptr; } LineSearchFunction::LineSearchFunction(Evaluator* evaluator) @@ -119,13 +119,13 @@ void LineSearchFunction::Evaluate(const double x, } output->vector_x_is_valid = true; - double* gradient = NULL; + double* gradient = nullptr; if (evaluate_gradient) { output->vector_gradient.resize(direction_.rows(), 1); gradient = output->vector_gradient.data(); } const bool eval_status = evaluator_->Evaluate( - output->vector_x.data(), &(output->value), NULL, gradient, NULL); + output->vector_x.data(), &(output->value), nullptr, gradient, nullptr); if (!eval_status || !std::isfinite(output->value)) { return; @@ -249,12 +249,12 @@ double LineSearch::InterpolatingPolynomialMinimizingStepSize( if (interpolation_type == QUADRATIC) { // Two point interpolation using function values and the // gradient at the lower bound. - samples.push_back(FunctionSample(current.x, current.value)); + samples.emplace_back(current.x, current.value); if (previous.value_is_valid) { // Three point interpolation, using function values and the // gradient at the lower bound. - samples.push_back(FunctionSample(previous.x, previous.value)); + samples.emplace_back(previous.x, previous.value); } } else if (interpolation_type == CUBIC) { // Two point interpolation using the function values and the gradients. diff --git a/extern/ceres/internal/ceres/line_search.h b/extern/ceres/internal/ceres/line_search.h index 634c9717532..c2c744afe00 100644 --- a/extern/ceres/internal/ceres/line_search.h +++ b/extern/ceres/internal/ceres/line_search.h @@ -33,12 +33,13 @@ #ifndef CERES_INTERNAL_LINE_SEARCH_H_ #define CERES_INTERNAL_LINE_SEARCH_H_ +#include <memory> #include <string> #include <vector> #include "ceres/function_sample.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -57,11 +58,11 @@ class LineSearchFunction; // sufficient decrease condition. Depending on the particular // condition used, we get a variety of different line search // algorithms, e.g., Armijo, Wolfe etc. -class LineSearch { +class CERES_NO_EXPORT LineSearch { public: struct Summary; - struct Options { + struct CERES_NO_EXPORT Options { // Degree of the polynomial used to approximate the objective // function. LineSearchInterpolationType interpolation_type = CUBIC; @@ -161,11 +162,12 @@ class LineSearch { }; explicit LineSearch(const LineSearch::Options& options); - virtual ~LineSearch() {} + virtual ~LineSearch(); - static LineSearch* Create(const LineSearchType line_search_type, - const LineSearch::Options& options, - std::string* error); + static std::unique_ptr<LineSearch> Create( + const LineSearchType line_search_type, + const LineSearch::Options& options, + std::string* error); // Perform the line search. // @@ -208,7 +210,7 @@ class LineSearch { // In practice, this object provides access to the objective // function value and the directional derivative of the underlying // optimization problem along a specific search direction. -class LineSearchFunction { +class CERES_NO_EXPORT LineSearchFunction { public: explicit LineSearchFunction(Evaluator* evaluator); void Init(const Vector& position, const Vector& direction); @@ -257,10 +259,9 @@ class LineSearchFunction { // minFunc package by Mark Schmidt. // // For more details: http://www.di.ens.fr/~mschmidt/Software/minFunc.html -class ArmijoLineSearch : public LineSearch { +class CERES_NO_EXPORT ArmijoLineSearch final : public LineSearch { public: explicit ArmijoLineSearch(const LineSearch::Options& options); - virtual ~ArmijoLineSearch() {} private: void DoSearch(double step_size_estimate, @@ -276,10 +277,9 @@ class ArmijoLineSearch : public LineSearch { // // [1] Nocedal J., Wright S., Numerical Optimization, 2nd Ed., Springer, 1999. // [2] http://www.di.ens.fr/~mschmidt/Software/minFunc.html. -class WolfeLineSearch : public LineSearch { +class CERES_NO_EXPORT WolfeLineSearch final : public LineSearch { public: explicit WolfeLineSearch(const LineSearch::Options& options); - virtual ~WolfeLineSearch() {} // Returns true iff either a valid point, or valid bracket are found. bool BracketingPhase(const FunctionSample& initial_position, diff --git a/extern/ceres/internal/ceres/line_search_direction.cc b/extern/ceres/internal/ceres/line_search_direction.cc index 48e6c9812e9..98e335a8029 100644 --- a/extern/ceres/internal/ceres/line_search_direction.cc +++ b/extern/ceres/internal/ceres/line_search_direction.cc @@ -30,7 +30,10 @@ #include "ceres/line_search_direction.h" +#include <memory> + #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/line_search_minimizer.h" #include "ceres/low_rank_inverse_hessian.h" #include "glog/logging.h" @@ -38,18 +41,18 @@ namespace ceres { namespace internal { -class SteepestDescent : public LineSearchDirection { +class CERES_NO_EXPORT SteepestDescent final : public LineSearchDirection { public: - virtual ~SteepestDescent() {} bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { *search_direction = -current.gradient; return true; } }; -class NonlinearConjugateGradient : public LineSearchDirection { +class CERES_NO_EXPORT NonlinearConjugateGradient final + : public LineSearchDirection { public: NonlinearConjugateGradient(const NonlinearConjugateGradientType type, const double function_tolerance) @@ -57,7 +60,7 @@ class NonlinearConjugateGradient : public LineSearchDirection { bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { double beta = 0.0; Vector gradient_change; switch (type_) { @@ -95,7 +98,7 @@ class NonlinearConjugateGradient : public LineSearchDirection { const double function_tolerance_; }; -class LBFGS : public LineSearchDirection { +class CERES_NO_EXPORT LBFGS final : public LineSearchDirection { public: LBFGS(const int num_parameters, const int max_lbfgs_rank, @@ -105,11 +108,9 @@ class LBFGS : public LineSearchDirection { use_approximate_eigenvalue_bfgs_scaling), is_positive_definite_(true) {} - virtual ~LBFGS() {} - bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { CHECK(is_positive_definite_) << "Ceres bug: NextDirection() called on L-BFGS after inverse Hessian " << "approximation has become indefinite, please contact the " @@ -141,7 +142,7 @@ class LBFGS : public LineSearchDirection { bool is_positive_definite_; }; -class BFGS : public LineSearchDirection { +class CERES_NO_EXPORT BFGS final : public LineSearchDirection { public: BFGS(const int num_parameters, const bool use_approximate_eigenvalue_scaling) : num_parameters_(num_parameters), @@ -161,11 +162,9 @@ class BFGS : public LineSearchDirection { inverse_hessian_ = Matrix::Identity(num_parameters, num_parameters); } - virtual ~BFGS() {} - bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, - Vector* search_direction) { + Vector* search_direction) override { CHECK(is_positive_definite_) << "Ceres bug: NextDirection() called on BFGS after inverse Hessian " << "approximation has become indefinite, please contact the " @@ -338,32 +337,34 @@ class BFGS : public LineSearchDirection { bool is_positive_definite_; }; -LineSearchDirection* LineSearchDirection::Create( +LineSearchDirection::~LineSearchDirection() = default; + +std::unique_ptr<LineSearchDirection> LineSearchDirection::Create( const LineSearchDirection::Options& options) { if (options.type == STEEPEST_DESCENT) { - return new SteepestDescent; + return std::make_unique<SteepestDescent>(); } if (options.type == NONLINEAR_CONJUGATE_GRADIENT) { - return new NonlinearConjugateGradient( + return std::make_unique<NonlinearConjugateGradient>( options.nonlinear_conjugate_gradient_type, options.function_tolerance); } if (options.type == ceres::LBFGS) { - return new ceres::internal::LBFGS( + return std::make_unique<ceres::internal::LBFGS>( options.num_parameters, options.max_lbfgs_rank, options.use_approximate_eigenvalue_bfgs_scaling); } if (options.type == ceres::BFGS) { - return new ceres::internal::BFGS( + return std::make_unique<ceres::internal::BFGS>( options.num_parameters, options.use_approximate_eigenvalue_bfgs_scaling); } LOG(ERROR) << "Unknown line search direction type: " << options.type; - return NULL; + return nullptr; } } // namespace internal diff --git a/extern/ceres/internal/ceres/line_search_direction.h b/extern/ceres/internal/ceres/line_search_direction.h index 2fcf4729ca5..47b256d7133 100644 --- a/extern/ceres/internal/ceres/line_search_direction.h +++ b/extern/ceres/internal/ceres/line_search_direction.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,35 +31,31 @@ #ifndef CERES_INTERNAL_LINE_SEARCH_DIRECTION_H_ #define CERES_INTERNAL_LINE_SEARCH_DIRECTION_H_ +#include <memory> + #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/line_search_minimizer.h" #include "ceres/types.h" namespace ceres { namespace internal { -class LineSearchDirection { +class CERES_NO_EXPORT LineSearchDirection { public: struct Options { - Options() - : num_parameters(0), - type(LBFGS), - nonlinear_conjugate_gradient_type(FLETCHER_REEVES), - function_tolerance(1e-12), - max_lbfgs_rank(20), - use_approximate_eigenvalue_bfgs_scaling(true) {} - - int num_parameters; - LineSearchDirectionType type; - NonlinearConjugateGradientType nonlinear_conjugate_gradient_type; - double function_tolerance; - int max_lbfgs_rank; - bool use_approximate_eigenvalue_bfgs_scaling; + int num_parameters{0}; + LineSearchDirectionType type{LBFGS}; + NonlinearConjugateGradientType nonlinear_conjugate_gradient_type{ + FLETCHER_REEVES}; + double function_tolerance{1e-12}; + int max_lbfgs_rank{20}; + bool use_approximate_eigenvalue_bfgs_scaling{true}; }; - static LineSearchDirection* Create(const Options& options); + static std::unique_ptr<LineSearchDirection> Create(const Options& options); - virtual ~LineSearchDirection() {} + virtual ~LineSearchDirection(); virtual bool NextDirection(const LineSearchMinimizer::State& previous, const LineSearchMinimizer::State& current, Vector* search_direction) = 0; diff --git a/extern/ceres/internal/ceres/line_search_minimizer.cc b/extern/ceres/internal/ceres/line_search_minimizer.cc index ea1c5072a14..ad1e1852386 100644 --- a/extern/ceres/internal/ceres/line_search_minimizer.cc +++ b/extern/ceres/internal/ceres/line_search_minimizer.cc @@ -51,7 +51,7 @@ #include "ceres/array_utils.h" #include "ceres/evaluator.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/line_search.h" #include "ceres/line_search_direction.h" #include "ceres/stringprintf.h" @@ -171,8 +171,8 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, line_search_direction_options.max_lbfgs_rank = options.max_lbfgs_rank; line_search_direction_options.use_approximate_eigenvalue_bfgs_scaling = options.use_approximate_eigenvalue_bfgs_scaling; - std::unique_ptr<LineSearchDirection> line_search_direction( - LineSearchDirection::Create(line_search_direction_options)); + std::unique_ptr<LineSearchDirection> line_search_direction = + LineSearchDirection::Create(line_search_direction_options); LineSearchFunction line_search_function(evaluator); @@ -280,8 +280,8 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, << options.max_num_line_search_direction_restarts << " [max]."; } - line_search_direction.reset( - LineSearchDirection::Create(line_search_direction_options)); + line_search_direction = + LineSearchDirection::Create(line_search_direction_options); current_state.search_direction = -current_state.gradient; } diff --git a/extern/ceres/internal/ceres/line_search_minimizer.h b/extern/ceres/internal/ceres/line_search_minimizer.h index 79e8dc9e49a..9a0e994dcfc 100644 --- a/extern/ceres/internal/ceres/line_search_minimizer.h +++ b/extern/ceres/internal/ceres/line_search_minimizer.h @@ -32,6 +32,7 @@ #define CERES_INTERNAL_LINE_SEARCH_MINIMIZER_H_ #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/minimizer.h" #include "ceres/solver.h" #include "ceres/types.h" @@ -43,7 +44,7 @@ namespace internal { // Generic line search minimization algorithm. // // For example usage, see SolverImpl::Minimize. -class LineSearchMinimizer : public Minimizer { +class CERES_NO_EXPORT LineSearchMinimizer final : public Minimizer { public: struct State { State(int num_parameters, int num_effective_parameters) @@ -63,7 +64,6 @@ class LineSearchMinimizer : public Minimizer { double step_size; }; - ~LineSearchMinimizer() {} void Minimize(const Minimizer::Options& options, double* parameters, Solver::Summary* summary) final; diff --git a/extern/ceres/internal/ceres/line_search_preprocessor.cc b/extern/ceres/internal/ceres/line_search_preprocessor.cc index 6a69425e764..26b8d99a4c7 100644 --- a/extern/ceres/internal/ceres/line_search_preprocessor.cc +++ b/extern/ceres/internal/ceres/line_search_preprocessor.cc @@ -63,15 +63,13 @@ bool SetupEvaluator(PreprocessedProblem* pp) { pp->evaluator_options.context = pp->problem->context(); pp->evaluator_options.evaluation_callback = pp->reduced_program->mutable_evaluation_callback(); - pp->evaluator.reset(Evaluator::Create( - pp->evaluator_options, pp->reduced_program.get(), &pp->error)); - return (pp->evaluator.get() != NULL); + pp->evaluator = Evaluator::Create( + pp->evaluator_options, pp->reduced_program.get(), &pp->error); + return (pp->evaluator.get() != nullptr); } } // namespace -LineSearchPreprocessor::~LineSearchPreprocessor() {} - bool LineSearchPreprocessor::Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* pp) { @@ -85,10 +83,10 @@ bool LineSearchPreprocessor::Preprocess(const Solver::Options& options, return false; } - pp->reduced_program.reset(program->CreateReducedProgram( - &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error)); + pp->reduced_program = program->CreateReducedProgram( + &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error); - if (pp->reduced_program.get() == NULL) { + if (pp->reduced_program.get() == nullptr) { return false; } diff --git a/extern/ceres/internal/ceres/line_search_preprocessor.h b/extern/ceres/internal/ceres/line_search_preprocessor.h index bd426c7f2f6..27e9c2db9b9 100644 --- a/extern/ceres/internal/ceres/line_search_preprocessor.h +++ b/extern/ceres/internal/ceres/line_search_preprocessor.h @@ -31,15 +31,15 @@ #ifndef CERES_INTERNAL_LINE_SEARCH_PREPROCESSOR_H_ #define CERES_INTERNAL_LINE_SEARCH_PREPROCESSOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preprocessor.h" namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL LineSearchPreprocessor : public Preprocessor { +class CERES_NO_EXPORT LineSearchPreprocessor final : public Preprocessor { public: - virtual ~LineSearchPreprocessor(); bool Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* preprocessed_problem) final; @@ -48,4 +48,6 @@ class CERES_EXPORT_INTERNAL LineSearchPreprocessor : public Preprocessor { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LINE_SEARCH_PREPROCESSOR_H_ diff --git a/extern/ceres/internal/ceres/linear_least_squares_problems.cc b/extern/ceres/internal/ceres/linear_least_squares_problems.cc index 299051c5bcf..2d415af338f 100644 --- a/extern/ceres/internal/ceres/linear_least_squares_problems.cc +++ b/extern/ceres/internal/ceres/linear_least_squares_problems.cc @@ -49,7 +49,8 @@ namespace internal { using std::string; -LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromId(int id) { +std::unique_ptr<LinearLeastSquaresProblem> +CreateLinearLeastSquaresProblemFromId(int id) { switch (id) { case 0: return LinearLeastSquaresProblem0(); @@ -64,7 +65,7 @@ LinearLeastSquaresProblem* CreateLinearLeastSquaresProblemFromId(int id) { default: LOG(FATAL) << "Unknown problem id requested " << id; } - return NULL; + return nullptr; } /* @@ -85,15 +86,16 @@ D = [1 x_D = [1.78448275; 2.82327586;] */ -LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem0() { + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - TripletSparseMatrix* A = new TripletSparseMatrix(3, 2, 6); - problem->b.reset(new double[3]); - problem->D.reset(new double[2]); + auto A = std::make_unique<TripletSparseMatrix>(3, 2, 6); + problem->b = std::make_unique<double[]>(3); + problem->D = std::make_unique<double[]>(2); - problem->x.reset(new double[2]); - problem->x_D.reset(new double[2]); + problem->x = std::make_unique<double[]>(2); + problem->x_D = std::make_unique<double[]>(2); int* Ai = A->mutable_rows(); int* Aj = A->mutable_cols(); @@ -115,7 +117,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { Ax[4] = 6; Ax[5] = -10; A->set_num_nonzeros(6); - problem->A.reset(A); + problem->A = std::move(A); problem->b[0] = 8; problem->b[1] = 18; @@ -161,7 +163,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { S = [ 42.3419 -1.4000 -11.5806 -1.4000 2.6000 1.0000 - 11.5806 1.0000 31.1935] + -11.5806 1.0000 31.1935] r = [ 4.3032 5.4000 @@ -181,15 +183,16 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem0() { // BlockSparseMatrix version of this problem. // TripletSparseMatrix version. -LinearLeastSquaresProblem* LinearLeastSquaresProblem1() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem1() { int num_rows = 6; int num_cols = 5; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; - TripletSparseMatrix* A = - new TripletSparseMatrix(num_rows, num_cols, num_rows * num_cols); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); + auto A = std::make_unique<TripletSparseMatrix>( + num_rows, num_cols, num_rows * num_cols); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 2; int* rows = A->mutable_rows(); @@ -271,7 +274,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem1() { A->set_num_nonzeros(nnz); CHECK(A->IsValid()); - problem->A.reset(A); + problem->A = std::move(A); for (int i = 0; i < num_cols; ++i) { problem->D.get()[i] = 1; @@ -285,21 +288,23 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem1() { } // BlockSparseMatrix version -LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem2() { int num_rows = 6; int num_cols = 5; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 2; - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; - std::unique_ptr<double[]> values(new double[num_rows * num_cols]); + auto* bs = new CompressedRowBlockStructure; + std::unique_ptr<double[]> values = + std::make_unique<double[]>(num_rows * num_cols); for (int c = 0; c < num_cols; ++c) { - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 1; bs->cols.back().position = c; } @@ -311,12 +316,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 1; values[nnz++] = 2; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 0; - row.cells.push_back(Cell(0, 0)); - row.cells.push_back(Cell(2, 1)); + row.cells.emplace_back(0, 0); + row.cells.emplace_back(2, 1); } // Row 2 @@ -324,12 +329,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 3; values[nnz++] = 4; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 1; - row.cells.push_back(Cell(0, 2)); - row.cells.push_back(Cell(3, 3)); + row.cells.emplace_back(0, 2); + row.cells.emplace_back(3, 3); } // Row 3 @@ -337,12 +342,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 5; values[nnz++] = 6; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 2; - row.cells.push_back(Cell(1, 4)); - row.cells.push_back(Cell(4, 5)); + row.cells.emplace_back(1, 4); + row.cells.emplace_back(4, 5); } // Row 4 @@ -350,12 +355,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 7; values[nnz++] = 8; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 3; - row.cells.push_back(Cell(1, 6)); - row.cells.push_back(Cell(2, 7)); + row.cells.emplace_back(1, 6); + row.cells.emplace_back(2, 7); } // Row 5 @@ -363,12 +368,12 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 9; values[nnz++] = 1; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 4; - row.cells.push_back(Cell(1, 8)); - row.cells.push_back(Cell(2, 9)); + row.cells.emplace_back(1, 8); + row.cells.emplace_back(2, 9); } // Row 6 @@ -377,16 +382,16 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { values[nnz++] = 1; values[nnz++] = 1; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 5; - row.cells.push_back(Cell(2, 10)); - row.cells.push_back(Cell(3, 11)); - row.cells.push_back(Cell(4, 12)); + row.cells.emplace_back(2, 10); + row.cells.emplace_back(3, 11); + row.cells.emplace_back(4, 12); } - BlockSparseMatrix* A = new BlockSparseMatrix(bs); + auto A = std::make_unique<BlockSparseMatrix>(bs); memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values())); for (int i = 0; i < num_cols; ++i) { @@ -397,7 +402,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { problem->b.get()[i] = i; } - problem->A.reset(A); + problem->A = std::move(A); return problem; } @@ -418,21 +423,23 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem2() { 5] */ // BlockSparseMatrix version -LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem3() { int num_rows = 5; int num_cols = 2; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 2; - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; - std::unique_ptr<double[]> values(new double[num_rows * num_cols]); + auto* bs = new CompressedRowBlockStructure; + std::unique_ptr<double[]> values = + std::make_unique<double[]>(num_rows * num_cols); for (int c = 0; c < num_cols; ++c) { - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 1; bs->cols.back().position = c; } @@ -442,54 +449,54 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { // Row 1 { values[nnz++] = 1; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 0; - row.cells.push_back(Cell(0, 0)); + row.cells.emplace_back(0, 0); } // Row 2 { values[nnz++] = 3; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 1; - row.cells.push_back(Cell(0, 1)); + row.cells.emplace_back(0, 1); } // Row 3 { values[nnz++] = 5; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 2; - row.cells.push_back(Cell(1, 2)); + row.cells.emplace_back(1, 2); } // Row 4 { values[nnz++] = 7; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 3; - row.cells.push_back(Cell(1, 3)); + row.cells.emplace_back(1, 3); } // Row 5 { values[nnz++] = 9; - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 4; - row.cells.push_back(Cell(1, 4)); + row.cells.emplace_back(1, 4); } - BlockSparseMatrix* A = new BlockSparseMatrix(bs); + auto A = std::make_unique<BlockSparseMatrix>(bs); memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values())); for (int i = 0; i < num_cols; ++i) { @@ -500,7 +507,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { problem->b.get()[i] = i; } - problem->A.reset(A); + problem->A = std::move(A); return problem; } @@ -525,29 +532,31 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem3() { // // NOTE: This problem is too small and rank deficient to be solved without // the diagonal regularization. -LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem4() { int num_rows = 3; int num_cols = 7; - LinearLeastSquaresProblem* problem = new LinearLeastSquaresProblem; + std::unique_ptr<LinearLeastSquaresProblem> problem = + std::make_unique<LinearLeastSquaresProblem>(); - problem->b.reset(new double[num_rows]); - problem->D.reset(new double[num_cols]); + problem->b = std::make_unique<double[]>(num_rows); + problem->D = std::make_unique<double[]>(num_cols); problem->num_eliminate_blocks = 1; - CompressedRowBlockStructure* bs = new CompressedRowBlockStructure; - std::unique_ptr<double[]> values(new double[num_rows * num_cols]); + auto* bs = new CompressedRowBlockStructure; + std::unique_ptr<double[]> values = + std::make_unique<double[]>(num_rows * num_cols); // Column block structure - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 2; bs->cols.back().position = 0; - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 3; bs->cols.back().position = 2; - bs->cols.push_back(Block()); + bs->cols.emplace_back(); bs->cols.back().size = 2; bs->cols.back().position = 5; @@ -555,18 +564,18 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { // Row 1 & 2 { - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 2; row.block.position = 0; - row.cells.push_back(Cell(0, nnz)); + row.cells.emplace_back(0, nnz); values[nnz++] = 1; values[nnz++] = 2; values[nnz++] = 1; values[nnz++] = 4; - row.cells.push_back(Cell(2, nnz)); + row.cells.emplace_back(2, nnz); values[nnz++] = 1; values[nnz++] = 1; values[nnz++] = 5; @@ -575,22 +584,22 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { // Row 3 { - bs->rows.push_back(CompressedRow()); + bs->rows.emplace_back(); CompressedRow& row = bs->rows.back(); row.block.size = 1; row.block.position = 2; - row.cells.push_back(Cell(1, nnz)); + row.cells.emplace_back(1, nnz); values[nnz++] = 9; values[nnz++] = 0; values[nnz++] = 0; - row.cells.push_back(Cell(2, nnz)); + row.cells.emplace_back(2, nnz); values[nnz++] = 3; values[nnz++] = 1; } - BlockSparseMatrix* A = new BlockSparseMatrix(bs); + auto A = std::make_unique<BlockSparseMatrix>(bs); memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values())); for (int i = 0; i < num_cols; ++i) { @@ -601,7 +610,7 @@ LinearLeastSquaresProblem* LinearLeastSquaresProblem4() { problem->b.get()[i] = i; } - problem->A.reset(A); + problem->A = std::move(A); return problem; } @@ -616,15 +625,15 @@ bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A, A->ToDenseMatrix(&AA); LOG(INFO) << "A^T: \n" << AA.transpose(); - if (D != NULL) { + if (D != nullptr) { LOG(INFO) << "A's appended diagonal:\n" << ConstVectorRef(D, A->num_cols()); } - if (b != NULL) { + if (b != nullptr) { LOG(INFO) << "b: \n" << ConstVectorRef(b, A->num_rows()); } - if (x != NULL) { + if (x != nullptr) { LOG(INFO) << "x: \n" << ConstVectorRef(x, A->num_cols()); } return true; @@ -673,21 +682,21 @@ bool DumpLinearLeastSquaresProblemToTextFile(const string& filename_base, A->num_cols()); } - if (D != NULL) { + if (D != nullptr) { string filename = filename_base + "_D.txt"; WriteArrayToFileOrDie(filename, D, A->num_cols()); StringAppendF( &matlab_script, "lsqp.D = load('%s', '-ascii');\n", filename.c_str()); } - if (b != NULL) { + if (b != nullptr) { string filename = filename_base + "_b.txt"; WriteArrayToFileOrDie(filename, b, A->num_rows()); StringAppendF( &matlab_script, "lsqp.b = load('%s', '-ascii');\n", filename.c_str()); } - if (x != NULL) { + if (x != nullptr) { string filename = filename_base + "_x.txt"; WriteArrayToFileOrDie(filename, x, A->num_cols()); StringAppendF( diff --git a/extern/ceres/internal/ceres/linear_least_squares_problems.h b/extern/ceres/internal/ceres/linear_least_squares_problems.h index cddaa9fd4d7..a1f67eb306e 100644 --- a/extern/ceres/internal/ceres/linear_least_squares_problems.h +++ b/extern/ceres/internal/ceres/linear_least_squares_problems.h @@ -35,7 +35,8 @@ #include <string> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" namespace ceres { @@ -43,15 +44,15 @@ namespace internal { // Structure defining a linear least squares problem and if possible // ground truth solutions. To be used by various LinearSolver tests. -struct CERES_EXPORT_INTERNAL LinearLeastSquaresProblem { - LinearLeastSquaresProblem() : num_eliminate_blocks(0) {} +struct CERES_NO_EXPORT LinearLeastSquaresProblem { + LinearLeastSquaresProblem() = default; std::unique_ptr<SparseMatrix> A; std::unique_ptr<double[]> b; std::unique_ptr<double[]> D; // If using the schur eliminator then how many of the variable // blocks are e_type blocks. - int num_eliminate_blocks; + int num_eliminate_blocks{0}; // Solution to min_x |Ax - b|^2 std::unique_ptr<double[]> x; @@ -60,17 +61,23 @@ struct CERES_EXPORT_INTERNAL LinearLeastSquaresProblem { }; // Factories for linear least squares problem. -CERES_EXPORT_INTERNAL LinearLeastSquaresProblem* +CERES_NO_EXPORT std::unique_ptr<LinearLeastSquaresProblem> CreateLinearLeastSquaresProblemFromId(int id); -LinearLeastSquaresProblem* LinearLeastSquaresProblem0(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem1(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem2(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem3(); -LinearLeastSquaresProblem* LinearLeastSquaresProblem4(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem0(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem1(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem2(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem3(); +CERES_NO_EXPORT +std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem4(); // Write the linear least squares problem to disk. The exact format // depends on dump_format_type. +CERES_NO_EXPORT bool DumpLinearLeastSquaresProblem(const std::string& filename_base, DumpFormatType dump_format_type, const SparseMatrix* A, @@ -81,4 +88,6 @@ bool DumpLinearLeastSquaresProblem(const std::string& filename_base, } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LINEAR_LEAST_SQUARES_PROBLEMS_H_ diff --git a/extern/ceres/internal/ceres/linear_operator.cc b/extern/ceres/internal/ceres/linear_operator.cc index 548c724267e..88b7cc752d4 100644 --- a/extern/ceres/internal/ceres/linear_operator.cc +++ b/extern/ceres/internal/ceres/linear_operator.cc @@ -33,7 +33,7 @@ namespace ceres { namespace internal { -LinearOperator::~LinearOperator() {} +LinearOperator::~LinearOperator() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/linear_operator.h b/extern/ceres/internal/ceres/linear_operator.h index 9c59fc39c13..c9e6188e2e8 100644 --- a/extern/ceres/internal/ceres/linear_operator.h +++ b/extern/ceres/internal/ceres/linear_operator.h @@ -33,7 +33,7 @@ #ifndef CERES_INTERNAL_LINEAR_OPERATOR_H_ #define CERES_INTERNAL_LINEAR_OPERATOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/types.h" namespace ceres { @@ -41,7 +41,7 @@ namespace internal { // This is an abstract base class for linear operators. It supports // access to size information and left and right multiply operators. -class CERES_EXPORT_INTERNAL LinearOperator { +class CERES_NO_EXPORT LinearOperator { public: virtual ~LinearOperator(); diff --git a/extern/ceres/internal/ceres/linear_solver.cc b/extern/ceres/internal/ceres/linear_solver.cc index 6cae2488f07..fe324f81301 100644 --- a/extern/ceres/internal/ceres/linear_solver.cc +++ b/extern/ceres/internal/ceres/linear_solver.cc @@ -30,10 +30,13 @@ #include "ceres/linear_solver.h" +#include <memory> + #include "ceres/cgnr_solver.h" #include "ceres/dense_normal_cholesky_solver.h" #include "ceres/dense_qr_solver.h" #include "ceres/dynamic_sparse_normal_cholesky_solver.h" +#include "ceres/internal/config.h" #include "ceres/iterative_schur_complement_solver.h" #include "ceres/schur_complement_solver.h" #include "ceres/sparse_normal_cholesky_solver.h" @@ -43,7 +46,7 @@ namespace ceres { namespace internal { -LinearSolver::~LinearSolver() {} +LinearSolver::~LinearSolver() = default; LinearSolverType LinearSolver::LinearSolverForZeroEBlocks( LinearSolverType linear_solver_type) { @@ -69,50 +72,51 @@ LinearSolverType LinearSolver::LinearSolverForZeroEBlocks( return linear_solver_type; } -LinearSolver* LinearSolver::Create(const LinearSolver::Options& options) { - CHECK(options.context != NULL); +std::unique_ptr<LinearSolver> LinearSolver::Create( + const LinearSolver::Options& options) { + CHECK(options.context != nullptr); switch (options.type) { case CGNR: - return new CgnrSolver(options); + return std::make_unique<CgnrSolver>(options); case SPARSE_NORMAL_CHOLESKY: #if defined(CERES_NO_SPARSE) - return NULL; + return nullptr; #else if (options.dynamic_sparsity) { - return new DynamicSparseNormalCholeskySolver(options); + return std::make_unique<DynamicSparseNormalCholeskySolver>(options); } - return new SparseNormalCholeskySolver(options); + return std::make_unique<SparseNormalCholeskySolver>(options); #endif case SPARSE_SCHUR: #if defined(CERES_NO_SPARSE) - return NULL; + return nullptr; #else - return new SparseSchurComplementSolver(options); + return std::make_unique<SparseSchurComplementSolver>(options); #endif case DENSE_SCHUR: - return new DenseSchurComplementSolver(options); + return std::make_unique<DenseSchurComplementSolver>(options); case ITERATIVE_SCHUR: if (options.use_explicit_schur_complement) { - return new SparseSchurComplementSolver(options); + return std::make_unique<SparseSchurComplementSolver>(options); } else { - return new IterativeSchurComplementSolver(options); + return std::make_unique<IterativeSchurComplementSolver>(options); } case DENSE_QR: - return new DenseQRSolver(options); + return std::make_unique<DenseQRSolver>(options); case DENSE_NORMAL_CHOLESKY: - return new DenseNormalCholeskySolver(options); + return std::make_unique<DenseNormalCholeskySolver>(options); default: LOG(FATAL) << "Unknown linear solver type :" << options.type; - return NULL; // MSVC doesn't understand that LOG(FATAL) never returns. + return nullptr; // MSVC doesn't understand that LOG(FATAL) never returns. } } diff --git a/extern/ceres/internal/ceres/linear_solver.h b/extern/ceres/internal/ceres/linear_solver.h index 49c6527acc9..2f709c297e5 100644 --- a/extern/ceres/internal/ceres/linear_solver.h +++ b/extern/ceres/internal/ceres/linear_solver.h @@ -36,6 +36,7 @@ #include <cstddef> #include <map> +#include <memory> #include <string> #include <vector> @@ -45,7 +46,8 @@ #include "ceres/context_impl.h" #include "ceres/dense_sparse_matrix.h" #include "ceres/execution_summary.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" #include "glog/logging.h" @@ -101,7 +103,7 @@ class LinearOperator; // The Options struct configures the LinearSolver object for its // lifetime. The PerSolveOptions struct is used to specify options for // a particular Solve call. -class CERES_EXPORT_INTERNAL LinearSolver { +class CERES_NO_EXPORT LinearSolver { public: struct Options { LinearSolverType type = SPARSE_NORMAL_CHOLESKY; @@ -284,11 +286,11 @@ class CERES_EXPORT_INTERNAL LinearSolver { // issues. Further, this calls are not expected to be frequent or // performance sensitive. virtual std::map<std::string, CallStatistics> Statistics() const { - return std::map<std::string, CallStatistics>(); + return {}; } // Factory - static LinearSolver* Create(const Options& options); + static std::unique_ptr<LinearSolver> Create(const Options& options); }; // This templated subclass of LinearSolver serves as a base class for @@ -301,12 +303,11 @@ class CERES_EXPORT_INTERNAL LinearSolver { template <typename MatrixType> class TypedLinearSolver : public LinearSolver { public: - virtual ~TypedLinearSolver() {} - virtual LinearSolver::Summary Solve( + LinearSolver::Summary Solve( LinearOperator* A, const double* b, const LinearSolver::PerSolveOptions& per_solve_options, - double* x) { + double* x) override { ScopedExecutionTimer total_time("LinearSolver::Solve", &execution_summary_); CHECK(A != nullptr); CHECK(b != nullptr); @@ -314,7 +315,7 @@ class TypedLinearSolver : public LinearSolver { return SolveImpl(down_cast<MatrixType*>(A), b, per_solve_options, x); } - virtual std::map<std::string, CallStatistics> Statistics() const { + std::map<std::string, CallStatistics> Statistics() const override { return execution_summary_.statistics(); } @@ -340,4 +341,6 @@ typedef TypedLinearSolver<TripletSparseMatrix> TripletSparseMatrixSolver; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_LINEAR_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/local_parameterization.cc b/extern/ceres/internal/ceres/local_parameterization.cc index 62947f06fcc..db6f95a1984 100644 --- a/extern/ceres/internal/ceres/local_parameterization.cc +++ b/extern/ceres/internal/ceres/local_parameterization.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -43,7 +43,7 @@ namespace ceres { using std::vector; -LocalParameterization::~LocalParameterization() {} +LocalParameterization::~LocalParameterization() = default; bool LocalParameterization::MultiplyByJacobian(const double* x, const int num_rows, @@ -107,8 +107,8 @@ SubsetParameterization::SubsetParameterization( << "of the parameter block."; CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end()) << "The set of constant parameters cannot contain duplicates"; - for (int i = 0; i < constant_parameters.size(); ++i) { - constancy_mask_[constant_parameters[i]] = 1; + for (int parameter : constant_parameters) { + constancy_mask_[parameter] = 1; } } diff --git a/extern/ceres/internal/ceres/loss_function.cc b/extern/ceres/internal/ceres/loss_function.cc index 353f29a5bf5..3392b3b7f81 100644 --- a/extern/ceres/internal/ceres/loss_function.cc +++ b/extern/ceres/internal/ceres/loss_function.cc @@ -39,6 +39,8 @@ namespace ceres { +LossFunction::~LossFunction() = default; + void TrivialLoss::Evaluate(double s, double rho[3]) const { rho[0] = s; rho[1] = 1.0; @@ -161,7 +163,7 @@ void ComposedLoss::Evaluate(double s, double rho[3]) const { } void ScaledLoss::Evaluate(double s, double rho[3]) const { - if (rho_.get() == NULL) { + if (rho_.get() == nullptr) { rho[0] = a_ * s; rho[1] = a_; rho[2] = 0.0; diff --git a/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc b/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc index c73e5dbf88d..2fd1ac83f00 100644 --- a/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc +++ b/extern/ceres/internal/ceres/low_rank_inverse_hessian.cc @@ -127,9 +127,7 @@ void LowRankInverseHessian::RightMultiply(const double* x_ptr, const int num_corrections = indices_.size(); Vector alpha(num_corrections); - for (list<int>::const_reverse_iterator it = indices_.rbegin(); - it != indices_.rend(); - ++it) { + for (auto it = indices_.rbegin(); it != indices_.rend(); ++it) { const double alpha_i = delta_x_history_.col(*it).dot(search_direction) / delta_x_dot_delta_gradient_(*it); search_direction -= alpha_i * delta_gradient_history_.col(*it); diff --git a/extern/ceres/internal/ceres/low_rank_inverse_hessian.h b/extern/ceres/internal/ceres/low_rank_inverse_hessian.h index 0028a988923..36519360262 100644 --- a/extern/ceres/internal/ceres/low_rank_inverse_hessian.h +++ b/extern/ceres/internal/ceres/low_rank_inverse_hessian.h @@ -37,6 +37,7 @@ #include <list> #include "ceres/internal/eigen.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" namespace ceres { @@ -59,7 +60,7 @@ namespace internal { // Byrd, R. H.; Nocedal, J.; Schnabel, R. B. (1994). // "Representations of Quasi-Newton Matrices and their use in // Limited Memory Methods". Mathematical Programming 63 (4): -class LowRankInverseHessian : public LinearOperator { +class CERES_NO_EXPORT LowRankInverseHessian final : public LinearOperator { public: // num_parameters is the row/column size of the Hessian. // max_num_corrections is the rank of the Hessian approximation. @@ -73,7 +74,6 @@ class LowRankInverseHessian : public LinearOperator { LowRankInverseHessian(int num_parameters, int max_num_corrections, bool use_approximate_eigenvalue_scaling); - virtual ~LowRankInverseHessian() {} // Update the low rank approximation. delta_x is the change in the // domain of Hessian, and delta_gradient is the change in the diff --git a/extern/ceres/internal/ceres/manifold.cc b/extern/ceres/internal/ceres/manifold.cc new file mode 100644 index 00000000000..f412a793f93 --- /dev/null +++ b/extern/ceres/internal/ceres/manifold.cc @@ -0,0 +1,321 @@ +#include "ceres/manifold.h" + +#include <algorithm> +#include <cmath> + +#include "ceres/internal/eigen.h" +#include "ceres/internal/fixed_array.h" +#include "glog/logging.h" + +namespace ceres { +namespace { + +struct CeresQuaternionOrder { + static constexpr int kW = 0; + static constexpr int kX = 1; + static constexpr int kY = 2; + static constexpr int kZ = 3; +}; + +struct EigenQuaternionOrder { + static constexpr int kW = 3; + static constexpr int kX = 0; + static constexpr int kY = 1; + static constexpr int kZ = 2; +}; + +template <typename Order> +inline void QuaternionPlusImpl(const double* x, + const double* delta, + double* x_plus_delta) { + // x_plus_delta = QuaternionProduct(q_delta, x), where q_delta is the + // quaternion constructed from delta. + const double norm_delta = std::sqrt( + delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]); + + if (norm_delta == 0.0) { + for (int i = 0; i < 4; ++i) { + x_plus_delta[i] = x[i]; + } + return; + } + + const double sin_delta_by_delta = (std::sin(norm_delta) / norm_delta); + double q_delta[4]; + q_delta[Order::kW] = std::cos(norm_delta); + q_delta[Order::kX] = sin_delta_by_delta * delta[0]; + q_delta[Order::kY] = sin_delta_by_delta * delta[1]; + q_delta[Order::kZ] = sin_delta_by_delta * delta[2]; + + x_plus_delta[Order::kW] = + q_delta[Order::kW] * x[Order::kW] - q_delta[Order::kX] * x[Order::kX] - + q_delta[Order::kY] * x[Order::kY] - q_delta[Order::kZ] * x[Order::kZ]; + x_plus_delta[Order::kX] = + q_delta[Order::kW] * x[Order::kX] + q_delta[Order::kX] * x[Order::kW] + + q_delta[Order::kY] * x[Order::kZ] - q_delta[Order::kZ] * x[Order::kY]; + x_plus_delta[Order::kY] = + q_delta[Order::kW] * x[Order::kY] - q_delta[Order::kX] * x[Order::kZ] + + q_delta[Order::kY] * x[Order::kW] + q_delta[Order::kZ] * x[Order::kX]; + x_plus_delta[Order::kZ] = + q_delta[Order::kW] * x[Order::kZ] + q_delta[Order::kX] * x[Order::kY] - + q_delta[Order::kY] * x[Order::kX] + q_delta[Order::kZ] * x[Order::kW]; +} + +template <typename Order> +inline void QuaternionPlusJacobianImpl(const double* x, double* jacobian_ptr) { + Eigen::Map<Eigen::Matrix<double, 4, 3, Eigen::RowMajor>> jacobian( + jacobian_ptr); + + jacobian(Order::kW, 0) = -x[Order::kX]; + jacobian(Order::kW, 1) = -x[Order::kY]; + jacobian(Order::kW, 2) = -x[Order::kZ]; + jacobian(Order::kX, 0) = x[Order::kW]; + jacobian(Order::kX, 1) = x[Order::kZ]; + jacobian(Order::kX, 2) = -x[Order::kY]; + jacobian(Order::kY, 0) = -x[Order::kZ]; + jacobian(Order::kY, 1) = x[Order::kW]; + jacobian(Order::kY, 2) = x[Order::kX]; + jacobian(Order::kZ, 0) = x[Order::kY]; + jacobian(Order::kZ, 1) = -x[Order::kX]; + jacobian(Order::kZ, 2) = x[Order::kW]; +} + +template <typename Order> +inline void QuaternionMinusImpl(const double* y, + const double* x, + double* y_minus_x) { + // ambient_y_minus_x = QuaternionProduct(y, -x) where -x is the conjugate of + // x. + double ambient_y_minus_x[4]; + ambient_y_minus_x[Order::kW] = + y[Order::kW] * x[Order::kW] + y[Order::kX] * x[Order::kX] + + y[Order::kY] * x[Order::kY] + y[Order::kZ] * x[Order::kZ]; + ambient_y_minus_x[Order::kX] = + -y[Order::kW] * x[Order::kX] + y[Order::kX] * x[Order::kW] - + y[Order::kY] * x[Order::kZ] + y[Order::kZ] * x[Order::kY]; + ambient_y_minus_x[Order::kY] = + -y[Order::kW] * x[Order::kY] + y[Order::kX] * x[Order::kZ] + + y[Order::kY] * x[Order::kW] - y[Order::kZ] * x[Order::kX]; + ambient_y_minus_x[Order::kZ] = + -y[Order::kW] * x[Order::kZ] - y[Order::kX] * x[Order::kY] + + y[Order::kY] * x[Order::kX] + y[Order::kZ] * x[Order::kW]; + + const double u_norm = + std::sqrt(ambient_y_minus_x[Order::kX] * ambient_y_minus_x[Order::kX] + + ambient_y_minus_x[Order::kY] * ambient_y_minus_x[Order::kY] + + ambient_y_minus_x[Order::kZ] * ambient_y_minus_x[Order::kZ]); + if (u_norm > 0.0) { + const double theta = std::atan2(u_norm, ambient_y_minus_x[Order::kW]); + y_minus_x[0] = theta * ambient_y_minus_x[Order::kX] / u_norm; + y_minus_x[1] = theta * ambient_y_minus_x[Order::kY] / u_norm; + y_minus_x[2] = theta * ambient_y_minus_x[Order::kZ] / u_norm; + } else { + y_minus_x[0] = 0.0; + y_minus_x[1] = 0.0; + y_minus_x[2] = 0.0; + } +} + +template <typename Order> +inline void QuaternionMinusJacobianImpl(const double* x, double* jacobian_ptr) { + Eigen::Map<Eigen::Matrix<double, 3, 4, Eigen::RowMajor>> jacobian( + jacobian_ptr); + + jacobian(0, Order::kW) = -x[Order::kX]; + jacobian(0, Order::kX) = x[Order::kW]; + jacobian(0, Order::kY) = -x[Order::kZ]; + jacobian(0, Order::kZ) = x[Order::kY]; + jacobian(1, Order::kW) = -x[Order::kY]; + jacobian(1, Order::kX) = x[Order::kZ]; + jacobian(1, Order::kY) = x[Order::kW]; + jacobian(1, Order::kZ) = -x[Order::kX]; + jacobian(2, Order::kW) = -x[Order::kZ]; + jacobian(2, Order::kX) = -x[Order::kY]; + jacobian(2, Order::kY) = x[Order::kX]; + jacobian(2, Order::kZ) = x[Order::kW]; +} + +} // namespace + +Manifold::~Manifold() = default; + +bool Manifold::RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const { + const int tangent_size = TangentSize(); + if (tangent_size == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + Matrix plus_jacobian(ambient_size, tangent_size); + if (!PlusJacobian(x, plus_jacobian.data())) { + return false; + } + + MatrixRef(tangent_matrix, num_rows, tangent_size) = + ConstMatrixRef(ambient_matrix, num_rows, ambient_size) * plus_jacobian; + return true; +} + +SubsetManifold::SubsetManifold(const int size, + const std::vector<int>& constant_parameters) + + : tangent_size_(size - constant_parameters.size()), + constancy_mask_(size, false) { + if (constant_parameters.empty()) { + return; + } + + std::vector<int> constant = constant_parameters; + std::sort(constant.begin(), constant.end()); + CHECK_GE(constant.front(), 0) << "Indices indicating constant parameter must " + "be greater than equal to zero."; + CHECK_LT(constant.back(), size) + << "Indices indicating constant parameter must be less than the size " + << "of the parameter block."; + CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end()) + << "The set of constant parameters cannot contain duplicates"; + + for (auto index : constant_parameters) { + constancy_mask_[index] = true; + } +} + +int SubsetManifold::AmbientSize() const { return constancy_mask_.size(); } + +int SubsetManifold::TangentSize() const { return tangent_size_; } + +bool SubsetManifold::Plus(const double* x, + const double* delta, + double* x_plus_delta) const { + const int ambient_size = AmbientSize(); + for (int i = 0, j = 0; i < ambient_size; ++i) { + if (constancy_mask_[i]) { + x_plus_delta[i] = x[i]; + } else { + x_plus_delta[i] = x[i] + delta[j++]; + } + } + return true; +} + +bool SubsetManifold::PlusJacobian(const double* x, + double* plus_jacobian) const { + if (tangent_size_ == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + MatrixRef m(plus_jacobian, ambient_size, tangent_size_); + m.setZero(); + for (int r = 0, c = 0; r < ambient_size; ++r) { + if (!constancy_mask_[r]) { + m(r, c++) = 1.0; + } + } + return true; +} + +bool SubsetManifold::RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const { + if (tangent_size_ == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + for (int r = 0; r < num_rows; ++r) { + for (int idx = 0, c = 0; idx < ambient_size; ++idx) { + if (!constancy_mask_[idx]) { + tangent_matrix[r * tangent_size_ + c++] = + ambient_matrix[r * ambient_size + idx]; + } + } + } + return true; +} + +bool SubsetManifold::Minus(const double* y, + const double* x, + double* y_minus_x) const { + if (tangent_size_ == 0) { + return true; + } + + const int ambient_size = AmbientSize(); + for (int i = 0, j = 0; i < ambient_size; ++i) { + if (!constancy_mask_[i]) { + y_minus_x[j++] = y[i] - x[i]; + } + } + return true; +} + +bool SubsetManifold::MinusJacobian(const double* x, + double* minus_jacobian) const { + const int ambient_size = AmbientSize(); + MatrixRef m(minus_jacobian, tangent_size_, ambient_size); + m.setZero(); + for (int c = 0, r = 0; c < ambient_size; ++c) { + if (!constancy_mask_[c]) { + m(r++, c) = 1.0; + } + } + return true; +} + +bool QuaternionManifold::Plus(const double* x, + const double* delta, + double* x_plus_delta) const { + QuaternionPlusImpl<CeresQuaternionOrder>(x, delta, x_plus_delta); + return true; +} + +bool QuaternionManifold::PlusJacobian(const double* x, double* jacobian) const { + QuaternionPlusJacobianImpl<CeresQuaternionOrder>(x, jacobian); + return true; +} + +bool QuaternionManifold::Minus(const double* y, + const double* x, + double* y_minus_x) const { + QuaternionMinusImpl<CeresQuaternionOrder>(y, x, y_minus_x); + return true; +} + +bool QuaternionManifold::MinusJacobian(const double* x, + double* jacobian) const { + QuaternionMinusJacobianImpl<CeresQuaternionOrder>(x, jacobian); + return true; +} + +bool EigenQuaternionManifold::Plus(const double* x, + const double* delta, + double* x_plus_delta) const { + QuaternionPlusImpl<EigenQuaternionOrder>(x, delta, x_plus_delta); + return true; +} + +bool EigenQuaternionManifold::PlusJacobian(const double* x, + double* jacobian) const { + QuaternionPlusJacobianImpl<EigenQuaternionOrder>(x, jacobian); + return true; +} + +bool EigenQuaternionManifold::Minus(const double* y, + const double* x, + double* y_minus_x) const { + QuaternionMinusImpl<EigenQuaternionOrder>(y, x, y_minus_x); + return true; +} + +bool EigenQuaternionManifold::MinusJacobian(const double* x, + double* jacobian) const { + QuaternionMinusJacobianImpl<EigenQuaternionOrder>(x, jacobian); + return true; +} + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/manifold_adapter.h b/extern/ceres/internal/ceres/manifold_adapter.h new file mode 100644 index 00000000000..9a21456a731 --- /dev/null +++ b/extern/ceres/internal/ceres/manifold_adapter.h @@ -0,0 +1,60 @@ +#include "ceres/internal/export.h" +#include "ceres/local_parameterization.h" +#include "ceres/manifold.h" +#include "glog/logging.h" + +namespace ceres { +namespace internal { + +// Adapter to wrap LocalParameterization and make them look like Manifolds. +// +// ManifoldAdapter NEVER takes ownership of local_parameterization. +class CERES_NO_EXPORT ManifoldAdapter final : public Manifold { + public: + explicit ManifoldAdapter(const LocalParameterization* local_parameterization) + : local_parameterization_(local_parameterization) { + CHECK(local_parameterization != nullptr); + } + + bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const override { + return local_parameterization_->Plus(x, delta, x_plus_delta); + } + + bool PlusJacobian(const double* x, double* jacobian) const override { + return local_parameterization_->ComputeJacobian(x, jacobian); + } + + bool RightMultiplyByPlusJacobian(const double* x, + const int num_rows, + const double* ambient_matrix, + double* tangent_matrix) const override { + return local_parameterization_->MultiplyByJacobian( + x, num_rows, ambient_matrix, tangent_matrix); + } + + bool Minus(const double* y, const double* x, double* delta) const override { + LOG(FATAL) << "This should never be called."; + return false; + } + + bool MinusJacobian(const double* x, double* jacobian) const override { + LOG(FATAL) << "This should never be called."; + return false; + } + + int AmbientSize() const override { + return local_parameterization_->GlobalSize(); + } + + int TangentSize() const override { + return local_parameterization_->LocalSize(); + } + + private: + const LocalParameterization* local_parameterization_; +}; + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/map_util.h b/extern/ceres/internal/ceres/map_util.h index 6e310f8db2d..5632c22e916 100644 --- a/extern/ceres/internal/ceres/map_util.h +++ b/extern/ceres/internal/ceres/map_util.h @@ -35,7 +35,7 @@ #include <utility> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -121,7 +121,7 @@ template <class Collection> void InsertOrDie(Collection* const collection, const typename Collection::value_type::first_type& key, const typename Collection::value_type::second_type& data) { - typedef typename Collection::value_type value_type; + using value_type = typename Collection::value_type; CHECK(collection->insert(value_type(key, data)).second) << "duplicate key: " << key; } diff --git a/extern/ceres/internal/ceres/minimizer.cc b/extern/ceres/internal/ceres/minimizer.cc index b96e0c9de44..449c728774d 100644 --- a/extern/ceres/internal/ceres/minimizer.cc +++ b/extern/ceres/internal/ceres/minimizer.cc @@ -30,6 +30,8 @@ #include "ceres/minimizer.h" +#include <memory> + #include "ceres/line_search_minimizer.h" #include "ceres/trust_region_minimizer.h" #include "ceres/types.h" @@ -38,20 +40,20 @@ namespace ceres { namespace internal { -Minimizer* Minimizer::Create(MinimizerType minimizer_type) { +std::unique_ptr<Minimizer> Minimizer::Create(MinimizerType minimizer_type) { if (minimizer_type == TRUST_REGION) { - return new TrustRegionMinimizer; + return std::make_unique<TrustRegionMinimizer>(); } if (minimizer_type == LINE_SEARCH) { - return new LineSearchMinimizer; + return std::make_unique<LineSearchMinimizer>(); } LOG(FATAL) << "Unknown minimizer_type: " << minimizer_type; - return NULL; + return nullptr; } -Minimizer::~Minimizer() {} +Minimizer::~Minimizer() = default; bool Minimizer::RunCallbacks(const Minimizer::Options& options, const IterationSummary& iteration_summary, diff --git a/extern/ceres/internal/ceres/minimizer.h b/extern/ceres/internal/ceres/minimizer.h index 246550de7cd..c2c1f71df9f 100644 --- a/extern/ceres/internal/ceres/minimizer.h +++ b/extern/ceres/internal/ceres/minimizer.h @@ -35,7 +35,8 @@ #include <string> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/solver.h" @@ -49,7 +50,7 @@ class CoordinateDescentMinimizer; class LinearSolver; // Interface for non-linear least squares solvers. -class CERES_EXPORT_INTERNAL Minimizer { +class CERES_NO_EXPORT Minimizer { public: // Options struct to control the behaviour of the Minimizer. Please // see solver.h for detailed information about the meaning and @@ -178,7 +179,7 @@ class CERES_EXPORT_INTERNAL Minimizer { std::shared_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer; }; - static Minimizer* Create(MinimizerType minimizer_type); + static std::unique_ptr<Minimizer> Create(MinimizerType minimizer_type); static bool RunCallbacks(const Options& options, const IterationSummary& iteration_summary, Solver::Summary* summary); @@ -195,4 +196,6 @@ class CERES_EXPORT_INTERNAL Minimizer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_MINIMIZER_H_ diff --git a/extern/ceres/internal/ceres/normal_prior.cc b/extern/ceres/internal/ceres/normal_prior.cc index 4a62132dbda..17de40f2e77 100644 --- a/extern/ceres/internal/ceres/normal_prior.cc +++ b/extern/ceres/internal/ceres/normal_prior.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -56,7 +56,7 @@ bool NormalPrior::Evaluate(double const* const* parameters, // r = A_ * (p - b_); // The extra eval is to get around a bug in the eigen library. r = A_ * (p - b_).eval(); - if ((jacobians != NULL) && (jacobians[0] != NULL)) { + if ((jacobians != nullptr) && (jacobians[0] != nullptr)) { MatrixRef(jacobians[0], num_residuals(), parameter_block_sizes()[0]) = A_; } return true; diff --git a/extern/ceres/internal/ceres/pair_hash.h b/extern/ceres/internal/ceres/pair_hash.h index abbedccf961..83ff2b46401 100644 --- a/extern/ceres/internal/ceres/pair_hash.h +++ b/extern/ceres/internal/ceres/pair_hash.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2018 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,10 +33,12 @@ #ifndef CERES_INTERNAL_PAIR_HASH_H_ #define CERES_INTERNAL_PAIR_HASH_H_ +#include <cstddef> #include <cstdint> +#include <functional> #include <utility> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { diff --git a/extern/ceres/internal/ceres/parallel_for.h b/extern/ceres/internal/ceres/parallel_for.h index b64bd310650..9528c267d49 100644 --- a/extern/ceres/internal/ceres/parallel_for.h +++ b/extern/ceres/internal/ceres/parallel_for.h @@ -28,37 +28,38 @@ // // Author: vitus@google.com (Michael Vitus) -#ifndef CERES_INTERNAL_PARALLEL_FOR_ -#define CERES_INTERNAL_PARALLEL_FOR_ +#ifndef CERES_INTERNAL_PARALLEL_FOR_H_ +#define CERES_INTERNAL_PARALLEL_FOR_H_ #include <functional> #include "ceres/context_impl.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { // Returns the maximum number of threads supported by the threading backend // Ceres was compiled with. +CERES_NO_EXPORT int MaxNumThreadsAvailable(); // Execute the function for every element in the range [start, end) with at most // num_threads. It will execute all the work on the calling thread if // num_threads is 1. -CERES_EXPORT_INTERNAL void ParallelFor( - ContextImpl* context, - int start, - int end, - int num_threads, - const std::function<void(int)>& function); +CERES_NO_EXPORT void ParallelFor(ContextImpl* context, + int start, + int end, + int num_threads, + const std::function<void(int)>& function); // Execute the function for every element in the range [start, end) with at most // num_threads. It will execute all the work on the calling thread if // num_threads is 1. Each invocation of function() will be passed a thread_id // in [0, num_threads) that is guaranteed to be distinct from the value passed // to any concurrent execution of function(). -CERES_EXPORT_INTERNAL void ParallelFor( +CERES_NO_EXPORT void ParallelFor( ContextImpl* context, int start, int end, @@ -67,4 +68,6 @@ CERES_EXPORT_INTERNAL void ParallelFor( } // namespace internal } // namespace ceres +#include "ceres/internal/disable_warnings.h" + #endif // CERES_INTERNAL_PARALLEL_FOR_H_ diff --git a/extern/ceres/internal/ceres/parallel_for_cxx.cc b/extern/ceres/internal/ceres/parallel_for_cxx.cc index 4da40c01eb6..5b78db19a44 100644 --- a/extern/ceres/internal/ceres/parallel_for_cxx.cc +++ b/extern/ceres/internal/ceres/parallel_for_cxx.cc @@ -29,7 +29,7 @@ // Author: vitus@google.com (Michael Vitus) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_USE_CXX_THREADS @@ -125,7 +125,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } @@ -167,7 +167,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int thread_id, int i)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } diff --git a/extern/ceres/internal/ceres/parallel_for_nothreads.cc b/extern/ceres/internal/ceres/parallel_for_nothreads.cc index d036569fcd7..1c1871662c8 100644 --- a/extern/ceres/internal/ceres/parallel_for_nothreads.cc +++ b/extern/ceres/internal/ceres/parallel_for_nothreads.cc @@ -29,7 +29,7 @@ // Author: alexs.mac@gmail.com (Alex Stewart) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_NO_THREADS @@ -47,7 +47,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } @@ -62,7 +62,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int thread_id, int i)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } diff --git a/extern/ceres/internal/ceres/parallel_for_openmp.cc b/extern/ceres/internal/ceres/parallel_for_openmp.cc index eb9d90581ae..1d44bf9977a 100644 --- a/extern/ceres/internal/ceres/parallel_for_openmp.cc +++ b/extern/ceres/internal/ceres/parallel_for_openmp.cc @@ -29,7 +29,7 @@ // Author: vitus@google.com (Michael Vitus) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #if defined(CERES_USE_OPENMP) @@ -50,7 +50,7 @@ void ParallelFor(ContextImpl* context, int num_threads, const std::function<void(int)>& function) { CHECK_GT(num_threads, 0); - CHECK(context != NULL); + CHECK(context != nullptr); if (end <= start) { return; } @@ -69,7 +69,7 @@ void ParallelFor(ContextImpl* context, int end, int num_threads, const std::function<void(int thread_id, int i)>& function) { - CHECK(context != NULL); + CHECK(context != nullptr); ThreadTokenProvider thread_token_provider(num_threads); ParallelFor(context, start, end, num_threads, [&](int i) { diff --git a/extern/ceres/internal/ceres/parallel_utils.h b/extern/ceres/internal/ceres/parallel_utils.h index 89d21106d74..b2d9e0da765 100644 --- a/extern/ceres/internal/ceres/parallel_utils.h +++ b/extern/ceres/internal/ceres/parallel_utils.h @@ -31,7 +31,7 @@ #ifndef CERES_INTERNAL_PARALLEL_UTILS_H_ #define CERES_INTERNAL_PARALLEL_UTILS_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -61,10 +61,10 @@ namespace internal { // }); // which in each iteration will produce i and j satisfying // 0 <= i <= j < n -CERES_EXPORT_INTERNAL void LinearIndexToUpperTriangularIndex(int k, - int n, - int* i, - int* j); +CERES_NO_EXPORT void LinearIndexToUpperTriangularIndex(int k, + int n, + int* i, + int* j); } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/parameter_block.h b/extern/ceres/internal/ceres/parameter_block.h index 88943dfbcff..a9845a3a9e3 100644 --- a/extern/ceres/internal/ceres/parameter_block.h +++ b/extern/ceres/internal/ceres/parameter_block.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -40,9 +40,10 @@ #include <unordered_set> #include "ceres/array_utils.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" -#include "ceres/local_parameterization.h" +#include "ceres/internal/export.h" +#include "ceres/manifold.h" #include "ceres/stringprintf.h" #include "glog/logging.h" @@ -58,12 +59,12 @@ class ResidualBlock; // methods are performance sensitive. // // The class is not thread-safe, unless only const methods are called. The -// parameter block may also hold a pointer to a local parameterization; the -// parameter block does not take ownership of this pointer, so the user is -// responsible for the proper disposal of the local parameterization. -class ParameterBlock { +// parameter block may also hold a pointer to a manifold; the parameter block +// does not take ownership of this pointer, so the user is responsible for the +// proper disposal of the manifold. +class CERES_NO_EXPORT ParameterBlock { public: - typedef std::unordered_set<ResidualBlock*> ResidualBlockSet; + using ResidualBlockSet = std::unordered_set<ResidualBlock*>; // Create a parameter block with the user state, size, and index specified. // The size is the size of the parameter block and the index is the position @@ -74,16 +75,13 @@ class ParameterBlock { state_(user_state), index_(index) {} - ParameterBlock(double* user_state, - int size, - int index, - LocalParameterization* local_parameterization) + ParameterBlock(double* user_state, int size, int index, Manifold* manifold) : user_state_(user_state), size_(size), state_(user_state), index_(index) { - if (local_parameterization != nullptr) { - SetParameterization(local_parameterization); + if (manifold != nullptr) { + SetManifold(manifold); } } @@ -98,7 +96,7 @@ class ParameterBlock { << "with user location " << user_state_; state_ = x; - return UpdateLocalParameterizationJacobian(); + return UpdatePlusJacobian(); } // Copy the current parameter state out to x. This is "GetState()" rather than @@ -114,17 +112,13 @@ class ParameterBlock { const double* state() const { return state_; } const double* user_state() const { return user_state_; } double* mutable_user_state() { return user_state_; } - const LocalParameterization* local_parameterization() const { - return local_parameterization_; - } - LocalParameterization* mutable_local_parameterization() { - return local_parameterization_; - } + const Manifold* manifold() const { return manifold_; } + Manifold* mutable_manifold() { return manifold_; } // Set this parameter block to vary or not. void SetConstant() { is_set_constant_ = true; } void SetVarying() { is_set_constant_ = false; } - bool IsConstant() const { return (is_set_constant_ || LocalSize() == 0); } + bool IsConstant() const { return (is_set_constant_ || TangentSize() == 0); } double UpperBound(int index) const { return (upper_bounds_ ? upper_bounds_[index] @@ -151,51 +145,46 @@ class ParameterBlock { int delta_offset() const { return delta_offset_; } void set_delta_offset(int delta_offset) { delta_offset_ = delta_offset; } - // Methods relating to the parameter block's parameterization. + // Methods relating to the parameter block's manifold. - // The local to global jacobian. Returns nullptr if there is no local - // parameterization for this parameter block. The returned matrix is row-major - // and has Size() rows and LocalSize() columns. - const double* LocalParameterizationJacobian() const { - return local_parameterization_jacobian_.get(); - } + // The local to global jacobian. Returns nullptr if there is no manifold for + // this parameter block. The returned matrix is row-major and has Size() rows + // and TangentSize() columns. + const double* PlusJacobian() const { return plus_jacobian_.get(); } - int LocalSize() const { - return (local_parameterization_ == nullptr) - ? size_ - : local_parameterization_->LocalSize(); + int TangentSize() const { + return (manifold_ == nullptr) ? size_ : manifold_->TangentSize(); } - // Set the parameterization. The parameter block does not take - // ownership of the parameterization. - void SetParameterization(LocalParameterization* new_parameterization) { - // Nothing to do if the new parameterization is the same as the - // old parameterization. - if (new_parameterization == local_parameterization_) { + // Set the manifold. The parameter block does not take ownership of + // the manifold. + void SetManifold(Manifold* new_manifold) { + // Nothing to do if the new manifold is the same as the old + // manifold. + if (new_manifold == manifold_) { return; } - if (new_parameterization == nullptr) { - local_parameterization_ = nullptr; + if (new_manifold == nullptr) { + manifold_ = nullptr; + plus_jacobian_ = nullptr; return; } - CHECK(new_parameterization->GlobalSize() == size_) - << "Invalid parameterization for parameter block. The parameter block " - << "has size " << size_ << " while the parameterization has a global " - << "size of " << new_parameterization->GlobalSize() << ". Did you " - << "accidentally use the wrong parameter block or parameterization?"; + CHECK_EQ(new_manifold->AmbientSize(), size_) + << "The parameter block has size = " << size_ + << " while the manifold has ambient size = " + << new_manifold->AmbientSize(); - CHECK_GE(new_parameterization->LocalSize(), 0) - << "Invalid parameterization. Parameterizations must have a " + CHECK_GE(new_manifold->TangentSize(), 0) + << "Invalid Manifold. Manifolds must have a " << "non-negative dimensional tangent space."; - local_parameterization_ = new_parameterization; - local_parameterization_jacobian_.reset( - new double[local_parameterization_->GlobalSize() * - local_parameterization_->LocalSize()]); - CHECK(UpdateLocalParameterizationJacobian()) - << "Local parameterization Jacobian computation failed for x: " + manifold_ = new_manifold; + plus_jacobian_ = std::make_unique<double[]>(manifold_->AmbientSize() * + manifold_->TangentSize()); + CHECK(UpdatePlusJacobian()) + << "Manifold::PlusJacobian computation failed for x: " << ConstVectorRef(state_, Size()).transpose(); } @@ -207,7 +196,7 @@ class ParameterBlock { } if (!upper_bounds_) { - upper_bounds_.reset(new double[size_]); + upper_bounds_ = std::make_unique<double[]>(size_); std::fill(upper_bounds_.get(), upper_bounds_.get() + size_, std::numeric_limits<double>::max()); @@ -224,7 +213,7 @@ class ParameterBlock { } if (!lower_bounds_) { - lower_bounds_.reset(new double[size_]); + lower_bounds_ = std::make_unique<double[]>(size_); std::fill(lower_bounds_.get(), lower_bounds_.get() + size_, -std::numeric_limits<double>::max()); @@ -234,11 +223,11 @@ class ParameterBlock { } // Generalization of the addition operation. This is the same as - // LocalParameterization::Plus() followed by projection onto the + // Manifold::Plus() followed by projection onto the // hyper cube implied by the bounds constraints. bool Plus(const double* x, const double* delta, double* x_plus_delta) { - if (local_parameterization_ != nullptr) { - if (!local_parameterization_->Plus(x, delta, x_plus_delta)) { + if (manifold_ != nullptr) { + if (!manifold_->Plus(x, delta, x_plus_delta)) { return false; } } else { @@ -281,7 +270,7 @@ class ParameterBlock { CHECK(residual_blocks_.get() == nullptr) << "Ceres bug: There is already a residual block collection " << "for parameter block: " << ToString(); - residual_blocks_.reset(new ResidualBlockSet); + residual_blocks_ = std::make_unique<ResidualBlockSet>(); } void AddResidualBlock(ResidualBlock* residual_block) { @@ -321,33 +310,30 @@ class ParameterBlock { } private: - bool UpdateLocalParameterizationJacobian() { - if (local_parameterization_ == nullptr) { + bool UpdatePlusJacobian() { + if (manifold_ == nullptr) { return true; } - // Update the local to global Jacobian. In some cases this is + // Update the Plus Jacobian. In some cases this is // wasted effort; if this is a bottleneck, we will find a solution // at that time. - - const int jacobian_size = Size() * LocalSize(); - InvalidateArray(jacobian_size, local_parameterization_jacobian_.get()); - if (!local_parameterization_->ComputeJacobian( - state_, local_parameterization_jacobian_.get())) { - LOG(WARNING) << "Local parameterization Jacobian computation failed" + const int jacobian_size = Size() * TangentSize(); + InvalidateArray(jacobian_size, plus_jacobian_.get()); + if (!manifold_->PlusJacobian(state_, plus_jacobian_.get())) { + LOG(WARNING) << "Manifold::PlusJacobian computation failed" "for x: " << ConstVectorRef(state_, Size()).transpose(); return false; } - if (!IsArrayValid(jacobian_size, local_parameterization_jacobian_.get())) { - LOG(WARNING) << "Local parameterization Jacobian computation returned" + if (!IsArrayValid(jacobian_size, plus_jacobian_.get())) { + LOG(WARNING) << "Manifold::PlusJacobian computation returned " << "an invalid matrix for x: " << ConstVectorRef(state_, Size()).transpose() << "\n Jacobian matrix : " - << ConstMatrixRef(local_parameterization_jacobian_.get(), - Size(), - LocalSize()); + << ConstMatrixRef( + plus_jacobian_.get(), Size(), TangentSize()); return false; } return true; @@ -356,14 +342,14 @@ class ParameterBlock { double* user_state_ = nullptr; int size_ = -1; bool is_set_constant_ = false; - LocalParameterization* local_parameterization_ = nullptr; + Manifold* manifold_ = nullptr; // The "state" of the parameter. These fields are only needed while the // solver is running. While at first glance using mutable is a bad idea, this // ends up simplifying the internals of Ceres enough to justify the potential // pitfalls of using "mutable." mutable const double* state_ = nullptr; - mutable std::unique_ptr<double[]> local_parameterization_jacobian_; + mutable std::unique_ptr<double[]> plus_jacobian_; // The index of the parameter. This is used by various other parts of Ceres to // permit switching from a ParameterBlock* to an index in another array. @@ -392,11 +378,13 @@ class ParameterBlock { std::unique_ptr<double[]> upper_bounds_; std::unique_ptr<double[]> lower_bounds_; - // Necessary so ProblemImpl can clean up the parameterizations. + // Necessary so ProblemImpl can clean up the manifolds. friend class ProblemImpl; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PARAMETER_BLOCK_H_ diff --git a/extern/ceres/internal/ceres/parameter_block_ordering.cc b/extern/ceres/internal/ceres/parameter_block_ordering.cc index 9899c243899..570a09c60ba 100644 --- a/extern/ceres/internal/ceres/parameter_block_ordering.cc +++ b/extern/ceres/internal/ceres/parameter_block_ordering.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -54,14 +54,14 @@ int ComputeStableSchurOrdering(const Program& program, CHECK(ordering != nullptr); ordering->clear(); EventLogger event_logger("ComputeStableSchurOrdering"); - std::unique_ptr<Graph<ParameterBlock*>> graph(CreateHessianGraph(program)); + auto graph = CreateHessianGraph(program); event_logger.AddEvent("CreateHessianGraph"); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); const std::unordered_set<ParameterBlock*>& vertices = graph->vertices(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - if (vertices.count(parameter_blocks[i]) > 0) { - ordering->push_back(parameter_blocks[i]); + for (auto* parameter_block : parameter_blocks) { + if (vertices.count(parameter_block) > 0) { + ordering->push_back(parameter_block); } } event_logger.AddEvent("Preordering"); @@ -70,8 +70,7 @@ int ComputeStableSchurOrdering(const Program& program, event_logger.AddEvent("StableIndependentSet"); // Add the excluded blocks to back of the ordering vector. - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { if (parameter_block->IsConstant()) { ordering->push_back(parameter_block); } @@ -86,13 +85,12 @@ int ComputeSchurOrdering(const Program& program, CHECK(ordering != nullptr); ordering->clear(); - std::unique_ptr<Graph<ParameterBlock*>> graph(CreateHessianGraph(program)); + auto graph = CreateHessianGraph(program); int independent_set_size = IndependentSetOrdering(*graph, ordering); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); // Add the excluded blocks to back of the ordering vector. - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { if (parameter_block->IsConstant()) { ordering->push_back(parameter_block); } @@ -106,7 +104,7 @@ void ComputeRecursiveIndependentSetOrdering(const Program& program, CHECK(ordering != nullptr); ordering->Clear(); const vector<ParameterBlock*> parameter_blocks = program.parameter_blocks(); - std::unique_ptr<Graph<ParameterBlock*>> graph(CreateHessianGraph(program)); + auto graph = CreateHessianGraph(program); int num_covered = 0; int round = 0; @@ -124,20 +122,19 @@ void ComputeRecursiveIndependentSetOrdering(const Program& program, } } -Graph<ParameterBlock*>* CreateHessianGraph(const Program& program) { - Graph<ParameterBlock*>* graph = new Graph<ParameterBlock*>; +std::unique_ptr<Graph<ParameterBlock*>> CreateHessianGraph( + const Program& program) { + auto graph = std::make_unique<Graph<ParameterBlock*>>(); CHECK(graph != nullptr); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks[i]; + for (auto* parameter_block : parameter_blocks) { if (!parameter_block->IsConstant()) { graph->AddVertex(parameter_block); } } const vector<ResidualBlock*>& residual_blocks = program.residual_blocks(); - for (int i = 0; i < residual_blocks.size(); ++i) { - const ResidualBlock* residual_block = residual_blocks[i]; + for (auto* residual_block : residual_blocks) { const int num_parameter_blocks = residual_block->NumParameterBlocks(); ParameterBlock* const* parameter_blocks = residual_block->parameter_blocks(); @@ -163,7 +160,7 @@ void OrderingToGroupSizes(const ParameterBlockOrdering* ordering, vector<int>* group_sizes) { CHECK(group_sizes != nullptr); group_sizes->clear(); - if (ordering == NULL) { + if (ordering == nullptr) { return; } diff --git a/extern/ceres/internal/ceres/parameter_block_ordering.h b/extern/ceres/internal/ceres/parameter_block_ordering.h index 82ab75dc6dc..f9a447adf87 100644 --- a/extern/ceres/internal/ceres/parameter_block_ordering.h +++ b/extern/ceres/internal/ceres/parameter_block_ordering.h @@ -31,10 +31,12 @@ #ifndef CERES_INTERNAL_PARAMETER_BLOCK_ORDERING_H_ #define CERES_INTERNAL_PARAMETER_BLOCK_ORDERING_H_ +#include <memory> #include <vector> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/ordered_groups.h" #include "ceres/types.h" @@ -57,20 +59,20 @@ class ParameterBlock; // ordering = [independent set, // complement of the independent set, // fixed blocks] -CERES_EXPORT_INTERNAL int ComputeSchurOrdering( +CERES_NO_EXPORT int ComputeSchurOrdering( const Program& program, std::vector<ParameterBlock*>* ordering); // Same as above, except that ties while computing the independent set // ordering are resolved in favour of the order in which the parameter // blocks occur in the program. -CERES_EXPORT_INTERNAL int ComputeStableSchurOrdering( +CERES_NO_EXPORT int ComputeStableSchurOrdering( const Program& program, std::vector<ParameterBlock*>* ordering); // Use an approximate independent set ordering to decompose the // parameter blocks of a problem in a sequence of independent // sets. The ordering covers all the non-constant parameter blocks in // the program. -CERES_EXPORT_INTERNAL void ComputeRecursiveIndependentSetOrdering( +CERES_NO_EXPORT void ComputeRecursiveIndependentSetOrdering( const Program& program, ParameterBlockOrdering* ordering); // Builds a graph on the parameter blocks of a Problem, whose @@ -78,15 +80,17 @@ CERES_EXPORT_INTERNAL void ComputeRecursiveIndependentSetOrdering( // vertex corresponds to a parameter block in the Problem except for // parameter blocks that are marked constant. An edge connects two // parameter blocks, if they co-occur in a residual block. -CERES_EXPORT_INTERNAL Graph<ParameterBlock*>* CreateHessianGraph( +CERES_NO_EXPORT std::unique_ptr<Graph<ParameterBlock*>> CreateHessianGraph( const Program& program); // Iterate over each of the groups in order of their priority and fill // summary with their sizes. -CERES_EXPORT_INTERNAL void OrderingToGroupSizes( +CERES_NO_EXPORT void OrderingToGroupSizes( const ParameterBlockOrdering* ordering, std::vector<int>* group_sizes); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PARAMETER_BLOCK_ORDERING_H_ diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view.cc b/extern/ceres/internal/ceres/partitioned_matrix_view.cc index b67bc905d15..d38f30a09d7 100644 --- a/extern/ceres/internal/ceres/partitioned_matrix_view.cc +++ b/extern/ceres/internal/ceres/partitioned_matrix_view.cc @@ -39,143 +39,147 @@ // // This file is generated using generate_template_specializations.py. +#include <memory> + #include "ceres/linear_solver.h" #include "ceres/partitioned_matrix_view.h" namespace ceres { namespace internal { -PartitionedMatrixViewBase* PartitionedMatrixViewBase::Create( +PartitionedMatrixViewBase::~PartitionedMatrixViewBase() = default; + +std::unique_ptr<PartitionedMatrixViewBase> PartitionedMatrixViewBase::Create( const LinearSolver::Options& options, const BlockSparseMatrix& matrix) { #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 2)) { - return new PartitionedMatrixView<2, 2, 2>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, 2>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<2, 2, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<2, 2, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 2)) { - return new PartitionedMatrixView<2, 2, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,2, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<2, 3, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<2, 3, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 6)) { - return new PartitionedMatrixView<2, 3, 6>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 6>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 9)) { - return new PartitionedMatrixView<2, 3, 9>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, 9>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 3)) { - return new PartitionedMatrixView<2, 3, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,3, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<2, 4, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<2, 4, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 6)) { - return new PartitionedMatrixView<2, 4, 6>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 6>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 8)) { - return new PartitionedMatrixView<2, 4, 8>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 8>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 9)) { - return new PartitionedMatrixView<2, 4, 9>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, 9>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 2) && (options.e_block_size == 4)) { - return new PartitionedMatrixView<2, 4, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,4, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if (options.row_block_size == 2) { - return new PartitionedMatrixView<2, Eigen::Dynamic, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<2,Eigen::Dynamic, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 3) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<3, 3, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<3,3, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 2)) { - return new PartitionedMatrixView<4, 4, 2>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, 2>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new PartitionedMatrixView<4, 4, 3>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, 3>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new PartitionedMatrixView<4, 4, 4>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, 4>>( + matrix, options.elimination_groups[0]); } if ((options.row_block_size == 4) && (options.e_block_size == 4)) { - return new PartitionedMatrixView<4, 4, Eigen::Dynamic>(matrix, - options.elimination_groups[0]); + return std::make_unique<PartitionedMatrixView<4,4, Eigen::Dynamic>>( + matrix, options.elimination_groups[0]); } #endif VLOG(1) << "Template specializations not found for <" << options.row_block_size << "," << options.e_block_size << "," << options.f_block_size << ">"; - return new PartitionedMatrixView<Eigen::Dynamic, - Eigen::Dynamic, - Eigen::Dynamic>( + return std::make_unique<PartitionedMatrixView<Eigen::Dynamic, + Eigen::Dynamic, + Eigen::Dynamic>>( matrix, options.elimination_groups[0]); }; diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view.h b/extern/ceres/internal/ceres/partitioned_matrix_view.h index 9f204ee1fdd..5623d3b6bca 100644 --- a/extern/ceres/internal/ceres/partitioned_matrix_view.h +++ b/extern/ceres/internal/ceres/partitioned_matrix_view.h @@ -38,11 +38,14 @@ #include <algorithm> #include <cstring> +#include <memory> #include <vector> #include "ceres/block_structure.h" +#include "ceres/internal/config.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/small_blas.h" #include "glog/logging.h" @@ -60,9 +63,9 @@ namespace internal { // block structure of the matrix does not satisfy the requirements of // the Schur complement solver it will result in unpredictable and // wrong output. -class CERES_EXPORT_INTERNAL PartitionedMatrixViewBase { +class CERES_NO_EXPORT PartitionedMatrixViewBase { public: - virtual ~PartitionedMatrixViewBase() {} + virtual ~PartitionedMatrixViewBase(); // y += E'x virtual void LeftMultiplyE(const double* x, double* y) const = 0; @@ -77,11 +80,11 @@ class CERES_EXPORT_INTERNAL PartitionedMatrixViewBase { virtual void RightMultiplyF(const double* x, double* y) const = 0; // Create and return the block diagonal of the matrix E'E. - virtual BlockSparseMatrix* CreateBlockDiagonalEtE() const = 0; + virtual std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalEtE() const = 0; // Create and return the block diagonal of the matrix F'F. Caller // owns the result. - virtual BlockSparseMatrix* CreateBlockDiagonalFtF() const = 0; + virtual std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalFtF() const = 0; // Compute the block diagonal of the matrix E'E and store it in // block_diagonal. The matrix block_diagonal is expected to have a @@ -108,26 +111,26 @@ class CERES_EXPORT_INTERNAL PartitionedMatrixViewBase { virtual int num_cols() const = 0; // clang-format on - static PartitionedMatrixViewBase* Create(const LinearSolver::Options& options, - const BlockSparseMatrix& matrix); + static std::unique_ptr<PartitionedMatrixViewBase> Create( + const LinearSolver::Options& options, const BlockSparseMatrix& matrix); }; template <int kRowBlockSize = Eigen::Dynamic, int kEBlockSize = Eigen::Dynamic, int kFBlockSize = Eigen::Dynamic> -class PartitionedMatrixView : public PartitionedMatrixViewBase { +class CERES_NO_EXPORT PartitionedMatrixView final + : public PartitionedMatrixViewBase { public: // matrix = [E F], where the matrix E contains the first // num_col_blocks_a column blocks. PartitionedMatrixView(const BlockSparseMatrix& matrix, int num_col_blocks_e); - virtual ~PartitionedMatrixView(); void LeftMultiplyE(const double* x, double* y) const final; void LeftMultiplyF(const double* x, double* y) const final; void RightMultiplyE(const double* x, double* y) const final; void RightMultiplyF(const double* x, double* y) const final; - BlockSparseMatrix* CreateBlockDiagonalEtE() const final; - BlockSparseMatrix* CreateBlockDiagonalFtF() const final; + std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalEtE() const final; + std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalFtF() const final; void UpdateBlockDiagonalEtE(BlockSparseMatrix* block_diagonal) const final; void UpdateBlockDiagonalFtF(BlockSparseMatrix* block_diagonal) const final; // clang-format off @@ -140,8 +143,8 @@ class PartitionedMatrixView : public PartitionedMatrixViewBase { // clang-format on private: - BlockSparseMatrix* CreateBlockDiagonalMatrixLayout(int start_col_block, - int end_col_block) const; + std::unique_ptr<BlockSparseMatrix> CreateBlockDiagonalMatrixLayout( + int start_col_block, int end_col_block) const; const BlockSparseMatrix& matrix_; int num_row_blocks_e_; @@ -154,4 +157,6 @@ class PartitionedMatrixView : public PartitionedMatrixViewBase { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PARTITIONED_MATRIX_VIEW_H_ diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h b/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h index 0b6a57fb9f2..2e818caa6ef 100644 --- a/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h +++ b/extern/ceres/internal/ceres/partitioned_matrix_view_impl.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -30,6 +30,7 @@ #include <algorithm> #include <cstring> +#include <memory> #include <vector> #include "ceres/block_sparse_matrix.h" @@ -57,8 +58,8 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // e_blocks. For a definition of what an e_block is, please see // explicit_schur_complement_solver.h num_row_blocks_e_ = 0; - for (int r = 0; r < bs->rows.size(); ++r) { - const std::vector<Cell>& cells = bs->rows[r].cells; + for (const auto& row : bs->rows) { + const std::vector<Cell>& cells = row.cells; if (cells[0].block_id < num_col_blocks_e_) { ++num_row_blocks_e_; } @@ -80,10 +81,6 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: CHECK_EQ(num_cols_e_ + num_cols_f_, matrix_.num_cols()); } -template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: - ~PartitionedMatrixView() {} - // The next four methods don't seem to be particularly cache // friendly. This is an artifact of how the BlockStructure of the // input matrix is constructed. These methods will benefit from @@ -145,13 +142,13 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: const int row_block_pos = bs->rows[r].block.position; const int row_block_size = bs->rows[r].block.size; const std::vector<Cell>& cells = bs->rows[r].cells; - for (int c = 0; c < cells.size(); ++c) { - const int col_block_id = cells[c].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_pos = bs->cols[col_block_id].position; const int col_block_size = bs->cols[col_block_id].size; // clang-format off MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values + cells[c].position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, x + col_block_pos - num_cols_e_, y + row_block_pos); // clang-format on @@ -215,13 +212,13 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: const int row_block_pos = bs->rows[r].block.position; const int row_block_size = bs->rows[r].block.size; const std::vector<Cell>& cells = bs->rows[r].cells; - for (int c = 0; c < cells.size(); ++c) { - const int col_block_id = cells[c].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_pos = bs->cols[col_block_id].position; const int col_block_size = bs->cols[col_block_id].size; // clang-format off MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( - values + cells[c].position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, x + row_block_pos, y + col_block_pos - num_cols_e_); // clang-format on @@ -235,13 +232,12 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // and return a BlockSparseMatrix with the this block structure. The // caller owns the result. template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -BlockSparseMatrix* +std::unique_ptr<BlockSparseMatrix> PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: CreateBlockDiagonalMatrixLayout(int start_col_block, int end_col_block) const { const CompressedRowBlockStructure* bs = matrix_.block_structure(); - CompressedRowBlockStructure* block_diagonal_structure = - new CompressedRowBlockStructure; + auto* block_diagonal_structure = new CompressedRowBlockStructure; int block_position = 0; int diagonal_cell_position = 0; @@ -250,16 +246,16 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // each column block. for (int c = start_col_block; c < end_col_block; ++c) { const Block& block = bs->cols[c]; - block_diagonal_structure->cols.push_back(Block()); + block_diagonal_structure->cols.emplace_back(); Block& diagonal_block = block_diagonal_structure->cols.back(); diagonal_block.size = block.size; diagonal_block.position = block_position; - block_diagonal_structure->rows.push_back(CompressedRow()); + block_diagonal_structure->rows.emplace_back(); CompressedRow& row = block_diagonal_structure->rows.back(); row.block = diagonal_block; - row.cells.push_back(Cell()); + row.cells.emplace_back(); Cell& cell = row.cells.back(); cell.block_id = c - start_col_block; cell.position = diagonal_cell_position; @@ -270,28 +266,27 @@ PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // Build a BlockSparseMatrix with the just computed block // structure. - return new BlockSparseMatrix(block_diagonal_structure); + return std::make_unique<BlockSparseMatrix>(block_diagonal_structure); } template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -BlockSparseMatrix* PartitionedMatrixView<kRowBlockSize, - kEBlockSize, - kFBlockSize>::CreateBlockDiagonalEtE() - const { - BlockSparseMatrix* block_diagonal = +std::unique_ptr<BlockSparseMatrix> +PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: + CreateBlockDiagonalEtE() const { + std::unique_ptr<BlockSparseMatrix> block_diagonal = CreateBlockDiagonalMatrixLayout(0, num_col_blocks_e_); - UpdateBlockDiagonalEtE(block_diagonal); + UpdateBlockDiagonalEtE(block_diagonal.get()); return block_diagonal; } template <int kRowBlockSize, int kEBlockSize, int kFBlockSize> -BlockSparseMatrix* PartitionedMatrixView<kRowBlockSize, - kEBlockSize, - kFBlockSize>::CreateBlockDiagonalFtF() - const { - BlockSparseMatrix* block_diagonal = CreateBlockDiagonalMatrixLayout( - num_col_blocks_e_, num_col_blocks_e_ + num_col_blocks_f_); - UpdateBlockDiagonalFtF(block_diagonal); +std::unique_ptr<BlockSparseMatrix> +PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: + CreateBlockDiagonalFtF() const { + std::unique_ptr<BlockSparseMatrix> block_diagonal = + CreateBlockDiagonalMatrixLayout(num_col_blocks_e_, + num_col_blocks_e_ + num_col_blocks_f_); + UpdateBlockDiagonalFtF(block_diagonal.get()); return block_diagonal; } @@ -366,8 +361,8 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: for (int r = num_row_blocks_e_; r < bs->rows.size(); ++r) { const int row_block_size = bs->rows[r].block.size; const std::vector<Cell>& cells = bs->rows[r].cells; - for (int c = 0; c < cells.size(); ++c) { - const int col_block_id = cells[c].block_id; + for (const auto& cell : cells) { + const int col_block_id = cell.block_id; const int col_block_size = bs->cols[col_block_id].size; const int diagonal_block_id = col_block_id - num_col_blocks_e_; const int cell_position = @@ -376,8 +371,8 @@ void PartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>:: // clang-format off MatrixTransposeMatrixMultiply <Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, 1>( - values + cells[c].position, row_block_size, col_block_size, - values + cells[c].position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, + values + cell.position, row_block_size, col_block_size, block_diagonal->mutable_values() + cell_position, 0, 0, col_block_size, col_block_size); // clang-format on diff --git a/extern/ceres/internal/ceres/partitioned_matrix_view_template.py b/extern/ceres/internal/ceres/partitioned_matrix_view_template.py deleted file mode 100644 index 05a25bf8335..00000000000 --- a/extern/ceres/internal/ceres/partitioned_matrix_view_template.py +++ /dev/null @@ -1,151 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2015 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: sameeragarwal@google.com (Sameer Agarwal) -# -# Script for explicitly generating template specialization of the -# PartitionedMatrixView class. Explicitly generating these -# instantiations in separate .cc files breaks the compilation into -# separate compilation unit rather than one large cc file. -# -# This script creates two sets of files. -# -# 1. partitioned_matrix_view_x_x_x.cc -# where the x indicates the template parameters and -# -# 2. partitioned_matrix_view.cc -# -# that contains a factory function for instantiating these classes -# based on runtime parameters. -# -# The list of tuples, specializations indicates the set of -# specializations that is generated. - -HEADER = """// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) -// -// Template specialization of PartitionedMatrixView. -// -// ======================================== -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -//========================================= -// -// This file is generated using generate_template_specializations.py. -""" - -DYNAMIC_FILE = """ -#include "ceres/partitioned_matrix_view_impl.h" - -namespace ceres { -namespace internal { - -template class PartitionedMatrixView<%s, - %s, - %s>; - -} // namespace internal -} // namespace ceres -""" - -SPECIALIZATION_FILE = """ -// This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" - -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION - -#include "ceres/partitioned_matrix_view_impl.h" - -namespace ceres { -namespace internal { - -template class PartitionedMatrixView<%s, %s, %s>; - -} // namespace internal -} // namespace ceres - -#endif // CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -FACTORY_FILE_HEADER = """ -#include "ceres/linear_solver.h" -#include "ceres/partitioned_matrix_view.h" - -namespace ceres { -namespace internal { - -PartitionedMatrixViewBase* PartitionedMatrixViewBase::Create( - const LinearSolver::Options& options, const BlockSparseMatrix& matrix) { -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION -""" -FACTORY = """ return new PartitionedMatrixView<%s, %s, %s>(matrix, - options.elimination_groups[0]);""" - -FACTORY_FOOTER = """ -#endif - VLOG(1) << "Template specializations not found for <" - << options.row_block_size << "," << options.e_block_size << "," - << options.f_block_size << ">"; - return new PartitionedMatrixView<Eigen::Dynamic, - Eigen::Dynamic, - Eigen::Dynamic>( - matrix, options.elimination_groups[0]); -}; - -} // namespace internal -} // namespace ceres -""" diff --git a/extern/ceres/internal/ceres/polynomial.cc b/extern/ceres/internal/ceres/polynomial.cc index 20812f4de81..96267aae97f 100644 --- a/extern/ceres/internal/ceres/polynomial.cc +++ b/extern/ceres/internal/ceres/polynomial.cc @@ -37,7 +37,7 @@ #include "Eigen/Dense" #include "ceres/function_sample.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" namespace ceres { @@ -128,12 +128,12 @@ void FindLinearPolynomialRoots(const Vector& polynomial, Vector* real, Vector* imaginary) { CHECK_EQ(polynomial.size(), 2); - if (real != NULL) { + if (real != nullptr) { real->resize(1); (*real)(0) = -polynomial(1) / polynomial(0); } - if (imaginary != NULL) { + if (imaginary != nullptr) { imaginary->setZero(1); } } @@ -147,16 +147,16 @@ void FindQuadraticPolynomialRoots(const Vector& polynomial, const double c = polynomial(2); const double D = b * b - 4 * a * c; const double sqrt_D = sqrt(fabs(D)); - if (real != NULL) { + if (real != nullptr) { real->setZero(2); } - if (imaginary != NULL) { + if (imaginary != nullptr) { imaginary->setZero(2); } // Real roots. if (D >= 0) { - if (real != NULL) { + if (real != nullptr) { // Stable quadratic roots according to BKP Horn. // http://people.csail.mit.edu/bkph/articles/Quadratics.pdf if (b >= 0) { @@ -171,11 +171,11 @@ void FindQuadraticPolynomialRoots(const Vector& polynomial, } // Use the normal quadratic formula for the complex case. - if (real != NULL) { + if (real != nullptr) { (*real)(0) = -b / (2.0 * a); (*real)(1) = -b / (2.0 * a); } - if (imaginary != NULL) { + if (imaginary != nullptr) { (*imaginary)(0) = sqrt_D / (2.0 * a); (*imaginary)(1) = -sqrt_D / (2.0 * a); } @@ -240,14 +240,14 @@ bool FindPolynomialRoots(const Vector& polynomial_in, } // Output roots - if (real != NULL) { + if (real != nullptr) { *real = solver.eigenvalues().real(); } else { - LOG(WARNING) << "NULL pointer passed as real argument to " + LOG(WARNING) << "nullptr pointer passed as real argument to " << "FindPolynomialRoots. Real parts of the roots will not " << "be returned."; } - if (imaginary != NULL) { + if (imaginary != nullptr) { *imaginary = solver.eigenvalues().imag(); } return true; @@ -304,7 +304,7 @@ void MinimizePolynomial(const Vector& polynomial, const Vector derivative = DifferentiatePolynomial(polynomial); Vector roots_real; - if (!FindPolynomialRoots(derivative, &roots_real, NULL)) { + if (!FindPolynomialRoots(derivative, &roots_real, nullptr)) { LOG(WARNING) << "Unable to find the critical points of " << "the interpolating polynomial."; return; @@ -376,8 +376,7 @@ void MinimizeInterpolatingPolynomial(const vector<FunctionSample>& samples, double* optimal_value) { const Vector polynomial = FindInterpolatingPolynomial(samples); MinimizePolynomial(polynomial, x_min, x_max, optimal_x, optimal_value); - for (int i = 0; i < samples.size(); ++i) { - const FunctionSample& sample = samples[i]; + for (const auto& sample : samples) { if ((sample.x < x_min) || (sample.x > x_max)) { continue; } diff --git a/extern/ceres/internal/ceres/polynomial.h b/extern/ceres/internal/ceres/polynomial.h index 20071f2c693..3ca753c4618 100644 --- a/extern/ceres/internal/ceres/polynomial.h +++ b/extern/ceres/internal/ceres/polynomial.h @@ -34,8 +34,9 @@ #include <vector> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -49,6 +50,7 @@ struct FunctionSample; // and are given by a vector of coefficients of size N + 1. // Evaluate the polynomial at x using the Horner scheme. +CERES_NO_EXPORT inline double EvaluatePolynomial(const Vector& polynomial, double x) { double v = 0.0; for (int i = 0; i < polynomial.size(); ++i) { @@ -64,15 +66,16 @@ inline double EvaluatePolynomial(const Vector& polynomial, double x) { // Failure indicates that the polynomial is invalid (of size 0) or // that the eigenvalues of the companion matrix could not be computed. // On failure, a more detailed message will be written to LOG(ERROR). -// If real is not NULL, the real parts of the roots will be returned in it. -// Likewise, if imaginary is not NULL, imaginary parts will be returned in it. -CERES_EXPORT_INTERNAL bool FindPolynomialRoots(const Vector& polynomial, - Vector* real, - Vector* imaginary); +// If real is not nullptr, the real parts of the roots will be returned in it. +// Likewise, if imaginary is not nullptr, imaginary parts will be returned in +// it. +CERES_NO_EXPORT bool FindPolynomialRoots(const Vector& polynomial, + Vector* real, + Vector* imaginary); // Return the derivative of the given polynomial. It is assumed that // the input polynomial is at least of degree zero. -CERES_EXPORT_INTERNAL Vector DifferentiatePolynomial(const Vector& polynomial); +CERES_NO_EXPORT Vector DifferentiatePolynomial(const Vector& polynomial); // Find the minimum value of the polynomial in the interval [x_min, // x_max]. The minimum is obtained by computing all the roots of the @@ -80,11 +83,11 @@ CERES_EXPORT_INTERNAL Vector DifferentiatePolynomial(const Vector& polynomial); // interval [x_min, x_max] are considered as well as the end points // x_min and x_max. Since polynomials are differentiable functions, // this ensures that the true minimum is found. -CERES_EXPORT_INTERNAL void MinimizePolynomial(const Vector& polynomial, - double x_min, - double x_max, - double* optimal_x, - double* optimal_value); +CERES_NO_EXPORT void MinimizePolynomial(const Vector& polynomial, + double x_min, + double x_max, + double* optimal_x, + double* optimal_value); // Given a set of function value and/or gradient samples, find a // polynomial whose value and gradients are exactly equal to the ones @@ -97,7 +100,7 @@ CERES_EXPORT_INTERNAL void MinimizePolynomial(const Vector& polynomial, // Of course its possible to sample a polynomial any number of times, // in which case, generally speaking the spurious higher order // coefficients will be zero. -CERES_EXPORT_INTERNAL Vector +CERES_NO_EXPORT Vector FindInterpolatingPolynomial(const std::vector<FunctionSample>& samples); // Interpolate the function described by samples with a polynomial, @@ -106,7 +109,7 @@ FindInterpolatingPolynomial(const std::vector<FunctionSample>& samples); // finding algorithms may fail due to numerical difficulties. But the // function is guaranteed to return its best guess of an answer, by // considering the samples and the end points as possible solutions. -CERES_EXPORT_INTERNAL void MinimizeInterpolatingPolynomial( +CERES_NO_EXPORT void MinimizeInterpolatingPolynomial( const std::vector<FunctionSample>& samples, double x_min, double x_max, @@ -116,4 +119,6 @@ CERES_EXPORT_INTERNAL void MinimizeInterpolatingPolynomial( } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_POLYNOMIAL_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/preconditioner.cc b/extern/ceres/internal/ceres/preconditioner.cc index 69ba04db8f5..17b9629cf94 100644 --- a/extern/ceres/internal/ceres/preconditioner.cc +++ b/extern/ceres/internal/ceres/preconditioner.cc @@ -35,7 +35,7 @@ namespace ceres { namespace internal { -Preconditioner::~Preconditioner() {} +Preconditioner::~Preconditioner() = default; PreconditionerType Preconditioner::PreconditionerForZeroEBlocks( PreconditionerType preconditioner_type) { @@ -53,7 +53,8 @@ SparseMatrixPreconditionerWrapper::SparseMatrixPreconditionerWrapper( CHECK(matrix != nullptr); } -SparseMatrixPreconditionerWrapper::~SparseMatrixPreconditionerWrapper() {} +SparseMatrixPreconditionerWrapper::~SparseMatrixPreconditionerWrapper() = + default; bool SparseMatrixPreconditionerWrapper::UpdateImpl(const SparseMatrix& A, const double* D) { diff --git a/extern/ceres/internal/ceres/preconditioner.h b/extern/ceres/internal/ceres/preconditioner.h index dd843b01ce3..6433cc7dd38 100644 --- a/extern/ceres/internal/ceres/preconditioner.h +++ b/extern/ceres/internal/ceres/preconditioner.h @@ -36,7 +36,8 @@ #include "ceres/casts.h" #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/context_impl.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" @@ -47,7 +48,7 @@ namespace internal { class BlockSparseMatrix; class SparseMatrix; -class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { +class CERES_NO_EXPORT Preconditioner : public LinearOperator { public: struct Options { PreconditionerType type = JACOBI; @@ -115,7 +116,7 @@ class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { static PreconditionerType PreconditionerForZeroEBlocks( PreconditionerType preconditioner_type); - virtual ~Preconditioner(); + ~Preconditioner() override; // Update the numerical value of the preconditioner for the linear // system: @@ -126,7 +127,7 @@ class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { // for some vector b. It is important that the matrix A have the // same block structure as the one used to construct this object. // - // D can be NULL, in which case its interpreted as a diagonal matrix + // D can be nullptr, in which case its interpreted as a diagonal matrix // of size zero. virtual bool Update(const LinearOperator& A, const double* D) = 0; @@ -147,9 +148,8 @@ class CERES_EXPORT_INTERNAL Preconditioner : public LinearOperator { // other preconditioners that depend on the particular matrix layout of // the underlying linear operator. template <typename MatrixType> -class TypedPreconditioner : public Preconditioner { +class CERES_NO_EXPORT TypedPreconditioner : public Preconditioner { public: - virtual ~TypedPreconditioner() {} bool Update(const LinearOperator& A, const double* D) final { return UpdateImpl(*down_cast<const MatrixType*>(&A), D); } @@ -161,28 +161,31 @@ class TypedPreconditioner : public Preconditioner { // Preconditioners that depend on access to the low level structure // of a SparseMatrix. // clang-format off -typedef TypedPreconditioner<SparseMatrix> SparseMatrixPreconditioner; -typedef TypedPreconditioner<BlockSparseMatrix> BlockSparseMatrixPreconditioner; -typedef TypedPreconditioner<CompressedRowSparseMatrix> CompressedRowSparseMatrixPreconditioner; +using SparseMatrixPreconditioner = TypedPreconditioner<SparseMatrix>; +using BlockSparseMatrixPreconditioner = TypedPreconditioner<BlockSparseMatrix>; +using CompressedRowSparseMatrixPreconditioner = TypedPreconditioner<CompressedRowSparseMatrix>; // clang-format on // Wrap a SparseMatrix object as a preconditioner. -class SparseMatrixPreconditionerWrapper : public SparseMatrixPreconditioner { +class CERES_NO_EXPORT SparseMatrixPreconditionerWrapper final + : public SparseMatrixPreconditioner { public: // Wrapper does NOT take ownership of the matrix pointer. explicit SparseMatrixPreconditionerWrapper(const SparseMatrix* matrix); - virtual ~SparseMatrixPreconditionerWrapper(); + ~SparseMatrixPreconditionerWrapper() override; // Preconditioner interface - virtual void RightMultiply(const double* x, double* y) const; - virtual int num_rows() const; + void RightMultiply(const double* x, double* y) const override; + int num_rows() const override; private: - virtual bool UpdateImpl(const SparseMatrix& A, const double* D); + bool UpdateImpl(const SparseMatrix& A, const double* D) override; const SparseMatrix* matrix_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/preprocessor.cc b/extern/ceres/internal/ceres/preprocessor.cc index 6a67d385645..44f0974dc5a 100644 --- a/extern/ceres/internal/ceres/preprocessor.cc +++ b/extern/ceres/internal/ceres/preprocessor.cc @@ -30,6 +30,8 @@ #include "ceres/preprocessor.h" +#include <memory> + #include "ceres/callbacks.h" #include "ceres/gradient_checking_cost_function.h" #include "ceres/line_search_preprocessor.h" @@ -41,22 +43,26 @@ namespace ceres { namespace internal { -Preprocessor* Preprocessor::Create(MinimizerType minimizer_type) { +std::unique_ptr<Preprocessor> Preprocessor::Create( + MinimizerType minimizer_type) { if (minimizer_type == TRUST_REGION) { - return new TrustRegionPreprocessor; + return std::make_unique<TrustRegionPreprocessor>(); } if (minimizer_type == LINE_SEARCH) { - return new LineSearchPreprocessor; + return std::make_unique<LineSearchPreprocessor>(); } LOG(FATAL) << "Unknown minimizer_type: " << minimizer_type; - return NULL; + return nullptr; } -Preprocessor::~Preprocessor() {} +Preprocessor::~Preprocessor() = default; void ChangeNumThreadsIfNeeded(Solver::Options* options) { + if (options->num_threads == 1) { + return; + } const int num_threads_available = MaxNumThreadsAvailable(); if (options->num_threads > num_threads_available) { LOG(WARNING) << "Specified options.num_threads: " << options->num_threads @@ -82,15 +88,15 @@ void SetupCommonMinimizerOptions(PreprocessedProblem* pp) { minimizer_options.evaluator = pp->evaluator; if (options.logging_type != SILENT) { - pp->logging_callback.reset(new LoggingCallback( - options.minimizer_type, options.minimizer_progress_to_stdout)); + pp->logging_callback = std::make_unique<LoggingCallback>( + options.minimizer_type, options.minimizer_progress_to_stdout); minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), pp->logging_callback.get()); } if (options.update_state_every_iteration) { - pp->state_updating_callback.reset( - new StateUpdatingCallback(program, reduced_parameters)); + pp->state_updating_callback = + std::make_unique<StateUpdatingCallback>(program, reduced_parameters); // This must get pushed to the front of the callbacks so that it // is run before any of the user callbacks. minimizer_options.callbacks.insert(minimizer_options.callbacks.begin(), diff --git a/extern/ceres/internal/ceres/preprocessor.h b/extern/ceres/internal/ceres/preprocessor.h index ec56c6e430a..b5db80af7e6 100644 --- a/extern/ceres/internal/ceres/preprocessor.h +++ b/extern/ceres/internal/ceres/preprocessor.h @@ -37,8 +37,9 @@ #include "ceres/coordinate_descent_minimizer.h" #include "ceres/evaluator.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/linear_solver.h" #include "ceres/minimizer.h" @@ -67,10 +68,10 @@ struct PreprocessedProblem; // // The output of the Preprocessor is stored in a PreprocessedProblem // object. -class CERES_EXPORT_INTERNAL Preprocessor { +class CERES_NO_EXPORT Preprocessor { public: // Factory. - static Preprocessor* Create(MinimizerType minimizer_type); + static std::unique_ptr<Preprocessor> Create(MinimizerType minimizer_type); virtual ~Preprocessor(); virtual bool Preprocess(const Solver::Options& options, ProblemImpl* problem, @@ -79,8 +80,8 @@ class CERES_EXPORT_INTERNAL Preprocessor { // A PreprocessedProblem is the result of running the Preprocessor on // a Problem and Solver::Options object. -struct PreprocessedProblem { - PreprocessedProblem() : fixed_cost(0.0) {} +struct CERES_NO_EXPORT PreprocessedProblem { + PreprocessedProblem() = default; std::string error; Solver::Options options; @@ -100,7 +101,7 @@ struct PreprocessedProblem { std::vector<double*> removed_parameter_blocks; Vector reduced_parameters; - double fixed_cost; + double fixed_cost{0.0}; }; // Common functions used by various preprocessors. @@ -108,14 +109,18 @@ struct PreprocessedProblem { // If the user has specified a num_threads > the maximum number of threads // available from the compiled threading model, bound the number of threads // to the maximum. +CERES_NO_EXPORT void ChangeNumThreadsIfNeeded(Solver::Options* options); // Extract the effective parameter vector from the preprocessed // problem and setup bits of the Minimizer::Options object that are // common to all Preprocessors. +CERES_NO_EXPORT void SetupCommonMinimizerOptions(PreprocessedProblem* pp); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PREPROCESSOR_H_ diff --git a/extern/ceres/internal/ceres/problem.cc b/extern/ceres/internal/ceres/problem.cc index f3ffd546ef7..4269ca3ebc3 100644 --- a/extern/ceres/internal/ceres/problem.cc +++ b/extern/ceres/internal/ceres/problem.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,6 +31,7 @@ #include "ceres/problem.h" +#include <memory> #include <vector> #include "ceres/crs_matrix.h" @@ -46,7 +47,7 @@ Problem::Problem(const Problem::Options& options) // Not inline defaulted in declaration due to use of std::unique_ptr. Problem::Problem(Problem&&) = default; Problem& Problem::operator=(Problem&&) = default; -Problem::~Problem() {} +Problem::~Problem() = default; ResidualBlockId Problem::AddResidualBlock( CostFunction* cost_function, @@ -76,6 +77,10 @@ void Problem::AddParameterBlock(double* values, impl_->AddParameterBlock(values, size, local_parameterization); } +void Problem::AddParameterBlock(double* values, int size, Manifold* manifold) { + impl_->AddParameterBlock(values, size, manifold); +} + void Problem::RemoveResidualBlock(ResidualBlockId residual_block) { impl_->RemoveResidualBlock(residual_block); } @@ -106,6 +111,22 @@ const LocalParameterization* Problem::GetParameterization( return impl_->GetParameterization(values); } +bool Problem::HasParameterization(const double* values) const { + return impl_->HasParameterization(values); +} + +void Problem::SetManifold(double* values, Manifold* manifold) { + impl_->SetManifold(values, manifold); +} + +const Manifold* Problem::GetManifold(const double* values) const { + return impl_->GetManifold(values); +} + +bool Problem::HasManifold(const double* values) const { + return impl_->HasManifold(values); +} + void Problem::SetParameterLowerBound(double* values, int index, double lower_bound) { @@ -169,12 +190,16 @@ int Problem::NumResidualBlocks() const { return impl_->NumResidualBlocks(); } int Problem::NumResiduals() const { return impl_->NumResiduals(); } -int Problem::ParameterBlockSize(const double* parameter_block) const { - return impl_->ParameterBlockSize(parameter_block); +int Problem::ParameterBlockSize(const double* values) const { + return impl_->ParameterBlockSize(values); +} + +int Problem::ParameterBlockLocalSize(const double* values) const { + return impl_->ParameterBlockTangentSize(values); } -int Problem::ParameterBlockLocalSize(const double* parameter_block) const { - return impl_->ParameterBlockLocalSize(parameter_block); +int Problem::ParameterBlockTangentSize(const double* values) const { + return impl_->ParameterBlockTangentSize(values); } bool Problem::HasParameterBlock(const double* values) const { diff --git a/extern/ceres/internal/ceres/problem_impl.cc b/extern/ceres/internal/ceres/problem_impl.cc index 3155bc3569e..01a22c128be 100644 --- a/extern/ceres/internal/ceres/problem_impl.cc +++ b/extern/ceres/internal/ceres/problem_impl.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -49,9 +49,11 @@ #include "ceres/crs_matrix.h" #include "ceres/evaluation_callback.h" #include "ceres/evaluator.h" +#include "ceres/internal/export.h" #include "ceres/internal/fixed_array.h" -#include "ceres/internal/port.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" +#include "ceres/manifold_adapter.h" #include "ceres/map_util.h" #include "ceres/parameter_block.h" #include "ceres/program.h" @@ -64,11 +66,6 @@ namespace ceres { namespace internal { - -using std::map; -using std::string; -using std::vector; - namespace { // Returns true if two regions of memory, a and b, with sizes size_a and size_b // respectively, overlap. @@ -130,7 +127,7 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values, << "for a parameter with size " << size; // Ignore the request if there is a block for the given pointer already. - ParameterMap::iterator it = parameter_block_map_.find(values); + auto it = parameter_block_map_.find(values); if (it != parameter_block_map_.end()) { if (!options_.disable_all_safety_checks) { int existing_size = it->second->Size(); @@ -146,11 +143,11 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values, // Before adding the parameter block, also check that it doesn't alias any // other parameter blocks. if (!parameter_block_map_.empty()) { - ParameterMap::iterator lb = parameter_block_map_.lower_bound(values); + auto lb = parameter_block_map_.lower_bound(values); // If lb is not the first block, check the previous block for aliasing. if (lb != parameter_block_map_.begin()) { - ParameterMap::iterator previous = lb; + auto previous = lb; --previous; CheckForNoAliasing( previous->first, previous->second->Size(), values, size); @@ -165,7 +162,7 @@ ParameterBlock* ProblemImpl::InternalAddParameterBlock(double* values, // Pass the index of the new parameter block as well to keep the index in // sync with the position of the parameter in the program's parameter vector. - ParameterBlock* new_parameter_block = + auto* new_parameter_block = new ParameterBlock(values, size, program_->parameter_blocks_.size()); // For dynamic problems, add the list of dependent residual blocks, which is @@ -192,7 +189,7 @@ void ProblemImpl::InternalRemoveResidualBlock(ResidualBlock* residual_block) { residual_block); } - ResidualBlockSet::iterator it = residual_block_set_.find(residual_block); + auto it = residual_block_set_.find(residual_block); residual_block_set_.erase(it); } DeleteBlockInVector(program_->mutable_residual_blocks(), residual_block); @@ -207,13 +204,13 @@ void ProblemImpl::DeleteBlock(ResidualBlock* residual_block) { // The const casts here are legit, since ResidualBlock holds these // pointers as const pointers but we have ownership of them and // have the right to destroy them when the destructor is called. - CostFunction* cost_function = + auto* cost_function = const_cast<CostFunction*>(residual_block->cost_function()); if (options_.cost_function_ownership == TAKE_OWNERSHIP) { DecrementValueOrDeleteKey(cost_function, &cost_function_ref_count_); } - LossFunction* loss_function = + auto* loss_function = const_cast<LossFunction*>(residual_block->loss_function()); if (options_.loss_function_ownership == TAKE_OWNERSHIP && loss_function != nullptr) { @@ -225,15 +222,7 @@ void ProblemImpl::DeleteBlock(ResidualBlock* residual_block) { // Deletes the parameter block in question, assuming there are no other // references to it inside the problem (e.g. by any residual blocks). -// Referenced parameterizations are tucked away for future deletion, since it -// is not possible to know whether other parts of the problem depend on them -// without doing a full scan. void ProblemImpl::DeleteBlock(ParameterBlock* parameter_block) { - if (options_.local_parameterization_ownership == TAKE_OWNERSHIP && - parameter_block->local_parameterization() != nullptr) { - local_parameterizations_to_delete_.push_back( - parameter_block->mutable_local_parameterization()); - } parameter_block_map_.erase(parameter_block->mutable_user_state()); delete parameter_block; } @@ -264,14 +253,18 @@ ProblemImpl::~ProblemImpl() { } // Collect the unique parameterizations and delete the parameters. - for (int i = 0; i < program_->parameter_blocks_.size(); ++i) { - DeleteBlock(program_->parameter_blocks_[i]); + for (auto* parameter_block : program_->parameter_blocks_) { + DeleteBlock(parameter_block); } // Delete the owned parameterizations. STLDeleteUniqueContainerPointers(local_parameterizations_to_delete_.begin(), local_parameterizations_to_delete_.end()); + // Delete the owned manifolds. + STLDeleteUniqueContainerPointers(manifolds_to_delete_.begin(), + manifolds_to_delete_.end()); + if (context_impl_owned_) { delete context_impl_; } @@ -286,7 +279,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( CHECK_EQ(num_parameter_blocks, cost_function->parameter_block_sizes().size()); // Check the sizes match. - const vector<int32_t>& parameter_block_sizes = + const std::vector<int32_t>& parameter_block_sizes = cost_function->parameter_block_sizes(); if (!options_.disable_all_safety_checks) { @@ -295,7 +288,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( << "that the cost function expects."; // Check for duplicate parameter blocks. - vector<double*> sorted_parameter_blocks( + std::vector<double*> sorted_parameter_blocks( parameter_blocks, parameter_blocks + num_parameter_blocks); sort(sorted_parameter_blocks.begin(), sorted_parameter_blocks.end()); const bool has_duplicate_items = @@ -303,7 +296,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( sorted_parameter_blocks.end()) != sorted_parameter_blocks.end()); if (has_duplicate_items) { - string blocks; + std::string blocks; for (int i = 0; i < num_parameter_blocks; ++i) { blocks += StringPrintf(" %p ", parameter_blocks[i]); } @@ -315,7 +308,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( } // Add parameter blocks and convert the double*'s to parameter blocks. - vector<ParameterBlock*> parameter_block_ptrs(num_parameter_blocks); + std::vector<ParameterBlock*> parameter_block_ptrs(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { parameter_block_ptrs[i] = InternalAddParameterBlock( parameter_blocks[i], parameter_block_sizes[i]); @@ -334,7 +327,7 @@ ResidualBlockId ProblemImpl::AddResidualBlock( } } - ResidualBlock* new_residual_block = + auto* new_residual_block = new ResidualBlock(cost_function, loss_function, parameter_block_ptrs, @@ -372,12 +365,50 @@ void ProblemImpl::AddParameterBlock(double* values, int size) { InternalAddParameterBlock(values, size); } +void ProblemImpl::InternalSetParameterization( + double* values, + ParameterBlock* parameter_block, + LocalParameterization* local_parameterization) { + parameter_block_to_local_param_[values] = local_parameterization; + Manifold* manifold = nullptr; + if (local_parameterization != nullptr) { + if (options_.local_parameterization_ownership == TAKE_OWNERSHIP) { + local_parameterizations_to_delete_.push_back(local_parameterization); + } + + manifold = new ManifoldAdapter(local_parameterization); + // Add the manifold to manifolds_to_delete_ unconditionally since + // we own it and it will need to be deleted. + manifolds_to_delete_.push_back(manifold); + } + + parameter_block->SetManifold(manifold); +} + +void ProblemImpl::InternalSetManifold(double* values, + ParameterBlock* parameter_block, + Manifold* manifold) { + // Reset any association between this parameter block and a local + // parameterization. This only needs done while we are in the transition from + // LocalParameterization to Manifold. + parameter_block_to_local_param_[values] = nullptr; + if (manifold != nullptr && options_.manifold_ownership == TAKE_OWNERSHIP) { + manifolds_to_delete_.push_back(manifold); + } + parameter_block->SetManifold(manifold); +} + void ProblemImpl::AddParameterBlock( double* values, int size, LocalParameterization* local_parameterization) { ParameterBlock* parameter_block = InternalAddParameterBlock(values, size); - if (local_parameterization != nullptr) { - parameter_block->SetParameterization(local_parameterization); - } + InternalSetParameterization(values, parameter_block, local_parameterization); +} + +void ProblemImpl::AddParameterBlock(double* values, + int size, + Manifold* manifold) { + ParameterBlock* parameter_block = InternalAddParameterBlock(values, size); + InternalSetManifold(values, parameter_block, manifold); } // Delete a block from a vector of blocks, maintaining the indexing invariant. @@ -385,7 +416,7 @@ void ProblemImpl::AddParameterBlock( // vector over the element to remove, then popping the last element. It // destroys the ordering in the interest of speed. template <typename Block> -void ProblemImpl::DeleteBlockInVector(vector<Block*>* mutable_blocks, +void ProblemImpl::DeleteBlockInVector(std::vector<Block*>* mutable_blocks, Block* block_to_remove) { CHECK_EQ((*mutable_blocks)[block_to_remove->index()], block_to_remove) << "You found a Ceres bug! \n" @@ -411,7 +442,7 @@ void ProblemImpl::RemoveResidualBlock(ResidualBlock* residual_block) { CHECK(residual_block != nullptr); // Verify that residual_block identifies a residual in the current problem. - const string residual_not_found_message = StringPrintf( + const std::string residual_not_found_message = StringPrintf( "Residual block to remove: %p not found. This usually means " "one of three things have happened:\n" " 1) residual_block is uninitialised and points to a random " @@ -449,11 +480,11 @@ void ProblemImpl::RemoveParameterBlock(const double* values) { if (options_.enable_fast_removal) { // Copy the dependent residuals from the parameter block because the set of // dependents will change after each call to RemoveResidualBlock(). - vector<ResidualBlock*> residual_blocks_to_remove( + std::vector<ResidualBlock*> residual_blocks_to_remove( parameter_block->mutable_residual_blocks()->begin(), parameter_block->mutable_residual_blocks()->end()); - for (int i = 0; i < residual_blocks_to_remove.size(); ++i) { - InternalRemoveResidualBlock(residual_blocks_to_remove[i]); + for (auto* residual_block : residual_blocks_to_remove) { + InternalRemoveResidualBlock(residual_block); } } else { // Scan all the residual blocks to remove ones that depend on the parameter @@ -518,20 +549,31 @@ void ProblemImpl::SetParameterization( << "you can set its local parameterization."; } - // If the parameter block already has a local parameterization and - // we are to take ownership of local parameterizations, then add it - // to local_parameterizations_to_delete_ for eventual deletion. - if (parameter_block->local_parameterization_ && - options_.local_parameterization_ownership == TAKE_OWNERSHIP) { - local_parameterizations_to_delete_.push_back( - parameter_block->local_parameterization_); + InternalSetParameterization(values, parameter_block, local_parameterization); +} + +void ProblemImpl::SetManifold(double* values, Manifold* manifold) { + ParameterBlock* parameter_block = + FindWithDefault(parameter_block_map_, values, nullptr); + if (parameter_block == nullptr) { + LOG(FATAL) << "Parameter block not found: " << values + << ". You must add the parameter block to the problem before " + << "you can set its manifold."; } - parameter_block->SetParameterization(local_parameterization); + InternalSetManifold(values, parameter_block, manifold); } const LocalParameterization* ProblemImpl::GetParameterization( const double* values) const { + return FindWithDefault(parameter_block_to_local_param_, values, nullptr); +} + +bool ProblemImpl::HasParameterization(const double* values) const { + return GetParameterization(values) != nullptr; +} + +const Manifold* ProblemImpl::GetManifold(const double* values) const { ParameterBlock* parameter_block = FindWithDefault( parameter_block_map_, const_cast<double*>(values), nullptr); if (parameter_block == nullptr) { @@ -540,7 +582,11 @@ const LocalParameterization* ProblemImpl::GetParameterization( << "you can get its local parameterization."; } - return parameter_block->local_parameterization(); + return parameter_block->manifold(); +} + +bool ProblemImpl::HasManifold(const double* values) const { + return GetManifold(values) != nullptr; } void ProblemImpl::SetParameterLowerBound(double* values, @@ -596,8 +642,8 @@ double ProblemImpl::GetParameterUpperBound(const double* values, bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, double* cost, - vector<double>* residuals, - vector<double>* gradient, + std::vector<double>* residuals, + std::vector<double>* gradient, CRSMatrix* jacobian) { if (cost == nullptr && residuals == nullptr && gradient == nullptr && jacobian == nullptr) { @@ -612,11 +658,11 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, ? evaluate_options.residual_blocks : program_->residual_blocks()); - const vector<double*>& parameter_block_ptrs = + const std::vector<double*>& parameter_block_ptrs = evaluate_options.parameter_blocks; - vector<ParameterBlock*> variable_parameter_blocks; - vector<ParameterBlock*>& parameter_blocks = + std::vector<ParameterBlock*> variable_parameter_blocks; + std::vector<ParameterBlock*>& parameter_blocks = *program.mutable_parameter_blocks(); if (parameter_block_ptrs.size() == 0) { @@ -649,11 +695,12 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, // columns of the jacobian, we need to make sure that they are // constant during evaluation and then make them variable again // after we are done. - vector<ParameterBlock*> all_parameter_blocks(program_->parameter_blocks()); - vector<ParameterBlock*> included_parameter_blocks( + std::vector<ParameterBlock*> all_parameter_blocks( + program_->parameter_blocks()); + std::vector<ParameterBlock*> included_parameter_blocks( program.parameter_blocks()); - vector<ParameterBlock*> excluded_parameter_blocks; + std::vector<ParameterBlock*> excluded_parameter_blocks; sort(all_parameter_blocks.begin(), all_parameter_blocks.end()); sort(included_parameter_blocks.begin(), included_parameter_blocks.end()); set_difference(all_parameter_blocks.begin(), @@ -663,8 +710,7 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, back_inserter(excluded_parameter_blocks)); variable_parameter_blocks.reserve(excluded_parameter_blocks.size()); - for (int i = 0; i < excluded_parameter_blocks.size(); ++i) { - ParameterBlock* parameter_block = excluded_parameter_blocks[i]; + for (auto* parameter_block : excluded_parameter_blocks) { if (!parameter_block->IsConstant()) { variable_parameter_blocks.push_back(parameter_block); parameter_block->SetConstant(); @@ -716,8 +762,8 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, std::unique_ptr<CompressedRowSparseMatrix> tmp_jacobian; if (jacobian != nullptr) { - tmp_jacobian.reset( - down_cast<CompressedRowSparseMatrix*>(evaluator->CreateJacobian())); + tmp_jacobian.reset(down_cast<CompressedRowSparseMatrix*>( + evaluator->CreateJacobian().release())); } // Point the state pointers to the user state pointers. This is @@ -749,8 +795,8 @@ bool ProblemImpl::Evaluate(const Problem::EvaluateOptions& evaluate_options, // Make the parameter blocks that were temporarily marked constant, // variable again. - for (int i = 0; i < variable_parameter_blocks.size(); ++i) { - variable_parameter_blocks[i]->SetVarying(); + for (auto* parameter_block : variable_parameter_blocks) { + parameter_block->SetVarying(); } if (status) { @@ -829,24 +875,25 @@ int ProblemImpl::ParameterBlockSize(const double* values) const { return parameter_block->Size(); } -int ProblemImpl::ParameterBlockLocalSize(const double* values) const { +int ProblemImpl::ParameterBlockTangentSize(const double* values) const { ParameterBlock* parameter_block = FindWithDefault( parameter_block_map_, const_cast<double*>(values), nullptr); if (parameter_block == nullptr) { LOG(FATAL) << "Parameter block not found: " << values << ". You must add the parameter block to the problem before " - << "you can get its local size."; + << "you can get its tangent size."; } - return parameter_block->LocalSize(); + return parameter_block->TangentSize(); } -bool ProblemImpl::HasParameterBlock(const double* parameter_block) const { - return (parameter_block_map_.find(const_cast<double*>(parameter_block)) != +bool ProblemImpl::HasParameterBlock(const double* values) const { + return (parameter_block_map_.find(const_cast<double*>(values)) != parameter_block_map_.end()); } -void ProblemImpl::GetParameterBlocks(vector<double*>* parameter_blocks) const { +void ProblemImpl::GetParameterBlocks( + std::vector<double*>* parameter_blocks) const { CHECK(parameter_blocks != nullptr); parameter_blocks->resize(0); parameter_blocks->reserve(parameter_block_map_.size()); @@ -856,14 +903,14 @@ void ProblemImpl::GetParameterBlocks(vector<double*>* parameter_blocks) const { } void ProblemImpl::GetResidualBlocks( - vector<ResidualBlockId>* residual_blocks) const { + std::vector<ResidualBlockId>* residual_blocks) const { CHECK(residual_blocks != nullptr); *residual_blocks = program().residual_blocks(); } void ProblemImpl::GetParameterBlocksForResidualBlock( const ResidualBlockId residual_block, - vector<double*>* parameter_blocks) const { + std::vector<double*>* parameter_blocks) const { int num_parameter_blocks = residual_block->NumParameterBlocks(); CHECK(parameter_blocks != nullptr); parameter_blocks->resize(num_parameter_blocks); @@ -884,7 +931,7 @@ const LossFunction* ProblemImpl::GetLossFunctionForResidualBlock( } void ProblemImpl::GetResidualBlocksForParameterBlock( - const double* values, vector<ResidualBlockId>* residual_blocks) const { + const double* values, std::vector<ResidualBlockId>* residual_blocks) const { ParameterBlock* parameter_block = FindWithDefault( parameter_block_map_, const_cast<double*>(values), nullptr); if (parameter_block == nullptr) { diff --git a/extern/ceres/internal/ceres/problem_impl.h b/extern/ceres/internal/ceres/problem_impl.h index 9abff3f19ae..22073b674f1 100644 --- a/extern/ceres/internal/ceres/problem_impl.h +++ b/extern/ceres/internal/ceres/problem_impl.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2019 Google Inc. All rights reserved. +// Copyright 2021 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -42,11 +42,15 @@ #include <array> #include <map> #include <memory> +#include <unordered_map> #include <unordered_set> #include <vector> #include "ceres/context_impl.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/internal/port.h" +#include "ceres/manifold.h" #include "ceres/problem.h" #include "ceres/types.h" @@ -63,12 +67,12 @@ namespace internal { class Program; class ResidualBlock; -class CERES_EXPORT_INTERNAL ProblemImpl { +class CERES_NO_EXPORT ProblemImpl { public: - typedef std::map<double*, ParameterBlock*> ParameterMap; - typedef std::unordered_set<ResidualBlock*> ResidualBlockSet; - typedef std::map<CostFunction*, int> CostFunctionRefCount; - typedef std::map<LossFunction*, int> LossFunctionRefCount; + using ParameterMap = std::map<double*, ParameterBlock*>; + using ResidualBlockSet = std::unordered_set<ResidualBlock*>; + using CostFunctionRefCount = std::map<CostFunction*, int>; + using LossFunctionRefCount = std::map<LossFunction*, int>; ProblemImpl(); explicit ProblemImpl(const Problem::Options& options); @@ -100,6 +104,8 @@ class CERES_EXPORT_INTERNAL ProblemImpl { int size, LocalParameterization* local_parameterization); + void AddParameterBlock(double* values, int size, Manifold* manifold); + void RemoveResidualBlock(ResidualBlock* residual_block); void RemoveParameterBlock(const double* values); @@ -110,6 +116,11 @@ class CERES_EXPORT_INTERNAL ProblemImpl { void SetParameterization(double* values, LocalParameterization* local_parameterization); const LocalParameterization* GetParameterization(const double* values) const; + bool HasParameterization(const double* values) const; + + void SetManifold(double* values, Manifold* manifold); + const Manifold* GetManifold(const double* values) const; + bool HasManifold(const double* values) const; void SetParameterLowerBound(double* values, int index, double lower_bound); void SetParameterUpperBound(double* values, int index, double upper_bound); @@ -134,10 +145,10 @@ class CERES_EXPORT_INTERNAL ProblemImpl { int NumResidualBlocks() const; int NumResiduals() const; - int ParameterBlockSize(const double* parameter_block) const; - int ParameterBlockLocalSize(const double* parameter_block) const; + int ParameterBlockSize(const double* values) const; + int ParameterBlockTangentSize(const double* values) const; - bool HasParameterBlock(const double* parameter_block) const; + bool HasParameterBlock(const double* values) const; void GetParameterBlocks(std::vector<double*>* parameter_blocks) const; void GetResidualBlocks(std::vector<ResidualBlockId>* residual_blocks) const; @@ -169,6 +180,14 @@ class CERES_EXPORT_INTERNAL ProblemImpl { private: ParameterBlock* InternalAddParameterBlock(double* values, int size); + void InternalSetParameterization( + double* values, + ParameterBlock* parameter_block, + LocalParameterization* local_parameterization); + void InternalSetManifold(double* values, + ParameterBlock* parameter_block, + Manifold* manifold); + void InternalRemoveResidualBlock(ResidualBlock* residual_block); // Delete the arguments in question. These differ from the Remove* functions @@ -194,23 +213,45 @@ class CERES_EXPORT_INTERNAL ProblemImpl { // The actual parameter and residual blocks. std::unique_ptr<internal::Program> program_; + // TODO(sameeragarwal): Unify the shared object handling across object types. + // Right now we are using vectors for LocalParameterization and Manifold + // objects and reference counting for CostFunctions and LossFunctions. Ideally + // this should be done uniformly. + // When removing parameter blocks, parameterizations have ambiguous // ownership. Instead of scanning the entire problem to see if the // parameterization is shared with other parameter blocks, buffer // them until destruction. - // - // TODO(keir): See if it makes sense to use sets instead. std::vector<LocalParameterization*> local_parameterizations_to_delete_; + // When removing parameter blocks, manifolds have ambiguous + // ownership. Instead of scanning the entire problem to see if the + // manifold is shared with other parameter blocks, buffer + // them until destruction. + std::vector<Manifold*> manifolds_to_delete_; + // For each cost function and loss function in the problem, a count // of the number of residual blocks that refer to them. When the // count goes to zero and the problem owns these objects, they are // destroyed. CostFunctionRefCount cost_function_ref_count_; LossFunctionRefCount loss_function_ref_count_; + + // Because we wrap LocalParameterization objects using a ManifoldAdapter, when + // the user calls GetParameterization we cannot use the same logic as + // GetManifold as the ParameterBlock object only returns a Manifold object. So + // this map stores the association between parameter blocks and local + // parameterizations. + // + // This is a temporary object which will be removed once the + // LocalParameterization to Manifold transition is complete. + std::unordered_map<const double*, LocalParameterization*> + parameter_block_to_local_param_; }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_PUBLIC_PROBLEM_IMPL_H_ diff --git a/extern/ceres/internal/ceres/program.cc b/extern/ceres/internal/ceres/program.cc index f1ded2e5d5a..1cb9ebcbe73 100644 --- a/extern/ceres/internal/ceres/program.cc +++ b/extern/ceres/internal/ceres/program.cc @@ -33,6 +33,7 @@ #include <algorithm> #include <map> #include <memory> +#include <string> #include <vector> #include "ceres/array_utils.h" @@ -40,9 +41,9 @@ #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/cost_function.h" #include "ceres/evaluator.h" -#include "ceres/internal/port.h" -#include "ceres/local_parameterization.h" +#include "ceres/internal/export.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" #include "ceres/map_util.h" #include "ceres/parameter_block.h" #include "ceres/problem.h" @@ -53,31 +54,19 @@ namespace ceres { namespace internal { -using std::max; -using std::set; -using std::string; -using std::vector; - -Program::Program() {} - -Program::Program(const Program& program) - : parameter_blocks_(program.parameter_blocks_), - residual_blocks_(program.residual_blocks_), - evaluation_callback_(program.evaluation_callback_) {} - -const vector<ParameterBlock*>& Program::parameter_blocks() const { +const std::vector<ParameterBlock*>& Program::parameter_blocks() const { return parameter_blocks_; } -const vector<ResidualBlock*>& Program::residual_blocks() const { +const std::vector<ResidualBlock*>& Program::residual_blocks() const { return residual_blocks_; } -vector<ParameterBlock*>* Program::mutable_parameter_blocks() { +std::vector<ParameterBlock*>* Program::mutable_parameter_blocks() { return ¶meter_blocks_; } -vector<ResidualBlock*>* Program::mutable_residual_blocks() { +std::vector<ResidualBlock*>* Program::mutable_residual_blocks() { return &residual_blocks_; } @@ -86,33 +75,32 @@ EvaluationCallback* Program::mutable_evaluation_callback() { } bool Program::StateVectorToParameterBlocks(const double* state) { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - if (!parameter_blocks_[i]->IsConstant() && - !parameter_blocks_[i]->SetState(state)) { + for (auto* parameter_block : parameter_blocks_) { + if (!parameter_block->IsConstant() && !parameter_block->SetState(state)) { return false; } - state += parameter_blocks_[i]->Size(); + state += parameter_block->Size(); } return true; } void Program::ParameterBlocksToStateVector(double* state) const { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->GetState(state); - state += parameter_blocks_[i]->Size(); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->GetState(state); + state += parameter_block->Size(); } } void Program::CopyParameterBlockStateToUserState() { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->GetState(parameter_blocks_[i]->mutable_user_state()); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->GetState(parameter_block->mutable_user_state()); } } bool Program::SetParameterBlockStatePtrsToUserStatePtrs() { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - if (!parameter_blocks_[i]->IsConstant() && - !parameter_blocks_[i]->SetState(parameter_blocks_[i]->user_state())) { + for (auto* parameter_block : parameter_blocks_) { + if (!parameter_block->IsConstant() && + !parameter_block->SetState(parameter_block->user_state())) { return false; } } @@ -122,13 +110,13 @@ bool Program::SetParameterBlockStatePtrsToUserStatePtrs() { bool Program::Plus(const double* state, const double* delta, double* state_plus_delta) const { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - if (!parameter_blocks_[i]->Plus(state, delta, state_plus_delta)) { + for (auto* parameter_block : parameter_blocks_) { + if (!parameter_block->Plus(state, delta, state_plus_delta)) { return false; } - state += parameter_blocks_[i]->Size(); - delta += parameter_blocks_[i]->LocalSize(); - state_plus_delta += parameter_blocks_[i]->Size(); + state += parameter_block->Size(); + delta += parameter_block->TangentSize(); + state_plus_delta += parameter_block->Size(); } return true; } @@ -136,8 +124,7 @@ bool Program::Plus(const double* state, void Program::SetParameterOffsetsAndIndex() { // Set positions for all parameters appearing as arguments to residuals to one // past the end of the parameter block array. - for (int i = 0; i < residual_blocks_.size(); ++i) { - ResidualBlock* residual_block = residual_blocks_[i]; + for (auto* residual_block : residual_blocks_) { for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) { residual_block->parameter_blocks()[j]->set_index(-1); } @@ -150,7 +137,7 @@ void Program::SetParameterOffsetsAndIndex() { parameter_blocks_[i]->set_state_offset(state_offset); parameter_blocks_[i]->set_delta_offset(delta_offset); state_offset += parameter_blocks_[i]->Size(); - delta_offset += parameter_blocks_[i]->LocalSize(); + delta_offset += parameter_blocks_[i]->TangentSize(); } } @@ -178,16 +165,15 @@ bool Program::IsValid() const { } state_offset += parameter_blocks_[i]->Size(); - delta_offset += parameter_blocks_[i]->LocalSize(); + delta_offset += parameter_blocks_[i]->TangentSize(); } return true; } -bool Program::ParameterBlocksAreFinite(string* message) const { +bool Program::ParameterBlocksAreFinite(std::string* message) const { CHECK(message != nullptr); - for (int i = 0; i < parameter_blocks_.size(); ++i) { - const ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { const double* array = parameter_block->user_state(); const int size = parameter_block->Size(); const int invalid_index = FindInvalidValue(size, array); @@ -207,8 +193,7 @@ bool Program::ParameterBlocksAreFinite(string* message) const { } bool Program::IsBoundsConstrained() const { - for (int i = 0; i < parameter_blocks_.size(); ++i) { - const ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { if (parameter_block->IsConstant()) { continue; } @@ -225,10 +210,9 @@ bool Program::IsBoundsConstrained() const { return false; } -bool Program::IsFeasible(string* message) const { +bool Program::IsFeasible(std::string* message) const { CHECK(message != nullptr); - for (int i = 0; i < parameter_blocks_.size(); ++i) { - const ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { const double* parameters = parameter_block->user_state(); const int size = parameter_block->Size(); if (parameter_block->IsConstant()) { @@ -284,42 +268,42 @@ bool Program::IsFeasible(string* message) const { return true; } -Program* Program::CreateReducedProgram( - vector<double*>* removed_parameter_blocks, +std::unique_ptr<Program> Program::CreateReducedProgram( + std::vector<double*>* removed_parameter_blocks, double* fixed_cost, - string* error) const { + std::string* error) const { CHECK(removed_parameter_blocks != nullptr); CHECK(fixed_cost != nullptr); CHECK(error != nullptr); - std::unique_ptr<Program> reduced_program(new Program(*this)); + std::unique_ptr<Program> reduced_program = std::make_unique<Program>(*this); if (!reduced_program->RemoveFixedBlocks( removed_parameter_blocks, fixed_cost, error)) { return nullptr; } reduced_program->SetParameterOffsetsAndIndex(); - return reduced_program.release(); + return reduced_program; } -bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks, +bool Program::RemoveFixedBlocks(std::vector<double*>* removed_parameter_blocks, double* fixed_cost, - string* error) { + std::string* error) { CHECK(removed_parameter_blocks != nullptr); CHECK(fixed_cost != nullptr); CHECK(error != nullptr); std::unique_ptr<double[]> residual_block_evaluate_scratch; - residual_block_evaluate_scratch.reset( - new double[MaxScratchDoublesNeededForEvaluate()]); + residual_block_evaluate_scratch = + std::make_unique<double[]>(MaxScratchDoublesNeededForEvaluate()); *fixed_cost = 0.0; bool need_to_call_prepare_for_evaluation = evaluation_callback_ != nullptr; // Mark all the parameters as unused. Abuse the index member of the // parameter blocks for the marking. - for (int i = 0; i < parameter_blocks_.size(); ++i) { - parameter_blocks_[i]->set_index(-1); + for (auto* parameter_block : parameter_blocks_) { + parameter_block->set_index(-1); } // Filter out residual that have all-constant parameters, and mark @@ -391,8 +375,7 @@ bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks, // Filter out unused or fixed parameter blocks. int num_active_parameter_blocks = 0; removed_parameter_blocks->clear(); - for (int i = 0; i < parameter_blocks_.size(); ++i) { - ParameterBlock* parameter_block = parameter_blocks_[i]; + for (auto* parameter_block : parameter_blocks_) { if (parameter_block->index() == -1) { removed_parameter_blocks->push_back( parameter_block->mutable_user_state()); @@ -412,7 +395,7 @@ bool Program::RemoveFixedBlocks(vector<double*>* removed_parameter_blocks, } bool Program::IsParameterBlockSetIndependent( - const set<double*>& independent_set) const { + const std::set<double*>& independent_set) const { // Loop over each residual block and ensure that no two parameter // blocks in the same residual block are part of // parameter_block_ptrs as that would violate the assumption that it @@ -483,24 +466,24 @@ int Program::NumParameterBlocks() const { return parameter_blocks_.size(); } int Program::NumResiduals() const { int num_residuals = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { - num_residuals += residual_blocks_[i]->NumResiduals(); + for (auto* residual_block : residual_blocks_) { + num_residuals += residual_block->NumResiduals(); } return num_residuals; } int Program::NumParameters() const { int num_parameters = 0; - for (int i = 0; i < parameter_blocks_.size(); ++i) { - num_parameters += parameter_blocks_[i]->Size(); + for (auto* parameter_block : parameter_blocks_) { + num_parameters += parameter_block->Size(); } return num_parameters; } int Program::NumEffectiveParameters() const { int num_parameters = 0; - for (int i = 0; i < parameter_blocks_.size(); ++i) { - num_parameters += parameter_blocks_[i]->LocalSize(); + for (auto* parameter_block : parameter_blocks_) { + num_parameters += parameter_block->TangentSize(); } return num_parameters; } @@ -511,48 +494,47 @@ int Program::NumEffectiveParameters() const { int Program::MaxScratchDoublesNeededForEvaluate() const { // Compute the scratch space needed for evaluate. int max_scratch_bytes_for_evaluate = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { + for (auto* residual_block : residual_blocks_) { max_scratch_bytes_for_evaluate = - max(max_scratch_bytes_for_evaluate, - residual_blocks_[i]->NumScratchDoublesForEvaluate()); + std::max(max_scratch_bytes_for_evaluate, + residual_block->NumScratchDoublesForEvaluate()); } return max_scratch_bytes_for_evaluate; } int Program::MaxDerivativesPerResidualBlock() const { int max_derivatives = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { + for (auto* residual_block : residual_blocks_) { int derivatives = 0; - ResidualBlock* residual_block = residual_blocks_[i]; int num_parameters = residual_block->NumParameterBlocks(); for (int j = 0; j < num_parameters; ++j) { derivatives += residual_block->NumResiduals() * - residual_block->parameter_blocks()[j]->LocalSize(); + residual_block->parameter_blocks()[j]->TangentSize(); } - max_derivatives = max(max_derivatives, derivatives); + max_derivatives = std::max(max_derivatives, derivatives); } return max_derivatives; } int Program::MaxParametersPerResidualBlock() const { int max_parameters = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { + for (auto* residual_block : residual_blocks_) { max_parameters = - max(max_parameters, residual_blocks_[i]->NumParameterBlocks()); + std::max(max_parameters, residual_block->NumParameterBlocks()); } return max_parameters; } int Program::MaxResidualsPerResidualBlock() const { int max_residuals = 0; - for (int i = 0; i < residual_blocks_.size(); ++i) { - max_residuals = max(max_residuals, residual_blocks_[i]->NumResiduals()); + for (auto* residual_block : residual_blocks_) { + max_residuals = std::max(max_residuals, residual_block->NumResiduals()); } return max_residuals; } -string Program::ToString() const { - string ret = "Program dump\n"; +std::string Program::ToString() const { + std::string ret = "Program dump\n"; ret += StringPrintf("Number of parameter blocks: %d\n", NumParameterBlocks()); ret += StringPrintf("Number of parameters: %d\n", NumParameters()); ret += "Parameters:\n"; diff --git a/extern/ceres/internal/ceres/program.h b/extern/ceres/internal/ceres/program.h index ca29d316284..4dbd1ba5ff1 100644 --- a/extern/ceres/internal/ceres/program.h +++ b/extern/ceres/internal/ceres/program.h @@ -37,7 +37,8 @@ #include <vector> #include "ceres/evaluation_callback.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -57,11 +58,8 @@ class TripletSparseMatrix; // another; for example, the first stage of solving involves stripping all // constant parameters and residuals. This is in contrast with Problem, which is // not built for transformation. -class CERES_EXPORT_INTERNAL Program { +class CERES_NO_EXPORT Program { public: - Program(); - explicit Program(const Program& program); - // The ordered parameter and residual blocks for the program. const std::vector<ParameterBlock*>& parameter_blocks() const; const std::vector<ResidualBlock*>& residual_blocks() const; @@ -72,9 +70,9 @@ class CERES_EXPORT_INTERNAL Program { // Serialize to/from the program and update states. // // NOTE: Setting the state of a parameter block can trigger the - // computation of the Jacobian of its local parameterization. If - // this computation fails for some reason, then this method returns - // false and the state of the parameter blocks cannot be trusted. + // computation of the Jacobian of its manifold. If this computation fails for + // some reason, then this method returns false and the state of the parameter + // blocks cannot be trusted. bool StateVectorToParameterBlocks(const double* state); void ParameterBlocksToStateVector(double* state) const; @@ -82,8 +80,8 @@ class CERES_EXPORT_INTERNAL Program { void CopyParameterBlockStateToUserState(); // Set the parameter block pointers to the user pointers. Since this - // runs parameter block set state internally, which may call local - // parameterizations, this can fail. False is returned on failure. + // runs parameter block set state internally, which may call manifold, this + // can fail. False is returned on failure. bool SetParameterBlockStatePtrsToUserStatePtrs(); // Update a state vector for the program given a delta. @@ -146,12 +144,13 @@ class CERES_EXPORT_INTERNAL Program { // fixed_cost will be equal to the sum of the costs of the residual // blocks that were removed. // - // If there was a problem, then the function will return a NULL + // If there was a problem, then the function will return a nullptr // pointer and error will contain a human readable description of // the problem. - Program* CreateReducedProgram(std::vector<double*>* removed_parameter_blocks, - double* fixed_cost, - std::string* error) const; + std::unique_ptr<Program> CreateReducedProgram( + std::vector<double*>* removed_parameter_blocks, + double* fixed_cost, + std::string* error) const; // See problem.h for what these do. int NumParameterBlocks() const; @@ -196,4 +195,6 @@ class CERES_EXPORT_INTERNAL Program { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_PROGRAM_H_ diff --git a/extern/ceres/internal/ceres/program_evaluator.h b/extern/ceres/internal/ceres/program_evaluator.h index 36c9c64baf6..826a73a9af1 100644 --- a/extern/ceres/internal/ceres/program_evaluator.h +++ b/extern/ceres/internal/ceres/program_evaluator.h @@ -59,11 +59,13 @@ // class JacobianWriter { // // Create a jacobian that this writer can write. Same as // // Evaluator::CreateJacobian. -// SparseMatrix* CreateJacobian() const; +// std::unique_ptr<SparseMatrix> CreateJacobian() const; // -// // Create num_threads evaluate preparers. Caller owns result which must -// // be freed with delete[]. Resulting preparers are valid while *this is. -// EvaluatePreparer* CreateEvaluatePreparers(int num_threads); +// // Create num_threads evaluate preparers.Resulting preparers are valid +// // while *this is. +// +// std::unique_ptr<EvaluatePreparer[]> CreateEvaluatePreparers( +// int num_threads); // // // Write the block jacobians from a residual block evaluation to the // // larger sparse jacobian. @@ -81,7 +83,7 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <atomic> @@ -109,14 +111,14 @@ struct NullJacobianFinalizer { template <typename EvaluatePreparer, typename JacobianWriter, typename JacobianFinalizer = NullJacobianFinalizer> -class ProgramEvaluator : public Evaluator { +class ProgramEvaluator final : public Evaluator { public: ProgramEvaluator(const Evaluator::Options& options, Program* program) : options_(options), program_(program), jacobian_writer_(options, program), - evaluate_preparers_( - jacobian_writer_.CreateEvaluatePreparers(options.num_threads)) { + evaluate_preparers_(std::move( + jacobian_writer_.CreateEvaluatePreparers(options.num_threads))) { #ifdef CERES_NO_THREADS if (options_.num_threads > 1) { LOG(WARNING) << "No threading support is compiled into this binary; " @@ -127,12 +129,12 @@ class ProgramEvaluator : public Evaluator { #endif // CERES_NO_THREADS BuildResidualLayout(*program, &residual_layout_); - evaluate_scratch_.reset( - CreateEvaluatorScratch(*program, options.num_threads)); + evaluate_scratch_ = + std::move(CreateEvaluatorScratch(*program, options.num_threads)); } // Implementation of Evaluator interface. - SparseMatrix* CreateJacobian() const final { + std::unique_ptr<SparseMatrix> CreateJacobian() const final { return jacobian_writer_.CreateJacobian(); } @@ -250,7 +252,7 @@ class ProgramEvaluator : public Evaluator { MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>( block_jacobians[j], num_residuals, - parameter_block->LocalSize(), + parameter_block->TangentSize(), block_residuals, scratch->gradient.get() + parameter_block->delta_offset()); } @@ -309,18 +311,19 @@ class ProgramEvaluator : public Evaluator { int max_scratch_doubles_needed_for_evaluate, int max_residuals_per_residual_block, int num_parameters) { - residual_block_evaluate_scratch.reset( - new double[max_scratch_doubles_needed_for_evaluate]); - gradient.reset(new double[num_parameters]); + residual_block_evaluate_scratch = + std::make_unique<double[]>(max_scratch_doubles_needed_for_evaluate); + gradient = std::make_unique<double[]>(num_parameters); VectorRef(gradient.get(), num_parameters).setZero(); - residual_block_residuals.reset( - new double[max_residuals_per_residual_block]); - jacobian_block_ptrs.reset(new double*[max_parameters_per_residual_block]); + residual_block_residuals = + std::make_unique<double[]>(max_residuals_per_residual_block); + jacobian_block_ptrs = + std::make_unique<double*[]>(max_parameters_per_residual_block); } double cost; std::unique_ptr<double[]> residual_block_evaluate_scratch; - // The gradient in the local parameterization. + // The gradient on the manifold. std::unique_ptr<double[]> gradient; // Enough space to store the residual for the largest residual block. std::unique_ptr<double[]> residual_block_residuals; @@ -341,8 +344,8 @@ class ProgramEvaluator : public Evaluator { } // Create scratch space for each thread evaluating the program. - static EvaluateScratch* CreateEvaluatorScratch(const Program& program, - int num_threads) { + static std::unique_ptr<EvaluateScratch[]> CreateEvaluatorScratch( + const Program& program, int num_threads) { int max_parameters_per_residual_block = program.MaxParametersPerResidualBlock(); int max_scratch_doubles_needed_for_evaluate = @@ -351,7 +354,7 @@ class ProgramEvaluator : public Evaluator { program.MaxResidualsPerResidualBlock(); int num_parameters = program.NumEffectiveParameters(); - EvaluateScratch* evaluate_scratch = new EvaluateScratch[num_threads]; + auto evaluate_scratch = std::make_unique<EvaluateScratch[]>(num_threads); for (int i = 0; i < num_threads; i++) { evaluate_scratch[i].Init(max_parameters_per_residual_block, max_scratch_doubles_needed_for_evaluate, diff --git a/extern/ceres/internal/ceres/random.h b/extern/ceres/internal/ceres/random.h index 6b280f9ee64..0495d67581d 100644 --- a/extern/ceres/internal/ceres/random.h +++ b/extern/ceres/internal/ceres/random.h @@ -35,7 +35,7 @@ #include <cmath> #include <cstdlib> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { @@ -50,7 +50,7 @@ inline int Uniform(int n) { } inline double RandDouble() { - double r = static_cast<double>(rand()); + auto r = static_cast<double>(rand()); return r / RAND_MAX; } diff --git a/extern/ceres/internal/ceres/reorder_program.cc b/extern/ceres/internal/ceres/reorder_program.cc index 5d802365f33..d552ebf3de3 100644 --- a/extern/ceres/internal/ceres/reorder_program.cc +++ b/extern/ceres/internal/ceres/reorder_program.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -37,7 +37,8 @@ #include "Eigen/SparseCore" #include "ceres/cxsparse.h" -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" +#include "ceres/internal/export.h" #include "ceres/ordered_groups.h" #include "ceres/parameter_block.h" #include "ceres/parameter_block_ordering.h" @@ -88,8 +89,8 @@ static int MinParameterBlock(const ResidualBlock* residual_block, #if defined(CERES_USE_EIGEN_SPARSE) Eigen::SparseMatrix<int> CreateBlockJacobian( const TripletSparseMatrix& block_jacobian_transpose) { - typedef Eigen::SparseMatrix<int> SparseMatrix; - typedef Eigen::Triplet<int> Triplet; + using SparseMatrix = Eigen::SparseMatrix<int>; + using Triplet = Eigen::Triplet<int>; const int* rows = block_jacobian_transpose.rows(); const int* cols = block_jacobian_transpose.cols(); @@ -97,7 +98,7 @@ Eigen::SparseMatrix<int> CreateBlockJacobian( vector<Triplet> triplets; triplets.reserve(num_nonzeros); for (int i = 0; i < num_nonzeros; ++i) { - triplets.push_back(Triplet(cols[i], rows[i], 1)); + triplets.emplace_back(cols[i], rows[i], 1); } SparseMatrix block_jacobian(block_jacobian_transpose.num_cols(), @@ -127,9 +128,9 @@ void OrderingForSparseNormalCholeskyUsingSuiteSparse( ss.ApproximateMinimumDegreeOrdering(block_jacobian_transpose, &ordering[0]); } else { vector<int> constraints; - for (int i = 0; i < parameter_blocks.size(); ++i) { + for (auto* parameter_block : parameter_blocks) { constraints.push_back(parameter_block_ordering.GroupId( - parameter_blocks[i]->mutable_user_state())); + parameter_block->mutable_user_state())); } // Renumber the entries of constraints to be contiguous integers @@ -188,7 +189,7 @@ void OrderingForSparseNormalCholeskyUsingEigenSparse( // things. The right thing to do here would be to get a compressed // row sparse matrix representation of the jacobian and go from // there. But that is a project for another day. - typedef Eigen::SparseMatrix<int> SparseMatrix; + using SparseMatrix = Eigen::SparseMatrix<int>; const SparseMatrix block_jacobian = CreateBlockJacobian(tsm_block_jacobian_transpose); @@ -279,7 +280,7 @@ bool LexicographicallyOrderResidualBlocks( CHECK(find(residual_blocks_per_e_block.begin(), residual_blocks_per_e_block.end() - 1, - 0) != residual_blocks_per_e_block.end()) + 0) == residual_blocks_per_e_block.end() - 1) << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; @@ -291,7 +292,7 @@ bool LexicographicallyOrderResidualBlocks( // filling is finished, the offset pointerts should have shifted down one // entry (this is verified below). vector<ResidualBlock*> reordered_residual_blocks( - (*residual_blocks).size(), static_cast<ResidualBlock*>(NULL)); + (*residual_blocks).size(), static_cast<ResidualBlock*>(nullptr)); for (int i = 0; i < residual_blocks->size(); ++i) { int bucket = min_position_per_residual[i]; @@ -299,7 +300,7 @@ bool LexicographicallyOrderResidualBlocks( offsets[bucket]--; // Sanity. - CHECK(reordered_residual_blocks[offsets[bucket]] == NULL) + CHECK(reordered_residual_blocks[offsets[bucket]] == nullptr) << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; @@ -313,9 +314,9 @@ bool LexicographicallyOrderResidualBlocks( << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; } - // Sanity check #2: No NULL's left behind. - for (int i = 0; i < reordered_residual_blocks.size(); ++i) { - CHECK(reordered_residual_blocks[i] != NULL) + // Sanity check #2: No nullptr's left behind. + for (auto* residual_block : reordered_residual_blocks) { + CHECK(residual_block != nullptr) << "Congratulations, you found a Ceres bug! Please report this error " << "to the developers."; } @@ -339,9 +340,9 @@ static void MaybeReorderSchurComplementColumnsUsingSuiteSparse( vector<ParameterBlock*>& parameter_blocks = *(program->mutable_parameter_blocks()); - for (int i = 0; i < parameter_blocks.size(); ++i) { + for (auto* parameter_block : parameter_blocks) { constraints.push_back(parameter_block_ordering.GroupId( - parameter_blocks[i]->mutable_user_state())); + parameter_block->mutable_user_state())); } // Renumber the entries of constraints to be contiguous integers as @@ -378,7 +379,7 @@ static void MaybeReorderSchurComplementColumnsUsingEigen( std::unique_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose( program->CreateJacobianBlockSparsityTranspose()); - typedef Eigen::SparseMatrix<int> SparseMatrix; + using SparseMatrix = Eigen::SparseMatrix<int>; const SparseMatrix block_jacobian = CreateBlockJacobian(*tsm_block_jacobian_transpose); const int num_rows = block_jacobian.rows(); @@ -441,7 +442,7 @@ bool ReorderProgramForSchurTypeLinearSolver( if (parameter_block_ordering->NumGroups() == 1) { // If the user supplied an parameter_block_ordering with just one - // group, it is equivalent to the user supplying NULL as an + // group, it is equivalent to the user supplying nullptr as an // parameter_block_ordering. Ceres is completely free to choose the // parameter block ordering as it sees fit. For Schur type solvers, // this means that the user wishes for Ceres to identify the diff --git a/extern/ceres/internal/ceres/reorder_program.h b/extern/ceres/internal/ceres/reorder_program.h index 2e0c3264377..fbc49231c33 100644 --- a/extern/ceres/internal/ceres/reorder_program.h +++ b/extern/ceres/internal/ceres/reorder_program.h @@ -33,7 +33,8 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block_ordering.h" #include "ceres/problem_impl.h" #include "ceres/types.h" @@ -44,7 +45,7 @@ namespace internal { class Program; // Reorder the parameter blocks in program using the ordering -CERES_EXPORT_INTERNAL bool ApplyOrdering( +CERES_NO_EXPORT bool ApplyOrdering( const ProblemImpl::ParameterMap& parameter_map, const ParameterBlockOrdering& ordering, Program* program, @@ -53,7 +54,7 @@ CERES_EXPORT_INTERNAL bool ApplyOrdering( // Reorder the residuals for program, if necessary, so that the residuals // involving each E block occur together. This is a necessary condition for the // Schur eliminator, which works on these "row blocks" in the jacobian. -CERES_EXPORT_INTERNAL bool LexicographicallyOrderResidualBlocks( +CERES_NO_EXPORT bool LexicographicallyOrderResidualBlocks( int size_of_first_elimination_group, Program* program, std::string* error); // Schur type solvers require that all parameter blocks eliminated @@ -72,7 +73,7 @@ CERES_EXPORT_INTERNAL bool LexicographicallyOrderResidualBlocks( // // Upon return, ordering contains the parameter block ordering that // was used to order the program. -CERES_EXPORT_INTERNAL bool ReorderProgramForSchurTypeLinearSolver( +CERES_NO_EXPORT bool ReorderProgramForSchurTypeLinearSolver( LinearSolverType linear_solver_type, SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type, const ProblemImpl::ParameterMap& parameter_map, @@ -90,7 +91,7 @@ CERES_EXPORT_INTERNAL bool ReorderProgramForSchurTypeLinearSolver( // fill-reducing ordering is available in the sparse linear algebra // library (SuiteSparse version >= 4.2.0) then the fill reducing // ordering will take it into account, otherwise it will be ignored. -CERES_EXPORT_INTERNAL bool ReorderProgramForSparseCholesky( +CERES_NO_EXPORT bool ReorderProgramForSparseCholesky( SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type, const ParameterBlockOrdering& parameter_block_ordering, int start_row_block, @@ -107,11 +108,13 @@ CERES_EXPORT_INTERNAL bool ReorderProgramForSparseCholesky( // bottom_residual_blocks.size() because we allow // bottom_residual_blocks to contain residual blocks not present in // the Program. -CERES_EXPORT_INTERNAL int ReorderResidualBlocksByPartition( +CERES_NO_EXPORT int ReorderResidualBlocksByPartition( const std::unordered_set<ResidualBlockId>& bottom_residual_blocks, Program* program); } // namespace internal } // namespace ceres -#endif // CERES_INTERNAL_REORDER_PROGRAM_ +#include "ceres/internal/reenable_warnings.h" + +#endif // CERES_INTERNAL_REORDER_PROGRAM_H_ diff --git a/extern/ceres/internal/ceres/residual_block.cc b/extern/ceres/internal/ceres/residual_block.cc index 067c9efe83d..cd408f2f98e 100644 --- a/extern/ceres/internal/ceres/residual_block.cc +++ b/extern/ceres/internal/ceres/residual_block.cc @@ -39,8 +39,8 @@ #include "ceres/cost_function.h" #include "ceres/internal/eigen.h" #include "ceres/internal/fixed_array.h" -#include "ceres/local_parameterization.h" #include "ceres/loss_function.h" +#include "ceres/manifold.h" #include "ceres/parameter_block.h" #include "ceres/residual_block_utils.h" #include "ceres/small_blas.h" @@ -87,7 +87,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, for (int i = 0; i < num_parameter_blocks; ++i) { const ParameterBlock* parameter_block = parameter_blocks_[i]; if (jacobians[i] != nullptr && - parameter_block->LocalParameterizationJacobian() != nullptr) { + parameter_block->PlusJacobian() != nullptr) { global_jacobians[i] = scratch; scratch += num_residuals * parameter_block->Size(); } else { @@ -132,27 +132,27 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, double squared_norm = VectorRef(residuals, num_residuals).squaredNorm(); - // Update the jacobians with the local parameterizations. + // Update the plus_jacobian for the manifolds. if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { if (jacobians[i] != nullptr) { const ParameterBlock* parameter_block = parameter_blocks_[i]; - // Apply local reparameterization to the jacobians. - if (parameter_block->LocalParameterizationJacobian() != nullptr) { + // Apply the Manifold::PlusJacobian to the ambient jacobians. + if (parameter_block->PlusJacobian() != nullptr) { // jacobians[i] = global_jacobians[i] * global_to_local_jacobian. MatrixMatrixMultiply<Dynamic, Dynamic, Dynamic, Dynamic, 0>( global_jacobians[i], num_residuals, parameter_block->Size(), - parameter_block->LocalParameterizationJacobian(), + parameter_block->PlusJacobian(), parameter_block->Size(), - parameter_block->LocalSize(), + parameter_block->TangentSize(), jacobians[i], 0, 0, num_residuals, - parameter_block->LocalSize()); + parameter_block->TangentSize()); } } } @@ -183,7 +183,7 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, // Correct the jacobians for the loss function. correct.CorrectJacobian(num_residuals, - parameter_block->LocalSize(), + parameter_block->TangentSize(), residuals, jacobians[i]); } @@ -199,16 +199,16 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function, int ResidualBlock::NumScratchDoublesForEvaluate() const { // Compute the amount of scratch space needed to store the full-sized - // jacobians. For parameters that have no local parameterization no storage - // is needed and the passed-in jacobian array is used directly. Also include - // space to store the residuals, which is needed for cost-only evaluations. - // This is slightly pessimistic, since both won't be needed all the time, but - // the amount of excess should not cause problems for the caller. + // jacobians. For parameters that have no manifold no storage is needed and + // the passed-in jacobian array is used directly. Also include space to store + // the residuals, which is needed for cost-only evaluations. This is slightly + // pessimistic, since both won't be needed all the time, but the amount of + // excess should not cause problems for the caller. int num_parameters = NumParameterBlocks(); int scratch_doubles = 1; for (int i = 0; i < num_parameters; ++i) { const ParameterBlock* parameter_block = parameter_blocks_[i]; - if (parameter_block->LocalParameterizationJacobian() != nullptr) { + if (parameter_block->PlusJacobian() != nullptr) { scratch_doubles += parameter_block->Size(); } } diff --git a/extern/ceres/internal/ceres/residual_block.h b/extern/ceres/internal/ceres/residual_block.h index f28fd42857c..978b94640fe 100644 --- a/extern/ceres/internal/ceres/residual_block.h +++ b/extern/ceres/internal/ceres/residual_block.h @@ -40,7 +40,8 @@ #include <vector> #include "ceres/cost_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/stringprintf.h" #include "ceres/types.h" @@ -65,7 +66,7 @@ class ParameterBlock; // // The residual block stores pointers to but does not own the cost functions, // loss functions, and parameter blocks. -class CERES_EXPORT_INTERNAL ResidualBlock { +class CERES_NO_EXPORT ResidualBlock { public: // Construct the residual block with the given cost/loss functions. Loss may // be null. The index is the index of the residual block in the Program's @@ -77,10 +78,10 @@ class CERES_EXPORT_INTERNAL ResidualBlock { // Evaluates the residual term, storing the scalar cost in *cost, the residual // components in *residuals, and the jacobians between the parameters and - // residuals in jacobians[i], in row-major order. If residuals is NULL, the - // residuals are not computed. If jacobians is NULL, no jacobians are - // computed. If jacobians[i] is NULL, then the jacobian for that parameter is - // not computed. + // residuals in jacobians[i], in row-major order. If residuals is nullptr, the + // residuals are not computed. If jacobians is nullptr, no jacobians are + // computed. If jacobians[i] is nullptr, then the jacobian for that parameter + // is not computed. // // cost must not be null. // @@ -92,10 +93,10 @@ class CERES_EXPORT_INTERNAL ResidualBlock { // false, the caller should expect the output memory locations to have // been modified. // - // The returned cost and jacobians have had robustification and local - // parameterizations applied already; for example, the jacobian for a - // 4-dimensional quaternion parameter using the "QuaternionParameterization" - // is num_residuals by 3 instead of num_residuals by 4. + // The returned cost and jacobians have had robustification and manifold + // projection applied already; for example, the jacobian for a 4-dimensional + // quaternion parameter using the "Quaternion" manifold is num_residuals by 3 + // instead of num_residuals by 4. // // apply_loss_function as the name implies allows the user to switch // the application of the loss function on and off. @@ -147,4 +148,6 @@ class CERES_EXPORT_INTERNAL ResidualBlock { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_RESIDUAL_BLOCK_H_ diff --git a/extern/ceres/internal/ceres/residual_block_utils.cc b/extern/ceres/internal/ceres/residual_block_utils.cc index d5b3fa1fa37..11c7623ce22 100644 --- a/extern/ceres/internal/ceres/residual_block_utils.cc +++ b/extern/ceres/internal/ceres/residual_block_utils.cc @@ -36,7 +36,7 @@ #include "ceres/array_utils.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block.h" #include "ceres/residual_block.h" #include "ceres/stringprintf.h" @@ -56,7 +56,7 @@ void InvalidateEvaluation(const ResidualBlock& block, InvalidateArray(1, cost); InvalidateArray(num_residuals, residuals); - if (jacobians != NULL) { + if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { const int parameter_block_size = block.parameter_blocks()[i]->Size(); InvalidateArray(num_residuals * parameter_block_size, jacobians[i]); @@ -104,9 +104,9 @@ string EvaluationToString(const ResidualBlock& block, StringAppendF(&result, "| "); for (int k = 0; k < num_residuals; ++k) { AppendArrayToString(1, - (jacobians != NULL && jacobians[i] != NULL) + (jacobians != nullptr && jacobians[i] != nullptr) ? jacobians[i] + k * parameter_block_size + j - : NULL, + : nullptr, &result); } StringAppendF(&result, "\n"); @@ -129,7 +129,7 @@ bool IsEvaluationValid(const ResidualBlock& block, return false; } - if (jacobians != NULL) { + if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { const int parameter_block_size = block.parameter_blocks()[i]->Size(); if (!IsArrayValid(num_residuals * parameter_block_size, jacobians[i])) { diff --git a/extern/ceres/internal/ceres/residual_block_utils.h b/extern/ceres/internal/ceres/residual_block_utils.h index 41ae81abc99..f75b6aecce9 100644 --- a/extern/ceres/internal/ceres/residual_block_utils.h +++ b/extern/ceres/internal/ceres/residual_block_utils.h @@ -45,14 +45,15 @@ #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { class ResidualBlock; -// Invalidate cost, resdual and jacobian arrays (if not NULL). +// Invalidate cost, resdual and jacobian arrays (if not nullptr). +CERES_NO_EXPORT void InvalidateEvaluation(const ResidualBlock& block, double* cost, double* residuals, @@ -60,6 +61,7 @@ void InvalidateEvaluation(const ResidualBlock& block, // Check if any of the arrays cost, residuals or jacobians contains an // NaN, return true if it does. +CERES_NO_EXPORT bool IsEvaluationValid(const ResidualBlock& block, double const* const* parameters, double* cost, @@ -69,6 +71,7 @@ bool IsEvaluationValid(const ResidualBlock& block, // Create a string representation of the Residual block containing the // value of the parameters, residuals and jacobians if present. // Useful for debugging output. +CERES_NO_EXPORT std::string EvaluationToString(const ResidualBlock& block, double const* const* parameters, double* cost, diff --git a/extern/ceres/internal/ceres/schur_complement_solver.cc b/extern/ceres/internal/ceres/schur_complement_solver.cc index 65e7854f9e5..bb442b4280b 100644 --- a/extern/ceres/internal/ceres/schur_complement_solver.cc +++ b/extern/ceres/internal/ceres/schur_complement_solver.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -46,7 +46,6 @@ #include "ceres/conjugate_gradients_solver.h" #include "ceres/detect_structure.h" #include "ceres/internal/eigen.h" -#include "ceres/lapack.h" #include "ceres/linear_solver.h" #include "ceres/sparse_cholesky.h" #include "ceres/triplet_sparse_matrix.h" @@ -63,14 +62,12 @@ using std::vector; namespace { -class BlockRandomAccessSparseMatrixAdapter : public LinearOperator { +class BlockRandomAccessSparseMatrixAdapter final : public LinearOperator { public: explicit BlockRandomAccessSparseMatrixAdapter( const BlockRandomAccessSparseMatrix& m) : m_(m) {} - virtual ~BlockRandomAccessSparseMatrixAdapter() {} - // y = y + Ax; void RightMultiply(const double* x, double* y) const final { m_.SymmetricRightMultiply(x, y); @@ -88,14 +85,12 @@ class BlockRandomAccessSparseMatrixAdapter : public LinearOperator { const BlockRandomAccessSparseMatrix& m_; }; -class BlockRandomAccessDiagonalMatrixAdapter : public LinearOperator { +class BlockRandomAccessDiagonalMatrixAdapter final : public LinearOperator { public: explicit BlockRandomAccessDiagonalMatrixAdapter( const BlockRandomAccessDiagonalMatrix& m) : m_(m) {} - virtual ~BlockRandomAccessDiagonalMatrixAdapter() {} - // y = y + Ax; void RightMultiply(const double* x, double* y) const final { m_.RightMultiply(x, y); @@ -115,6 +110,14 @@ class BlockRandomAccessDiagonalMatrixAdapter : public LinearOperator { } // namespace +SchurComplementSolver::SchurComplementSolver( + const LinearSolver::Options& options) + : options_(options) { + CHECK_GT(options.elimination_groups.size(), 1); + CHECK_GT(options.elimination_groups[0], 0); + CHECK(options.context != nullptr); +} + LinearSolver::Summary SchurComplementSolver::SolveImpl( BlockSparseMatrix* A, const double* b, @@ -123,7 +126,7 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl( EventLogger event_logger("SchurComplementSolver::Solve"); const CompressedRowBlockStructure* bs = A->block_structure(); - if (eliminator_.get() == NULL) { + if (eliminator_.get() == nullptr) { const int num_eliminate_blocks = options_.elimination_groups[0]; const int num_f_blocks = bs->cols.size() - num_eliminate_blocks; @@ -141,9 +144,9 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl( // mechanism that does not cause binary bloat. if (options_.row_block_size == 2 && options_.e_block_size == 3 && options_.f_block_size == 6 && num_f_blocks == 1) { - eliminator_.reset(new SchurEliminatorForOneFBlock<2, 3, 6>); + eliminator_ = std::make_unique<SchurEliminatorForOneFBlock<2, 3, 6>>(); } else { - eliminator_.reset(SchurEliminatorBase::Create(options_)); + eliminator_ = SchurEliminatorBase::Create(options_); } CHECK(eliminator_); @@ -174,6 +177,12 @@ LinearSolver::Summary SchurComplementSolver::SolveImpl( return summary; } +DenseSchurComplementSolver::DenseSchurComplementSolver( + const LinearSolver::Options& options) + : SchurComplementSolver(options), + cholesky_(DenseCholesky::Create(options)) {} + +DenseSchurComplementSolver::~DenseSchurComplementSolver() = default; // Initialize a BlockRandomAccessDenseMatrix to store the Schur // complement. @@ -187,8 +196,8 @@ void DenseSchurComplementSolver::InitStorage( blocks[j] = bs->cols[i].size; } - set_lhs(new BlockRandomAccessDenseMatrix(blocks)); - set_rhs(new double[lhs()->num_rows()]); + set_lhs(std::make_unique<BlockRandomAccessDenseMatrix>(blocks)); + set_rhs(std::make_unique<double[]>(lhs()->num_rows())); } // Solve the system Sx = r, assuming that the matrix S is stored in a @@ -201,8 +210,7 @@ LinearSolver::Summary DenseSchurComplementSolver::SolveReducedLinearSystem( summary.termination_type = LINEAR_SOLVER_SUCCESS; summary.message = "Success."; - const BlockRandomAccessDenseMatrix* m = - down_cast<const BlockRandomAccessDenseMatrix*>(lhs()); + auto* m = down_cast<BlockRandomAccessDenseMatrix*>(mutable_lhs()); const int num_rows = m->num_rows(); // The case where there are no f blocks, and the system is block @@ -212,26 +220,8 @@ LinearSolver::Summary DenseSchurComplementSolver::SolveReducedLinearSystem( } summary.num_iterations = 1; - - if (options().dense_linear_algebra_library_type == EIGEN) { - Eigen::LLT<Matrix, Eigen::Upper> llt = - ConstMatrixRef(m->values(), num_rows, num_rows) - .selfadjointView<Eigen::Upper>() - .llt(); - if (llt.info() != Eigen::Success) { - summary.termination_type = LINEAR_SOLVER_FAILURE; - summary.message = - "Eigen failure. Unable to perform dense Cholesky factorization."; - return summary; - } - - VectorRef(solution, num_rows) = llt.solve(ConstVectorRef(rhs(), num_rows)); - } else { - VectorRef(solution, num_rows) = ConstVectorRef(rhs(), num_rows); - summary.termination_type = LAPACK::SolveInPlaceUsingCholesky( - num_rows, m->values(), solution, &summary.message); - } - + summary.termination_type = cholesky_->FactorAndSolve( + num_rows, m->mutable_values(), rhs(), solution, &summary.message); return summary; } @@ -243,7 +233,7 @@ SparseSchurComplementSolver::SparseSchurComplementSolver( } } -SparseSchurComplementSolver::~SparseSchurComplementSolver() {} +SparseSchurComplementSolver::~SparseSchurComplementSolver() = default; // Determine the non-zero blocks in the Schur Complement matrix, and // initialize a BlockRandomAccessSparseMatrix object. @@ -303,8 +293,8 @@ void SparseSchurComplementSolver::InitStorage( CHECK_GE(row.cells.front().block_id, num_eliminate_blocks); for (int i = 0; i < row.cells.size(); ++i) { int r_block1_id = row.cells[i].block_id - num_eliminate_blocks; - for (int j = 0; j < row.cells.size(); ++j) { - int r_block2_id = row.cells[j].block_id - num_eliminate_blocks; + for (const auto& cell : row.cells) { + int r_block2_id = cell.block_id - num_eliminate_blocks; if (r_block1_id <= r_block2_id) { block_pairs.insert(make_pair(r_block1_id, r_block2_id)); } @@ -312,8 +302,9 @@ void SparseSchurComplementSolver::InitStorage( } } - set_lhs(new BlockRandomAccessSparseMatrix(blocks_, block_pairs)); - set_rhs(new double[lhs()->num_rows()]); + set_lhs( + std::make_unique<BlockRandomAccessSparseMatrix>(blocks_, block_pairs)); + set_rhs(std::make_unique<double[]>(lhs()->num_rows())); } LinearSolver::Summary SparseSchurComplementSolver::SolveReducedLinearSystem( @@ -338,11 +329,10 @@ LinearSolver::Summary SparseSchurComplementSolver::SolveReducedLinearSystem( const CompressedRowSparseMatrix::StorageType storage_type = sparse_cholesky_->StorageType(); if (storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR) { - lhs.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::UPPER_TRIANGULAR); } else { - lhs.reset( - CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::LOWER_TRIANGULAR); } @@ -373,12 +363,12 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients( // Only SCHUR_JACOBI is supported over here right now. CHECK_EQ(options().preconditioner_type, SCHUR_JACOBI); - if (preconditioner_.get() == NULL) { - preconditioner_.reset(new BlockRandomAccessDiagonalMatrix(blocks_)); + if (preconditioner_.get() == nullptr) { + preconditioner_ = + std::make_unique<BlockRandomAccessDiagonalMatrix>(blocks_); } - BlockRandomAccessSparseMatrix* sc = down_cast<BlockRandomAccessSparseMatrix*>( - const_cast<BlockRandomAccessMatrix*>(lhs())); + auto* sc = down_cast<BlockRandomAccessSparseMatrix*>(mutable_lhs()); // Extract block diagonal from the Schur complement to construct the // schur_jacobi preconditioner. @@ -404,10 +394,11 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingConjugateGradients( VectorRef(solution, num_rows).setZero(); - std::unique_ptr<LinearOperator> lhs_adapter( - new BlockRandomAccessSparseMatrixAdapter(*sc)); - std::unique_ptr<LinearOperator> preconditioner_adapter( - new BlockRandomAccessDiagonalMatrixAdapter(*preconditioner_)); + std::unique_ptr<LinearOperator> lhs_adapter = + std::make_unique<BlockRandomAccessSparseMatrixAdapter>(*sc); + std::unique_ptr<LinearOperator> preconditioner_adapter = + std::make_unique<BlockRandomAccessDiagonalMatrixAdapter>( + *preconditioner_); LinearSolver::Options cg_options; cg_options.min_num_iterations = options().min_num_iterations; diff --git a/extern/ceres/internal/ceres/schur_complement_solver.h b/extern/ceres/internal/ceres/schur_complement_solver.h index 3bfa22f22e4..859a086cdf4 100644 --- a/extern/ceres/internal/ceres/schur_complement_solver.h +++ b/extern/ceres/internal/ceres/schur_complement_solver.h @@ -40,7 +40,9 @@ #include "ceres/block_random_access_matrix.h" #include "ceres/block_sparse_matrix.h" #include "ceres/block_structure.h" -#include "ceres/internal/port.h" +#include "ceres/dense_cholesky.h" +#include "ceres/internal/config.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "ceres/schur_eliminator.h" #include "ceres/types.h" @@ -50,6 +52,8 @@ #include "Eigen/SparseCholesky" #endif +#include "ceres/internal/disable_warnings.h" + namespace ceres { namespace internal { @@ -107,20 +111,12 @@ class SparseCholesky; // set to DENSE_SCHUR and SPARSE_SCHUR // respectively. LinearSolver::Options::elimination_groups[0] should // be at least 1. -class CERES_EXPORT_INTERNAL SchurComplementSolver - : public BlockSparseMatrixSolver { +class CERES_NO_EXPORT SchurComplementSolver : public BlockSparseMatrixSolver { public: - explicit SchurComplementSolver(const LinearSolver::Options& options) - : options_(options) { - CHECK_GT(options.elimination_groups.size(), 1); - CHECK_GT(options.elimination_groups[0], 0); - CHECK(options.context != NULL); - } + explicit SchurComplementSolver(const LinearSolver::Options& options); SchurComplementSolver(const SchurComplementSolver&) = delete; void operator=(const SchurComplementSolver&) = delete; - // LinearSolver methods - virtual ~SchurComplementSolver() {} LinearSolver::Summary SolveImpl( BlockSparseMatrix* A, const double* b, @@ -130,10 +126,14 @@ class CERES_EXPORT_INTERNAL SchurComplementSolver protected: const LinearSolver::Options& options() const { return options_; } + void set_lhs(std::unique_ptr<BlockRandomAccessMatrix> lhs) { + lhs_ = std::move(lhs); + } const BlockRandomAccessMatrix* lhs() const { return lhs_.get(); } - void set_lhs(BlockRandomAccessMatrix* lhs) { lhs_.reset(lhs); } + BlockRandomAccessMatrix* mutable_lhs() { return lhs_.get(); } + + void set_rhs(std::unique_ptr<double[]> rhs) { rhs_ = std::move(rhs); } const double* rhs() const { return rhs_.get(); } - void set_rhs(double* rhs) { rhs_.reset(rhs); } private: virtual void InitStorage(const CompressedRowBlockStructure* bs) = 0; @@ -149,30 +149,33 @@ class CERES_EXPORT_INTERNAL SchurComplementSolver }; // Dense Cholesky factorization based solver. -class DenseSchurComplementSolver : public SchurComplementSolver { +class CERES_NO_EXPORT DenseSchurComplementSolver final + : public SchurComplementSolver { public: - explicit DenseSchurComplementSolver(const LinearSolver::Options& options) - : SchurComplementSolver(options) {} + explicit DenseSchurComplementSolver(const LinearSolver::Options& options); DenseSchurComplementSolver(const DenseSchurComplementSolver&) = delete; void operator=(const DenseSchurComplementSolver&) = delete; - virtual ~DenseSchurComplementSolver() {} + ~DenseSchurComplementSolver() override; private: void InitStorage(const CompressedRowBlockStructure* bs) final; LinearSolver::Summary SolveReducedLinearSystem( const LinearSolver::PerSolveOptions& per_solve_options, double* solution) final; + + std::unique_ptr<DenseCholesky> cholesky_; }; // Sparse Cholesky factorization based solver. -class SparseSchurComplementSolver : public SchurComplementSolver { +class CERES_NO_EXPORT SparseSchurComplementSolver final + : public SchurComplementSolver { public: explicit SparseSchurComplementSolver(const LinearSolver::Options& options); SparseSchurComplementSolver(const SparseSchurComplementSolver&) = delete; void operator=(const SparseSchurComplementSolver&) = delete; - virtual ~SparseSchurComplementSolver(); + ~SparseSchurComplementSolver() override; private: void InitStorage(const CompressedRowBlockStructure* bs) final; @@ -191,4 +194,6 @@ class SparseSchurComplementSolver : public SchurComplementSolver { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCHUR_COMPLEMENT_SOLVER_H_ diff --git a/extern/ceres/internal/ceres/schur_eliminator.cc b/extern/ceres/internal/ceres/schur_eliminator.cc index 613ae9558e9..22e7358070f 100644 --- a/extern/ceres/internal/ceres/schur_eliminator.cc +++ b/extern/ceres/internal/ceres/schur_eliminator.cc @@ -39,121 +39,126 @@ // // This file is generated using generate_template_specializations.py. +#include <memory> + #include "ceres/linear_solver.h" #include "ceres/schur_eliminator.h" namespace ceres { namespace internal { -SchurEliminatorBase* SchurEliminatorBase::Create( +SchurEliminatorBase::~SchurEliminatorBase() = default; + +std::unique_ptr<SchurEliminatorBase> SchurEliminatorBase::Create( const LinearSolver::Options& options) { #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 2)) { - return new SchurEliminator<2, 2, 2>(options); + return std::make_unique<SchurEliminator<2, 2, 2>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 3)) { - return new SchurEliminator<2, 2, 3>(options); + return std::make_unique<SchurEliminator<2, 2, 3>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 2) && (options.f_block_size == 4)) { - return new SchurEliminator<2, 2, 4>(options); + return std::make_unique<SchurEliminator<2, 2, 4>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 2)) { - return new SchurEliminator<2, 2, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, 2, Eigen::Dynamic>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new SchurEliminator<2, 3, 3>(options); + return std::make_unique<SchurEliminator<2, 3, 3>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 4)) { - return new SchurEliminator<2, 3, 4>(options); + return std::make_unique<SchurEliminator<2, 3, 4>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 6)) { - return new SchurEliminator<2, 3, 6>(options); + return std::make_unique<SchurEliminator<2, 3, 6>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3) && (options.f_block_size == 9)) { - return new SchurEliminator<2, 3, 9>(options); + return std::make_unique<SchurEliminator<2, 3, 9>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 3)) { - return new SchurEliminator<2, 3, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, 3, Eigen::Dynamic>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new SchurEliminator<2, 4, 3>(options); + return std::make_unique<SchurEliminator<2, 4, 3>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new SchurEliminator<2, 4, 4>(options); + return std::make_unique<SchurEliminator<2, 4, 4>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 6)) { - return new SchurEliminator<2, 4, 6>(options); + return std::make_unique<SchurEliminator<2, 4, 6>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 8)) { - return new SchurEliminator<2, 4, 8>(options); + return std::make_unique<SchurEliminator<2, 4, 8>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4) && (options.f_block_size == 9)) { - return new SchurEliminator<2, 4, 9>(options); + return std::make_unique<SchurEliminator<2, 4, 9>>(options); } if ((options.row_block_size == 2) && (options.e_block_size == 4)) { - return new SchurEliminator<2, 4, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, 4, Eigen::Dynamic>>(options); } if (options.row_block_size == 2) { - return new SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>>(options); } if ((options.row_block_size == 3) && (options.e_block_size == 3) && (options.f_block_size == 3)) { - return new SchurEliminator<3, 3, 3>(options); + return std::make_unique<SchurEliminator<3, 3, 3>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 2)) { - return new SchurEliminator<4, 4, 2>(options); + return std::make_unique<SchurEliminator<4, 4, 2>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 3)) { - return new SchurEliminator<4, 4, 3>(options); + return std::make_unique<SchurEliminator<4, 4, 3>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4) && (options.f_block_size == 4)) { - return new SchurEliminator<4, 4, 4>(options); + return std::make_unique<SchurEliminator<4, 4, 4>>(options); } if ((options.row_block_size == 4) && (options.e_block_size == 4)) { - return new SchurEliminator<4, 4, Eigen::Dynamic>(options); + return std::make_unique<SchurEliminator<4, 4, Eigen::Dynamic>>(options); } #endif VLOG(1) << "Template specializations not found for <" << options.row_block_size << "," << options.e_block_size << "," << options.f_block_size << ">"; - return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>( - options); + return std::make_unique<SchurEliminator<Eigen::Dynamic, + Eigen::Dynamic, + Eigen::Dynamic>>(options); } } // namespace internal diff --git a/extern/ceres/internal/ceres/schur_eliminator.h b/extern/ceres/internal/ceres/schur_eliminator.h index 42c016ee9b0..91831dceb5a 100644 --- a/extern/ceres/internal/ceres/schur_eliminator.h +++ b/extern/ceres/internal/ceres/schur_eliminator.h @@ -40,8 +40,10 @@ #include "ceres/block_random_access_matrix.h" #include "ceres/block_sparse_matrix.h" #include "ceres/block_structure.h" +#include "ceres/internal/config.h" +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -56,9 +58,8 @@ namespace internal { // Where x = [y;z] is a partition of the variables. The partitioning // of the variables is such that, E'E is a block diagonal matrix. Or // in other words, the parameter blocks in E form an independent set -// of the of the graph implied by the block matrix A'A. Then, this -// class provides the functionality to compute the Schur complement -// system +// of the graph implied by the block matrix A'A. Then, this class +// provides the functionality to compute the Schur complement system // // S z = r // @@ -164,9 +165,9 @@ namespace internal { // 2008 for an example of such use]. // // Example usage: Please see schur_complement_solver.cc -class CERES_EXPORT_INTERNAL SchurEliminatorBase { +class CERES_NO_EXPORT SchurEliminatorBase { public: - virtual ~SchurEliminatorBase() {} + virtual ~SchurEliminatorBase(); // Initialize the eliminator. It is the user's responsibilty to call // this function before calling Eliminate or BackSubstitute. It is @@ -210,7 +211,8 @@ class CERES_EXPORT_INTERNAL SchurEliminatorBase { const double* z, double* y) = 0; // Factory - static SchurEliminatorBase* Create(const LinearSolver::Options& options); + static std::unique_ptr<SchurEliminatorBase> Create( + const LinearSolver::Options& options); }; // Templated implementation of the SchurEliminatorBase interface. The @@ -223,7 +225,7 @@ class CERES_EXPORT_INTERNAL SchurEliminatorBase { template <int kRowBlockSize = Eigen::Dynamic, int kEBlockSize = Eigen::Dynamic, int kFBlockSize = Eigen::Dynamic> -class SchurEliminator : public SchurEliminatorBase { +class CERES_NO_EXPORT SchurEliminator final : public SchurEliminatorBase { public: explicit SchurEliminator(const LinearSolver::Options& options) : num_threads_(options.num_threads), context_(options.context) { @@ -231,7 +233,7 @@ class SchurEliminator : public SchurEliminatorBase { } // SchurEliminatorBase Interface - virtual ~SchurEliminator(); + ~SchurEliminator() override; void Init(int num_eliminate_blocks, bool assume_full_rank_ete, const CompressedRowBlockStructure* bs) final; @@ -272,9 +274,9 @@ class SchurEliminator : public SchurEliminatorBase { // buffer_layout[z1] = 0 // buffer_layout[z5] = y1 * z1 // buffer_layout[z2] = y1 * z1 + y1 * z5 - typedef std::map<int, int> BufferLayoutType; + using BufferLayoutType = std::map<int, int>; struct Chunk { - Chunk() : size(0) {} + explicit Chunk(int start) : size(0), start(start) {} int size; int start; BufferLayoutType buffer_layout; @@ -378,9 +380,9 @@ class SchurEliminator : public SchurEliminatorBase { template <int kRowBlockSize = Eigen::Dynamic, int kEBlockSize = Eigen::Dynamic, int kFBlockSize = Eigen::Dynamic> -class SchurEliminatorForOneFBlock : public SchurEliminatorBase { +class CERES_NO_EXPORT SchurEliminatorForOneFBlock final + : public SchurEliminatorBase { public: - virtual ~SchurEliminatorForOneFBlock() {} void Init(int num_eliminate_blocks, bool assume_full_rank_ete, const CompressedRowBlockStructure* bs) override { @@ -484,7 +486,7 @@ class SchurEliminatorForOneFBlock : public SchurEliminatorBase { Eigen::Matrix<double, kFBlockSize, 1> f_t_b; // Add the square of the diagonal to e_t_e. - if (D != NULL) { + if (D != nullptr) { const typename EigenTypes<kEBlockSize>::ConstVectorRef diag( D + bs->cols[e_block_id].position, kEBlockSize); e_t_e = diag.array().square().matrix().asDiagonal(); @@ -624,4 +626,6 @@ class SchurEliminatorForOneFBlock : public SchurEliminatorBase { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCHUR_ELIMINATOR_H_ diff --git a/extern/ceres/internal/ceres/schur_eliminator_impl.h b/extern/ceres/internal/ceres/schur_eliminator_impl.h index 1f0b4fa481d..de3ba3e5dcb 100644 --- a/extern/ceres/internal/ceres/schur_eliminator_impl.h +++ b/extern/ceres/internal/ceres/schur_eliminator_impl.h @@ -47,7 +47,7 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <algorithm> @@ -125,10 +125,8 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Init( break; } - chunks_.push_back(Chunk()); + chunks_.push_back(Chunk(r)); Chunk& chunk = chunks_.back(); - chunk.size = 0; - chunk.start = r; int buffer_size = 0; const int e_block_size = bs->cols[chunk_block_id].size; @@ -161,12 +159,13 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Init( uneliminated_row_begins_ = chunk.start + chunk.size; - buffer_.reset(new double[buffer_size_ * num_threads_]); + buffer_ = std::make_unique<double[]>(buffer_size_ * num_threads_); // chunk_outer_product_buffer_ only needs to store e_block_size * // f_block_size, which is always less than buffer_size_, so we just // allocate buffer_size_ per thread. - chunk_outer_product_buffer_.reset(new double[buffer_size_ * num_threads_]); + chunk_outer_product_buffer_ = + std::make_unique<double[]>(buffer_size_ * num_threads_); STLDeleteElements(&rhs_locks_); rhs_locks_.resize(num_col_blocks - num_eliminate_blocks_); @@ -193,7 +192,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate( const int num_col_blocks = bs->cols.size(); // Add the diagonal to the schur complement. - if (D != NULL) { + if (D != nullptr) { ParallelFor(context_, num_eliminate_blocks_, num_col_blocks, @@ -203,7 +202,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate( int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell( block_id, block_id, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { const int block_size = bs->cols[i].size; typename EigenTypes<Eigen::Dynamic>::ConstVectorRef diag( D + bs->cols[i].position, block_size); @@ -245,7 +244,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate( typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix ete(e_block_size, e_block_size); - if (D != NULL) { + if (D != nullptr) { const typename EigenTypes<kEBlockSize>::ConstVectorRef diag( D + bs->cols[e_block_id].position, e_block_size); ete = diag.array().square().matrix().asDiagonal(); @@ -327,7 +326,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::BackSubstitute( typename EigenTypes<kEBlockSize, kEBlockSize>::Matrix ete(e_block_size, e_block_size); - if (D != NULL) { + if (D != nullptr) { const typename EigenTypes<kEBlockSize>::ConstVectorRef diag( D + bs->cols[e_block_id].position, e_block_size); ete = diag.array().square().matrix().asDiagonal(); @@ -525,7 +524,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: // computation of the right-hand matrix product, but memory // references to the left hand side. const int e_block_size = inverse_ete.rows(); - BufferLayoutType::const_iterator it1 = buffer_layout.begin(); + auto it1 = buffer_layout.begin(); double* b1_transpose_inverse_ete = chunk_outer_product_buffer_.get() + thread_id * buffer_size_; @@ -542,14 +541,14 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: b1_transpose_inverse_ete, 0, 0, block1_size, e_block_size); // clang-format on - BufferLayoutType::const_iterator it2 = it1; + auto it2 = it1; for (; it2 != buffer_layout.end(); ++it2) { const int block2 = it2->first - num_eliminate_blocks_; int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { const int block2_size = bs->cols[it2->first].size; std::lock_guard<std::mutex> l(cell_info->m); // clang-format off @@ -627,7 +626,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block1, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { std::lock_guard<std::mutex> l(cell_info->m); // This multiply currently ignores the fact that this is a // symmetric outer product. @@ -647,7 +646,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { const int block2_size = bs->cols[row.cells[j].block_id].size; std::lock_guard<std::mutex> l(cell_info->m); // clang-format off @@ -682,7 +681,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block1, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { std::lock_guard<std::mutex> l(cell_info->m); // block += b1.transpose() * b1; // clang-format off @@ -702,7 +701,7 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>:: int r, c, row_stride, col_stride; CellInfo* cell_info = lhs->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - if (cell_info != NULL) { + if (cell_info != nullptr) { // block += b1.transpose() * b2; std::lock_guard<std::mutex> l(cell_info->m); // clang-format off diff --git a/extern/ceres/internal/ceres/schur_eliminator_template.py b/extern/ceres/internal/ceres/schur_eliminator_template.py deleted file mode 100644 index 50515956e89..00000000000 --- a/extern/ceres/internal/ceres/schur_eliminator_template.py +++ /dev/null @@ -1,151 +0,0 @@ -# Ceres Solver - A fast non-linear least squares minimizer -# Copyright 2017 Google Inc. All rights reserved. -# http://ceres-solver.org/ -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of Google Inc. nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# Author: sameeragarwal@google.com (Sameer Agarwal) -# -# Script for explicitly generating template specialization of the -# SchurEliminator class. It is a rather large class -# and the number of explicit instantiations is also large. Explicitly -# generating these instantiations in separate .cc files breaks the -# compilation into separate compilation unit rather than one large cc -# file which takes 2+GB of RAM to compile. -# -# This script creates two sets of files. -# -# 1. schur_eliminator_x_x_x.cc -# where, the x indicates the template parameters and -# -# 2. schur_eliminator.cc -# -# that contains a factory function for instantiating these classes -# based on runtime parameters. -# -# The list of tuples, specializations indicates the set of -# specializations that is generated. - -# Set of template specializations to generate - -HEADER = """// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2017 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: sameeragarwal@google.com (Sameer Agarwal) -// -// Template specialization of SchurEliminator. -// -// ======================================== -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -// THIS FILE IS AUTOGENERATED. DO NOT EDIT. -//========================================= -// -// This file is generated using generate_template_specializations.py. -""" - -DYNAMIC_FILE = """ -#include "ceres/schur_eliminator_impl.h" - -namespace ceres { -namespace internal { - -template class SchurEliminator<%s, %s, %s>; - -} // namespace internal -} // namespace ceres -""" - -SPECIALIZATION_FILE = """ -// This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" - -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION - -#include "ceres/schur_eliminator_impl.h" - -namespace ceres { -namespace internal { - -template class SchurEliminator<%s, %s, %s>; - -} // namespace internal -} // namespace ceres - -#endif // CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -FACTORY_FILE_HEADER = """ -#include "ceres/linear_solver.h" -#include "ceres/schur_eliminator.h" - -namespace ceres { -namespace internal { - -SchurEliminatorBase* SchurEliminatorBase::Create( - const LinearSolver::Options& options) { -#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION -""" - -FACTORY = """ return new SchurEliminator<%s, %s, %s>(options);""" - -FACTORY_FOOTER = """ -#endif - VLOG(1) << "Template specializations not found for <" - << options.row_block_size << "," << options.e_block_size << "," - << options.f_block_size << ">"; - return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>( - options); -} - -} // namespace internal -} // namespace ceres -""" diff --git a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc index 89d770b405a..3ecec728262 100644 --- a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc +++ b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.cc @@ -43,26 +43,25 @@ namespace ceres { namespace internal { SchurJacobiPreconditioner::SchurJacobiPreconditioner( - const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options) - : options_(options) { + const CompressedRowBlockStructure& bs, Preconditioner::Options options) + : options_(std::move(options)) { CHECK_GT(options_.elimination_groups.size(), 1); CHECK_GT(options_.elimination_groups[0], 0); const int num_blocks = bs.cols.size() - options_.elimination_groups[0]; CHECK_GT(num_blocks, 0) << "Jacobian should have at least 1 f_block for " << "SCHUR_JACOBI preconditioner."; - CHECK(options_.context != NULL); + CHECK(options_.context != nullptr); std::vector<int> blocks(num_blocks); for (int i = 0; i < num_blocks; ++i) { blocks[i] = bs.cols[i + options_.elimination_groups[0]].size; } - m_.reset(new BlockRandomAccessDiagonalMatrix(blocks)); + m_ = std::make_unique<BlockRandomAccessDiagonalMatrix>(blocks); InitEliminator(bs); } -SchurJacobiPreconditioner::~SchurJacobiPreconditioner() {} +SchurJacobiPreconditioner::~SchurJacobiPreconditioner() = default; // Initialize the SchurEliminator. void SchurJacobiPreconditioner::InitEliminator( @@ -74,7 +73,7 @@ void SchurJacobiPreconditioner::InitEliminator( eliminator_options.f_block_size = options_.f_block_size; eliminator_options.row_block_size = options_.row_block_size; eliminator_options.context = options_.context; - eliminator_.reset(SchurEliminatorBase::Create(eliminator_options)); + eliminator_ = SchurEliminatorBase::Create(eliminator_options); const bool kFullRankETE = true; eliminator_->Init( eliminator_options.elimination_groups[0], kFullRankETE, &bs); diff --git a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h index 372b790b82f..a43bc3388a1 100644 --- a/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h +++ b/extern/ceres/internal/ceres/schur_jacobi_preconditioner.h @@ -43,6 +43,8 @@ #include <utility> #include <vector> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preconditioner.h" namespace ceres { @@ -69,10 +71,11 @@ class SchurEliminatorBase; // options.elimination_groups.push_back(num_cameras); // SchurJacobiPreconditioner preconditioner( // *A.block_structure(), options); -// preconditioner.Update(A, NULL); +// preconditioner.Update(A, nullptr); // preconditioner.RightMultiply(x, y); // -class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner { +class CERES_NO_EXPORT SchurJacobiPreconditioner + : public BlockSparseMatrixPreconditioner { public: // Initialize the symbolic structure of the preconditioner. bs is // the block structure of the linear system to be solved. It is used @@ -81,11 +84,11 @@ class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner { // It has the same structural requirement as other Schur complement // based solvers. Please see schur_eliminator.h for more details. SchurJacobiPreconditioner(const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options); + Preconditioner::Options options); SchurJacobiPreconditioner(const SchurJacobiPreconditioner&) = delete; void operator=(const SchurJacobiPreconditioner&) = delete; - virtual ~SchurJacobiPreconditioner(); + ~SchurJacobiPreconditioner() override; // Preconditioner interface. void RightMultiply(const double* x, double* y) const final; @@ -104,4 +107,6 @@ class SchurJacobiPreconditioner : public BlockSparseMatrixPreconditioner { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCHUR_JACOBI_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/schur_templates.h b/extern/ceres/internal/ceres/schur_templates.h index 90aee0a1afc..cacee20c412 100644 --- a/extern/ceres/internal/ceres/schur_templates.h +++ b/extern/ceres/internal/ceres/schur_templates.h @@ -32,11 +32,14 @@ #ifndef CERES_INTERNAL_SCHUR_TEMPLATES_H_ #define CERES_INTERNAL_SCHUR_TEMPLATES_H_ +#include "ceres/internal/config.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { namespace internal { +CERES_NO_EXPORT void GetBestSchurTemplateSpecialization(int* row_block_size, int* e_block_size, int* f_block_size); diff --git a/extern/ceres/internal/ceres/scoped_thread_token.h b/extern/ceres/internal/ceres/scoped_thread_token.h index c167397cce9..533bfd5a387 100644 --- a/extern/ceres/internal/ceres/scoped_thread_token.h +++ b/extern/ceres/internal/ceres/scoped_thread_token.h @@ -31,6 +31,7 @@ #ifndef CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_ #define CERES_INTERNAL_SCOPED_THREAD_TOKEN_H_ +#include "ceres/internal/export.h" #include "ceres/thread_token_provider.h" namespace ceres { @@ -38,21 +39,20 @@ namespace internal { // Helper class for ThreadTokenProvider. This object acquires a token in its // constructor and puts that token back with destruction. -class ScopedThreadToken { +class CERES_NO_EXPORT ScopedThreadToken { public: - ScopedThreadToken(ThreadTokenProvider* provider) + explicit ScopedThreadToken(ThreadTokenProvider* provider) : provider_(provider), token_(provider->Acquire()) {} ~ScopedThreadToken() { provider_->Release(token_); } + ScopedThreadToken(ScopedThreadToken&) = delete; + ScopedThreadToken& operator=(ScopedThreadToken&) = delete; int token() const { return token_; } private: ThreadTokenProvider* provider_; int token_; - - ScopedThreadToken(ScopedThreadToken&); - ScopedThreadToken& operator=(ScopedThreadToken&); }; } // namespace internal diff --git a/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc b/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc index 9905b220fbf..0a1b0f3e7d1 100644 --- a/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc +++ b/extern/ceres/internal/ceres/scratch_evaluate_preparer.cc @@ -30,6 +30,8 @@ #include "ceres/scratch_evaluate_preparer.h" +#include <memory> + #include "ceres/parameter_block.h" #include "ceres/program.h" #include "ceres/residual_block.h" @@ -37,9 +39,9 @@ namespace ceres { namespace internal { -ScratchEvaluatePreparer* ScratchEvaluatePreparer::Create(const Program& program, - int num_threads) { - ScratchEvaluatePreparer* preparers = new ScratchEvaluatePreparer[num_threads]; +std::unique_ptr<ScratchEvaluatePreparer[]> ScratchEvaluatePreparer::Create( + const Program& program, int num_threads) { + auto preparers = std::make_unique<ScratchEvaluatePreparer[]>(num_threads); int max_derivatives_per_residual_block = program.MaxDerivativesPerResidualBlock(); for (int i = 0; i < num_threads; i++) { @@ -49,7 +51,8 @@ ScratchEvaluatePreparer* ScratchEvaluatePreparer::Create(const Program& program, } void ScratchEvaluatePreparer::Init(int max_derivatives_per_residual_block) { - jacobian_scratch_.reset(new double[max_derivatives_per_residual_block]); + jacobian_scratch_ = + std::make_unique<double[]>(max_derivatives_per_residual_block); } // Point the jacobian blocks into the scratch area of this evaluate preparer. @@ -64,10 +67,10 @@ void ScratchEvaluatePreparer::Prepare(const ResidualBlock* residual_block, const ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; if (parameter_block->IsConstant()) { - jacobians[j] = NULL; + jacobians[j] = nullptr; } else { jacobians[j] = jacobian_block_cursor; - jacobian_block_cursor += num_residuals * parameter_block->LocalSize(); + jacobian_block_cursor += num_residuals * parameter_block->TangentSize(); } } } diff --git a/extern/ceres/internal/ceres/scratch_evaluate_preparer.h b/extern/ceres/internal/ceres/scratch_evaluate_preparer.h index 2d2745d6269..3f4e7df8de0 100644 --- a/extern/ceres/internal/ceres/scratch_evaluate_preparer.h +++ b/extern/ceres/internal/ceres/scratch_evaluate_preparer.h @@ -37,6 +37,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" + namespace ceres { namespace internal { @@ -44,11 +47,11 @@ class Program; class ResidualBlock; class SparseMatrix; -class ScratchEvaluatePreparer { +class CERES_NO_EXPORT ScratchEvaluatePreparer { public: // Create num_threads ScratchEvaluatePreparers. - static ScratchEvaluatePreparer* Create(const Program& program, - int num_threads); + static std::unique_ptr<ScratchEvaluatePreparer[]> Create( + const Program& program, int num_threads); // EvaluatePreparer interface void Init(int max_derivatives_per_residual_block); @@ -66,4 +69,6 @@ class ScratchEvaluatePreparer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SCRATCH_EVALUATE_PREPARER_H_ diff --git a/extern/ceres/internal/ceres/single_linkage_clustering.h b/extern/ceres/internal/ceres/single_linkage_clustering.h index e891a9eec0a..b4a7e077619 100644 --- a/extern/ceres/internal/ceres/single_linkage_clustering.h +++ b/extern/ceres/internal/ceres/single_linkage_clustering.h @@ -34,7 +34,8 @@ #include <unordered_map> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -55,12 +56,14 @@ struct SingleLinkageClusteringOptions { // // The return value of this function is the number of clusters // identified by the algorithm. -int CERES_EXPORT_INTERNAL -ComputeSingleLinkageClustering(const SingleLinkageClusteringOptions& options, - const WeightedGraph<int>& graph, - std::unordered_map<int, int>* membership); +CERES_NO_EXPORT int ComputeSingleLinkageClustering( + const SingleLinkageClusteringOptions& options, + const WeightedGraph<int>& graph, + std::unordered_map<int, int>* membership); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SINGLE_LINKAGE_CLUSTERING_H_ diff --git a/extern/ceres/internal/ceres/small_blas.h b/extern/ceres/internal/ceres/small_blas.h index 4ee9229f35f..1cf41a5f1c2 100644 --- a/extern/ceres/internal/ceres/small_blas.h +++ b/extern/ceres/internal/ceres/small_blas.h @@ -36,7 +36,7 @@ #define CERES_INTERNAL_SMALL_BLAS_H_ #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "glog/logging.h" #include "small_blas_generic.h" @@ -210,7 +210,7 @@ CERES_GEMM_BEGIN(MatrixMatrixMultiplyNaive) { // Process the couple columns in remainder if present. if (NUM_COL_C & 2) { - int col = NUM_COL_C & (int)(~(span - 1)); + int col = NUM_COL_C & (~(span - 1)); const double* pa = &A[0]; for (int row = 0; row < NUM_ROW_C; ++row, pa += NUM_COL_A) { const double* pb = &B[col]; @@ -232,7 +232,7 @@ CERES_GEMM_BEGIN(MatrixMatrixMultiplyNaive) { } // Calculate the main part with multiples of 4. - int col_m = NUM_COL_C & (int)(~(span - 1)); + int col_m = NUM_COL_C & (~(span - 1)); for (int col = 0; col < col_m; col += span) { for (int row = 0; row < NUM_ROW_C; ++row) { const int index = (row + start_row_c) * col_stride_c + start_col_c + col; @@ -315,7 +315,7 @@ CERES_GEMM_BEGIN(MatrixTransposeMatrixMultiplyNaive) { // Process the couple columns in remainder if present. if (NUM_COL_C & 2) { - int col = NUM_COL_C & (int)(~(span - 1)); + int col = NUM_COL_C & (~(span - 1)); for (int row = 0; row < NUM_ROW_C; ++row) { const double* pa = &A[row]; const double* pb = &B[col]; @@ -339,7 +339,7 @@ CERES_GEMM_BEGIN(MatrixTransposeMatrixMultiplyNaive) { } // Process the main part with multiples of 4. - int col_m = NUM_COL_C & (int)(~(span - 1)); + int col_m = NUM_COL_C & (~(span - 1)); for (int col = 0; col < col_m; col += span) { for (int row = 0; row < NUM_ROW_C; ++row) { const int index = (row + start_row_c) * col_stride_c + start_col_c + col; @@ -435,7 +435,7 @@ inline void MatrixVectorMultiply(const double* A, // Process the couple rows in remainder if present. if (NUM_ROW_A & 2) { - int row = NUM_ROW_A & (int)(~(span - 1)); + int row = NUM_ROW_A & (~(span - 1)); const double* pa1 = &A[row * NUM_COL_A]; const double* pa2 = pa1 + NUM_COL_A; const double* pb = &b[0]; @@ -454,7 +454,7 @@ inline void MatrixVectorMultiply(const double* A, } // Calculate the main part with multiples of 4. - int row_m = NUM_ROW_A & (int)(~(span - 1)); + int row_m = NUM_ROW_A & (~(span - 1)); for (int row = 0; row < row_m; row += span) { // clang-format off MVM_mat4x1(NUM_COL_A, &A[row * NUM_COL_A], NUM_COL_A, @@ -522,7 +522,7 @@ inline void MatrixTransposeVectorMultiply(const double* A, // Process the couple columns in remainder if present. if (NUM_COL_A & 2) { - int row = NUM_COL_A & (int)(~(span - 1)); + int row = NUM_COL_A & (~(span - 1)); const double* pa = &A[row]; const double* pb = &b[0]; double tmp1 = 0.0, tmp2 = 0.0; @@ -543,7 +543,7 @@ inline void MatrixTransposeVectorMultiply(const double* A, } // Calculate the main part with multiples of 4. - int row_m = NUM_COL_A & (int)(~(span - 1)); + int row_m = NUM_COL_A & (~(span - 1)); for (int row = 0; row < row_m; row += span) { // clang-format off MTV_mat4x1(NUM_ROW_A, &A[row], NUM_COL_A, diff --git a/extern/ceres/internal/ceres/small_blas_generic.h b/extern/ceres/internal/ceres/small_blas_generic.h index 3f3ea424c80..f5aa909a8a3 100644 --- a/extern/ceres/internal/ceres/small_blas_generic.h +++ b/extern/ceres/internal/ceres/small_blas_generic.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2018 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -100,10 +100,11 @@ static inline void MMM_mat1x4(const int col_a, #define CERES_GEMM_OPT_MMM_MAT1X4_MUL \ av = pa[k]; \ pb = b + bi; \ - c0 += av * *pb++; \ - c1 += av * *pb++; \ - c2 += av * *pb++; \ - c3 += av * *pb++; \ + c0 += av * pb[0]; \ + c1 += av * pb[1]; \ + c2 += av * pb[2]; \ + c3 += av * pb[3]; \ + pb += 4; \ bi += col_stride_b; \ k++; @@ -167,10 +168,11 @@ static inline void MTM_mat1x4(const int col_a, #define CERES_GEMM_OPT_MTM_MAT1X4_MUL \ av = pa[ai]; \ pb = b + bi; \ - c0 += av * *pb++; \ - c1 += av * *pb++; \ - c2 += av * *pb++; \ - c3 += av * *pb++; \ + c0 += av * pb[0]; \ + c1 += av * pb[1]; \ + c2 += av * pb[2]; \ + c3 += av * pb[3]; \ + pb += 4; \ ai += col_stride_a; \ bi += col_stride_b; diff --git a/extern/ceres/internal/ceres/solver.cc b/extern/ceres/internal/ceres/solver.cc index dfde1221b61..150c5550fc9 100644 --- a/extern/ceres/internal/ceres/solver.cc +++ b/extern/ceres/internal/ceres/solver.cc @@ -41,7 +41,7 @@ #include "ceres/context_impl.h" #include "ceres/detect_structure.h" #include "ceres/gradient_checking_cost_function.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/parameter_block_ordering.h" #include "ceres/preprocessor.h" #include "ceres/problem.h" @@ -141,16 +141,20 @@ bool TrustRegionOptionsAreValid(const Solver::Options& options, string* error) { return false; } - if (options.dense_linear_algebra_library_type == LAPACK && - !IsDenseLinearAlgebraLibraryTypeAvailable(LAPACK) && + if (!IsDenseLinearAlgebraLibraryTypeAvailable( + options.dense_linear_algebra_library_type) && (options.linear_solver_type == DENSE_NORMAL_CHOLESKY || options.linear_solver_type == DENSE_QR || options.linear_solver_type == DENSE_SCHUR)) { *error = StringPrintf( "Can't use %s with " - "Solver::Options::dense_linear_algebra_library_type = LAPACK " - "because LAPACK was not enabled when Ceres was built.", - LinearSolverTypeToString(options.linear_solver_type)); + "Solver::Options::dense_linear_algebra_library_type = %s " + "because %s was not enabled when Ceres was built.", + LinearSolverTypeToString(options.linear_solver_type), + DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type), + DenseLinearAlgebraLibraryTypeToString( + options.dense_linear_algebra_library_type)); return false; } @@ -367,7 +371,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, &(summary->inner_iteration_ordering_used)); // clang-format off - summary->inner_iterations_used = pp.inner_iteration_minimizer.get() != NULL; // NOLINT + summary->inner_iterations_used = pp.inner_iteration_minimizer.get() != nullptr; // NOLINT summary->linear_solver_type_used = pp.linear_solver_options.type; summary->num_threads_used = pp.options.num_threads; summary->preconditioner_type_used = pp.options.preconditioner_type; @@ -375,7 +379,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, internal::SetSummaryFinalCost(summary); - if (pp.reduced_program.get() != NULL) { + if (pp.reduced_program.get() != nullptr) { SummarizeReducedProgram(*pp.reduced_program, summary); } @@ -385,7 +389,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, // case if the preprocessor failed, or if the reduced problem did // not contain any parameter blocks. Thus, only extract the // evaluator statistics if one exists. - if (pp.evaluator.get() != NULL) { + if (pp.evaluator.get() != nullptr) { const map<string, CallStatistics>& evaluator_statistics = pp.evaluator->Statistics(); { @@ -407,7 +411,7 @@ void PostSolveSummarize(const internal::PreprocessedProblem& pp, // Again, like the evaluator, there may or may not be a linear // solver from which we can extract run time statistics. In // particular the line search solver does not use a linear solver. - if (pp.linear_solver.get() != NULL) { + if (pp.linear_solver.get() != nullptr) { const map<string, CallStatistics>& linear_solver_statistics = pp.linear_solver->Statistics(); const CallStatistics& call_stats = FindWithDefault( @@ -436,8 +440,7 @@ void Minimize(internal::PreprocessedProblem* pp, Solver::Summary* summary) { } const Vector original_reduced_parameters = pp->reduced_parameters; - std::unique_ptr<Minimizer> minimizer( - Minimizer::Create(pp->options.minimizer_type)); + auto minimizer = Minimizer::Create(pp->options.minimizer_type); minimizer->Minimize( pp->minimizer_options, pp->reduced_parameters.data(), summary); @@ -485,7 +488,7 @@ bool Solver::Options::IsValid(string* error) const { return LineSearchOptionsAreValid(*this, error); } -Solver::~Solver() {} +Solver::~Solver() = default; void Solver::Solve(const Solver::Options& options, Problem* problem, @@ -518,11 +521,11 @@ void Solver::Solve(const Solver::Options& options, Solver::Options modified_options = options; if (options.check_gradients) { modified_options.callbacks.push_back(&gradient_checking_callback); - gradient_checking_problem.reset(CreateGradientCheckingProblemImpl( + gradient_checking_problem = CreateGradientCheckingProblemImpl( problem_impl, options.gradient_check_numeric_derivative_relative_step_size, options.gradient_check_relative_precision, - &gradient_checking_callback)); + &gradient_checking_callback); problem_impl = gradient_checking_problem.get(); program = problem_impl->mutable_program(); } @@ -534,8 +537,7 @@ void Solver::Solve(const Solver::Options& options, // The main thread also does work so we only need to launch num_threads - 1. problem_impl->context()->EnsureMinimumThreads(options.num_threads - 1); - std::unique_ptr<Preprocessor> preprocessor( - Preprocessor::Create(modified_options.minimizer_type)); + auto preprocessor = Preprocessor::Create(modified_options.minimizer_type); PreprocessedProblem pp; const bool status = diff --git a/extern/ceres/internal/ceres/solver_utils.cc b/extern/ceres/internal/ceres/solver_utils.cc index eb5aafa061c..22fa137055d 100644 --- a/extern/ceres/internal/ceres/solver_utils.cc +++ b/extern/ceres/internal/ceres/solver_utils.cc @@ -34,8 +34,11 @@ #include "Eigen/Core" #include "ceres/internal/config.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/version.h" +#ifndef CERES_NO_CUDA +#include "cuda_runtime.h" +#endif // CERES_NO_CUDA namespace ceres { namespace internal { @@ -87,6 +90,10 @@ std::string VersionString() { value += "-no_custom_blas"; #endif +#ifndef CERES_NO_CUDA + value += "-cuda-(" + std::to_string(CUDART_VERSION) + ")"; +#endif + return value; } diff --git a/extern/ceres/internal/ceres/solver_utils.h b/extern/ceres/internal/ceres/solver_utils.h index 85fbf3776ab..298564a897d 100644 --- a/extern/ceres/internal/ceres/solver_utils.h +++ b/extern/ceres/internal/ceres/solver_utils.h @@ -28,9 +28,14 @@ // // Author: sameeragarwal@google.com (Sameer Agarwal) +#ifndef CERES_INTERNAL_SOLVER_UTILS_H_ +#define CERES_INTERNAL_SOLVER_UTILS_H_ + #include <algorithm> #include <string> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/iteration_callback.h" #include "ceres/types.h" @@ -55,7 +60,12 @@ void SetSummaryFinalCost(SummaryType* summary) { } } +CERES_NO_EXPORT std::string VersionString(); } // namespace internal } // namespace ceres + +#include "ceres/internal/reenable_warnings.h" + +#endif // CERES_INTERNAL_SOLVER_UTILS_H_ diff --git a/extern/ceres/internal/ceres/sparse_cholesky.cc b/extern/ceres/internal/ceres/sparse_cholesky.cc index 91cdf671b1a..4a80470ffb7 100644 --- a/extern/ceres/internal/ceres/sparse_cholesky.cc +++ b/extern/ceres/internal/ceres/sparse_cholesky.cc @@ -30,6 +30,8 @@ #include "ceres/sparse_cholesky.h" +#include <memory> + #include "ceres/accelerate_sparse.h" #include "ceres/cxsparse.h" #include "ceres/eigensparse.h" @@ -113,7 +115,7 @@ std::unique_ptr<SparseCholesky> SparseCholesky::Create( return sparse_cholesky; } -SparseCholesky::~SparseCholesky() {} +SparseCholesky::~SparseCholesky() = default; LinearSolverTerminationType SparseCholesky::FactorAndSolve( CompressedRowSparseMatrix* lhs, @@ -133,7 +135,7 @@ RefinedSparseCholesky::RefinedSparseCholesky( : sparse_cholesky_(std::move(sparse_cholesky)), iterative_refiner_(std::move(iterative_refiner)) {} -RefinedSparseCholesky::~RefinedSparseCholesky() {} +RefinedSparseCholesky::~RefinedSparseCholesky() = default; CompressedRowSparseMatrix::StorageType RefinedSparseCholesky::StorageType() const { diff --git a/extern/ceres/internal/ceres/sparse_cholesky.h b/extern/ceres/internal/ceres/sparse_cholesky.h index a6af6b2c207..80c5cb2b83b 100644 --- a/extern/ceres/internal/ceres/sparse_cholesky.h +++ b/extern/ceres/internal/ceres/sparse_cholesky.h @@ -33,11 +33,13 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on #include <memory> +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" #include "glog/logging.h" @@ -67,7 +69,7 @@ namespace internal { // CHECK_EQ(sparse_cholesky->Solve(rhs.data(), solution.data(), &message), // LINEAR_SOLVER_SUCCESS); -class CERES_EXPORT_INTERNAL SparseCholesky { +class CERES_NO_EXPORT SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create( const LinearSolver::Options& options); @@ -104,29 +106,28 @@ class CERES_EXPORT_INTERNAL SparseCholesky { // Convenience method which combines a call to Factorize and // Solve. Solve is only called if Factorize returns // LINEAR_SOLVER_SUCCESS. - virtual LinearSolverTerminationType FactorAndSolve( - CompressedRowSparseMatrix* lhs, - const double* rhs, - double* solution, - std::string* message); + LinearSolverTerminationType FactorAndSolve(CompressedRowSparseMatrix* lhs, + const double* rhs, + double* solution, + std::string* message); }; class IterativeRefiner; // Computes an initial solution using the given instance of // SparseCholesky, and then refines it using the IterativeRefiner. -class CERES_EXPORT_INTERNAL RefinedSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT RefinedSparseCholesky final : public SparseCholesky { public: RefinedSparseCholesky(std::unique_ptr<SparseCholesky> sparse_cholesky, std::unique_ptr<IterativeRefiner> iterative_refiner); - virtual ~RefinedSparseCholesky(); + ~RefinedSparseCholesky() override; - virtual CompressedRowSparseMatrix::StorageType StorageType() const; - virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, - std::string* message); - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, - std::string* message); + CompressedRowSparseMatrix::StorageType StorageType() const override; + LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, + std::string* message) override; + LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) override; private: std::unique_ptr<SparseCholesky> sparse_cholesky_; @@ -137,4 +138,6 @@ class CERES_EXPORT_INTERNAL RefinedSparseCholesky : public SparseCholesky { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SPARSE_CHOLESKY_H_ diff --git a/extern/ceres/internal/ceres/sparse_matrix.cc b/extern/ceres/internal/ceres/sparse_matrix.cc index 32388f58fc3..bc757ead361 100644 --- a/extern/ceres/internal/ceres/sparse_matrix.cc +++ b/extern/ceres/internal/ceres/sparse_matrix.cc @@ -33,7 +33,7 @@ namespace ceres { namespace internal { -SparseMatrix::~SparseMatrix() {} +SparseMatrix::~SparseMatrix() = default; } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/sparse_matrix.h b/extern/ceres/internal/ceres/sparse_matrix.h index b57f10890fc..1dbb96e6070 100644 --- a/extern/ceres/internal/ceres/sparse_matrix.h +++ b/extern/ceres/internal/ceres/sparse_matrix.h @@ -36,7 +36,7 @@ #include <cstdio> #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/linear_operator.h" #include "ceres/types.h" @@ -64,14 +64,14 @@ namespace internal { // matrix type dependent and we are at this stage unable to come up // with an efficient high level interface that spans multiple sparse // matrix types. -class CERES_EXPORT_INTERNAL SparseMatrix : public LinearOperator { +class CERES_NO_EXPORT SparseMatrix : public LinearOperator { public: - virtual ~SparseMatrix(); + ~SparseMatrix() override; // y += Ax; - virtual void RightMultiply(const double* x, double* y) const = 0; + void RightMultiply(const double* x, double* y) const override = 0; // y += A'x; - virtual void LeftMultiply(const double* x, double* y) const = 0; + void LeftMultiply(const double* x, double* y) const override = 0; // In MATLAB notation sum(A.*A, 1) virtual void SquaredColumnNorm(double* x) const = 0; @@ -98,8 +98,8 @@ class CERES_EXPORT_INTERNAL SparseMatrix : public LinearOperator { virtual double* mutable_values() = 0; virtual const double* values() const = 0; - virtual int num_rows() const = 0; - virtual int num_cols() const = 0; + int num_rows() const override = 0; + int num_cols() const override = 0; virtual int num_nonzeros() const = 0; }; diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc index 0f2e589d041..2e52ae6d908 100644 --- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc @@ -54,7 +54,7 @@ SparseNormalCholeskySolver::SparseNormalCholeskySolver( sparse_cholesky_ = SparseCholesky::Create(options); } -SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {} +SparseNormalCholeskySolver::~SparseNormalCholeskySolver() = default; LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl( BlockSparseMatrix* A, @@ -75,21 +75,21 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl( A->LeftMultiply(b, rhs_.data()); event_logger.AddEvent("Compute RHS"); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { // Temporarily append a diagonal block to the A matrix, but undo // it before returning the matrix to the user. - std::unique_ptr<BlockSparseMatrix> regularizer; - regularizer.reset(BlockSparseMatrix::CreateDiagonalMatrix( - per_solve_options.D, A->block_structure()->cols)); + std::unique_ptr<BlockSparseMatrix> regularizer = + BlockSparseMatrix::CreateDiagonalMatrix(per_solve_options.D, + A->block_structure()->cols); event_logger.AddEvent("Diagonal"); A->AppendRows(*regularizer); event_logger.AddEvent("Append"); } event_logger.AddEvent("Append Rows"); - if (inner_product_computer_.get() == NULL) { - inner_product_computer_.reset( - InnerProductComputer::Create(*A, sparse_cholesky_->StorageType())); + if (inner_product_computer_.get() == nullptr) { + inner_product_computer_ = + InnerProductComputer::Create(*A, sparse_cholesky_->StorageType()); event_logger.AddEvent("InnerProductComputer::Create"); } @@ -97,7 +97,7 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl( inner_product_computer_->Compute(); event_logger.AddEvent("InnerProductComputer::Compute"); - if (per_solve_options.D != NULL) { + if (per_solve_options.D != nullptr) { A->DeleteRowBlocks(A->block_structure()->cols.size()); } diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h index ef3274323f5..caec566612e 100644 --- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h +++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.h @@ -36,11 +36,13 @@ // This include must come before any #ifndef check on Ceres compile options. // clang-format off -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" // clang-format on +#include <memory> #include <vector> +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -52,13 +54,14 @@ class SparseCholesky; // Solves the normal equations (A'A + D'D) x = A'b, using the sparse // linear algebra library of the user's choice. -class SparseNormalCholeskySolver : public BlockSparseMatrixSolver { +class CERES_NO_EXPORT SparseNormalCholeskySolver + : public BlockSparseMatrixSolver { public: explicit SparseNormalCholeskySolver(const LinearSolver::Options& options); SparseNormalCholeskySolver(const SparseNormalCholeskySolver&) = delete; void operator=(const SparseNormalCholeskySolver&) = delete; - virtual ~SparseNormalCholeskySolver(); + ~SparseNormalCholeskySolver() override; private: LinearSolver::Summary SolveImpl(BlockSparseMatrix* A, diff --git a/extern/ceres/internal/ceres/split.cc b/extern/ceres/internal/ceres/split.cc deleted file mode 100644 index 804f4412deb..00000000000 --- a/extern/ceres/internal/ceres/split.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: keir@google.com (Keir Mierle) - -#include "ceres/split.h" - -#include <iterator> -#include <string> -#include <vector> - -#include "ceres/internal/port.h" - -namespace ceres { -namespace internal { - -using std::string; -using std::vector; - -// If we know how much to allocate for a vector of strings, we can allocate the -// vector<string> only once and directly to the right size. This saves in -// between 33-66 % of memory space needed for the result, and runs faster in the -// microbenchmarks. -// -// The reserve is only implemented for the single character delim. -// -// The implementation for counting is cut-and-pasted from -// SplitStringToIteratorUsing. I could have written my own counting iterator, -// and use the existing template function, but probably this is more clear and -// more sure to get optimized to reasonable code. -static int CalculateReserveForVector(const string& full, const char* delim) { - int count = 0; - if (delim[0] != '\0' && delim[1] == '\0') { - // Optimize the common case where delim is a single character. - char c = delim[0]; - const char* p = full.data(); - const char* end = p + full.size(); - while (p != end) { - if (*p == c) { // This could be optimized with hasless(v,1) trick. - ++p; - } else { - while (++p != end && *p != c) { - // Skip to the next occurence of the delimiter. - } - ++count; - } - } - } - return count; -} - -template <typename StringType, typename ITR> -static inline void SplitStringToIteratorUsing(const StringType& full, - const char* delim, - ITR& result) { - // Optimize the common case where delim is a single character. - if (delim[0] != '\0' && delim[1] == '\0') { - char c = delim[0]; - const char* p = full.data(); - const char* end = p + full.size(); - while (p != end) { - if (*p == c) { - ++p; - } else { - const char* start = p; - while (++p != end && *p != c) { - // Skip to the next occurence of the delimiter. - } - *result++ = StringType(start, p - start); - } - } - return; - } - - string::size_type begin_index, end_index; - begin_index = full.find_first_not_of(delim); - while (begin_index != string::npos) { - end_index = full.find_first_of(delim, begin_index); - if (end_index == string::npos) { - *result++ = full.substr(begin_index); - return; - } - *result++ = full.substr(begin_index, (end_index - begin_index)); - begin_index = full.find_first_not_of(delim, end_index); - } -} - -void SplitStringUsing(const string& full, - const char* delim, - vector<string>* result) { - result->reserve(result->size() + CalculateReserveForVector(full, delim)); - std::back_insert_iterator<vector<string>> it(*result); - SplitStringToIteratorUsing(full, delim, it); -} - -} // namespace internal -} // namespace ceres diff --git a/extern/ceres/internal/ceres/stl_util.h b/extern/ceres/internal/ceres/stl_util.h index d3411b73376..2af2518f837 100644 --- a/extern/ceres/internal/ceres/stl_util.h +++ b/extern/ceres/internal/ceres/stl_util.h @@ -73,7 +73,7 @@ void STLDeleteUniqueContainerPointers(ForwardIterator begin, // hash_set, or any other STL container which defines sensible begin(), end(), // and clear() methods. // -// If container is NULL, this function is a no-op. +// If container is nullptr, this function is a no-op. // // As an alternative to calling STLDeleteElements() directly, consider // ElementDeleter (defined below), which ensures that your container's elements diff --git a/extern/ceres/internal/ceres/stringprintf.cc b/extern/ceres/internal/ceres/stringprintf.cc index b0e2acce8f9..e45b4301eef 100644 --- a/extern/ceres/internal/ceres/stringprintf.cc +++ b/extern/ceres/internal/ceres/stringprintf.cc @@ -36,7 +36,7 @@ #include <string> #include <vector> -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -66,7 +66,7 @@ void StringAppendV(string* dst, const char* format, va_list ap) { // Error or MSVC running out of space. MSVC 8.0 and higher // can be asked about space needed with the special idiom below: va_copy(backup_ap, ap); - result = vsnprintf(NULL, 0, format, backup_ap); + result = vsnprintf(nullptr, 0, format, backup_ap); va_end(backup_ap); #endif diff --git a/extern/ceres/internal/ceres/stringprintf.h b/extern/ceres/internal/ceres/stringprintf.h index 4d512784905..e24325fbd35 100644 --- a/extern/ceres/internal/ceres/stringprintf.h +++ b/extern/ceres/internal/ceres/stringprintf.h @@ -41,7 +41,8 @@ #include <cstdarg> #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -63,32 +64,35 @@ namespace internal { #endif // Return a C++ string. -CERES_EXPORT_INTERNAL extern std::string StringPrintf(const char* format, ...) +CERES_NO_EXPORT extern std::string StringPrintf(const char* format, ...) // Tell the compiler to do printf format string checking. CERES_PRINTF_ATTRIBUTE(1, 2); // Store result into a supplied string and return it. -CERES_EXPORT_INTERNAL extern const std::string& SStringPrintf( - std::string* dst, const char* format, ...) +CERES_NO_EXPORT extern const std::string& SStringPrintf(std::string* dst, + const char* format, + ...) // Tell the compiler to do printf format string checking. CERES_PRINTF_ATTRIBUTE(2, 3); // Append result to a supplied string. -CERES_EXPORT_INTERNAL extern void StringAppendF(std::string* dst, - const char* format, - ...) +CERES_NO_EXPORT extern void StringAppendF(std::string* dst, + const char* format, + ...) // Tell the compiler to do printf format string checking. CERES_PRINTF_ATTRIBUTE(2, 3); // Lower-level routine that takes a va_list and appends to a specified string. // All other routines are just convenience wrappers around it. -CERES_EXPORT_INTERNAL extern void StringAppendV(std::string* dst, - const char* format, - va_list ap); +CERES_NO_EXPORT extern void StringAppendV(std::string* dst, + const char* format, + va_list ap); #undef CERES_PRINTF_ATTRIBUTE } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_STRINGPRINTF_H_ diff --git a/extern/ceres/internal/ceres/subset_preconditioner.cc b/extern/ceres/internal/ceres/subset_preconditioner.cc index 779a34ae741..221530c0dd5 100644 --- a/extern/ceres/internal/ceres/subset_preconditioner.cc +++ b/extern/ceres/internal/ceres/subset_preconditioner.cc @@ -32,6 +32,7 @@ #include <memory> #include <string> +#include <utility> #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/inner_product_computer.h" @@ -42,9 +43,9 @@ namespace ceres { namespace internal { -SubsetPreconditioner::SubsetPreconditioner( - const Preconditioner::Options& options, const BlockSparseMatrix& A) - : options_(options), num_cols_(A.num_cols()) { +SubsetPreconditioner::SubsetPreconditioner(Preconditioner::Options options, + const BlockSparseMatrix& A) + : options_(std::move(options)), num_cols_(A.num_cols()) { CHECK_GE(options_.subset_preconditioner_start_row_block, 0) << "Congratulations, you found a bug in Ceres. Please report it."; @@ -55,7 +56,7 @@ SubsetPreconditioner::SubsetPreconditioner( sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options); } -SubsetPreconditioner::~SubsetPreconditioner() {} +SubsetPreconditioner::~SubsetPreconditioner() = default; void SubsetPreconditioner::RightMultiply(const double* x, double* y) const { CHECK(x != nullptr); @@ -66,14 +67,14 @@ void SubsetPreconditioner::RightMultiply(const double* x, double* y) const { bool SubsetPreconditioner::UpdateImpl(const BlockSparseMatrix& A, const double* D) { - BlockSparseMatrix* m = const_cast<BlockSparseMatrix*>(&A); + auto* m = const_cast<BlockSparseMatrix*>(&A); const CompressedRowBlockStructure* bs = m->block_structure(); // A = [P] // [Q] // Now add D to A if needed. - if (D != NULL) { + if (D != nullptr) { // A = [P] // [Q] // [D] @@ -82,19 +83,19 @@ bool SubsetPreconditioner::UpdateImpl(const BlockSparseMatrix& A, m->AppendRows(*regularizer); } - if (inner_product_computer_.get() == NULL) { - inner_product_computer_.reset(InnerProductComputer::Create( + if (inner_product_computer_ == nullptr) { + inner_product_computer_ = InnerProductComputer::Create( *m, options_.subset_preconditioner_start_row_block, bs->rows.size(), - sparse_cholesky_->StorageType())); + sparse_cholesky_->StorageType()); } // Compute inner_product = [Q'*Q + D'*D] inner_product_computer_->Compute(); // Unappend D if needed. - if (D != NULL) { + if (D != nullptr) { // A = [P] // [Q] m->DeleteRowBlocks(bs->cols.size()); diff --git a/extern/ceres/internal/ceres/subset_preconditioner.h b/extern/ceres/internal/ceres/subset_preconditioner.h index 9844a669f45..6d07995a136 100644 --- a/extern/ceres/internal/ceres/subset_preconditioner.h +++ b/extern/ceres/internal/ceres/subset_preconditioner.h @@ -33,7 +33,8 @@ #include <memory> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preconditioner.h" namespace ceres { @@ -67,12 +68,12 @@ class InnerProductComputer; // computationally expensive this preconditioner will be. // // See the tests for example usage. -class CERES_EXPORT_INTERNAL SubsetPreconditioner +class CERES_NO_EXPORT SubsetPreconditioner : public BlockSparseMatrixPreconditioner { public: - SubsetPreconditioner(const Preconditioner::Options& options, + SubsetPreconditioner(Preconditioner::Options options, const BlockSparseMatrix& A); - virtual ~SubsetPreconditioner(); + ~SubsetPreconditioner() override; // Preconditioner interface void RightMultiply(const double* x, double* y) const final; @@ -91,4 +92,6 @@ class CERES_EXPORT_INTERNAL SubsetPreconditioner } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_SUBSET_PRECONDITIONER_H_ diff --git a/extern/ceres/internal/ceres/suitesparse.cc b/extern/ceres/internal/ceres/suitesparse.cc index 0d6f6bdfb88..883dcc8f63e 100644 --- a/extern/ceres/internal/ceres/suitesparse.cc +++ b/extern/ceres/internal/ceres/suitesparse.cc @@ -29,9 +29,10 @@ // Author: sameeragarwal@google.com (Sameer Agarwal) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_SUITESPARSE +#include <memory> #include <vector> #include "ceres/compressed_col_sparse_matrix_utils.h" @@ -368,7 +369,7 @@ SuiteSparseCholesky::~SuiteSparseCholesky() { LinearSolverTerminationType SuiteSparseCholesky::Factorize( CompressedRowSparseMatrix* lhs, string* message) { if (lhs == nullptr) { - *message = "Failure: Input lhs is NULL."; + *message = "Failure: Input lhs is nullptr."; return LINEAR_SOLVER_FATAL_ERROR; } diff --git a/extern/ceres/internal/ceres/suitesparse.h b/extern/ceres/internal/ceres/suitesparse.h index 5dcc53f0167..3f62e7c7b7d 100644 --- a/extern/ceres/internal/ceres/suitesparse.h +++ b/extern/ceres/internal/ceres/suitesparse.h @@ -34,11 +34,12 @@ #define CERES_INTERNAL_SUITESPARSE_H_ // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifndef CERES_NO_SUITESPARSE #include <cstring> +#include <memory> #include <string> #include <vector> @@ -70,6 +71,8 @@ #define SuiteSparse_long UF_long #endif +#include "ceres/internal/disable_warnings.h" + namespace ceres { namespace internal { @@ -81,7 +84,7 @@ class TripletSparseMatrix; // provides the user with a simpler interface. The methods here cannot // be static as a cholmod_common object serves as a global variable // for all cholmod function calls. -class SuiteSparse { +class CERES_NO_EXPORT SuiteSparse { public: SuiteSparse(); ~SuiteSparse(); @@ -106,7 +109,7 @@ class SuiteSparse { cholmod_dense CreateDenseVectorView(const double* x, int size); // Given a vector x, build a cholmod_dense vector of size out_size - // with the first in_size entries copied from x. If x is NULL, then + // with the first in_size entries copied from x. If x is nullptr, then // an all zeros vector is returned. Caller owns the result. cholmod_dense* CreateDenseVector(const double* x, int in_size, int out_size); @@ -123,7 +126,7 @@ class SuiteSparse { // Create and return a matrix m = A * A'. Caller owns the // result. The matrix A is not modified. cholmod_sparse* AATranspose(cholmod_sparse* A) { - cholmod_sparse* m = cholmod_aat(A, NULL, A->nrow, 1, &cc_); + cholmod_sparse* m = cholmod_aat(A, nullptr, A->nrow, 1, &cc_); m->stype = 1; // Pay attention to the upper triangular part. return m; } @@ -196,7 +199,7 @@ class SuiteSparse { // Given a Cholesky factorization of a matrix A = LL^T, solve the // linear system Ax = b, and return the result. If the Solve fails - // NULL is returned. Caller owns the result. + // nullptr is returned. Caller owns the result. // // message contains an explanation of the failures if any. cholmod_dense* Solve(cholmod_factor* L, @@ -288,12 +291,12 @@ class SuiteSparse { cholmod_common cc_; }; -class SuiteSparseCholesky : public SparseCholesky { +class CERES_NO_EXPORT SuiteSparseCholesky final : public SparseCholesky { public: static std::unique_ptr<SparseCholesky> Create(OrderingType ordering_type); // SparseCholesky interface. - virtual ~SuiteSparseCholesky(); + ~SuiteSparseCholesky() override; CompressedRowSparseMatrix::StorageType StorageType() const final; LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, std::string* message) final; @@ -302,7 +305,7 @@ class SuiteSparseCholesky : public SparseCholesky { std::string* message) final; private: - SuiteSparseCholesky(const OrderingType ordering_type); + explicit SuiteSparseCholesky(const OrderingType ordering_type); const OrderingType ordering_type_; SuiteSparse ss_; @@ -312,14 +315,18 @@ class SuiteSparseCholesky : public SparseCholesky { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #else // CERES_NO_SUITESPARSE typedef void cholmod_factor; +#include "ceres/internal/disable_warnings.h" + namespace ceres { namespace internal { -class SuiteSparse { +class CERES_NO_EXPORT SuiteSparse { public: // Defining this static function even when SuiteSparse is not // available, allows client code to check for the presence of CAMD @@ -332,12 +339,14 @@ class SuiteSparse { return false; } - void Free(void* arg) {} + void Free(void* /*arg*/) {} }; } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_NO_SUITESPARSE #endif // CERES_INTERNAL_SUITESPARSE_H_ diff --git a/extern/ceres/internal/ceres/thread_pool.cc b/extern/ceres/internal/ceres/thread_pool.cc index 821431cedb4..57f01af5476 100644 --- a/extern/ceres/internal/ceres/thread_pool.cc +++ b/extern/ceres/internal/ceres/thread_pool.cc @@ -29,7 +29,7 @@ // Author: vitus@google.com (Michael Vitus) // This include must come before any #ifndef check on Ceres compile options. -#include "ceres/internal/port.h" +#include "ceres/internal/config.h" #ifdef CERES_USE_CXX_THREADS @@ -57,7 +57,7 @@ int ThreadPool::MaxNumThreadsAvailable() { : num_hardware_threads; } -ThreadPool::ThreadPool() {} +ThreadPool::ThreadPool() = default; ThreadPool::ThreadPool(int num_threads) { Resize(num_threads); } @@ -83,7 +83,7 @@ void ThreadPool::Resize(int num_threads) { GetNumAllowedThreads(num_threads) - num_current_threads; for (int i = 0; i < create_num_threads; ++i) { - thread_pool_.push_back(std::thread(&ThreadPool::ThreadMainLoop, this)); + thread_pool_.emplace_back(&ThreadPool::ThreadMainLoop, this); } } diff --git a/extern/ceres/internal/ceres/thread_pool.h b/extern/ceres/internal/ceres/thread_pool.h index cdf6625e196..94ab1e66bd4 100644 --- a/extern/ceres/internal/ceres/thread_pool.h +++ b/extern/ceres/internal/ceres/thread_pool.h @@ -37,7 +37,7 @@ #include <vector> #include "ceres/concurrent_queue.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -58,7 +58,7 @@ namespace internal { // workers to stop. The workers will finish all of the tasks that have already // been added to the thread pool. // -class CERES_EXPORT_INTERNAL ThreadPool { +class CERES_NO_EXPORT ThreadPool { public: // Returns the maximum number of hardware threads. static int MaxNumThreadsAvailable(); diff --git a/extern/ceres/internal/ceres/thread_token_provider.h b/extern/ceres/internal/ceres/thread_token_provider.h index 06dc0438572..918c687eb24 100644 --- a/extern/ceres/internal/ceres/thread_token_provider.h +++ b/extern/ceres/internal/ceres/thread_token_provider.h @@ -32,7 +32,7 @@ #define CERES_INTERNAL_THREAD_TOKEN_PROVIDER_H_ #include "ceres/internal/config.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #ifdef CERES_USE_CXX_THREADS #include "ceres/concurrent_queue.h" @@ -66,9 +66,9 @@ namespace internal { // ttp.Release(token); // return token to the pool // } // -class ThreadTokenProvider { +class CERES_NO_EXPORT ThreadTokenProvider { public: - ThreadTokenProvider(int num_threads); + explicit ThreadTokenProvider(int num_threads); // Returns the first token from the queue. The acquired value must be // given back by Release(). @@ -87,8 +87,8 @@ class ThreadTokenProvider { ConcurrentQueue<int> pool_; #endif - ThreadTokenProvider(ThreadTokenProvider&); - ThreadTokenProvider& operator=(ThreadTokenProvider&); + ThreadTokenProvider(ThreadTokenProvider&) = delete; + ThreadTokenProvider& operator=(ThreadTokenProvider&) = delete; }; } // namespace internal diff --git a/extern/ceres/internal/ceres/triplet_sparse_matrix.cc b/extern/ceres/internal/ceres/triplet_sparse_matrix.cc index 5dbf0e7cd3a..bbb5f676a5d 100644 --- a/extern/ceres/internal/ceres/triplet_sparse_matrix.cc +++ b/extern/ceres/internal/ceres/triplet_sparse_matrix.cc @@ -31,10 +31,10 @@ #include "ceres/triplet_sparse_matrix.h" #include <algorithm> -#include <cstddef> +#include <memory> #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/random.h" #include "ceres/types.h" #include "glog/logging.h" @@ -45,7 +45,7 @@ namespace internal { TripletSparseMatrix::TripletSparseMatrix() : num_rows_(0), num_cols_(0), max_num_nonzeros_(0), num_nonzeros_(0) {} -TripletSparseMatrix::~TripletSparseMatrix() {} +TripletSparseMatrix::~TripletSparseMatrix() = default; TripletSparseMatrix::TripletSparseMatrix(int num_rows, int num_cols, @@ -109,8 +109,9 @@ bool TripletSparseMatrix::AllTripletsWithinBounds() const { for (int i = 0; i < num_nonzeros_; ++i) { // clang-format off if ((rows_[i] < 0) || (rows_[i] >= num_rows_) || - (cols_[i] < 0) || (cols_[i] >= num_cols_)) + (cols_[i] < 0) || (cols_[i] >= num_cols_)) { return false; + } // clang-format on } return true; @@ -123,9 +124,12 @@ void TripletSparseMatrix::Reserve(int new_max_num_nonzeros) { // Nothing to do if we have enough space already. if (new_max_num_nonzeros <= max_num_nonzeros_) return; - int* new_rows = new int[new_max_num_nonzeros]; - int* new_cols = new int[new_max_num_nonzeros]; - double* new_values = new double[new_max_num_nonzeros]; + std::unique_ptr<int[]> new_rows = + std::make_unique<int[]>(new_max_num_nonzeros); + std::unique_ptr<int[]> new_cols = + std::make_unique<int[]>(new_max_num_nonzeros); + std::unique_ptr<double[]> new_values = + std::make_unique<double[]>(new_max_num_nonzeros); for (int i = 0; i < num_nonzeros_; ++i) { new_rows[i] = rows_[i]; @@ -133,10 +137,9 @@ void TripletSparseMatrix::Reserve(int new_max_num_nonzeros) { new_values[i] = values_[i]; } - rows_.reset(new_rows); - cols_.reset(new_cols); - values_.reset(new_values); - + rows_ = std::move(new_rows); + cols_ = std::move(new_cols); + values_ = std::move(new_values); max_num_nonzeros_ = new_max_num_nonzeros; } @@ -152,9 +155,9 @@ void TripletSparseMatrix::set_num_nonzeros(int num_nonzeros) { } void TripletSparseMatrix::AllocateMemory() { - rows_.reset(new int[max_num_nonzeros_]); - cols_.reset(new int[max_num_nonzeros_]); - values_.reset(new double[max_num_nonzeros_]); + rows_ = std::make_unique<int[]>(max_num_nonzeros_); + cols_ = std::make_unique<int[]>(max_num_nonzeros_); + values_ = std::make_unique<double[]>(max_num_nonzeros_); } void TripletSparseMatrix::CopyData(const TripletSparseMatrix& orig) { @@ -252,10 +255,11 @@ void TripletSparseMatrix::Resize(int new_num_rows, int new_num_cols) { num_nonzeros_ -= dropped_terms; } -TripletSparseMatrix* TripletSparseMatrix::CreateSparseDiagonalMatrix( - const double* values, int num_rows) { - TripletSparseMatrix* m = - new TripletSparseMatrix(num_rows, num_rows, num_rows); +std::unique_ptr<TripletSparseMatrix> +TripletSparseMatrix::CreateSparseDiagonalMatrix(const double* values, + int num_rows) { + std::unique_ptr<TripletSparseMatrix> m = + std::make_unique<TripletSparseMatrix>(num_rows, num_rows, num_rows); for (int i = 0; i < num_rows; ++i) { m->mutable_rows()[i] = i; m->mutable_cols()[i] = i; @@ -272,7 +276,7 @@ void TripletSparseMatrix::ToTextFile(FILE* file) const { } } -TripletSparseMatrix* TripletSparseMatrix::CreateRandomMatrix( +std::unique_ptr<TripletSparseMatrix> TripletSparseMatrix::CreateRandomMatrix( const TripletSparseMatrix::RandomMatrixOptions& options) { CHECK_GT(options.num_rows, 0); CHECK_GT(options.num_cols, 0); @@ -297,7 +301,7 @@ TripletSparseMatrix* TripletSparseMatrix::CreateRandomMatrix( } } - return new TripletSparseMatrix( + return std::make_unique<TripletSparseMatrix>( options.num_rows, options.num_cols, rows, cols, values); } diff --git a/extern/ceres/internal/ceres/triplet_sparse_matrix.h b/extern/ceres/internal/ceres/triplet_sparse_matrix.h index cc9fee572a2..065c690dba3 100644 --- a/extern/ceres/internal/ceres/triplet_sparse_matrix.h +++ b/extern/ceres/internal/ceres/triplet_sparse_matrix.h @@ -34,8 +34,9 @@ #include <memory> #include <vector> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/sparse_matrix.h" #include "ceres/types.h" @@ -46,7 +47,7 @@ namespace internal { // manipulate sparse matrices in triplet (i,j,s) form. This object is // inspired by the design of the cholmod_triplet struct used in the // SuiteSparse package and is memory layout compatible with it. -class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { +class CERES_NO_EXPORT TripletSparseMatrix final : public SparseMatrix { public: TripletSparseMatrix(); TripletSparseMatrix(int num_rows, int num_cols, int max_num_nonzeros); @@ -56,11 +57,11 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { const std::vector<int>& cols, const std::vector<double>& values); - explicit TripletSparseMatrix(const TripletSparseMatrix& orig); + TripletSparseMatrix(const TripletSparseMatrix& orig); TripletSparseMatrix& operator=(const TripletSparseMatrix& rhs); - virtual ~TripletSparseMatrix(); + ~TripletSparseMatrix() override; // Implementation of the SparseMatrix interface. void SetZero() final; @@ -115,8 +116,8 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { // Build a sparse diagonal matrix of size num_rows x num_rows from // the array values. Entries of the values array are copied into the // sparse matrix. - static TripletSparseMatrix* CreateSparseDiagonalMatrix(const double* values, - int num_rows); + static std::unique_ptr<TripletSparseMatrix> CreateSparseDiagonalMatrix( + const double* values, int num_rows); // Options struct to control the generation of random // TripletSparseMatrix objects. @@ -132,9 +133,7 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { // Create a random CompressedRowSparseMatrix whose entries are // normally distributed and whose structure is determined by // RandomMatrixOptions. - // - // Caller owns the result. - static TripletSparseMatrix* CreateRandomMatrix( + static std::unique_ptr<TripletSparseMatrix> CreateRandomMatrix( const TripletSparseMatrix::RandomMatrixOptions& options); private: @@ -158,4 +157,6 @@ class CERES_EXPORT_INTERNAL TripletSparseMatrix : public SparseMatrix { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRIPLET_SPARSE_MATRIX_H__ diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.cc b/extern/ceres/internal/ceres/trust_region_minimizer.cc index bcf05b3ddfb..9ef5167ba6c 100644 --- a/extern/ceres/internal/ceres/trust_region_minimizer.cc +++ b/extern/ceres/internal/ceres/trust_region_minimizer.cc @@ -62,8 +62,6 @@ namespace ceres { namespace internal { -TrustRegionMinimizer::~TrustRegionMinimizer() {} - void TrustRegionMinimizer::Minimize(const Minimizer::Options& options, double* parameters, Solver::Summary* solver_summary) { @@ -75,11 +73,11 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options, // Create the TrustRegionStepEvaluator. The construction needs to be // delayed to this point because we need the cost for the starting // point to initialize the step evaluator. - step_evaluator_.reset(new TrustRegionStepEvaluator( + step_evaluator_ = std::make_unique<TrustRegionStepEvaluator>( x_cost_, options_.use_nonmonotonic_steps ? options_.max_consecutive_nonmonotonic_steps - : 0)); + : 0); while (FinalizeIterationAndCheckIfMinimizerCanContinue()) { iteration_start_time_in_secs_ = WallTimeInSeconds(); @@ -750,14 +748,12 @@ bool TrustRegionMinimizer::FunctionToleranceReached() { // Compute candidate_x_ = Plus(x_, delta_) // Evaluate the cost of candidate_x_ as candidate_cost_. // -// Failure to compute the step or the cost mean that candidate_cost_ -// is set to std::numeric_limits<double>::max(). Unlike -// EvaluateGradientAndJacobian, failure in this function is not fatal -// as we are only computing and evaluating a candidate point, and if -// for some reason we are unable to evaluate it, we consider it to be -// a point with very high cost. This allows the user to deal with edge -// cases/constraints as part of the LocalParameterization and -// CostFunction objects. +// Failure to compute the step or the cost mean that candidate_cost_ is set to +// std::numeric_limits<double>::max(). Unlike EvaluateGradientAndJacobian, +// failure in this function is not fatal as we are only computing and evaluating +// a candidate point, and if for some reason we are unable to evaluate it, we +// consider it to be a point with very high cost. This allows the user to deal +// with edge cases/constraints as part of the Manifold and CostFunction objects. void TrustRegionMinimizer::ComputeCandidatePointAndEvaluateCost() { if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) { if (is_not_silent_) { diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.h b/extern/ceres/internal/ceres/trust_region_minimizer.h index be4d40653c4..c6fc542a063 100644 --- a/extern/ceres/internal/ceres/trust_region_minimizer.h +++ b/extern/ceres/internal/ceres/trust_region_minimizer.h @@ -33,8 +33,9 @@ #include <memory> +#include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/port.h" +#include "ceres/internal/export.h" #include "ceres/minimizer.h" #include "ceres/solver.h" #include "ceres/sparse_matrix.h" @@ -48,10 +49,8 @@ namespace internal { // Generic trust region minimization algorithm. // // For example usage, see SolverImpl::Minimize. -class CERES_EXPORT_INTERNAL TrustRegionMinimizer : public Minimizer { +class CERES_NO_EXPORT TrustRegionMinimizer final : public Minimizer { public: - ~TrustRegionMinimizer(); - // This method is not thread safe. void Minimize(const Minimizer::Options& options, double* parameters, @@ -164,4 +163,6 @@ class CERES_EXPORT_INTERNAL TrustRegionMinimizer : public Minimizer { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_ diff --git a/extern/ceres/internal/ceres/trust_region_preprocessor.cc b/extern/ceres/internal/ceres/trust_region_preprocessor.cc index 0943edbba85..edba47d88a5 100644 --- a/extern/ceres/internal/ceres/trust_region_preprocessor.cc +++ b/extern/ceres/internal/ceres/trust_region_preprocessor.cc @@ -55,13 +55,14 @@ using std::vector; namespace { -ParameterBlockOrdering* CreateDefaultLinearSolverOrdering( +std::shared_ptr<ParameterBlockOrdering> CreateDefaultLinearSolverOrdering( const Program& program) { - ParameterBlockOrdering* ordering = new ParameterBlockOrdering; + std::shared_ptr<ParameterBlockOrdering> ordering = + std::make_shared<ParameterBlockOrdering>(); const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks(); - for (int i = 0; i < parameter_blocks.size(); ++i) { + for (auto* parameter_block : parameter_blocks) { ordering->AddElementToGroup( - const_cast<double*>(parameter_blocks[i]->user_state()), 0); + const_cast<double*>(parameter_block->user_state()), 0); } return ordering; } @@ -160,8 +161,8 @@ bool SetupLinearSolver(PreprocessedProblem* pp) { // assume that they are giving all the freedom to us in choosing // the best possible ordering. This intent can be indicated by // putting all the parameter blocks in the same elimination group. - options.linear_solver_ordering.reset( - CreateDefaultLinearSolverOrdering(*pp->reduced_program)); + options.linear_solver_ordering = + CreateDefaultLinearSolverOrdering(*pp->reduced_program); } else { // If the user supplied an ordering, then check if the first // elimination group is still non-empty after the reduced problem @@ -247,7 +248,7 @@ bool SetupLinearSolver(PreprocessedProblem* pp) { } } - pp->linear_solver.reset(LinearSolver::Create(pp->linear_solver_options)); + pp->linear_solver = LinearSolver::Create(pp->linear_solver_options); return (pp->linear_solver != nullptr); } @@ -269,8 +270,8 @@ bool SetupEvaluator(PreprocessedProblem* pp) { pp->evaluator_options.context = pp->problem->context(); pp->evaluator_options.evaluation_callback = pp->reduced_program->mutable_evaluation_callback(); - pp->evaluator.reset(Evaluator::Create( - pp->evaluator_options, pp->reduced_program.get(), &pp->error)); + pp->evaluator = Evaluator::Create( + pp->evaluator_options, pp->reduced_program.get(), &pp->error); return (pp->evaluator != nullptr); } @@ -316,12 +317,12 @@ bool SetupInnerIterationMinimizer(PreprocessedProblem* pp) { } } else { // The user did not supply an ordering, so create one. - options.inner_iteration_ordering.reset( - CoordinateDescentMinimizer::CreateOrdering(*pp->reduced_program)); + options.inner_iteration_ordering = + CoordinateDescentMinimizer::CreateOrdering(*pp->reduced_program); } - pp->inner_iteration_minimizer.reset( - new CoordinateDescentMinimizer(pp->problem->context())); + pp->inner_iteration_minimizer = + std::make_unique<CoordinateDescentMinimizer>(pp->problem->context()); return pp->inner_iteration_minimizer->Init(*pp->reduced_program, pp->problem->parameter_map(), *options.inner_iteration_ordering, @@ -335,7 +336,7 @@ void SetupMinimizerOptions(PreprocessedProblem* pp) { SetupCommonMinimizerOptions(pp); pp->minimizer_options.is_constrained = pp->reduced_program->IsBoundsConstrained(); - pp->minimizer_options.jacobian.reset(pp->evaluator->CreateJacobian()); + pp->minimizer_options.jacobian = pp->evaluator->CreateJacobian(); pp->minimizer_options.inner_iteration_minimizer = pp->inner_iteration_minimizer; @@ -348,15 +349,13 @@ void SetupMinimizerOptions(PreprocessedProblem* pp) { strategy_options.trust_region_strategy_type = options.trust_region_strategy_type; strategy_options.dogleg_type = options.dogleg_type; - pp->minimizer_options.trust_region_strategy.reset( - TrustRegionStrategy::Create(strategy_options)); + pp->minimizer_options.trust_region_strategy = + TrustRegionStrategy::Create(strategy_options); CHECK(pp->minimizer_options.trust_region_strategy != nullptr); } } // namespace -TrustRegionPreprocessor::~TrustRegionPreprocessor() {} - bool TrustRegionPreprocessor::Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* pp) { @@ -370,10 +369,10 @@ bool TrustRegionPreprocessor::Preprocess(const Solver::Options& options, return false; } - pp->reduced_program.reset(program->CreateReducedProgram( - &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error)); + pp->reduced_program = program->CreateReducedProgram( + &pp->removed_parameter_blocks, &pp->fixed_cost, &pp->error); - if (pp->reduced_program.get() == NULL) { + if (pp->reduced_program.get() == nullptr) { return false; } diff --git a/extern/ceres/internal/ceres/trust_region_preprocessor.h b/extern/ceres/internal/ceres/trust_region_preprocessor.h index 2655abe4b2e..26ef8fad37d 100644 --- a/extern/ceres/internal/ceres/trust_region_preprocessor.h +++ b/extern/ceres/internal/ceres/trust_region_preprocessor.h @@ -31,15 +31,15 @@ #ifndef CERES_INTERNAL_TRUST_REGION_PREPROCESSOR_H_ #define CERES_INTERNAL_TRUST_REGION_PREPROCESSOR_H_ -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/preprocessor.h" namespace ceres { namespace internal { -class CERES_EXPORT_INTERNAL TrustRegionPreprocessor : public Preprocessor { +class CERES_NO_EXPORT TrustRegionPreprocessor final : public Preprocessor { public: - virtual ~TrustRegionPreprocessor(); bool Preprocess(const Solver::Options& options, ProblemImpl* problem, PreprocessedProblem* preprocessed_problem) override; @@ -48,4 +48,6 @@ class CERES_EXPORT_INTERNAL TrustRegionPreprocessor : public Preprocessor { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRUST_REGION_PREPROCESSOR_H_ diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.h b/extern/ceres/internal/ceres/trust_region_step_evaluator.h index 03c00362dac..8e0c4e91f49 100644 --- a/extern/ceres/internal/ceres/trust_region_step_evaluator.h +++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.h @@ -31,6 +31,8 @@ #ifndef CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ #define CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ +#include "ceres/internal/export.h" + namespace ceres { namespace internal { @@ -74,7 +76,7 @@ namespace internal { // x = x + delta; // step_evaluator->StepAccepted(cost, model_cost_change); // } -class TrustRegionStepEvaluator { +class CERES_NO_EXPORT TrustRegionStepEvaluator { public: // initial_cost is as the name implies the cost of the starting // state of the trust region minimizer. diff --git a/extern/ceres/internal/ceres/trust_region_strategy.cc b/extern/ceres/internal/ceres/trust_region_strategy.cc index 7e429d5e557..1096cd3c8aa 100644 --- a/extern/ceres/internal/ceres/trust_region_strategy.cc +++ b/extern/ceres/internal/ceres/trust_region_strategy.cc @@ -32,20 +32,23 @@ #include "ceres/trust_region_strategy.h" +#include <memory> + #include "ceres/dogleg_strategy.h" #include "ceres/levenberg_marquardt_strategy.h" namespace ceres { namespace internal { -TrustRegionStrategy::~TrustRegionStrategy() {} +TrustRegionStrategy::~TrustRegionStrategy() = default; -TrustRegionStrategy* TrustRegionStrategy::Create(const Options& options) { +std::unique_ptr<TrustRegionStrategy> TrustRegionStrategy::Create( + const Options& options) { switch (options.trust_region_strategy_type) { case LEVENBERG_MARQUARDT: - return new LevenbergMarquardtStrategy(options); + return std::make_unique<LevenbergMarquardtStrategy>(options); case DOGLEG: - return new DoglegStrategy(options); + return std::make_unique<DoglegStrategy>(options); default: LOG(FATAL) << "Unknown trust region strategy: " << options.trust_region_strategy_type; @@ -53,7 +56,7 @@ TrustRegionStrategy* TrustRegionStrategy::Create(const Options& options) { LOG(FATAL) << "Unknown trust region strategy: " << options.trust_region_strategy_type; - return NULL; + return nullptr; } } // namespace internal diff --git a/extern/ceres/internal/ceres/trust_region_strategy.h b/extern/ceres/internal/ceres/trust_region_strategy.h index 176f73a4876..33086cafb52 100644 --- a/extern/ceres/internal/ceres/trust_region_strategy.h +++ b/extern/ceres/internal/ceres/trust_region_strategy.h @@ -31,9 +31,11 @@ #ifndef CERES_INTERNAL_TRUST_REGION_STRATEGY_H_ #define CERES_INTERNAL_TRUST_REGION_STRATEGY_H_ +#include <memory> #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/linear_solver.h" namespace ceres { @@ -54,7 +56,7 @@ class SparseMatrix; // the LevenbergMarquardtStrategy uses the inverse of the trust region // radius to scale the damping term, which controls the step size, but // does not set a hard limit on its size. -class CERES_EXPORT_INTERNAL TrustRegionStrategy { +class CERES_NO_EXPORT TrustRegionStrategy { public: struct Options { TrustRegionStrategyType trust_region_strategy_type = LEVENBERG_MARQUARDT; @@ -75,7 +77,7 @@ class CERES_EXPORT_INTERNAL TrustRegionStrategy { }; // Factory. - static TrustRegionStrategy* Create(const Options& options); + static std::unique_ptr<TrustRegionStrategy> Create(const Options& options); virtual ~TrustRegionStrategy(); @@ -142,4 +144,6 @@ class CERES_EXPORT_INTERNAL TrustRegionStrategy { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_TRUST_REGION_STRATEGY_H_ diff --git a/extern/ceres/internal/ceres/types.cc b/extern/ceres/internal/ceres/types.cc index 39bb2d8cc4d..48242678b46 100644 --- a/extern/ceres/internal/ceres/types.cc +++ b/extern/ceres/internal/ceres/types.cc @@ -34,6 +34,7 @@ #include <cctype> #include <string> +#include "ceres/internal/config.h" #include "glog/logging.h" namespace ceres { @@ -128,6 +129,7 @@ const char* DenseLinearAlgebraLibraryTypeToString( switch (type) { CASESTR(EIGEN); CASESTR(LAPACK); + CASESTR(CUDA); default: return "UNKNOWN"; } @@ -138,6 +140,7 @@ bool StringToDenseLinearAlgebraLibraryType( UpperCase(&value); STRENUM(EIGEN); STRENUM(LAPACK); + STRENUM(CUDA); return false; } @@ -417,6 +420,7 @@ bool IsDenseLinearAlgebraLibraryTypeAvailable( if (type == EIGEN) { return true; } + if (type == LAPACK) { #ifdef CERES_NO_LAPACK return false; @@ -425,6 +429,14 @@ bool IsDenseLinearAlgebraLibraryTypeAvailable( #endif } + if (type == CUDA) { +#ifdef CERES_NO_CUDA + return false; +#else + return true; +#endif + } + LOG(WARNING) << "Unknown dense linear algebra library " << type; return false; } diff --git a/extern/ceres/internal/ceres/visibility.cc b/extern/ceres/internal/ceres/visibility.cc index 82bf6f170b8..f666ce0c4bb 100644 --- a/extern/ceres/internal/ceres/visibility.cc +++ b/extern/ceres/internal/ceres/visibility.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ #include <algorithm> #include <cmath> #include <ctime> +#include <memory> #include <set> #include <unordered_map> #include <utility> @@ -62,8 +63,8 @@ void ComputeVisibility(const CompressedRowBlockStructure& block_structure, visibility->resize(0); visibility->resize(block_structure.cols.size() - num_eliminate_blocks); - for (int i = 0; i < block_structure.rows.size(); ++i) { - const vector<Cell>& cells = block_structure.rows[i].cells; + for (const auto& row : block_structure.rows) { + const vector<Cell>& cells = row.cells; int block_id = cells[0].block_id; // If the first block is not an e_block, then skip this row block. if (block_id >= num_eliminate_blocks) { @@ -79,16 +80,16 @@ void ComputeVisibility(const CompressedRowBlockStructure& block_structure, } } -WeightedGraph<int>* CreateSchurComplementGraph( +std::unique_ptr<WeightedGraph<int>> CreateSchurComplementGraph( const vector<set<int>>& visibility) { - const time_t start_time = time(NULL); + const time_t start_time = time(nullptr); // Compute the number of e_blocks/point blocks. Since the visibility // set for each e_block/camera contains the set of e_blocks/points // visible to it, we find the maximum across all visibility sets. int num_points = 0; - for (int i = 0; i < visibility.size(); i++) { - if (visibility[i].size() > 0) { - num_points = max(num_points, (*visibility[i].rbegin()) + 1); + for (const auto& visible : visibility) { + if (!visible.empty()) { + num_points = max(num_points, (*visible.rbegin()) + 1); } } @@ -100,7 +101,7 @@ WeightedGraph<int>* CreateSchurComplementGraph( vector<set<int>> inverse_visibility(num_points); for (int i = 0; i < visibility.size(); i++) { const set<int>& visibility_set = visibility[i]; - for (const int v : visibility_set) { + for (int v : visibility_set) { inverse_visibility[v].insert(i); } } @@ -111,17 +112,17 @@ WeightedGraph<int>* CreateSchurComplementGraph( // Count the number of points visible to each camera/f_block pair. for (const auto& inverse_visibility_set : inverse_visibility) { - for (set<int>::const_iterator camera1 = inverse_visibility_set.begin(); + for (auto camera1 = inverse_visibility_set.begin(); camera1 != inverse_visibility_set.end(); ++camera1) { - set<int>::const_iterator camera2 = camera1; + auto camera2 = camera1; for (++camera2; camera2 != inverse_visibility_set.end(); ++camera2) { ++(camera_pairs[make_pair(*camera1, *camera2)]); } } } - WeightedGraph<int>* graph = new WeightedGraph<int>; + auto graph = std::make_unique<WeightedGraph<int>>(); // Add vertices and initialize the pairs for self edges so that self // edges are guaranteed. This is needed for the Canonical views @@ -146,7 +147,7 @@ WeightedGraph<int>* CreateSchurComplementGraph( graph->AddEdge(camera1, camera2, weight); } - VLOG(2) << "Schur complement graph time: " << (time(NULL) - start_time); + VLOG(2) << "Schur complement graph time: " << (time(nullptr) - start_time); return graph; } diff --git a/extern/ceres/internal/ceres/visibility.h b/extern/ceres/internal/ceres/visibility.h index 68c6723fad7..d8f6968d98f 100644 --- a/extern/ceres/internal/ceres/visibility.h +++ b/extern/ceres/internal/ceres/visibility.h @@ -35,11 +35,13 @@ #ifndef CERES_INTERNAL_VISIBILITY_H_ #define CERES_INTERNAL_VISIBILITY_H_ +#include <memory> #include <set> #include <vector> #include "ceres/graph.h" -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" namespace ceres { namespace internal { @@ -54,7 +56,7 @@ struct CompressedRowBlockStructure; // // In a structure from motion problem, e_blocks correspond to 3D // points and f_blocks correspond to cameras. -CERES_EXPORT_INTERNAL void ComputeVisibility( +CERES_NO_EXPORT void ComputeVisibility( const CompressedRowBlockStructure& block_structure, int num_eliminate_blocks, std::vector<std::set<int>>* visibility); @@ -72,10 +74,12 @@ CERES_EXPORT_INTERNAL void ComputeVisibility( // // Caller acquires ownership of the returned WeightedGraph pointer // (heap-allocated). -CERES_EXPORT_INTERNAL WeightedGraph<int>* CreateSchurComplementGraph( +CERES_NO_EXPORT std::unique_ptr<WeightedGraph<int>> CreateSchurComplementGraph( const std::vector<std::set<int>>& visibility); } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_VISIBILITY_H_ diff --git a/extern/ceres/internal/ceres/visibility_based_preconditioner.cc b/extern/ceres/internal/ceres/visibility_based_preconditioner.cc index 0cf4afaae06..831a8663027 100644 --- a/extern/ceres/internal/ceres/visibility_based_preconditioner.cc +++ b/extern/ceres/internal/ceres/visibility_based_preconditioner.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2022 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -70,9 +70,8 @@ static constexpr double kCanonicalViewsSimilarityPenaltyWeight = 0.0; static constexpr double kSingleLinkageMinSimilarity = 0.9; VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( - const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options) - : options_(options), num_blocks_(0), num_clusters_(0) { + const CompressedRowBlockStructure& bs, Preconditioner::Options options) + : options_(std::move(options)), num_blocks_(0), num_clusters_(0) { CHECK_GT(options_.elimination_groups.size(), 1); CHECK_GT(options_.elimination_groups[0], 0); CHECK(options_.type == CLUSTER_JACOBI || options_.type == CLUSTER_TRIDIAGONAL) @@ -80,7 +79,7 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( num_blocks_ = bs.cols.size() - options_.elimination_groups[0]; CHECK_GT(num_blocks_, 0) << "Jacobian should have at least 1 f_block for " << "visibility based preconditioning."; - CHECK(options_.context != NULL); + CHECK(options_.context != nullptr); // Vector of camera block sizes block_size_.resize(num_blocks_); @@ -88,7 +87,7 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( block_size_[i] = bs.cols[i + options_.elimination_groups[0]].size; } - const time_t start_time = time(NULL); + const time_t start_time = time(nullptr); switch (options_.type) { case CLUSTER_JACOBI: ComputeClusterJacobiSparsity(bs); @@ -99,11 +98,11 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( default: LOG(FATAL) << "Unknown preconditioner type"; } - const time_t structure_time = time(NULL); + const time_t structure_time = time(nullptr); InitStorage(bs); - const time_t storage_time = time(NULL); + const time_t storage_time = time(nullptr); InitEliminator(bs); - const time_t eliminator_time = time(NULL); + const time_t eliminator_time = time(nullptr); LinearSolver::Options sparse_cholesky_options; sparse_cholesky_options.sparse_linear_algebra_library_type = @@ -118,14 +117,14 @@ VisibilityBasedPreconditioner::VisibilityBasedPreconditioner( sparse_cholesky_options.use_postordering = true; sparse_cholesky_ = SparseCholesky::Create(sparse_cholesky_options); - const time_t init_time = time(NULL); + const time_t init_time = time(nullptr); VLOG(2) << "init time: " << init_time - start_time << " structure time: " << structure_time - start_time << " storage time:" << storage_time - structure_time << " eliminator time: " << eliminator_time - storage_time; } -VisibilityBasedPreconditioner::~VisibilityBasedPreconditioner() {} +VisibilityBasedPreconditioner::~VisibilityBasedPreconditioner() = default; // Determine the sparsity structure of the CLUSTER_JACOBI // preconditioner. It clusters cameras using their scene @@ -162,11 +161,9 @@ void VisibilityBasedPreconditioner::ComputeClusterTridiagonalSparsity( // maximum spanning forest of this graph. vector<set<int>> cluster_visibility; ComputeClusterVisibility(visibility, &cluster_visibility); - std::unique_ptr<WeightedGraph<int>> cluster_graph( - CreateClusterGraph(cluster_visibility)); + auto cluster_graph = CreateClusterGraph(cluster_visibility); CHECK(cluster_graph != nullptr); - std::unique_ptr<WeightedGraph<int>> forest( - Degree2MaximumSpanningForest(*cluster_graph)); + auto forest = Degree2MaximumSpanningForest(*cluster_graph); CHECK(forest != nullptr); ForestToClusterPairs(*forest, &cluster_pairs_); } @@ -175,7 +172,8 @@ void VisibilityBasedPreconditioner::ComputeClusterTridiagonalSparsity( void VisibilityBasedPreconditioner::InitStorage( const CompressedRowBlockStructure& bs) { ComputeBlockPairsInPreconditioner(bs); - m_.reset(new BlockRandomAccessSparseMatrix(block_size_, block_pairs_)); + m_ = std::make_unique<BlockRandomAccessSparseMatrix>(block_size_, + block_pairs_); } // Call the canonical views algorithm and cluster the cameras based on @@ -186,8 +184,7 @@ void VisibilityBasedPreconditioner::InitStorage( // memberships for each camera block. void VisibilityBasedPreconditioner::ClusterCameras( const vector<set<int>>& visibility) { - std::unique_ptr<WeightedGraph<int>> schur_complement_graph( - CreateSchurComplementGraph(visibility)); + auto schur_complement_graph = CreateSchurComplementGraph(visibility); CHECK(schur_complement_graph != nullptr); std::unordered_map<int, int> membership; @@ -285,14 +282,12 @@ void VisibilityBasedPreconditioner::ComputeBlockPairsInPreconditioner( } } - for (set<int>::const_iterator block1 = f_blocks.begin(); - block1 != f_blocks.end(); - ++block1) { - set<int>::const_iterator block2 = block1; + for (auto block1 = f_blocks.begin(); block1 != f_blocks.end(); ++block1) { + auto block2 = block1; ++block2; for (; block2 != f_blocks.end(); ++block2) { if (IsBlockPairInPreconditioner(*block1, *block2)) { - block_pairs_.insert(make_pair(*block1, *block2)); + block_pairs_.emplace(*block1, *block2); } } } @@ -304,8 +299,8 @@ void VisibilityBasedPreconditioner::ComputeBlockPairsInPreconditioner( CHECK_GE(row.cells.front().block_id, num_eliminate_blocks); for (int i = 0; i < row.cells.size(); ++i) { const int block1 = row.cells[i].block_id - num_eliminate_blocks; - for (int j = 0; j < row.cells.size(); ++j) { - const int block2 = row.cells[j].block_id - num_eliminate_blocks; + for (const auto& cell : row.cells) { + const int block2 = cell.block_id - num_eliminate_blocks; if (block1 <= block2) { if (IsBlockPairInPreconditioner(block1, block2)) { block_pairs_.insert(make_pair(block1, block2)); @@ -328,7 +323,7 @@ void VisibilityBasedPreconditioner::InitEliminator( eliminator_options.f_block_size = options_.f_block_size; eliminator_options.row_block_size = options_.row_block_size; eliminator_options.context = options_.context; - eliminator_.reset(SchurEliminatorBase::Create(eliminator_options)); + eliminator_ = SchurEliminatorBase::Create(eliminator_options); const bool kFullRankETE = true; eliminator_->Init( eliminator_options.elimination_groups[0], kFullRankETE, &bs); @@ -337,7 +332,7 @@ void VisibilityBasedPreconditioner::InitEliminator( // Update the values of the preconditioner matrix and factorize it. bool VisibilityBasedPreconditioner::UpdateImpl(const BlockSparseMatrix& A, const double* D) { - const time_t start_time = time(NULL); + const time_t start_time = time(nullptr); const int num_rows = m_->num_rows(); CHECK_GT(num_rows, 0); @@ -375,7 +370,7 @@ bool VisibilityBasedPreconditioner::UpdateImpl(const BlockSparseMatrix& A, status = Factorize(); } - VLOG(2) << "Compute time: " << time(NULL) - start_time; + VLOG(2) << "Compute time: " << time(nullptr) - start_time; return (status == LINEAR_SOLVER_SUCCESS); } @@ -395,7 +390,7 @@ void VisibilityBasedPreconditioner::ScaleOffDiagonalCells() { int r, c, row_stride, col_stride; CellInfo* cell_info = m_->GetCell(block1, block2, &r, &c, &row_stride, &col_stride); - CHECK(cell_info != NULL) + CHECK(cell_info != nullptr) << "Cell missing for block pair (" << block1 << "," << block2 << ")" << " cluster pair (" << cluster_membership_[block1] << " " << cluster_membership_[block2] << ")"; @@ -420,11 +415,10 @@ LinearSolverTerminationType VisibilityBasedPreconditioner::Factorize() { const CompressedRowSparseMatrix::StorageType storage_type = sparse_cholesky_->StorageType(); if (storage_type == CompressedRowSparseMatrix::UPPER_TRIANGULAR) { - lhs.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::UPPER_TRIANGULAR); } else { - lhs.reset( - CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm)); + lhs = CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm); lhs->set_storage_type(CompressedRowSparseMatrix::LOWER_TRIANGULAR); } @@ -503,9 +497,10 @@ void VisibilityBasedPreconditioner::ComputeClusterVisibility( // Construct a graph whose vertices are the clusters, and the edge // weights are the number of 3D points visible to cameras in both the // vertices. -WeightedGraph<int>* VisibilityBasedPreconditioner::CreateClusterGraph( +std::unique_ptr<WeightedGraph<int>> +VisibilityBasedPreconditioner::CreateClusterGraph( const vector<set<int>>& cluster_visibility) const { - WeightedGraph<int>* cluster_graph = new WeightedGraph<int>; + auto cluster_graph = std::make_unique<WeightedGraph<int>>(); for (int i = 0; i < num_clusters_; ++i) { cluster_graph->AddVertex(i); diff --git a/extern/ceres/internal/ceres/visibility_based_preconditioner.h b/extern/ceres/internal/ceres/visibility_based_preconditioner.h index 0457b9a376a..8079dc3f3ce 100644 --- a/extern/ceres/internal/ceres/visibility_based_preconditioner.h +++ b/extern/ceres/internal/ceres/visibility_based_preconditioner.h @@ -122,9 +122,10 @@ class SchurEliminatorBase; // options.elimination_groups.push_back(num_cameras); // VisibilityBasedPreconditioner preconditioner( // *A.block_structure(), options); -// preconditioner.Update(A, NULL); +// preconditioner.Update(A, nullptr); // preconditioner.RightMultiply(x, y); -class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner { +class CERES_NO_EXPORT VisibilityBasedPreconditioner + : public BlockSparseMatrixPreconditioner { public: // Initialize the symbolic structure of the preconditioner. bs is // the block structure of the linear system to be solved. It is used @@ -133,11 +134,11 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner { // It has the same structural requirement as other Schur complement // based solvers. Please see schur_eliminator.h for more details. VisibilityBasedPreconditioner(const CompressedRowBlockStructure& bs, - const Preconditioner::Options& options); + Preconditioner::Options options); VisibilityBasedPreconditioner(const VisibilityBasedPreconditioner&) = delete; void operator=(const VisibilityBasedPreconditioner&) = delete; - virtual ~VisibilityBasedPreconditioner(); + ~VisibilityBasedPreconditioner() override; // Preconditioner interface void RightMultiply(const double* x, double* y) const final; @@ -160,7 +161,7 @@ class VisibilityBasedPreconditioner : public BlockSparseMatrixPreconditioner { void ComputeClusterVisibility( const std::vector<std::set<int>>& visibility, std::vector<std::set<int>>* cluster_visibility) const; - WeightedGraph<int>* CreateClusterGraph( + std::unique_ptr<WeightedGraph<int>> CreateClusterGraph( const std::vector<std::set<int>>& visibility) const; void ForestToClusterPairs( const WeightedGraph<int>& forest, diff --git a/extern/ceres/internal/ceres/wall_time.cc b/extern/ceres/internal/ceres/wall_time.cc index 716392741e9..a54ab640b3e 100644 --- a/extern/ceres/internal/ceres/wall_time.cc +++ b/extern/ceres/internal/ceres/wall_time.cc @@ -30,6 +30,8 @@ #include "ceres/wall_time.h" +#include "ceres/internal/config.h" + #ifdef CERES_USE_OPENMP #include <omp.h> #else @@ -58,7 +60,7 @@ double WallTimeInSeconds() { static_cast<double>(frequency.QuadPart); #else timeval time_val; - gettimeofday(&time_val, NULL); + gettimeofday(&time_val, nullptr); return (time_val.tv_sec + time_val.tv_usec * 1e-6); #endif #endif diff --git a/extern/ceres/internal/ceres/wall_time.h b/extern/ceres/internal/ceres/wall_time.h index 9c92e9e60ef..f093eed0418 100644 --- a/extern/ceres/internal/ceres/wall_time.h +++ b/extern/ceres/internal/ceres/wall_time.h @@ -34,7 +34,8 @@ #include <map> #include <string> -#include "ceres/internal/port.h" +#include "ceres/internal/disable_warnings.h" +#include "ceres/internal/export.h" #include "ceres/stringprintf.h" #include "glog/logging.h" @@ -45,7 +46,7 @@ namespace internal { // OpenMP is available then the high precision openmp_get_wtime() // function is used. Otherwise on unixes, gettimeofday is used. The // granularity is in seconds on windows systems. -CERES_EXPORT_INTERNAL double WallTimeInSeconds(); +CERES_NO_EXPORT double WallTimeInSeconds(); // Log a series of events, recording for each event the time elapsed // since the last event and since the creation of the object. @@ -71,7 +72,7 @@ CERES_EXPORT_INTERNAL double WallTimeInSeconds(); // Bar1: time1 time1 // Bar2: time2 time1 + time2; // Total: time3 time1 + time2 + time3; -class EventLogger { +class CERES_NO_EXPORT EventLogger { public: explicit EventLogger(const std::string& logger_name); ~EventLogger(); @@ -86,4 +87,6 @@ class EventLogger { } // namespace internal } // namespace ceres +#include "ceres/internal/reenable_warnings.h" + #endif // CERES_INTERNAL_WALL_TIME_H_ diff --git a/extern/ceres/mkfiles.sh b/extern/ceres/mkfiles.sh deleted file mode 100755 index cb07663e94a..00000000000 --- a/extern/ceres/mkfiles.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -find ./include/ -type f | sed -r 's/^\.\///' | sort > files.txt -find ./internal/ -type f | sed -r 's/^\.\///' | sort >> files.txt -find ./config/ -type f | sed -r 's/^\.\///' | sort >> files.txt diff --git a/extern/ceres/patches/series b/extern/ceres/patches/series deleted file mode 100644 index e69de29bb2d..00000000000 --- a/extern/ceres/patches/series +++ /dev/null diff --git a/intern/cycles/blender/image.cpp b/intern/cycles/blender/image.cpp index ca4c8f5904a..e01b72c1653 100644 --- a/intern/cycles/blender/image.cpp +++ b/intern/cycles/blender/image.cpp @@ -13,9 +13,11 @@ CCL_NAMESPACE_BEGIN BlenderImageLoader::BlenderImageLoader(BL::Image b_image, const int frame, + const int tile_number, const bool is_preview_render) : b_image(b_image), frame(frame), + tile_number(tile_number), /* Don't free cache for preview render to avoid race condition from T93560, to be fixed properly later as we are close to release. */ free_cache(!is_preview_render && !b_image.has_data()) @@ -66,12 +68,11 @@ bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata, { const size_t num_pixels = ((size_t)metadata.width) * metadata.height; const int channels = metadata.channels; - const int tile = 0; /* TODO(lukas): Support tiles here? */ if (b_image.is_float()) { /* image data */ float *image_pixels; - image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile); + image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile_number); if (image_pixels && num_pixels * channels == pixels_size) { memcpy(pixels, image_pixels, pixels_size * sizeof(float)); @@ -99,7 +100,7 @@ bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata, } } else { - unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile); + unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile_number); if (image_pixels && num_pixels * channels == pixels_size) { memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char)); @@ -153,7 +154,13 @@ string BlenderImageLoader::name() const bool BlenderImageLoader::equals(const ImageLoader &other) const { const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other; - return b_image == other_loader.b_image && frame == other_loader.frame; + return b_image == other_loader.b_image && frame == other_loader.frame && + tile_number == other_loader.tile_number; +} + +int BlenderImageLoader::get_tile_number() const +{ + return tile_number; } /* Point Density */ diff --git a/intern/cycles/blender/image.h b/intern/cycles/blender/image.h index ee576b31f7e..c2cc0f51b9b 100644 --- a/intern/cycles/blender/image.h +++ b/intern/cycles/blender/image.h @@ -12,7 +12,10 @@ CCL_NAMESPACE_BEGIN class BlenderImageLoader : public ImageLoader { public: - BlenderImageLoader(BL::Image b_image, const int frame, const bool is_preview_render); + BlenderImageLoader(BL::Image b_image, + const int frame, + const int tile_number, + const bool is_preview_render); bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override; bool load_pixels(const ImageMetaData &metadata, @@ -22,8 +25,11 @@ class BlenderImageLoader : public ImageLoader { string name() const override; bool equals(const ImageLoader &other) const override; + int get_tile_number() const override; + BL::Image b_image; int frame; + int tile_number; bool free_cache; }; diff --git a/intern/cycles/blender/mesh.cpp b/intern/cycles/blender/mesh.cpp index 624a07762f2..c76ce3801d4 100644 --- a/intern/cycles/blender/mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -301,11 +301,11 @@ static void attr_create_sculpt_vertex_color(Scene *scene, template<typename TypeInCycles, typename GetValueAtIndex> static void fill_generic_attribute(BL::Mesh &b_mesh, TypeInCycles *data, - const AttributeElement element, + const BL::Attribute::domain_enum b_domain, const GetValueAtIndex &get_value_at_index) { - switch (element) { - case ATTR_ELEMENT_CORNER: { + switch (b_domain) { + case BL::Attribute::domain_CORNER: { for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { const int index = t.index() * 3; BL::Array<int, 3> loops = t.loops(); @@ -315,14 +315,37 @@ static void fill_generic_attribute(BL::Mesh &b_mesh, } break; } - case ATTR_ELEMENT_VERTEX: { + case BL::Attribute::domain_EDGE: { + /* Average edge attributes at vertices. */ + const size_t num_verts = b_mesh.vertices.length(); + vector<int> count(num_verts, 0); + + for (BL::MeshEdge &e : b_mesh.edges) { + BL::Array<int, 2> vertices = e.vertices(); + TypeInCycles value = get_value_at_index(e.index()); + + data[vertices[0]] += value; + data[vertices[1]] += value; + count[vertices[0]]++; + count[vertices[1]]++; + } + + for (size_t i = 0; i < num_verts; i++) { + if (count[i] > 1) { + data[i] /= (float)count[i]; + } + } + + break; + } + case BL::Attribute::domain_POINT: { const int num_verts = b_mesh.vertices.length(); for (int i = 0; i < num_verts; i++) { data[i] = get_value_at_index(i); } break; } - case ATTR_ELEMENT_FACE: { + case BL::Attribute::domain_FACE: { for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { data[t.index()] = get_value_at_index(t.polygon_index()); } @@ -404,6 +427,9 @@ static void attr_create_generic(Scene *scene, case BL::Attribute::domain_POINT: element = ATTR_ELEMENT_VERTEX; break; + case BL::Attribute::domain_EDGE: + element = ATTR_ELEMENT_VERTEX; + break; case BL::Attribute::domain_FACE: element = ATTR_ELEMENT_FACE; break; @@ -420,15 +446,16 @@ static void attr_create_generic(Scene *scene, Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); fill_generic_attribute( - b_mesh, data, element, [&](int i) { return b_float_attribute.data[i].value(); }); + b_mesh, data, b_domain, [&](int i) { return b_float_attribute.data[i].value(); }); break; } case BL::Attribute::data_type_BOOLEAN: { BL::BoolAttribute b_bool_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); - fill_generic_attribute( - b_mesh, data, element, [&](int i) { return (float)b_bool_attribute.data[i].value(); }); + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { + return (float)b_bool_attribute.data[i].value(); + }); break; } case BL::Attribute::data_type_INT: { @@ -436,14 +463,14 @@ static void attr_create_generic(Scene *scene, Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); fill_generic_attribute( - b_mesh, data, element, [&](int i) { return (float)b_int_attribute.data[i].value(); }); + b_mesh, data, b_domain, [&](int i) { return (float)b_int_attribute.data[i].value(); }); break; } case BL::Attribute::data_type_FLOAT_VECTOR: { BL::FloatVectorAttribute b_vector_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeVector, element); float3 *data = attr->data_float3(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 3> v = b_vector_attribute.data[i].vector(); return make_float3(v[0], v[1], v[2]); }); @@ -453,7 +480,7 @@ static void attr_create_generic(Scene *scene, BL::FloatColorAttribute b_color_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeRGBA, element); float4 *data = attr->data_float4(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 4> v = b_color_attribute.data[i].color(); return make_float4(v[0], v[1], v[2], v[3]); }); @@ -463,7 +490,7 @@ static void attr_create_generic(Scene *scene, BL::Float2Attribute b_float2_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeFloat2, element); float2 *data = attr->data_float2(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 2> v = b_float2_attribute.data[i].vector(); return make_float2(v[0], v[1]); }); diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 35c98a71558..81a64457c88 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -776,9 +776,21 @@ static ShaderNode *add_node(Scene *scene, */ int scene_frame = b_scene.frame_current(); int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame); - image->handle = scene->image_manager->add_image( - new BlenderImageLoader(b_image, image_frame, b_engine.is_preview()), - image->image_params()); + if (b_image.source() != BL::Image::source_TILED) { + image->handle = scene->image_manager->add_image( + new BlenderImageLoader(b_image, image_frame, 0, b_engine.is_preview()), + image->image_params()); + } + else { + vector<ImageLoader *> loaders; + loaders.reserve(image->get_tiles().size()); + for (int tile_number : image->get_tiles()) { + loaders.push_back( + new BlenderImageLoader(b_image, image_frame, tile_number, b_engine.is_preview())); + } + + image->handle = scene->image_manager->add_image(loaders, image->image_params()); + } } else { ustring filename = ustring( @@ -814,7 +826,7 @@ static ShaderNode *add_node(Scene *scene, int scene_frame = b_scene.frame_current(); int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame); env->handle = scene->image_manager->add_image( - new BlenderImageLoader(b_image, image_frame, b_engine.is_preview()), + new BlenderImageLoader(b_image, image_frame, 0, b_engine.is_preview()), env->image_params()); } else { diff --git a/intern/cycles/blender/util.h b/intern/cycles/blender/util.h index eead6cec08c..49cecb6d0f3 100644 --- a/intern/cycles/blender/util.h +++ b/intern/cycles/blender/util.h @@ -262,8 +262,11 @@ static inline bool BKE_object_is_modified(BL::Object &self, BL::Scene &scene, bo static inline bool BKE_object_is_deform_modified(BObjectInfo &self, BL::Scene &scene, bool preview) { if (!self.is_real_object_data()) { - return false; + /* Comes from geometry nodes, can't use heuristic to guess if it's animated. */ + return true; } + + /* Use heuristic to quickly check if object is potentially animated. */ return self.real_object.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false; } diff --git a/intern/cycles/device/metal/device_impl.h b/intern/cycles/device/metal/device_impl.h index 27c58ce6d2f..7506b9b069f 100644 --- a/intern/cycles/device/metal/device_impl.h +++ b/intern/cycles/device/metal/device_impl.h @@ -28,7 +28,8 @@ class MetalDevice : public Device { id<MTLCommandQueue> mtlGeneralCommandQueue = nil; id<MTLArgumentEncoder> mtlAncillaryArgEncoder = nil; /* encoder used for fetching device pointers from MTLBuffers */ - string source_used_for_compile[PSO_NUM]; + string source[PSO_NUM]; + string source_md5[PSO_NUM]; KernelParamsMetal launch_params = {0}; @@ -72,7 +73,6 @@ class MetalDevice : public Device { id<MTLBuffer> texture_bindings_3d = nil; std::vector<id<MTLTexture>> texture_slot_map; - MetalDeviceKernels kernels; bool use_metalrt = false; bool use_function_specialisation = false; @@ -110,6 +110,8 @@ class MetalDevice : public Device { virtual void build_bvh(BVH *bvh, Progress &progress, bool refit) override; + id<MTLLibrary> compile(string const &source); + /* ------------------------------------------------------------------ */ /* low-level memory management */ diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index c01f51fb506..e1438a9d6e2 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -275,96 +275,44 @@ bool MetalDevice::load_kernels(const uint _kernel_features) * active, but may still need to be rendered without motion blur if that isn't active as well. */ motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION; - NSError *error = NULL; + source[PSO_GENERIC] = get_source(kernel_features); + mtlLibrary[PSO_GENERIC] = compile(source[PSO_GENERIC]); - for (int i = 0; i < PSO_NUM; i++) { - if (mtlLibrary[i]) { - [mtlLibrary[i] release]; - mtlLibrary[i] = nil; - } - } + MD5Hash md5; + md5.append(source[PSO_GENERIC]); + source_md5[PSO_GENERIC] = md5.get_hex(); + + metal_printf("Front-end compilation finished (generic)\n"); + + bool result = MetalDeviceKernels::load(this, false); + + reserve_local_memory(kernel_features); + + return result; +} +id<MTLLibrary> MetalDevice::compile(string const &source) +{ MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; options.fastMathEnabled = YES; if (@available(macOS 12.0, *)) { options.languageVersion = MTLLanguageVersion2_4; } - else { - return false; - } - string metalsrc; - - /* local helper: dump source to disk and return filepath */ - auto dump_source = [&](int kernel_type) -> string { - string &source = source_used_for_compile[kernel_type]; - string metalsrc = path_cache_get(path_join("kernels", - string_printf("%s.%s.metal", - kernel_type_as_string(kernel_type), - util_md5_string(source).c_str()))); - path_write_text(metalsrc, source); - return metalsrc; - }; - - /* local helper: fetch the kernel source code, adjust it for specific PSO_.. kernel_type flavor, - * then compile it into a MTLLibrary */ - auto fetch_and_compile_source = [&](int kernel_type) { - /* Record the source used to compile this library, for hash building later. */ - string &source = source_used_for_compile[kernel_type]; - - switch (kernel_type) { - case PSO_GENERIC: { - source = get_source(kernel_features); - break; - } - case PSO_SPECIALISED: { - /* PSO_SPECIALISED derives from PSO_GENERIC */ - string &generic_source = source_used_for_compile[PSO_GENERIC]; - if (generic_source.empty()) { - generic_source = get_source(kernel_features); - } - source = "#define __KERNEL_METAL_USE_FUNCTION_SPECIALISATION__\n" + generic_source; - break; - } - default: - assert(0); - } - - /* create MTLLibrary (front-end compilation) */ - mtlLibrary[kernel_type] = [mtlDevice newLibraryWithSource:@(source.c_str()) + NSError *error = NULL; + id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str()) options:options error:&error]; - bool do_source_dump = (getenv("CYCLES_METAL_DUMP_SOURCE") != nullptr); - - if (!mtlLibrary[kernel_type] || do_source_dump) { - string metalsrc = dump_source(kernel_type); - - if (!mtlLibrary[kernel_type]) { - NSString *err = [error localizedDescription]; - set_error(string_printf("Failed to compile library:\n%s", [err UTF8String])); - - return false; - } - } - return true; - }; - - fetch_and_compile_source(PSO_GENERIC); - - if (use_function_specialisation) { - fetch_and_compile_source(PSO_SPECIALISED); + if (!mtlLibrary) { + NSString *err = [error localizedDescription]; + set_error(string_printf("Failed to compile library:\n%s", [err UTF8String])); } - metal_printf("Front-end compilation finished\n"); - - bool result = kernels.load(this, PSO_GENERIC); - [options release]; - reserve_local_memory(kernel_features); - return result; + return mtlLibrary; } void MetalDevice::reserve_local_memory(const uint kernel_features) diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h index b12491d820d..69b2a686ecc 100644 --- a/intern/cycles/device/metal/kernel.h +++ b/intern/cycles/device/metal/kernel.h @@ -54,103 +54,41 @@ enum { const char *kernel_type_as_string(int kernel_type); struct MetalKernelPipeline { - void release() - { - if (pipeline) { - [pipeline release]; - pipeline = nil; - if (@available(macOS 11.0, *)) { - for (int i = 0; i < METALRT_TABLE_NUM; i++) { - if (intersection_func_table[i]) { - [intersection_func_table[i] release]; - intersection_func_table[i] = nil; - } - } - } - } - if (function) { - [function release]; - function = nil; - } - if (@available(macOS 11.0, *)) { - for (int i = 0; i < METALRT_TABLE_NUM; i++) { - if (intersection_func_table[i]) { - [intersection_func_table[i] release]; - } - } - } - } - bool loaded = false; - id<MTLFunction> function = nil; - id<MTLComputePipelineState> pipeline = nil; - - API_AVAILABLE(macos(11.0)) - id<MTLIntersectionFunctionTable> intersection_func_table[METALRT_TABLE_NUM] = {nil}; -}; - -struct MetalKernelLoadDesc { - int pso_index = 0; - const char *function_name = nullptr; - int kernel_index = 0; - int threads_per_threadgroup = 0; - MTLFunctionConstantValues *constant_values = nullptr; - NSArray *linked_functions = nullptr; - - struct IntersectorFunctions { - NSArray *defaults; - NSArray *shadow; - NSArray *local; - NSArray *operator[](int index) const - { - if (index == METALRT_TABLE_DEFAULT) - return defaults; - if (index == METALRT_TABLE_SHADOW) - return shadow; - return local; - } - } intersector_functions = {nullptr}; -}; - -/* Metal kernel and associate occupancy information. */ -class MetalDeviceKernel { - public: - ~MetalDeviceKernel(); + void compile(); - bool load(MetalDevice *device, MetalKernelLoadDesc const &desc, class MD5Hash const &md5); + id<MTLLibrary> mtlLibrary = nil; + bool scene_specialized; + string source_md5; - void mark_loaded(int pso_index) - { - pso[pso_index].loaded = true; - } + bool use_metalrt; + bool metalrt_hair; + bool metalrt_hair_thick; + bool metalrt_pointcloud; - int get_num_threads_per_block() const - { - return num_threads_per_block; - } - const MetalKernelPipeline &get_pso() const; + int threads_per_threadgroup; - double load_duration = 0.0; + DeviceKernel device_kernel; + bool loaded = false; + id<MTLDevice> mtlDevice = nil; + id<MTLFunction> function = nil; + id<MTLComputePipelineState> pipeline = nil; + int num_threads_per_block = 0; - private: - MetalKernelPipeline pso[PSO_NUM]; + string error_str; - int num_threads_per_block = 0; + API_AVAILABLE(macos(11.0)) + id<MTLIntersectionFunctionTable> intersection_func_table[METALRT_TABLE_NUM] = {nil}; + id<MTLFunction> rt_intersection_function[METALRT_FUNC_NUM] = {nil}; }; /* Cache of Metal kernels for each DeviceKernel. */ -class MetalDeviceKernels { - public: - bool load(MetalDevice *device, int kernel_type); - bool available(DeviceKernel kernel) const; - const MetalDeviceKernel &get(DeviceKernel kernel) const; +namespace MetalDeviceKernels { - MetalDeviceKernel kernels_[DEVICE_KERNEL_NUM]; +bool load(MetalDevice *device, bool scene_specialized); +const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel); - id<MTLFunction> rt_intersection_funcs[PSO_NUM][METALRT_FUNC_NUM] = {{nil}}; - - string loaded_md5[PSO_NUM]; -}; +} /* namespace MetalDeviceKernels */ CCL_NAMESPACE_END diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index 9555ca03c8e..fc9a8cecd75 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -9,6 +9,7 @@ # include "util/path.h" # include "util/tbb.h" # include "util/time.h" +# include "util/unique_ptr.h" CCL_NAMESPACE_BEGIN @@ -28,82 +29,376 @@ const char *kernel_type_as_string(int kernel_type) return ""; } -MetalDeviceKernel::~MetalDeviceKernel() +bool kernel_has_intersection(DeviceKernel device_kernel) { - for (int i = 0; i < PSO_NUM; i++) { - pso[i].release(); + return (device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || + device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE || + device_kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK || + device_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); +} + +struct ShaderCache { + ShaderCache(id<MTLDevice> _mtlDevice) : mtlDevice(_mtlDevice) + { + } + ~ShaderCache(); + + /* Get the fastest available pipeline for the specified kernel. */ + MetalKernelPipeline *get_best_pipeline(DeviceKernel kernel, const MetalDevice *device); + + /* Non-blocking request for a kernel, optionally specialized to the scene being rendered by + * device. */ + void load_kernel(DeviceKernel kernel, MetalDevice *device, bool scene_specialized); + + void wait_for_all(); + + private: + friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice); + + void compile_thread_func(int thread_index); + + using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>; + + struct PipelineRequest { + MetalKernelPipeline *pipeline = nullptr; + std::function<void(MetalKernelPipeline *)> completionHandler; + }; + + std::mutex cache_mutex; + + PipelineCollection pipelines[DEVICE_KERNEL_NUM]; + id<MTLDevice> mtlDevice; + + bool running = false; + std::condition_variable cond_var; + std::deque<PipelineRequest> request_queue; + std::vector<std::thread> compile_threads; + std::atomic_int incomplete_requests = 0; +}; + +std::mutex g_shaderCacheMutex; +std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache; + +ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice) +{ + thread_scoped_lock lock(g_shaderCacheMutex); + auto it = g_shaderCache.find(mtlDevice); + if (it != g_shaderCache.end()) { + return it->second.get(); + } + + g_shaderCache[mtlDevice] = make_unique<ShaderCache>(mtlDevice); + return g_shaderCache[mtlDevice].get(); +} + +ShaderCache::~ShaderCache() +{ + metal_printf("ShaderCache shutting down with incomplete_requests = %d\n", + int(incomplete_requests)); + + running = false; + cond_var.notify_all(); + for (auto &thread : compile_threads) { + thread.join(); + } +} + +void ShaderCache::wait_for_all() +{ + while (incomplete_requests > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +void ShaderCache::compile_thread_func(int thread_index) +{ + while (1) { + + /* wait for / acquire next request */ + PipelineRequest request; + { + thread_scoped_lock lock(cache_mutex); + cond_var.wait(lock, [&] { return !running || !request_queue.empty(); }); + if (!running) { + break; + } + + if (!request_queue.empty()) { + request = request_queue.front(); + request_queue.pop_front(); + } + } + + /* service request */ + if (request.pipeline) { + request.pipeline->compile(); + incomplete_requests--; + } } } -bool MetalDeviceKernel::load(MetalDevice *device, - MetalKernelLoadDesc const &desc_in, - MD5Hash const &md5) +void ShaderCache::load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + bool scene_specialized) { - __block MetalKernelLoadDesc const desc(desc_in); - if (desc.kernel_index == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + { + /* create compiler threads on first run */ + thread_scoped_lock lock(cache_mutex); + if (compile_threads.empty()) { + running = true; + for (int i = 0; i < max_mtlcompiler_threads; i++) { + compile_threads.push_back(std::thread([&] { compile_thread_func(i); })); + } + } + } + + if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { /* skip megakernel */ - return true; + return; } - bool use_binary_archive = true; - if (device->device_vendor == METAL_GPU_APPLE) { - /* Workaround for T94142: Cycles Metal crash with simultaneous viewport and final render */ - use_binary_archive = false; + if (scene_specialized) { + /* Only specialize kernels where it can make an impact. */ + if (device_kernel < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || + device_kernel > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + return; + } } - if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { - use_binary_archive = (atoi(str) == 0); + { + /* check whether the kernel has already been requested / cached */ + thread_scoped_lock lock(cache_mutex); + for (auto &pipeline : pipelines[device_kernel]) { + if (scene_specialized) { + if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { + /* we already requested a pipeline that is specialised for this kernel data */ + metal_printf("Specialized kernel already requested (%s)\n", + device_kernel_as_string(device_kernel)); + return; + } + } + else { + if (pipeline->source_md5 == device->source_md5[PSO_GENERIC]) { + /* we already requested a generic pipeline for this kernel */ + metal_printf("Generic kernel already requested (%s)\n", + device_kernel_as_string(device_kernel)); + return; + } + } + } } - id<MTLBinaryArchive> archive = nil; - string metalbin_path; - if (use_binary_archive) { - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - string osVersion = [[processInfo operatingSystemVersionString] UTF8String]; - MD5Hash local_md5(md5); - local_md5.append(osVersion); - string metalbin_name = string(desc.function_name) + "." + local_md5.get_hex() + - to_string(desc.pso_index) + ".bin"; - metalbin_path = path_cache_get(path_join("kernels", metalbin_name)); - path_create_directories(metalbin_path); + incomplete_requests++; + + PipelineRequest request; + request.pipeline = new MetalKernelPipeline; + request.pipeline->scene_specialized = scene_specialized; + request.pipeline->mtlDevice = mtlDevice; + request.pipeline->source_md5 = + device->source_md5[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; + request.pipeline->mtlLibrary = + device->mtlLibrary[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; + request.pipeline->device_kernel = device_kernel; + request.pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup; + + /* metalrt options */ + request.pipeline->use_metalrt = device->use_metalrt; + request.pipeline->metalrt_hair = device->use_metalrt && + (device->kernel_features & KERNEL_FEATURE_HAIR); + request.pipeline->metalrt_hair_thick = device->use_metalrt && + (device->kernel_features & KERNEL_FEATURE_HAIR_THICK); + request.pipeline->metalrt_pointcloud = device->use_metalrt && + (device->kernel_features & KERNEL_FEATURE_POINTCLOUD); + + { + thread_scoped_lock lock(cache_mutex); + pipelines[device_kernel].push_back(unique_ptr<MetalKernelPipeline>(request.pipeline)); + request_queue.push_back(request); + } + cond_var.notify_one(); +} - if (path_exists(metalbin_path) && use_binary_archive) { - if (@available(macOS 11.0, *)) { - MTLBinaryArchiveDescriptor *archiveDesc = [[MTLBinaryArchiveDescriptor alloc] init]; - archiveDesc.url = [NSURL fileURLWithPath:@(metalbin_path.c_str())]; - archive = [device->mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; - [archiveDesc release]; +MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const MetalDevice *device) +{ + thread_scoped_lock lock(cache_mutex); + auto &collection = pipelines[kernel]; + if (collection.empty()) { + return nullptr; + } + + /* metalrt options */ + bool use_metalrt = device->use_metalrt; + bool metalrt_hair = use_metalrt && (device->kernel_features & KERNEL_FEATURE_HAIR); + bool metalrt_hair_thick = use_metalrt && (device->kernel_features & KERNEL_FEATURE_HAIR_THICK); + bool metalrt_pointcloud = use_metalrt && (device->kernel_features & KERNEL_FEATURE_POINTCLOUD); + + MetalKernelPipeline *best_pipeline = nullptr; + for (auto &pipeline : collection) { + if (!pipeline->loaded) { + /* still loading - ignore */ + continue; + } + + if (pipeline->use_metalrt != use_metalrt || pipeline->metalrt_hair != metalrt_hair || + pipeline->metalrt_hair_thick != metalrt_hair_thick || + pipeline->metalrt_pointcloud != metalrt_pointcloud) { + /* wrong combination of metalrt options */ + continue; + } + + if (pipeline->scene_specialized) { + if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { + best_pipeline = pipeline.get(); } } + else if (!best_pipeline) { + best_pipeline = pipeline.get(); + } + } + + return best_pipeline; +} + +void MetalKernelPipeline::compile() +{ + int pso_type = scene_specialized ? PSO_SPECIALISED : PSO_GENERIC; + + const std::string function_name = std::string("cycles_metal_") + + device_kernel_as_string(device_kernel); + + int threads_per_threadgroup = this->threads_per_threadgroup; + if (device_kernel > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL && + device_kernel < DEVICE_KERNEL_INTEGRATOR_RESET) { + /* Always use 512 for the sorting kernels */ + threads_per_threadgroup = 512; } - NSString *entryPoint = [@(desc.function_name) copy]; + NSString *entryPoint = [@(function_name.c_str()) copy]; NSError *error = NULL; if (@available(macOS 11.0, *)) { MTLFunctionDescriptor *func_desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; func_desc.name = entryPoint; - if (desc.constant_values) { - func_desc.constantValues = desc.constant_values; - } - pso[desc.pso_index].function = [device->mtlLibrary[desc.pso_index] - newFunctionWithDescriptor:func_desc - error:&error]; + function = [mtlLibrary newFunctionWithDescriptor:func_desc error:&error]; } + [entryPoint release]; - if (pso[desc.pso_index].function == nil) { + if (function == nil) { NSString *err = [error localizedDescription]; string errors = [err UTF8String]; + metal_printf("Error getting function \"%s\": %s", function_name.c_str(), errors.c_str()); + return; + } + + function.label = [entryPoint copy]; + + if (use_metalrt) { + if (@available(macOS 11.0, *)) { + /* create the id<MTLFunction> for each intersection function */ + const char *function_names[] = { + "__anyhit__cycles_metalrt_visibility_test_tri", + "__anyhit__cycles_metalrt_visibility_test_box", + "__anyhit__cycles_metalrt_shadow_all_hit_tri", + "__anyhit__cycles_metalrt_shadow_all_hit_box", + "__anyhit__cycles_metalrt_local_hit_tri", + "__anyhit__cycles_metalrt_local_hit_box", + "__intersection__curve_ribbon", + "__intersection__curve_ribbon_shadow", + "__intersection__curve_all", + "__intersection__curve_all_shadow", + "__intersection__point", + "__intersection__point_shadow", + }; + assert(sizeof(function_names) / sizeof(function_names[0]) == METALRT_FUNC_NUM); + + MTLFunctionDescriptor *desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; + for (int i = 0; i < METALRT_FUNC_NUM; i++) { + const char *function_name = function_names[i]; + desc.name = [@(function_name) copy]; + + NSError *error = NULL; + rt_intersection_function[i] = [mtlLibrary newFunctionWithDescriptor:desc error:&error]; - device->set_error( - string_printf("Error getting function \"%s\": %s", desc.function_name, errors.c_str())); - return false; + if (rt_intersection_function[i] == nil) { + NSString *err = [error localizedDescription]; + string errors = [err UTF8String]; + + error_str = string_printf( + "Error getting intersection function \"%s\": %s", function_name, errors.c_str()); + break; + } + + rt_intersection_function[i].label = [@(function_name) copy]; + } + } } - pso[desc.pso_index].function.label = [@(desc.function_name) copy]; + NSArray *table_functions[METALRT_TABLE_NUM] = {nil}; + NSArray *linked_functions = nil; + + if (use_metalrt) { + id<MTLFunction> curve_intersect_default = nil; + id<MTLFunction> curve_intersect_shadow = nil; + id<MTLFunction> point_intersect_default = nil; + id<MTLFunction> point_intersect_shadow = nil; + if (metalrt_hair) { + /* Add curve intersection programs. */ + if (metalrt_hair_thick) { + /* Slower programs for thick hair since that also slows down ribbons. + * Ideally this should not be needed. */ + curve_intersect_default = rt_intersection_function[METALRT_FUNC_CURVE_ALL]; + curve_intersect_shadow = rt_intersection_function[METALRT_FUNC_CURVE_ALL_SHADOW]; + } + else { + curve_intersect_default = rt_intersection_function[METALRT_FUNC_CURVE_RIBBON]; + curve_intersect_shadow = rt_intersection_function[METALRT_FUNC_CURVE_RIBBON_SHADOW]; + } + } + if (metalrt_pointcloud) { + point_intersect_default = rt_intersection_function[METALRT_FUNC_POINT]; + point_intersect_shadow = rt_intersection_function[METALRT_FUNC_POINT_SHADOW]; + } + table_functions[METALRT_TABLE_DEFAULT] = [NSArray + arrayWithObjects:rt_intersection_function[METALRT_FUNC_DEFAULT_TRI], + curve_intersect_default ? + curve_intersect_default : + rt_intersection_function[METALRT_FUNC_DEFAULT_BOX], + point_intersect_default ? + point_intersect_default : + rt_intersection_function[METALRT_FUNC_DEFAULT_BOX], + nil]; + table_functions[METALRT_TABLE_SHADOW] = [NSArray + arrayWithObjects:rt_intersection_function[METALRT_FUNC_SHADOW_TRI], + curve_intersect_shadow ? + curve_intersect_shadow : + rt_intersection_function[METALRT_FUNC_SHADOW_BOX], + point_intersect_shadow ? + point_intersect_shadow : + rt_intersection_function[METALRT_FUNC_SHADOW_BOX], + nil]; + table_functions[METALRT_TABLE_LOCAL] = [NSArray + arrayWithObjects:rt_intersection_function[METALRT_FUNC_LOCAL_TRI], + rt_intersection_function[METALRT_FUNC_LOCAL_BOX], + rt_intersection_function[METALRT_FUNC_LOCAL_BOX], + nil]; + + NSMutableSet *unique_functions = [NSMutableSet + setWithArray:table_functions[METALRT_TABLE_DEFAULT]]; + [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_SHADOW]]; + [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_LOCAL]]; + + if (kernel_has_intersection(device_kernel)) { + linked_functions = [[NSArray arrayWithArray:[unique_functions allObjects]] + sortedArrayUsingComparator:^NSComparisonResult(id<MTLFunction> f1, id<MTLFunction> f2) { + return [f1.label compare:f2.label]; + }]; + } + unique_functions = nil; + } - __block MTLComputePipelineDescriptor *computePipelineStateDescriptor = + MTLComputePipelineDescriptor *computePipelineStateDescriptor = [[MTLComputePipelineDescriptor alloc] init]; computePipelineStateDescriptor.buffers[0].mutability = MTLMutabilityImmutable; @@ -111,52 +406,86 @@ bool MetalDeviceKernel::load(MetalDevice *device, computePipelineStateDescriptor.buffers[2].mutability = MTLMutabilityImmutable; if (@available(macos 10.14, *)) { - computePipelineStateDescriptor.maxTotalThreadsPerThreadgroup = desc.threads_per_threadgroup; + computePipelineStateDescriptor.maxTotalThreadsPerThreadgroup = threads_per_threadgroup; } computePipelineStateDescriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; - computePipelineStateDescriptor.computeFunction = pso[desc.pso_index].function; + computePipelineStateDescriptor.computeFunction = function; + if (@available(macOS 11.0, *)) { /* Attach the additional functions to an MTLLinkedFunctions object */ - if (desc.linked_functions) { + if (linked_functions) { computePipelineStateDescriptor.linkedFunctions = [[MTLLinkedFunctions alloc] init]; - computePipelineStateDescriptor.linkedFunctions.functions = desc.linked_functions; + computePipelineStateDescriptor.linkedFunctions.functions = linked_functions; } - computePipelineStateDescriptor.maxCallStackDepth = 1; + if (use_metalrt) { + computePipelineStateDescriptor.maxCallStackDepth = 8; + } } - /* Create a new Compute pipeline state object */ MTLPipelineOption pipelineOptions = MTLPipelineOptionNone; - bool creating_new_archive = false; + bool use_binary_archive = true; + if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { + use_binary_archive = (atoi(str) == 0); + } + + id<MTLBinaryArchive> archive = nil; + string metalbin_path; + string metalbin_name; + if (use_binary_archive) { + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + string osVersion = [[processInfo operatingSystemVersionString] UTF8String]; + MD5Hash local_md5; + local_md5.append(source_md5); + local_md5.append(osVersion); + local_md5.append((uint8_t *)&this->threads_per_threadgroup, + sizeof(this->threads_per_threadgroup)); + + string options; + if (use_metalrt && kernel_has_intersection(device_kernel)) { + /* incorporate any MetalRT specialisations into the archive name */ + options += string_printf(".hair_%d.hair_thick_%d.pointcloud_%d", + metalrt_hair ? 1 : 0, + metalrt_hair_thick ? 1 : 0, + metalrt_pointcloud ? 1 : 0); + } + + /* Replace non-alphanumerical characters with underscores. */ + string device_name = [mtlDevice.name UTF8String]; + for (char &c : device_name) { + if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) { + c = '_'; + } + } + + metalbin_name = device_name; + metalbin_name = path_join(metalbin_name, device_kernel_as_string(device_kernel)); + metalbin_name = path_join(metalbin_name, kernel_type_as_string(pso_type)); + metalbin_name = path_join(metalbin_name, local_md5.get_hex() + options + ".bin"); + + metalbin_path = path_cache_get(path_join("kernels", metalbin_name)); + path_create_directories(metalbin_path); + + if (path_exists(metalbin_path) && use_binary_archive) { + if (@available(macOS 11.0, *)) { + MTLBinaryArchiveDescriptor *archiveDesc = [[MTLBinaryArchiveDescriptor alloc] init]; + archiveDesc.url = [NSURL fileURLWithPath:@(metalbin_path.c_str())]; + archive = [mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; + [archiveDesc release]; + } + } + } + + __block bool creating_new_archive = false; if (@available(macOS 11.0, *)) { if (use_binary_archive) { if (!archive) { MTLBinaryArchiveDescriptor *archiveDesc = [[MTLBinaryArchiveDescriptor alloc] init]; archiveDesc.url = nil; - archive = [device->mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; + archive = [mtlDevice newBinaryArchiveWithDescriptor:archiveDesc error:nil]; creating_new_archive = true; - - double starttime = time_dt(); - - if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor - error:&error]) { - NSString *errStr = [error localizedDescription]; - metal_printf("Failed to add PSO to archive:\n%s\n", - errStr ? [errStr UTF8String] : "nil"); - } - else { - double duration = time_dt() - starttime; - metal_printf("%2d | %-55s | %7.2fs\n", - desc.kernel_index, - device_kernel_as_string((DeviceKernel)desc.kernel_index), - duration); - - if (desc.pso_index == PSO_GENERIC) { - this->load_duration = duration; - } - } } computePipelineStateDescriptor.binaryArchives = [NSArray arrayWithObjects:archive, nil]; pipelineOptions = MTLPipelineOptionFailOnBinaryArchiveMiss; @@ -170,17 +499,14 @@ bool MetalDeviceKernel::load(MetalDevice *device, MTLComputePipelineReflection *reflection, NSError *error) { bool recreate_archive = false; - if (computePipelineState == nil && archive && !creating_new_archive) { - - assert(0); - + if (computePipelineState == nil && archive) { NSString *errStr = [error localizedDescription]; metal_printf( "Failed to create compute pipeline state \"%s\" from archive - attempting recreation... " "(error: %s)\n", - device_kernel_as_string((DeviceKernel)desc.kernel_index), + device_kernel_as_string((DeviceKernel)device_kernel), errStr ? [errStr UTF8String] : "nil"); - computePipelineState = [device->mtlDevice + computePipelineState = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor options:MTLPipelineOptionNone reflection:nullptr @@ -192,32 +518,23 @@ bool MetalDeviceKernel::load(MetalDevice *device, if (computePipelineState == nil) { NSString *errStr = [error localizedDescription]; - device->set_error(string_printf("Failed to create compute pipeline state \"%s\", error: \n", - device_kernel_as_string((DeviceKernel)desc.kernel_index)) + - (errStr ? [errStr UTF8String] : "nil")); - metal_printf("%2d | %-55s | %7.2fs | FAILED!\n", - desc.kernel_index, - device_kernel_as_string((DeviceKernel)desc.kernel_index), + error_str = string_printf("Failed to create compute pipeline state \"%s\", error: \n", + device_kernel_as_string((DeviceKernel)device_kernel)); + error_str += (errStr ? [errStr UTF8String] : "nil"); + metal_printf("%16s | %2d | %-55s | %7.2fs | FAILED!\n", + kernel_type_as_string(pso_type), + device_kernel, + device_kernel_as_string((DeviceKernel)device_kernel), duration); return; } - pso[desc.pso_index].pipeline = computePipelineState; - num_threads_per_block = round_down(computePipelineState.maxTotalThreadsPerThreadgroup, - computePipelineState.threadExecutionWidth); + int num_threads_per_block = round_down(computePipelineState.maxTotalThreadsPerThreadgroup, + computePipelineState.threadExecutionWidth); num_threads_per_block = std::max(num_threads_per_block, (int)computePipelineState.threadExecutionWidth); - - if (!use_binary_archive) { - metal_printf("%2d | %-55s | %7.2fs\n", - desc.kernel_index, - device_kernel_as_string((DeviceKernel)desc.kernel_index), - duration); - - if (desc.pso_index == PSO_GENERIC) { - this->load_duration = duration; - } - } + this->pipeline = computePipelineState; + this->num_threads_per_block = num_threads_per_block; if (@available(macOS 11.0, *)) { if (creating_new_archive || recreate_archive) { @@ -228,304 +545,85 @@ bool MetalDeviceKernel::load(MetalDevice *device, } } } + }; - [computePipelineStateDescriptor release]; - computePipelineStateDescriptor = nil; - - if (device->use_metalrt && desc.linked_functions) { - for (int table = 0; table < METALRT_TABLE_NUM; table++) { - if (@available(macOS 11.0, *)) { - MTLIntersectionFunctionTableDescriptor *ift_desc = - [[MTLIntersectionFunctionTableDescriptor alloc] init]; - ift_desc.functionCount = desc.intersector_functions[table].count; - - pso[desc.pso_index].intersection_func_table[table] = [pso[desc.pso_index].pipeline - newIntersectionFunctionTableWithDescriptor:ift_desc]; - - /* Finally write the function handles into this pipeline's table */ - for (int i = 0; i < 2; i++) { - id<MTLFunctionHandle> handle = [pso[desc.pso_index].pipeline - functionHandleWithFunction:desc.intersector_functions[table][i]]; - [pso[desc.pso_index].intersection_func_table[table] setFunction:handle atIndex:i]; - } + /* Block on load to ensure we continue with a valid kernel function */ + if (creating_new_archive) { + starttime = time_dt(); + NSError *error; + if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor + error:&error]) { + NSString *errStr = [error localizedDescription]; + metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil"); + } + } + id<MTLComputePipelineState> pipeline = [mtlDevice + newComputePipelineStateWithDescriptor:computePipelineStateDescriptor + options:pipelineOptions + reflection:nullptr + error:&error]; + completionHandler(pipeline, nullptr, error); + + this->loaded = true; + [computePipelineStateDescriptor release]; + computePipelineStateDescriptor = nil; + + if (use_metalrt && linked_functions) { + for (int table = 0; table < METALRT_TABLE_NUM; table++) { + if (@available(macOS 11.0, *)) { + MTLIntersectionFunctionTableDescriptor *ift_desc = + [[MTLIntersectionFunctionTableDescriptor alloc] init]; + ift_desc.functionCount = table_functions[table].count; + intersection_func_table[table] = [this->pipeline + newIntersectionFunctionTableWithDescriptor:ift_desc]; + + /* Finally write the function handles into this pipeline's table */ + for (int i = 0; i < 2; i++) { + id<MTLFunctionHandle> handle = [pipeline + functionHandleWithFunction:table_functions[table][i]]; + [intersection_func_table[table] setFunction:handle atIndex:i]; } } } + } - mark_loaded(desc.pso_index); - }; + double duration = time_dt() - starttime; - if (desc.pso_index == PSO_SPECIALISED) { - /* Asynchronous load */ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSError *error; - id<MTLComputePipelineState> pipeline = [device->mtlDevice - newComputePipelineStateWithDescriptor:computePipelineStateDescriptor - options:pipelineOptions - reflection:nullptr - error:&error]; - completionHandler(pipeline, nullptr, error); - }); + if (!use_binary_archive) { + metal_printf("%16s | %2d | %-55s | %7.2fs\n", + kernel_type_as_string(pso_type), + int(device_kernel), + device_kernel_as_string(device_kernel), + duration); } else { - /* Block on load to ensure we continue with a valid kernel function */ - id<MTLComputePipelineState> pipeline = [device->mtlDevice - newComputePipelineStateWithDescriptor:computePipelineStateDescriptor - options:pipelineOptions - reflection:nullptr - error:&error]; - completionHandler(pipeline, nullptr, error); - } - - return true; -} - -const MetalKernelPipeline &MetalDeviceKernel::get_pso() const -{ - if (pso[PSO_SPECIALISED].loaded) { - return pso[PSO_SPECIALISED]; + metal_printf("%16s | %2d | %-55s | %7.2fs | %s: %s\n", + kernel_type_as_string(pso_type), + device_kernel, + device_kernel_as_string((DeviceKernel)device_kernel), + duration, + creating_new_archive ? " new" : "load", + metalbin_name.c_str()); } - - assert(pso[PSO_GENERIC].loaded); - return pso[PSO_GENERIC]; } -bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type) +bool MetalDeviceKernels::load(MetalDevice *device, bool scene_specialized) { - bool any_error = false; - - MD5Hash md5; - - /* Build the function constant table */ - MTLFunctionConstantValues *constant_values = nullptr; - if (kernel_type == PSO_SPECIALISED) { - constant_values = [MTLFunctionConstantValues new]; - -# define KERNEL_FILM(_type, name) \ - [constant_values setConstantValue:&data.film.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_film_##name]; \ - md5.append((uint8_t *)&data.film.name, sizeof(data.film.name)); - -# define KERNEL_BACKGROUND(_type, name) \ - [constant_values setConstantValue:&data.background.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_background_##name]; \ - md5.append((uint8_t *)&data.background.name, sizeof(data.background.name)); - -# define KERNEL_INTEGRATOR(_type, name) \ - [constant_values setConstantValue:&data.integrator.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_integrator_##name]; \ - md5.append((uint8_t *)&data.integrator.name, sizeof(data.integrator.name)); - -# define KERNEL_BVH(_type, name) \ - [constant_values setConstantValue:&data.bvh.name \ - type:get_MTLDataType_##_type() \ - atIndex:KernelData_bvh_##name]; \ - md5.append((uint8_t *)&data.bvh.name, sizeof(data.bvh.name)); - - /* METAL_WIP: populate constant_values based on KernelData */ - assert(0); - /* - const KernelData &data = device->launch_params.data; - # include "kernel/types/background.h" - # include "kernel/types/bvh.h" - # include "kernel/types/film.h" - # include "kernel/types/integrator.h" - */ - } - - if (device->use_metalrt) { - if (@available(macOS 11.0, *)) { - /* create the id<MTLFunction> for each intersection function */ - const char *function_names[] = { - "__anyhit__cycles_metalrt_visibility_test_tri", - "__anyhit__cycles_metalrt_visibility_test_box", - "__anyhit__cycles_metalrt_shadow_all_hit_tri", - "__anyhit__cycles_metalrt_shadow_all_hit_box", - "__anyhit__cycles_metalrt_local_hit_tri", - "__anyhit__cycles_metalrt_local_hit_box", - "__intersection__curve_ribbon", - "__intersection__curve_ribbon_shadow", - "__intersection__curve_all", - "__intersection__curve_all_shadow", - "__intersection__point", - "__intersection__point_shadow", - }; - assert(sizeof(function_names) / sizeof(function_names[0]) == METALRT_FUNC_NUM); - - MTLFunctionDescriptor *desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; - if (kernel_type == PSO_SPECIALISED) { - desc.constantValues = constant_values; - } - for (int i = 0; i < METALRT_FUNC_NUM; i++) { - const char *function_name = function_names[i]; - desc.name = [@(function_name) copy]; - - NSError *error = NULL; - rt_intersection_funcs[kernel_type][i] = [device->mtlLibrary[kernel_type] - newFunctionWithDescriptor:desc - error:&error]; - - if (rt_intersection_funcs[kernel_type][i] == nil) { - NSString *err = [error localizedDescription]; - string errors = [err UTF8String]; - - device->set_error(string_printf( - "Error getting intersection function \"%s\": %s", function_name, errors.c_str())); - any_error = true; - break; - } - - rt_intersection_funcs[kernel_type][i].label = [@(function_name) copy]; - } - } - } - md5.append(device->source_used_for_compile[kernel_type]); - - string hash = md5.get_hex(); - if (loaded_md5[kernel_type] == hash) { - return true; - } - - if (!any_error) { - NSArray *table_functions[METALRT_TABLE_NUM] = {nil}; - NSArray *function_list = nil; - - if (device->use_metalrt) { - id<MTLFunction> curve_intersect_default = nil; - id<MTLFunction> curve_intersect_shadow = nil; - id<MTLFunction> point_intersect_default = nil; - id<MTLFunction> point_intersect_shadow = nil; - if (device->kernel_features & KERNEL_FEATURE_HAIR) { - /* Add curve intersection programs. */ - if (device->kernel_features & KERNEL_FEATURE_HAIR_THICK) { - /* Slower programs for thick hair since that also slows down ribbons. - * Ideally this should not be needed. */ - curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL]; - curve_intersect_shadow = - rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_ALL_SHADOW]; - } - else { - curve_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON]; - curve_intersect_shadow = - rt_intersection_funcs[kernel_type][METALRT_FUNC_CURVE_RIBBON_SHADOW]; - } - } - if (device->kernel_features & KERNEL_FEATURE_POINTCLOUD) { - point_intersect_default = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT]; - point_intersect_shadow = rt_intersection_funcs[kernel_type][METALRT_FUNC_POINT_SHADOW]; - } - table_functions[METALRT_TABLE_DEFAULT] = [NSArray - arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_TRI], - curve_intersect_default ? - curve_intersect_default : - rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX], - point_intersect_default ? - point_intersect_default : - rt_intersection_funcs[kernel_type][METALRT_FUNC_DEFAULT_BOX], - nil]; - table_functions[METALRT_TABLE_SHADOW] = [NSArray - arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_TRI], - curve_intersect_shadow ? - curve_intersect_shadow : - rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX], - point_intersect_shadow ? - point_intersect_shadow : - rt_intersection_funcs[kernel_type][METALRT_FUNC_SHADOW_BOX], - nil]; - table_functions[METALRT_TABLE_LOCAL] = [NSArray - arrayWithObjects:rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_TRI], - rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX], - rt_intersection_funcs[kernel_type][METALRT_FUNC_LOCAL_BOX], - nil]; - - NSMutableSet *unique_functions = [NSMutableSet - setWithArray:table_functions[METALRT_TABLE_DEFAULT]]; - [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_SHADOW]]; - [unique_functions addObjectsFromArray:table_functions[METALRT_TABLE_LOCAL]]; - - function_list = [[NSArray arrayWithArray:[unique_functions allObjects]] - sortedArrayUsingComparator:^NSComparisonResult(id<MTLFunction> f1, id<MTLFunction> f2) { - return [f1.label compare:f2.label]; - }]; - - unique_functions = nil; - } - - metal_printf("Starting %s \"cycles_metal_...\" pipeline builds\n", - kernel_type_as_string(kernel_type)); - - tbb::task_arena local_arena(max_mtlcompiler_threads); - local_arena.execute([&]() { - parallel_for(int(0), int(DEVICE_KERNEL_NUM), [&](int i) { - /* skip megakernel */ - if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - return; - } - - /* Only specialize kernels where it can make an impact. */ - if (kernel_type == PSO_SPECIALISED) { - if (i < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || - i > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - return; - } - } - - MetalDeviceKernel &kernel = kernels_[i]; - - const std::string function_name = std::string("cycles_metal_") + - device_kernel_as_string((DeviceKernel)i); - int threads_per_threadgroup = device->max_threads_per_threadgroup; - if (i > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL && i < DEVICE_KERNEL_INTEGRATOR_RESET) { - /* Always use 512 for the sorting kernels */ - threads_per_threadgroup = 512; - } - - NSArray *kernel_function_list = nil; - - if (i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || - i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || - i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE || - i == DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK || - i == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { - kernel_function_list = function_list; - } - - MetalKernelLoadDesc desc; - desc.pso_index = kernel_type; - desc.kernel_index = i; - desc.linked_functions = kernel_function_list; - desc.intersector_functions.defaults = table_functions[METALRT_TABLE_DEFAULT]; - desc.intersector_functions.shadow = table_functions[METALRT_TABLE_SHADOW]; - desc.intersector_functions.local = table_functions[METALRT_TABLE_LOCAL]; - desc.constant_values = constant_values; - desc.threads_per_threadgroup = threads_per_threadgroup; - desc.function_name = function_name.c_str(); - - bool success = kernel.load(device, desc, md5); - - any_error |= !success; - }); - }); + auto shader_cache = get_shader_cache(device->mtlDevice); + for (int i = 0; i < DEVICE_KERNEL_NUM; i++) { + shader_cache->load_kernel((DeviceKernel)i, device, scene_specialized); } - bool loaded = !any_error; - if (loaded) { - loaded_md5[kernel_type] = hash; + if (!scene_specialized || getenv("CYCLES_METAL_PROFILING")) { + shader_cache->wait_for_all(); } - return loaded; -} - -const MetalDeviceKernel &MetalDeviceKernels::get(DeviceKernel kernel) const -{ - return kernels_[(int)kernel]; + return true; } -bool MetalDeviceKernels::available(DeviceKernel kernel) const +const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevice *device, + DeviceKernel kernel) { - return kernels_[(int)kernel].get_pso().function != nil; + return get_shader_cache(device->mtlDevice)->get_best_pipeline(kernel, device); } CCL_NAMESPACE_END diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 1686ab95ffa..ec10e091b25 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -108,9 +108,6 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, VLOG(3) << "Metal queue launch " << device_kernel_as_string(kernel) << ", work_size " << work_size; - const MetalDeviceKernel &metal_kernel = metal_device->kernels.get(kernel); - const MetalKernelPipeline &metal_kernel_pso = metal_kernel.get_pso(); - id<MTLComputeCommandEncoder> mtlComputeCommandEncoder = get_compute_encoder(kernel); /* Determine size requirement for argument buffer. */ @@ -212,6 +209,14 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } bytes_written = globals_offsets + sizeof(KernelParamsMetal); + const MetalKernelPipeline *metal_kernel_pso = MetalDeviceKernels::get_best_pipeline(metal_device, + kernel); + if (!metal_kernel_pso) { + metal_device->set_error( + string_printf("No MetalKernelPipeline for %s\n", device_kernel_as_string(kernel))); + return false; + } + /* Encode ancillaries */ [metal_device->mtlAncillaryArgEncoder setArgumentBuffer:arg_buffer offset:metal_offsets]; [metal_device->mtlAncillaryArgEncoder setBuffer:metal_device->texture_bindings_2d @@ -228,14 +233,14 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } for (int table = 0; table < METALRT_TABLE_NUM; table++) { - if (metal_kernel_pso.intersection_func_table[table]) { - [metal_kernel_pso.intersection_func_table[table] setBuffer:arg_buffer - offset:globals_offsets - atIndex:1]; + if (metal_kernel_pso->intersection_func_table[table]) { + [metal_kernel_pso->intersection_func_table[table] setBuffer:arg_buffer + offset:globals_offsets + atIndex:1]; [metal_device->mtlAncillaryArgEncoder - setIntersectionFunctionTable:metal_kernel_pso.intersection_func_table[table] + setIntersectionFunctionTable:metal_kernel_pso->intersection_func_table[table] atIndex:3 + table]; - [mtlComputeCommandEncoder useResource:metal_kernel_pso.intersection_func_table[table] + [mtlComputeCommandEncoder useResource:metal_kernel_pso->intersection_func_table[table] usage:MTLResourceUsageRead]; } else { @@ -281,10 +286,10 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, } } - [mtlComputeCommandEncoder setComputePipelineState:metal_kernel_pso.pipeline]; + [mtlComputeCommandEncoder setComputePipelineState:metal_kernel_pso->pipeline]; /* Compute kernel launch parameters. */ - const int num_threads_per_block = metal_kernel.get_num_threads_per_block(); + const int num_threads_per_block = metal_kernel_pso->num_threads_per_block; int shared_mem_bytes = 0; @@ -314,7 +319,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, threadsPerThreadgroup:size_threads_per_threadgroup]; [mtlCommandBuffer addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) { - NSString *kernel_name = metal_kernel_pso.function.label; + NSString *kernel_name = metal_kernel_pso->function.label; /* Enhanced command buffer errors are only available in 11.0+ */ if (@available(macos 11.0, *)) { @@ -547,6 +552,8 @@ id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel computeCommandEncoderWithDispatchType:concurrent ? MTLDispatchTypeConcurrent : MTLDispatchTypeSerial]; + [mtlComputeEncoder setLabel:@(device_kernel_as_string(kernel))]; + /* declare usage of MTLBuffers etc */ prepare_resources(kernel); } diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index 41e5165623c..b074408e229 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -626,6 +626,7 @@ uint OIDNDenoiser::get_device_type_mask() const Device *OIDNDenoiser::ensure_denoiser_device(Progress *progress) { #ifndef WITH_OPENIMAGEDENOISE + (void)progress; path_trace_device_->set_error("Build without OpenImageDenoiser"); return nullptr; #else diff --git a/intern/cycles/kernel/device/metal/compat.h b/intern/cycles/kernel/device/metal/compat.h index 4e309f16c08..0ed52074a90 100644 --- a/intern/cycles/kernel/device/metal/compat.h +++ b/intern/cycles/kernel/device/metal/compat.h @@ -29,10 +29,26 @@ using namespace metal::raytracing; /* Qualifiers */ -#define ccl_device -#define ccl_device_inline ccl_device -#define ccl_device_forceinline ccl_device -#define ccl_device_noinline ccl_device __attribute__((noinline)) +#if defined(__KERNEL_METAL_APPLE__) + +/* Inline everything for Apple GPUs. + * This gives ~1.1x speedup and 10% spill reduction for integator_shade_surface + * at the cost of longer compile times (~4.5 minutes on M1 Max). */ + +# define ccl_device __attribute__((always_inline)) +# define ccl_device_inline __attribute__((always_inline)) +# define ccl_device_forceinline __attribute__((always_inline)) +# define ccl_device_noinline __attribute__((always_inline)) + +#else + +# define ccl_device +# define ccl_device_inline ccl_device +# define ccl_device_forceinline ccl_device +# define ccl_device_noinline ccl_device __attribute__((noinline)) + +#endif + #define ccl_device_noinline_cpu ccl_device #define ccl_device_inline_method ccl_device #define ccl_global device diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 455d15b28c2..7820d71f15c 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -1026,7 +1026,9 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, if (bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID || - bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID) { + bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID || + bsdf->type == CLOSURE_BSDF_REFRACTION_ID || + bsdf->type == CLOSURE_BSDF_SHARP_GLASS_ID) { /* Note that CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID and * CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID are treated as * CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID further below. */ @@ -1038,11 +1040,14 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, const float eta = (sd_mnee->flag & SD_BACKFACING) ? 1.0f / microfacet_bsdf->ior : microfacet_bsdf->ior; - /* Sample transmissive microfacet bsdf. */ - float bsdf_u, bsdf_v; - path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - float2 h = mnee_sample_bsdf_dh( - bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); + float2 h = zero_float2(); + if (microfacet_bsdf->alpha_x > 0.f && microfacet_bsdf->alpha_y > 0.f) { + /* Sample transmissive microfacet bsdf. */ + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + h = mnee_sample_bsdf_dh( + bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); + } /* Setup differential geometry on vertex. */ mnee_setup_manifold_vertex( diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 5acfc92cca1..9bbbd5b0d10 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -143,7 +143,7 @@ ccl_device_inline float3 shadow_ray_smooth_surface_offset( float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ + object_dir_transform(kg, sd, &n); /* Normal x scale, to world space */ } /* Parabolic approximation */ diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 832498f1f73..e2e10b5b83f 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -1304,8 +1304,38 @@ bool OSLRenderServices::texture(ustring filename, break; } case OSLTextureHandle::SVM: { - /* Packed texture. */ - float4 rgba = kernel_tex_image_interp(kernel_globals, handle->svm_slot, s, 1.0f - t); + int id = -1; + if (handle->svm_slots[0].w == -1) { + /* Packed single texture. */ + id = handle->svm_slots[0].y; + } + else { + /* Packed tiled texture. */ + int tx = (int)s; + int ty = (int)t; + int tile = 1001 + 10 * ty + tx; + for (int4 tile_node : handle->svm_slots) { + if (tile_node.x == tile) { + id = tile_node.y; + break; + } + if (tile_node.z == tile) { + id = tile_node.w; + break; + } + } + s -= tx; + t -= ty; + } + + float4 rgba; + if (id == -1) { + rgba = make_float4( + TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A); + } + else { + rgba = kernel_tex_image_interp(kernel_globals, id, s, 1.0f - t); + } result[0] = rgba[0]; if (nchannels > 1) @@ -1319,7 +1349,7 @@ bool OSLRenderServices::texture(ustring filename, } case OSLTextureHandle::IES: { /* IES light. */ - result[0] = kernel_ies_interp(kernel_globals, handle->svm_slot, s, t); + result[0] = kernel_ies_interp(kernel_globals, handle->svm_slots[0].y, s, t); status = true; break; } @@ -1413,7 +1443,7 @@ bool OSLRenderServices::texture3d(ustring filename, /* Packed texture. */ ShaderData *sd = (ShaderData *)(sg->renderstate); KernelGlobals kernel_globals = sd->osl_globals; - int slot = handle->svm_slot; + int slot = handle->svm_slots[0].y; float3 P_float3 = make_float3(P.x, P.y, P.z); float4 rgba = kernel_tex_image_interp_3d(kernel_globals, slot, P_float3, INTERPOLATION_NONE); diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h index 653fa017140..27d7f7fb8e1 100644 --- a/intern/cycles/kernel/osl/services.h +++ b/intern/cycles/kernel/osl/services.h @@ -39,18 +39,26 @@ struct KernelGlobalsCPU; * with additional data. * * These are stored in a concurrent hash map, because OSL can compile multiple - * shaders in parallel. */ + * shaders in parallel. + * + * Note: The svm_slots array contains a compressed mapping of tile to svm_slot pairs + * stored as follows: x:tile_a, y:svm_slot_a, z:tile_b, w:svm_slot_b etc. */ struct OSLTextureHandle : public OIIO::RefCnt { enum Type { OIIO, SVM, IES, BEVEL, AO }; + OSLTextureHandle(Type type, const vector<int4> &svm_slots) + : type(type), svm_slots(svm_slots), oiio_handle(NULL), processor(NULL) + { + } + OSLTextureHandle(Type type = OIIO, int svm_slot = -1) - : type(type), svm_slot(svm_slot), oiio_handle(NULL), processor(NULL) + : OSLTextureHandle(type, {make_int4(0, svm_slot, -1, -1)}) { } Type type; - int svm_slot; + vector<int4> svm_slots; OSL::TextureSystem::TextureHandle *oiio_handle; ColorSpaceProcessor *processor; }; diff --git a/intern/cycles/scene/image.cpp b/intern/cycles/scene/image.cpp index 21fde88915e..c61ad1f1d71 100644 --- a/intern/cycles/scene/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -117,12 +117,12 @@ void ImageHandle::clear() manager = NULL; } -bool ImageHandle::empty() +bool ImageHandle::empty() const { return tile_slots.empty(); } -int ImageHandle::num_tiles() +int ImageHandle::num_tiles() const { return tile_slots.size(); } @@ -154,6 +154,35 @@ int ImageHandle::svm_slot(const int tile_index) const return tile_slots[tile_index]; } +vector<int4> ImageHandle::get_svm_slots() const +{ + const size_t num_nodes = divide_up(tile_slots.size(), 2); + + vector<int4> svm_slots; + svm_slots.reserve(num_nodes); + for (size_t i = 0; i < num_nodes; i++) { + int4 node; + + int slot = tile_slots[2 * i]; + node.x = manager->images[slot]->loader->get_tile_number(); + node.y = slot; + + if ((2 * i + 1) < tile_slots.size()) { + slot = tile_slots[2 * i + 1]; + node.z = manager->images[slot]->loader->get_tile_number(); + node.w = slot; + } + else { + node.z = -1; + node.w = -1; + } + + svm_slots.push_back(node); + } + + return svm_slots; +} + device_texture *ImageHandle::image_memory(const int tile_index) const { if (tile_index >= tile_slots.size()) { @@ -266,6 +295,11 @@ ustring ImageLoader::osl_filepath() const return ustring(); } +int ImageLoader::get_tile_number() const +{ + return 0; +} + bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b) { if (a == NULL && b == NULL) { @@ -397,6 +431,19 @@ ImageHandle ImageManager::add_image(ImageLoader *loader, return handle; } +ImageHandle ImageManager::add_image(const vector<ImageLoader *> &loaders, + const ImageParams ¶ms) +{ + ImageHandle handle; + for (ImageLoader *loader : loaders) { + const int slot = add_image_slot(loader, params, true); + handle.tile_slots.push_back(slot); + } + + handle.manager = this; + return handle; +} + int ImageManager::add_image_slot(ImageLoader *loader, const ImageParams ¶ms, const bool builtin) diff --git a/intern/cycles/scene/image.h b/intern/cycles/scene/image.h index 4d0dee35eca..9edb6a7eaf5 100644 --- a/intern/cycles/scene/image.h +++ b/intern/cycles/scene/image.h @@ -112,6 +112,9 @@ class ImageLoader { /* Optional for OSL texture cache. */ virtual ustring osl_filepath() const; + /* Optional for tiled textures loaded externally. */ + virtual int get_tile_number() const; + /* Free any memory used for loading metadata and pixels. */ virtual void cleanup(){}; @@ -139,11 +142,12 @@ class ImageHandle { void clear(); - bool empty(); - int num_tiles(); + bool empty() const; + int num_tiles() const; ImageMetaData metadata(); int svm_slot(const int tile_index = 0) const; + vector<int4> get_svm_slots() const; device_texture *image_memory(const int tile_index = 0) const; VDBImageLoader *vdb_loader(const int tile_index = 0) const; @@ -169,6 +173,7 @@ class ImageManager { const ImageParams ¶ms, const array<int> &tiles); ImageHandle add_image(ImageLoader *loader, const ImageParams ¶ms, const bool builtin = true); + ImageHandle add_image(const vector<ImageLoader *> &loaders, const ImageParams ¶ms); void device_update(Device *device, Scene *scene, Progress &progress); void device_update_slot(Device *device, Scene *scene, int slot, Progress *progress); diff --git a/intern/cycles/scene/osl.cpp b/intern/cycles/scene/osl.cpp index ffa1a2f5623..6698e6e2cce 100644 --- a/intern/cycles/scene/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -1211,14 +1211,15 @@ void OSLCompiler::parameter_texture(const char *name, ustring filename, ustring parameter(name, filename); } -void OSLCompiler::parameter_texture(const char *name, int svm_slot) +void OSLCompiler::parameter_texture(const char *name, const ImageHandle &handle) { /* Texture loaded through SVM image texture system. We generate a unique * name, which ends up being used in OSLRenderServices::get_texture_handle * to get handle again. Note that this name must be unique between multiple * render sessions as the render services are shared. */ ustring filename(string_printf("@svm%d", texture_shared_unique_id++).c_str()); - services->textures.insert(filename, new OSLTextureHandle(OSLTextureHandle::SVM, svm_slot)); + services->textures.insert(filename, + new OSLTextureHandle(OSLTextureHandle::SVM, handle.get_svm_slots())); parameter(name, filename); } @@ -1290,7 +1291,7 @@ void OSLCompiler::parameter_texture(const char * /* name */, { } -void OSLCompiler::parameter_texture(const char * /* name */, int /* svm_slot */) +void OSLCompiler::parameter_texture(const char * /* name */, const ImageHandle & /*handle*/) { } diff --git a/intern/cycles/scene/osl.h b/intern/cycles/scene/osl.h index f0f97dbcaad..bf27069b1b1 100644 --- a/intern/cycles/scene/osl.h +++ b/intern/cycles/scene/osl.h @@ -147,7 +147,7 @@ class OSLCompiler { void parameter_attribute(const char *name, ustring s); void parameter_texture(const char *name, ustring filename, ustring colorspace); - void parameter_texture(const char *name, int svm_slot); + void parameter_texture(const char *name, const ImageHandle &handle); void parameter_texture_ies(const char *name, int svm_slot); ShaderType output_type() diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 06a2052d4cb..03c152928d5 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -19,7 +19,6 @@ #include "util/color.h" #include "util/foreach.h" #include "util/log.h" -#include "util/string.h" #include "util/transform.h" #include "kernel/tables.h" @@ -450,22 +449,19 @@ void ImageTextureNode::compile(OSLCompiler &compiler) const ustring known_colorspace = metadata.colorspace; if (handle.svm_slot() == -1) { - /* OIIO currently does not support <UVTILE> substitutions natively. Replace with a format they - * understand. */ - std::string osl_filename = filename.string(); - string_replace(osl_filename, "<UVTILE>", "<U>_<V>"); compiler.parameter_texture( - "filename", ustring(osl_filename), compress_as_srgb ? u_colorspace_raw : known_colorspace); + "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); } const bool unassociate_alpha = !(ColorSpaceManager::colorspace_is_data(colorspace) || alpha_type == IMAGE_ALPHA_CHANNEL_PACKED || alpha_type == IMAGE_ALPHA_IGNORE); const bool is_tiled = (filename.find("<UDIM>") != string::npos || - filename.find("<UVTILE>") != string::npos); + filename.find("<UVTILE>") != string::npos) || + handle.num_tiles() > 1; compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend"); @@ -610,7 +606,7 @@ void EnvironmentTextureNode::compile(OSLCompiler &compiler) "filename", filename, compress_as_srgb ? u_colorspace_raw : known_colorspace); } else { - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); } compiler.parameter(this, "projection"); @@ -965,7 +961,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler) compiler.parameter_array("nishita_data", sunsky.nishita_data, 10); /* nishita texture */ if (sky_type == NODE_SKY_NISHITA) { - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); } compiler.add(this, "node_sky_texture"); } @@ -1860,7 +1856,7 @@ void PointDensityTextureNode::compile(OSLCompiler &compiler) handle = image_manager->add_image(filename.string(), image_params()); } - compiler.parameter_texture("filename", handle.svm_slot()); + compiler.parameter_texture("filename", handle); if (space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.parameter("mapping", tfm); compiler.parameter("use_mapping", 1); diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt index f9fef9f7a29..e0ed68eb20e 100644 --- a/intern/libmv/CMakeLists.txt +++ b/intern/libmv/CMakeLists.txt @@ -26,7 +26,6 @@ if(WITH_LIBMV) endif() add_definitions(${GFLAGS_DEFINES}) add_definitions(${GLOG_DEFINES}) - add_definitions(${CERES_DEFINES}) add_definitions(-DLIBMV_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE}) list(APPEND INC diff --git a/intern/libmv/bundle.sh b/intern/libmv/bundle.sh index 6808e244c05..82293068745 100755 --- a/intern/libmv/bundle.sh +++ b/intern/libmv/bundle.sh @@ -124,7 +124,6 @@ if(WITH_LIBMV) endif() add_definitions(\${GFLAGS_DEFINES}) add_definitions(\${GLOG_DEFINES}) - add_definitions(\${CERES_DEFINES}) add_definitions(-DLIBMV_GFLAGS_NAMESPACE=\${GFLAGS_NAMESPACE}) list(APPEND INC diff --git a/intern/libmv/libmv/simple_pipeline/bundle.cc b/intern/libmv/libmv/simple_pipeline/bundle.cc index e86c3bca57f..355c167d000 100644 --- a/intern/libmv/libmv/simple_pipeline/bundle.cc +++ b/intern/libmv/libmv/simple_pipeline/bundle.cc @@ -685,7 +685,7 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, PackCamerasRotationAndTranslation(*reconstruction); // Parameterization used to restrict camera motion for modal solvers. - ceres::SubsetParameterization* constant_translation_parameterization = NULL; + ceres::SubsetManifold* constant_translation_manifold = NULL; if (bundle_constraints & BUNDLE_NO_TRANSLATION) { std::vector<int> constant_translation; @@ -694,8 +694,8 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, constant_translation.push_back(4); constant_translation.push_back(5); - constant_translation_parameterization = - new ceres::SubsetParameterization(6, constant_translation); + constant_translation_manifold = + new ceres::SubsetManifold(6, constant_translation); } // Add residual blocks to the problem. @@ -735,8 +735,7 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, } if (bundle_constraints & BUNDLE_NO_TRANSLATION) { - problem.SetParameterization(current_camera_R_t, - constant_translation_parameterization); + problem.SetManifold(current_camera_R_t, constant_translation_manifold); } zero_weight_tracks_flags[marker.track] = false; @@ -787,11 +786,11 @@ void EuclideanBundleCommonIntrinsics(const Tracks& tracks, #undef MAYBE_SET_CONSTANT if (!constant_intrinsics.empty()) { - ceres::SubsetParameterization* subset_parameterization = - new ceres::SubsetParameterization(PackedIntrinsics::NUM_PARAMETERS, - constant_intrinsics); + ceres::SubsetManifold* subset_parameterization = + new ceres::SubsetManifold(PackedIntrinsics::NUM_PARAMETERS, + constant_intrinsics); - problem.SetParameterization(intrinsics_block, subset_parameterization); + problem.SetManifold(intrinsics_block, subset_parameterization); } } diff --git a/intern/libmv/libmv/simple_pipeline/modal_solver.cc b/intern/libmv/libmv/simple_pipeline/modal_solver.cc index 845b299e31e..206d264f1f8 100644 --- a/intern/libmv/libmv/simple_pipeline/modal_solver.cc +++ b/intern/libmv/libmv/simple_pipeline/modal_solver.cc @@ -180,7 +180,7 @@ void ModalSolver(const Tracks& tracks, // NOTE: Parameterization is lazily initialized when it is really needed, // and is re-used by all parameters block. - ceres::LocalParameterization* quaternion_parameterization = NULL; + ceres::Manifold* quaternion_manifold = NULL; int num_residuals = 0; for (int i = 0; i < all_markers.size(); ++i) { @@ -197,12 +197,11 @@ void ModalSolver(const Tracks& tracks, &quaternion(0)); num_residuals++; - if (quaternion_parameterization == NULL) { - quaternion_parameterization = new ceres::QuaternionParameterization(); + if (quaternion_manifold == NULL) { + quaternion_manifold = new ceres::QuaternionManifold(); } - problem.SetParameterization(&quaternion(0), - quaternion_parameterization); + problem.SetManifold(&quaternion(0), quaternion_manifold); } } diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 6f0216176d7..f8164d1f646 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -13658,6 +13658,38 @@ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> </g> <g + id="g17058" + transform="translate(-20.839982,-20.882701)" + style="display:inline;enable-background:new"> + <g + id="g7978"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.928338;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 373.21906,563.36205 c -2.04533,0 -3.71335,1.66802 -3.71335,3.71335 0,2.04534 1.66802,3.71336 3.71335,3.71336 2.04534,0 3.71336,-1.66802 3.71336,-3.71336 0,-2.04533 -1.66802,-3.71335 -3.71336,-3.71335 z" + id="path7726" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:922.783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 364.3828,556.88974 c -0.8999,0 -1.63379,0.73389 -1.63379,1.6338 0,0.89988 0.73389,1.6338 1.63379,1.6338 0.89991,0 1.6338,-0.7339 1.6338,-1.6338 0,-0.8999 -0.73389,-1.6338 -1.6338,-1.6338 z" + id="path7808" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.85;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:922.783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 366.50464,561.82783 c -1.31395,0 -2.38552,1.07155 -2.38552,2.3855 0,1.31395 1.07157,2.38555 2.38552,2.38555 1.31396,0 2.38553,-1.07158 2.38553,-2.38555 0,-1.31395 -1.07157,-2.3855 -2.38553,-2.3855 z" + id="path7890" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1881.46;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" + d="m 368.88012,557.69285 c -1.06196,0 -1.928,0.86602 -1.928,1.92801 0,1.06192 0.86604,1.92798 1.928,1.92798 1.06195,0 1.92801,-0.86606 1.92801,-1.92798 0,-1.06195 -0.86606,-1.92801 -1.92801,-1.92801 z" + id="path7972" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssss" /> + </g> + </g> + <g id="g7662" style="fill:#ffffff" transform="matrix(1,0,0,-1,0,1085.9844)"> diff --git a/release/datafiles/blender_icons16/icon16_geometry_nodes.dat b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat Binary files differnew file mode 100644 index 00000000000..195a5b9d6d9 --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_geometry_nodes.dat diff --git a/release/datafiles/blender_icons32/icon32_geometry_nodes.dat b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat Binary files differnew file mode 100644 index 00000000000..50971291fcc --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_geometry_nodes.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 2e715d54178e24ea463c7bc0cdeb27c682c39ec +Subproject fb1eac2ec80c0adee69990a5386b74a5bd4ca00 diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 599a8db33c45c2ad94f8d482f01b28125279977 +Subproject 67e43f9083b79b33f5aa6ac4d1630946804d953 diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 87d54213d1b..604a577eec9 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -341,7 +341,8 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): msgsrc = "bpy.types." + bl_rna.identifier msgctxt = bl_rna.translation_context or default_context - if bl_rna.name and (bl_rna.name != bl_rna.identifier or msgctxt != default_context): + if bl_rna.name and (bl_rna.name != bl_rna.identifier or + (msgctxt != default_context and not hasattr(bl_rna, 'bl_label'))): process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports, check_ctxt_rna, settings) if bl_rna.description: diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py index 1af9048ebfd..15a8d00cddc 100644 --- a/release/scripts/modules/bpy_extras/__init__.py +++ b/release/scripts/modules/bpy_extras/__init__.py @@ -16,4 +16,5 @@ __all__ = ( "mesh_utils", "node_utils", "view3d_utils", + "id_map_utils", ) diff --git a/release/scripts/modules/bpy_extras/id_map_utils.py b/release/scripts/modules/bpy_extras/id_map_utils.py new file mode 100644 index 00000000000..cf39f2185c6 --- /dev/null +++ b/release/scripts/modules/bpy_extras/id_map_utils.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# <pep8 compliant> + +from typing import Dict, Set +import bpy +from bpy.types import ID + + +__all__ = ( + "get_id_reference_map", + "get_all_referenced_ids", +) + + +def get_id_reference_map() -> Dict[ID, Set[ID]]: + """Return a dictionary of direct datablock references for every datablock in the blend file.""" + inv_map = {} + for key, values in bpy.data.user_map().items(): + for value in values: + if value == key: + # So an object is not considered to be referencing itself. + continue + inv_map.setdefault(value, set()).add(key) + return inv_map + + +def recursive_get_referenced_ids( + ref_map: Dict[ID, Set[ID]], id: ID, referenced_ids: Set, visited: Set +): + """Recursively populate referenced_ids with IDs referenced by id.""" + if id in visited: + # Avoid infinite recursion from circular references. + return + visited.add(id) + for ref in ref_map.get(id, []): + referenced_ids.add(ref) + recursive_get_referenced_ids( + ref_map=ref_map, id=ref, referenced_ids=referenced_ids, visited=visited + ) + + +def get_all_referenced_ids(id: ID, ref_map: Dict[ID, Set[ID]]) -> Set[ID]: + """Return a set of IDs directly or indirectly referenced by id.""" + referenced_ids = set() + recursive_get_referenced_ids( + ref_map=ref_map, id=id, referenced_ids=referenced_ids, visited=set() + ) + return referenced_ids diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 99563c83bf9..3e0d3dadcf1 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2023,37 +2023,20 @@ def km_node_editor(params): {"items": items}, ) - def node_select_ops(select_mouse): - return [ - ("node.select", {"type": select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "alt": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "alt": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, - {"properties": [("extend", True)]}), - ] - - # Allow node selection with both for RMB select if not params.legacy: + items.extend(_template_node_select(type=params.select_mouse, + value=params.select_mouse_value, select_passthrough=True)) + # Allow node selection with both for RMB select. if params.select_mouse == 'RIGHTMOUSE': - items.extend(node_select_ops('LEFTMOUSE')) - items.extend(node_select_ops('RIGHTMOUSE')) - else: - items.extend(node_select_ops('LEFTMOUSE')) + items.extend(_template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)) else: - items.extend(node_select_ops('RIGHTMOUSE')) + items.extend(_template_node_select( + type='RIGHTMOUSE', value=params.select_mouse_value, select_passthrough=False)) items.extend([ ("node.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": [("deselect_all", False)]}), ("node.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), + {"properties": [("toggle", True)]}), ]) items.extend([ @@ -4788,6 +4771,35 @@ def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=Tru ] +def _template_node_select(*, type, value, select_passthrough): + items = [ + ("node.select", {"type": type, "value": value}, + {"properties": [("deselect_all", True), ("select_passthrough", True)]}), + ("node.select", {"type": type, "value": value, "ctrl": True}, None), + ("node.select", {"type": type, "value": value, "alt": True}, None), + ("node.select", {"type": type, "value": value, "ctrl": True, "alt": True}, None), + ("node.select", {"type": type, "value": value, "shift": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "ctrl": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "alt": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "ctrl": True, "alt": True}, + {"properties": [("toggle", True)]}), + ] + + if select_passthrough and (value == 'PRESS'): + # Add an additional click item to de-select all other items, + # needed so pass-through is able to de-select other items. + items.append(( + "node.select", + {"type": type, "value": 'CLICK'}, + {"properties": [("deselect_all", True)]}, + )) + + return items + + def _template_uv_select(*, type, value, select_passthrough, legacy): # See: `use_tweak_select_passthrough` doc-string. @@ -6549,10 +6561,8 @@ def km_node_editor_tool_select(params, *, fallback): _fallback_id("Node Tool: Tweak", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else [ - ("node.select", {"type": params.select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", not params.legacy)]}), - ]), + *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else + _template_node_select(type=params.select_mouse, value='PRESS', select_passthrough=True)), ]}, ) @@ -6569,6 +6579,8 @@ def km_node_editor_tool_select_box(params, *, fallback): params.tool_tweak_event), properties=[("tweak", True)], )), + *([] if (params.select_mouse == 'RIGHTMOUSE') else + _template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)), ]}, ) diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index d6e21053e3b..a421428c067 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -203,7 +203,7 @@ class DATA_PT_geometry_curve_start_end(CurveButtonsPanelCurve, Panel): @classmethod def poll(cls, context): # Text objects don't support these properties - return (type(context.curve) in {Curve}) + return (type(context.curve) == Curve) def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 782fec91f91..b8136f26e90 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -405,7 +405,13 @@ class FalloffPanel(BrushPanel): if not super().poll(context): return False settings = cls.paint_settings(context) - return (settings and settings.brush and settings.brush.curve) + if not (settings and settings.brush and settings.brush.curve): + return False + if cls.get_brush_mode(context) == 'SCULPT_CURVES': + brush = settings.brush + if brush.curves_sculpt_tool in {'ADD', 'DELETE'}: + return False + return True def draw(self, context): layout = self.layout @@ -432,7 +438,13 @@ class FalloffPanel(BrushPanel): row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' - if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'SCULPT_CURVES'} and brush.sculpt_tool != 'POSE': + show_fallof_shape = False + if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT'} and brush.sculpt_tool != 'POSE': + show_fallof_shape = True + if not show_fallof_shape and mode == 'SCULPT_CURVES' and context.space_data.type == 'PROPERTIES': + show_fallof_shape = True + + if show_fallof_shape: col.separator() row = col.row(align=True) row.use_property_split = True @@ -772,10 +784,19 @@ def brush_settings(layout, context, brush, popover=False): elif mode == 'SCULPT_CURVES': if brush.curves_sculpt_tool == 'ADD': layout.prop(brush.curves_sculpt_settings, "add_amount") - layout.prop(brush.curves_sculpt_settings, "points_per_curve") layout.prop(brush.curves_sculpt_settings, "curve_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_shape") + col = layout.column(heading="Interpolate", align=True) + col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length") + col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape") + col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_length + col.prop(brush.curves_sculpt_settings, "curve_length") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_point_count + col.prop(brush.curves_sculpt_settings, "points_per_curve") use_frontface = True elif brush.curves_sculpt_tool == 'GROW_SHRINK': layout.prop(brush.curves_sculpt_settings, "scale_uniform") @@ -839,7 +860,7 @@ def brush_shared_settings(layout, context, brush, popover=False): if mode == 'SCULPT_CURVES': size = True strength = True - direction = brush.curves_sculpt_tool == "GROW_SHRINK" + direction = brush.curves_sculpt_tool == 'GROW_SHRINK' ### Draw settings. ### ups = context.scene.tool_settings.unified_paint_settings diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py index c1b213766df..2e7e68f02ef 100644 --- a/release/scripts/startup/bl_ui/properties_physics_field.py +++ b/release/scripts/startup/bl_ui/properties_physics_field.py @@ -77,7 +77,6 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): elif field.type == 'GUIDE': col = flow.column() - col.prop(field, "guide_minimum") col.prop(field, "guide_free") col.prop(field, "falloff_power") col.prop(field, "use_guide_path_add") @@ -88,11 +87,20 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): col = flow.column() col.prop(field, "guide_clump_amount", text="Clumping Amount") col.prop(field, "guide_clump_shape") - col.prop(field, "use_max_distance") - sub = col.column() + col.separator() + + col.prop(field, "guide_minimum", text="Min Distance") + + col = layout.column(align=False, heading="Max Distance") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(field, "use_max_distance", text="") + sub = sub.row(align=True) sub.active = field.use_max_distance - sub.prop(field, "distance_max") + sub.prop(field, "distance_max", text="") + row.prop_decorator(field, "distance_max") elif field.type == 'TEXTURE': col = flow.column() diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 5ceaf8fc644..1f9362f02b5 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -840,7 +840,6 @@ class TEXTURE_PT_colors_ramp(TextureButtonsPanel, TextureColorsPoll, Panel): if is_active: layout.template_color_ramp(tex, "color_ramp", expand=True) else: - layout.alignment = 'RIGHT' layout.label(text="Enable the Color Ramp first") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 6654053bef4..d4d1e6ace76 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2278,6 +2278,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): ({"property": "use_new_point_cloud_type"}, "T75717"), ({"property": "use_full_frame_compositor"}, "T88150"), ({"property": "enable_eevee_next"}, "T93220"), + ({"property": "use_draw_manager_acquire_lock"}, "T98016"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index fd94da4de84..ad5097ff874 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -246,7 +246,7 @@ class _draw_tool_settings_context_mode: unified_name="use_unified_size", text="Radius", slider=True, - header=True + header=True, ) # strength, use_strength_pressure @@ -259,7 +259,7 @@ class _draw_tool_settings_context_mode: pressure_name=pressure_name, unified_name="use_unified_strength", text="Strength", - header=True + header=True, ) # direction @@ -320,7 +320,7 @@ class _draw_tool_settings_context_mode: "weight", unified_name="use_unified_weight", slider=True, - header=True + header=True, ) UnifiedPaintPanel.prop_unified( @@ -332,7 +332,7 @@ class _draw_tool_settings_context_mode: unified_name="use_unified_size", slider=True, text="Radius", - header=True + header=True, ) UnifiedPaintPanel.prop_unified( layout, @@ -341,7 +341,7 @@ class _draw_tool_settings_context_mode: "strength", pressure_name="use_pressure_strength", unified_name="use_unified_strength", - header=True + header=True, ) return True @@ -484,7 +484,6 @@ class _draw_tool_settings_context_mode: tool_settings = context.tool_settings paint = tool_settings.curves_sculpt - layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush if brush is None: @@ -496,9 +495,10 @@ class _draw_tool_settings_context_mode: brush, "size", unified_name="use_unified_size", + pressure_name="use_pressure_size", text="Radius", slider=True, - header=True + header=True, ) if brush.curves_sculpt_tool not in {'ADD', 'DELETE'}: @@ -508,32 +508,29 @@ class _draw_tool_settings_context_mode: brush, "strength", unified_name="use_unified_strength", - header=True + pressure_name="use_pressure_strength", + header=True, ) if brush.curves_sculpt_tool == 'COMB': layout.prop(brush, "falloff_shape", expand=True) - layout.prop(brush, "curve_preset") + layout.popover("VIEW3D_PT_tools_brush_falloff") if brush.curves_sculpt_tool == 'ADD': - layout.prop(brush, "use_frontface", text="Front Faces Only") layout.prop(brush, "falloff_shape", expand=True) layout.prop(brush.curves_sculpt_settings, "add_amount") - layout.prop(brush.curves_sculpt_settings, "points_per_curve") - layout.prop(brush.curves_sculpt_settings, "curve_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_length") - layout.prop(brush.curves_sculpt_settings, "interpolate_shape") + layout.popover("VIEW3D_PT_curves_sculpt_add_shape", text="Curve Shape") + layout.prop(brush, "use_frontface", text="Front Faces Only") if brush.curves_sculpt_tool == 'GROW_SHRINK': layout.prop(brush, "direction", expand=True, text="") layout.prop(brush, "falloff_shape", expand=True) - layout.prop(brush.curves_sculpt_settings, "scale_uniform") - layout.prop(brush.curves_sculpt_settings, "minimum_length") - layout.prop(brush, "curve_preset") + layout.popover("VIEW3D_PT_curves_sculpt_grow_shrink_scaling", text="Scaling") + layout.popover("VIEW3D_PT_tools_brush_falloff") if brush.curves_sculpt_tool == 'SNAKE_HOOK': layout.prop(brush, "falloff_shape", expand=True) - layout.prop(brush, "curve_preset") + layout.popover("VIEW3D_PT_tools_brush_falloff") if brush.curves_sculpt_tool == 'DELETE': layout.prop(brush, "falloff_shape", expand=True) @@ -1237,24 +1234,23 @@ class VIEW3D_MT_view_viewpoint(Menu): def draw(self, _context): layout = self.layout - i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_text_ctxt) + layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_contexts.editor_view3d) layout.separator() - layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt).type = 'TOP' - layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt).type = 'BOTTOM' + layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_contexts.editor_view3d).type = 'TOP' + layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_contexts.editor_view3d).type = 'BOTTOM' layout.separator() - layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt).type = 'FRONT' - layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt).type = 'BACK' + layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_contexts.editor_view3d).type = 'FRONT' + layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_contexts.editor_view3d).type = 'BACK' layout.separator() - layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt).type = 'RIGHT' - layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt).type = 'LEFT' + layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_contexts.editor_view3d).type = 'RIGHT' + layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_contexts.editor_view3d).type = 'LEFT' class VIEW3D_MT_view_navigation(Menu): @@ -1321,33 +1317,32 @@ class VIEW3D_MT_view_align_selected(Menu): def draw(self, _context): layout = self.layout - i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'TOP' - props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'BOTTOM' layout.separator() - props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'FRONT' - props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'BACK' layout.separator() - props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'RIGHT' - props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'LEFT' @@ -2347,6 +2342,7 @@ class VIEW3D_MT_object(Menu): layout.separator() layout.operator("object.shade_smooth") + layout.operator("object.shade_smooth", text="Shade Auto Smooth").use_auto_smooth = True layout.operator("object.shade_flat") layout.separator() @@ -2589,7 +2585,8 @@ class VIEW3D_MT_object_context_menu(Menu): # Shared among some object types. if obj is not None: if obj.type in {'MESH', 'CURVE', 'SURFACE'}: - layout.operator("object.shade_smooth", text="Shade Smooth") + layout.operator("object.shade_smooth") + layout.operator("object.shade_smooth", text="Shade Auto Smooth").use_auto_smooth = True layout.operator("object.shade_flat", text="Shade Flat") layout.separator() @@ -7633,6 +7630,55 @@ class TOPBAR_PT_gpencil_vertexcolor(GreasePencilVertexcolorPanel, Panel): return ob and ob.type == 'GPENCIL' +class VIEW3D_PT_curves_sculpt_add_shape(Panel): + # Only for popover, these are dummy values. + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_label = "Curves Sculpt Add Curve Options" + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + settings = UnifiedPaintPanel.paint_settings(context) + brush = settings.brush + + col = layout.column(heading="Interpolate", align=True) + col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length") + col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape") + col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_length + col.prop(brush.curves_sculpt_settings, "curve_length", text="Length") + + col = layout.column() + col.active = not brush.curves_sculpt_settings.interpolate_point_count + col.prop(brush.curves_sculpt_settings, "points_per_curve", text="Points") + + +class VIEW3D_PT_curves_sculpt_grow_shrink_scaling(Panel): + # Only for popover, these are dummy values. + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_label = "Curves Grow/Shrink Scaling" + bl_ui_units_x = 12 + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + settings = UnifiedPaintPanel.paint_settings(context) + brush = settings.brush + + layout.prop(brush.curves_sculpt_settings, "scale_uniform") + layout.prop(brush.curves_sculpt_settings, "minimum_length") + + classes = ( VIEW3D_HT_header, VIEW3D_HT_tool_header, @@ -7865,6 +7911,8 @@ classes = ( TOPBAR_PT_gpencil_materials, TOPBAR_PT_gpencil_vertexcolor, TOPBAR_PT_annotation_layers, + VIEW3D_PT_curves_sculpt_add_shape, + VIEW3D_PT_curves_sculpt_grow_shrink_scaling, ) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 698f0dadc33..3e1754fd908 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -391,6 +391,11 @@ class VIEW3D_PT_tools_brush_settings_advanced(Panel, View3DPaintBrushPanel): bl_options = {'DEFAULT_CLOSED'} bl_ui_units_x = 14 + @classmethod + def poll(cls, context): + mode = cls.get_brush_mode(context) + return mode is not None and mode != 'SCULPT_CURVES' + def draw(self, context): layout = self.layout diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ffc4d37f622..d0592e9a405 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -15,8 +15,9 @@ if(WITH_CLANG_TIDY AND NOT MSVC) endif() find_package(ClangTidy REQUIRED) - set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) - set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) + set(CMAKE_C_CLANG_TIDY + ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) endif() add_subdirectory(blender) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index bf2d50f63be..87fa26a4f73 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -50,7 +50,7 @@ struct BasisCache { Vector<int> start_indices; /** - * The result of #check_valid_size_and_order, to avoid retrieving its inputs later on. + * The result of #check_valid_num_and_order, to avoid retrieving its inputs later on. * If this is true, the data above will be invalid, and original data should be copied * to the evaluated result. */ @@ -127,7 +127,7 @@ class CurvesGeometry : public ::CurvesGeometry { * Create curves with the given size. Only the position attribute is created, along with the * offsets. */ - CurvesGeometry(int point_size, int curve_size); + CurvesGeometry(int point_num, int curve_num); CurvesGeometry(const CurvesGeometry &other); CurvesGeometry(CurvesGeometry &&other); CurvesGeometry &operator=(const CurvesGeometry &other); @@ -418,7 +418,7 @@ namespace curves { * The number of segments between control points, accounting for the last segment of cyclic * curves. The logic is simple, but this function should be used to make intentions clearer. */ -inline int curve_segment_size(const int points_num, const bool cyclic) +inline int curve_segment_num(const int points_num, const bool cyclic) { BLI_assert(points_num > 0); return (cyclic && points_num > 1) ? points_num : points_num - 1; @@ -585,11 +585,11 @@ namespace catmull_rom { * \param points_num: The number of points in the curve. * \param resolution: The resolution for each segment. */ -int calculate_evaluated_size(int points_num, bool cyclic, int resolution); +int calculate_evaluated_num(int points_num, bool cyclic, int resolution); /** * Evaluate the Catmull Rom curve. The length of the #dst span should be calculated with - * #calculate_evaluated_size and is expected to divide evenly by the #src span's segment size. + * #calculate_evaluated_num and is expected to divide evenly by the #src span's segment size. */ void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst); @@ -606,7 +606,7 @@ namespace nurbs { /** * Checks the conditions that a NURBS curve needs to evaluate. */ -bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); +bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); /** * Calculate the standard evaluated size for a NURBS curve, using the standard that @@ -616,7 +616,7 @@ bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, Knots * for predictability and so that cached basis weights of NURBS curves with these properties can be * shared. */ -int calculate_evaluated_size( +int calculate_evaluated_num( int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); /** @@ -624,7 +624,7 @@ int calculate_evaluated_size( * The knots must be longer for a cyclic curve, for example, in order to provide weights for the * last evaluated points that are also influenced by the first control points. */ -int knots_size(int points_num, int8_t order, bool cyclic); +int knots_num(int points_num, int8_t order, bool cyclic); /** * Calculate the knots for a curve given its properties, based on built-in standards defined by @@ -644,7 +644,7 @@ void calculate_knots( * and a weight for each control point. */ void calculate_basis_cache(int points_num, - int evaluated_size, + int evaluated_num, int8_t order, bool cyclic, Span<float> knots, @@ -671,6 +671,7 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, } // namespace curves Curves *curves_new_nomain(int points_num, int curves_num); +Curves *curves_new_nomain(CurvesGeometry curves); /** * Create a new curves data-block containing a single curve with the given length and type. @@ -685,11 +686,11 @@ std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &typ inline int CurvesGeometry::points_num() const { - return this->point_size; + return this->point_num; } inline int CurvesGeometry::curves_num() const { - return this->curve_size; + return this->curve_num; } inline IndexRange CurvesGeometry::points_range() const { @@ -719,7 +720,7 @@ inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts inline IndexRange CurvesGeometry::points_for_curve(const int index) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[index]; const int offset_next = this->curve_offsets[index + 1]; @@ -729,7 +730,7 @@ inline IndexRange CurvesGeometry::points_for_curve(const int index) const inline IndexRange CurvesGeometry::points_for_curves(const IndexRange curves) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[curves.start()]; const int offset_next = this->curve_offsets[curves.one_after_last()]; @@ -751,7 +752,7 @@ inline IndexRange CurvesGeometry::evaluated_points_for_curve(int index) const inline IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange curves) const { BLI_assert(!this->runtime->offsets_cache_dirty); - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); const int offset = this->runtime->evaluated_offsets_cache[curves.start()]; const int offset_next = this->runtime->evaluated_offsets_cache[curves.one_after_last()]; return {offset, offset_next - offset}; @@ -769,7 +770,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, BLI_assert(cyclic == this->cyclic()[curve_index]); const IndexRange points = this->evaluated_points_for_curve(curve_index); const int start = points.start() + curve_index; - return {start, curves::curve_segment_size(points.size(), cyclic)}; + return {start, curves::curve_segment_num(points.size(), cyclic)}; } inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh new file mode 100644 index 00000000000..62b060093e9 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.hh" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +namespace blender::bke::curves { + +/** + * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. + */ +void fill_curve_counts(const bke::CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + MutableSpan<int> counts); + +/** + * Turn an array of sizes into the offset at each index including all previous sizes. + */ +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0); + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index dfd9fccebbd..849c430fd7b 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -98,7 +98,7 @@ class GeometryComponent { /** * Return the length of a specific domain, or 0 if the domain is not supported. */ - virtual int attribute_domain_size(AttributeDomain domain) const; + virtual int attribute_domain_num(AttributeDomain domain) const; /** * Return true if the attribute name corresponds to a built-in attribute with a hardcoded domain @@ -560,7 +560,7 @@ class MeshComponent : public GeometryComponent { */ Mesh *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -623,7 +623,7 @@ class PointCloudComponent : public GeometryComponent { */ PointCloud *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -664,7 +664,7 @@ class CurveComponentLegacy : public GeometryComponent { const CurveEval *get_for_read() const; CurveEval *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -715,7 +715,7 @@ class CurveComponent : public GeometryComponent { const Curves *get_for_read() const; Curves *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -949,8 +949,8 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan<blender::float4x4> instance_transforms(); blender::Span<blender::float4x4> instance_transforms() const; - int instances_amount() const; - int references_amount() const; + int instances_num() const; + int references_num() const; /** * Remove the indices that are not contained in the mask input, and remove unused instance @@ -963,7 +963,7 @@ class InstancesComponent : public GeometryComponent { blender::bke::CustomDataAttributes &attributes(); const blender::bke::CustomDataAttributes &attributes() const; - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; void foreach_referenced_geometry( blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 42d0e66cf49..25f20b82900 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -454,7 +454,7 @@ bool BKE_image_is_dirty_writable(struct Image *image, bool *r_is_writable); int BKE_image_sequence_guess_offset(struct Image *image); bool BKE_image_has_anim(struct Image *image); bool BKE_image_has_packedfile(const struct Image *image); -bool BKE_image_has_filepath(struct Image *ima); +bool BKE_image_has_filepath(const struct Image *ima); /** * Checks the image buffer changes with time (not keyframed values). */ diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 091f30825ae..3e06a9d9e9c 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -204,6 +204,7 @@ bool BKE_mesh_material_index_used(struct Mesh *me, short index); void BKE_mesh_material_index_clear(struct Mesh *me); void BKE_mesh_material_remap(struct Mesh *me, const unsigned int *remap, unsigned int remap_len); void BKE_mesh_smooth_flag_set(struct Mesh *me, bool use_smooth); +void BKE_mesh_auto_smooth_flag_set(struct Mesh *me, bool use_auto_smooth, float auto_smooth_angle); /** * Needed after converting a mesh with subsurf optimal display to mesh. diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index a38bfb497a2..4749aab4379 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -102,6 +102,7 @@ typedef enum { /** Accepts #BMesh input (without conversion). */ eModifierTypeFlag_AcceptsBMesh = (1 << 11), } ModifierTypeFlag; +ENUM_OPERATORS(ModifierTypeFlag, eModifierTypeFlag_AcceptsBMesh) typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); typedef void (*TexWalkFunc)(void *userData, diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 6cbb47dc709..28f326a4ad4 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -102,7 +102,7 @@ class Spline { /** Return the number of control points. */ virtual int size() const = 0; - int segments_size() const; + int segments_num() const; bool is_cyclic() const; void set_cyclic(bool value); @@ -127,8 +127,8 @@ class Spline { * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. */ virtual void mark_cache_invalid() = 0; - virtual int evaluated_points_size() const = 0; - int evaluated_edges_size() const; + virtual int evaluated_points_num() const = 0; + int evaluated_edges_num() const; float length() const; @@ -164,7 +164,7 @@ class Spline { /** * The index of the evaluated point after the result location, accounting for wrapping when * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will - * be the last index (#evaluated_points_size - 1). + * be the last index (#evaluated_points_num - 1). */ int next_evaluated_index; /** @@ -191,7 +191,7 @@ class Spline { * indices and factors to the next index encoded in floats. The logic for converting from the * float values to interpolation data is in #lookup_data_from_index_factor. */ - blender::Array<float> sample_uniform_index_factors(int samples_size) const; + blender::Array<float> sample_uniform_index_factors(int samples_num) const; LookupResult lookup_data_from_index_factor(float index_factor) const; /** @@ -344,7 +344,7 @@ class BezierSpline final : public Spline { bool point_is_sharp(int index) const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; /** * Returns access to a cache of offsets into the evaluated point array for each control point. @@ -472,7 +472,7 @@ class NURBSpline final : public Spline { /** * Determines where and how the control points affect the evaluated points. The length should - * always be the value returned by #knots_size(), and each value should be greater than or equal + * always be the value returned by #knots_num(), and each value should be greater than or equal * to the previous. Only invalidated when a point is added or removed. */ mutable blender::Vector<float> knots_; @@ -514,8 +514,8 @@ class NURBSpline final : public Spline { uint8_t order() const; void set_order(uint8_t value); - bool check_valid_size_and_order() const; - int knots_size() const; + bool check_valid_num_and_order() const; + int knots_num() const; void resize(int size) final; blender::MutableSpan<blender::float3> positions() final; @@ -530,7 +530,7 @@ class NURBSpline final : public Spline { blender::Span<float> weights() const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -582,7 +582,7 @@ class PolySpline final : public Spline { blender::Span<float> tilts() const final; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -665,7 +665,7 @@ struct CurveEval { blender::Array<float> accumulated_spline_lengths() const; float total_length() const; - int total_control_point_size() const; + int total_control_point_num() const; void mark_cache_invalid(); diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h index d6de95a19b7..823d32f83ba 100644 --- a/source/blender/blenkernel/BKE_unit.h +++ b/source/blender/blenkernel/BKE_unit.h @@ -91,7 +91,7 @@ const char *BKE_unit_identifier_get(const void *usys_pt, int index); double BKE_unit_scalar_get(const void *usys_pt, int index); bool BKE_unit_is_suppressed(const void *usys_pt, int index); -/** Aligned with #PropertyUnit. */ +/** Aligned with #PropertyUnit and `bpyunits_ucategories_items` in `bpy_utils_units.c`. */ enum { B_UNIT_NONE = 0, B_UNIT_LENGTH = 1, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index c9e88362b80..0b4f81df452 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -117,6 +117,7 @@ set(SRC intern/curveprofile.cc intern/curves.cc intern/curves_geometry.cc + intern/curves_utils.cc intern/customdata.cc intern/customdata_file.c intern/data_transfer.c @@ -356,6 +357,7 @@ set(SRC BKE_curveprofile.h BKE_curves.h BKE_curves.hh + BKE_curves_utils.hh BKE_customdata.h BKE_customdata_file.h BKE_data_transfer.h diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc index 6ce6bee547c..636e0af0edf 100644 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -41,7 +41,7 @@ static std::string get_new_internal_name() { static std::atomic<int> index = 0; const int next_index = index.fetch_add(1); - return "anonymous_attribute_" + std::to_string(next_index); + return ".a_" + std::to_string(next_index); } AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 11f36e32b74..81eb1786322 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -318,7 +318,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes) const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); ASSERT_NE(nullptr, found_by_id); EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) - << "Backslashes should be normalised when loading from disk."; + << "Backslashes should be normalized when loading from disk."; EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name); const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes"); diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index 0cb0704ff80..7f93eb7b393 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -75,9 +75,9 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) case ID_CV: { Curves *curves = (Curves *)id; info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data; - info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size; + info[ATTR_DOMAIN_POINT].length = curves->geometry.point_num; info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data; - info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size; + info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num; break; } default: diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d33b64c493b..e86bac21084 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -184,16 +184,16 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); return data != nullptr; } case AttributeInit::Type::VArray: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); if (data == nullptr) { return false; } @@ -204,7 +204,7 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = CustomData_add_layer( - &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + &custom_data, data_type, CD_ASSIGN, source_data, domain_num); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -221,35 +221,35 @@ static void *add_generic_custom_data_layer(CustomData &custom_data, const CustomDataType data_type, const eCDAllocType alloctype, void *layer_data, - const int domain_size, + const int domain_num, const AttributeIDRef &attribute_id) { if (attribute_id.is_named()) { char attribute_name_c[MAX_NAME]; attribute_id.name().copy(attribute_name_c); return CustomData_add_layer_named( - &custom_data, data_type, alloctype, layer_data, domain_size, attribute_name_c); + &custom_data, data_type, alloctype, layer_data, domain_num, attribute_name_c); } const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); return CustomData_add_layer_anonymous( - &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); + &custom_data, data_type, alloctype, layer_data, domain_num, &anonymous_id); } static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); return data != nullptr; } case AttributeInit::Type::VArray: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); if (data == nullptr) { return false; } @@ -260,7 +260,7 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); + custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -303,8 +303,8 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent return {}; } - const int domain_size = component.attribute_domain_size(domain_); - return as_read_attribute_(data, domain_size); + const int domain_num = component.attribute_domain_num(domain_); + return as_read_attribute_(data, domain_num); } WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( @@ -317,7 +317,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data; if (stored_as_named_attribute_) { @@ -333,10 +333,10 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( void *new_data; if (stored_as_named_attribute_) { new_data = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, name_.c_str(), domain_size); + custom_data, stored_type_, name_.c_str(), domain_num); } else { - new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num); } if (data != new_data) { @@ -353,7 +353,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( }; } - return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; + return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)}; } bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const @@ -366,7 +366,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); int layer_index; if (stored_as_named_attribute_) { for (const int i : IndexRange(custom_data->totlayer)) { @@ -381,7 +381,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co } const bool delete_success = CustomData_free_layer( - custom_data, stored_type_, domain_size, layer_index); + custom_data, stored_type_, domain_num, layer_index); if (delete_success) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); @@ -401,7 +401,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); bool success; if (stored_as_named_attribute_) { if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { @@ -409,7 +409,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_custom_data_layer_from_attribute_init( - name_, *custom_data, stored_type_, domain_size, initializer); + name_, *custom_data, stored_type_, domain_num, initializer); } else { if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { @@ -417,7 +417,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_size, initializer); + *custom_data, stored_type_, domain_num, initializer); } if (success) { if (custom_data_access_.update_custom_data_pointers) { @@ -446,7 +446,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; @@ -455,7 +455,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (type == nullptr) { continue; } - GSpan data{*type, layer.data, domain_size}; + GSpan data{*type, layer.data, domain_num}; return {GVArray::ForSpan(data), domain_}; } return {}; @@ -468,24 +468,23 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named( - custom_data, layer.type, layer.name, domain_size); + CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_num); } else { CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + custom_data, layer.type, &attribute_id.anonymous_id(), domain_num); } const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); if (type == nullptr) { continue; } - GMutableSpan data{*type, layer.data, domain_size}; + GMutableSpan data{*type, layer.data, domain_num}; return {GVMutableArray::ForSpan(data), domain_}; } return {}; @@ -498,12 +497,12 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, if (custom_data == nullptr) { return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (this->type_is_supported((CustomDataType)layer.type) && custom_data_layer_matches_attribute_id(layer, attribute_id)) { - CustomData_free_layer(custom_data, layer.type, domain_size, i); + CustomData_free_layer(custom_data, layer.type, domain_num, i); return true; } } @@ -531,9 +530,9 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); add_custom_data_layer_from_attribute_init( - attribute_id, *custom_data, data_type, domain_size, initializer); + attribute_id, *custom_data, data_type, domain_num, initializer); return true; } @@ -567,8 +566,8 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - return {as_read_attribute_(layer.data, domain_size), domain_}; + const int domain_num = component.attribute_domain_num(domain_); + return {as_read_attribute_(layer.data, domain_num), domain_}; } } } @@ -585,16 +584,16 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, layer.name, domain_size); + custom_data, stored_type_, layer.name, domain_num); if (data_old != data_new) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } } - return {as_write_attribute_(layer.data, domain_size), domain_}; + return {as_write_attribute_(layer.data, domain_num), domain_}; } } } @@ -612,8 +611,8 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - CustomData_free_layer(custom_data, stored_type_, domain_size, i); + const int domain_num = component.attribute_domain_num(domain_); + CustomData_free_layer(custom_data, stored_type_, domain_num, i); if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } @@ -702,9 +701,9 @@ GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, std::optional<GSpan> attribute = this->get_for_read(attribute_id); if (!attribute) { - const int domain_size = this->size_; + const int domain_num = this->size_; return GVArray::ForSingle( - *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value); + *type, domain_num, (default_value == nullptr) ? type->default_value() : default_value); } if (attribute->type() == *type) { @@ -822,7 +821,7 @@ bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) return providers->supported_domains().contains(domain); } -int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const +int GeometryComponent::attribute_domain_num(const AttributeDomain UNUSED(domain)) const { return 0; } @@ -1157,8 +1156,8 @@ blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef if (default_value == nullptr) { default_value = type->default_value(); } - const int domain_size = this->attribute_domain_size(domain); - return blender::GVArray::ForSingle(*type, domain_size, default_value); + const int domain_num = this->attribute_domain_num(domain); + return blender::GVArray::ForSingle(*type, domain_num, default_value); } class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { @@ -1267,10 +1266,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); component.attribute_try_create_builtin( attribute_name, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); @@ -1301,7 +1300,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, ignore_old_values); } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { @@ -1310,7 +1309,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, attribute_id, domain, data_type, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); @@ -1333,8 +1332,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing * attribute after processing is done. */ - void *data = MEM_mallocN_aligned( - cpp_type->size() * domain_size, cpp_type->alignment(), __func__); + void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__); if (ignore_old_values) { /* This does nothing for trivially constructible types, but is necessary for correctness. */ cpp_type->default_construct_n(data, domain); @@ -1343,10 +1341,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Fill the temporary array with values from the existing attribute. */ GVArray old_varray = component.attribute_get_for_read( attribute_id, domain, data_type, default_value); - old_varray.materialize_to_uninitialized(IndexRange(domain_size), data); + old_varray.materialize_to_uninitialized(IndexRange(domain_num), data); } GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>( - GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); + GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } @@ -1429,7 +1427,7 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c const StringRef name = get_random_id_attribute_name(domain); GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); if (attribute) { - BLI_assert(attribute.size() == component.attribute_domain_size(domain)); + BLI_assert(attribute.size() == component.attribute_domain_num(domain)); return attribute; } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 8c021ed0e21..f0f47cb7a11 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -172,8 +172,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -207,14 +207,14 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; }; -template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size) +template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_num) { - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); + return VArray<T>::ForSpan(Span<T>((const T *)data, domain_num)); } -template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size) +template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_num) { - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); + return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_num)); } /** @@ -226,8 +226,8 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index 2db183eea3e..4b2174c912c 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -9,11 +9,11 @@ namespace blender::bke::curves::catmull_rom { -int calculate_evaluated_size(const int points_num, const bool cyclic, const int resolution) +int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution) { - const int eval_size = resolution * curve_segment_size(points_num, cyclic); + const int eval_num = resolution * curve_segment_num(points_num, cyclic); /* If the curve isn't cyclic, one last point is added to the final point. */ - return cyclic ? eval_size : eval_size + 1; + return cyclic ? eval_num : eval_num + 1; } /* Adapted from Cycles #catmull_rom_basis_eval function. */ @@ -46,7 +46,7 @@ static void interpolate_to_evaluated(const Span<T> src, MutableSpan<T> dst) { - BLI_assert(dst.size() == calculate_evaluated_size(src.size(), cyclic, resolution)); + BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution)); /* - First deal with one and two point curves need special attention. * - Then evaluate the first and last segment(s) whose control points need to wrap around diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 3d9dd3ecf31..c507e7934a8 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -117,7 +117,7 @@ float CurveEval::total_length() const return length; } -int CurveEval::total_control_point_size() const +int CurveEval::total_control_point_num() const { int count = 0; for (const SplinePtr &spline : this->splines()) { @@ -144,7 +144,7 @@ blender::Array<int> CurveEval::evaluated_point_offsets() const int offset = 0; for (const int i : splines_.index_range()) { offsets[i] = offset; - offset += splines_[i]->evaluated_points_size(); + offset += splines_[i]->evaluated_points_num(); } offsets.last() = offset; return offsets; @@ -463,7 +463,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) Curves *curve_eval_to_curves(const CurveEval &curve_eval) { - Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(), + Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(), curve_eval.splines().size()); CurveComponent dst_component; dst_component.replace(curves, GeometryOwnershipType::Editable); diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 45440358221..cd6b64e9a03 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -10,10 +10,10 @@ namespace blender::bke::curves::nurbs { -bool check_valid_size_and_order(const int points_num, - const int8_t order, - const bool cyclic, - const KnotsMode knots_mode) +bool check_valid_num_and_order(const int points_num, + const int8_t order, + const bool cyclic, + const KnotsMode knots_mode) { if (points_num < order) { return false; @@ -29,19 +29,19 @@ bool check_valid_size_and_order(const int points_num, return true; } -int calculate_evaluated_size(const int points_num, - const int8_t order, - const bool cyclic, - const int resolution, - const KnotsMode knots_mode) +int calculate_evaluated_num(const int points_num, + const int8_t order, + const bool cyclic, + const int resolution, + const KnotsMode knots_mode) { - if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) { + if (!check_valid_num_and_order(points_num, order, cyclic, knots_mode)) { return points_num; } - return resolution * curve_segment_size(points_num, cyclic); + return resolution * curve_segment_num(points_num, cyclic); } -int knots_size(const int points_num, const int8_t order, const bool cyclic) +int knots_num(const int points_num, const int8_t order, const bool cyclic) { if (cyclic) { return points_num + order * 2 - 1; @@ -55,7 +55,7 @@ void calculate_knots(const int points_num, const bool cyclic, MutableSpan<float> knots) { - BLI_assert(knots.size() == knots_size(points_num, order, cyclic)); + BLI_assert(knots.size() == knots_num(points_num, order, cyclic)); UNUSED_VARS_NDEBUG(points_num); const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); @@ -147,7 +147,7 @@ static void calculate_basis_for_point(const float parameter, } void calculate_basis_cache(const int points_num, - const int evaluated_size, + const int evaluated_num, const int8_t order, const bool cyclic, const Span<float> knots, @@ -157,10 +157,10 @@ void calculate_basis_cache(const int points_num, const int8_t degree = order - 1; - basis_cache.weights.resize(evaluated_size * order); - basis_cache.start_indices.resize(evaluated_size); + basis_cache.weights.resize(evaluated_num * order); + basis_cache.start_indices.resize(evaluated_num); - if (evaluated_size == 0) { + if (evaluated_num == 0) { return; } @@ -168,12 +168,12 @@ void calculate_basis_cache(const int points_num, MutableSpan<int> basis_start_indices(basis_cache.start_indices); const int last_control_point_index = cyclic ? points_num + degree : points_num; - const int evaluated_segment_size = curve_segment_size(evaluated_size, cyclic); + const int evaluated_segment_num = curve_segment_num(evaluated_num, cyclic); const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / evaluated_segment_size; - for (const int i : IndexRange(evaluated_size)) { + const float step = (end - start) / evaluated_segment_num; + for (const int i : IndexRange(evaluated_num)) { /* Clamp parameter due to floating point inaccuracy. */ const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]); diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index ef921797698..0cd324cfe2c 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -39,8 +39,8 @@ static void fill_mesh_topology(const int vert_offset, MutableSpan<MLoop> loops, MutableSpan<MPoly> polys) { - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); if (profile_point_num == 1) { for (const int i : IndexRange(main_point_num - 1)) { @@ -134,9 +134,9 @@ static void fill_mesh_topology(const int vert_offset, const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; if (has_caps) { - const int poly_size = main_segment_num * profile_segment_num; - const int cap_loop_offset = loop_offset + poly_size * 4; - const int cap_poly_offset = poly_offset + poly_size; + const int poly_num = main_segment_num * profile_segment_num; + const int cap_loop_offset = loop_offset + poly_num * 4; + const int cap_poly_offset = poly_offset + poly_num; MPoly &poly_start = polys[cap_poly_offset]; poly_start.loopstart = cap_loop_offset; @@ -273,7 +273,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool for (const int i_main : info.main.curves_range()) { const bool main_cyclic = info.main_cyclic[i_main]; const int main_point_num = info.main.evaluated_points_for_curve(i_main).size(); - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); for (const int i_profile : info.profile.curves_range()) { result.vert[mesh_index] = vert_offset; result.edge[mesh_index] = edge_offset; @@ -285,8 +285,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool const bool profile_cyclic = info.profile_cyclic[i_profile]; const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size(); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, - profile_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; const int tube_face_num = main_segment_num * profile_segment_num; @@ -408,8 +407,8 @@ static void foreach_curve_combination(const CurvesInfo &info, profile_points, main_cyclic, profile_cyclic, - curves::curve_segment_size(main_points.size(), main_cyclic), - curves::curve_segment_size(profile_points.size(), profile_cyclic), + curves::curve_segment_num(main_points.size(), main_cyclic), + curves::curve_segment_num(profile_points.size(), profile_cyclic), offsets_to_range(offsets.vert.as_span(), i), offsets_to_range(offsets.edge.as_span(), i), offsets_to_range(offsets.poly.as_span(), i), diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 1df1492bac1..84ba98db54b 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -78,12 +78,12 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, * shallow copy from the source to the destination, and because the copy-on-write functionality * isn't supported more generically yet. */ - dst.point_size = src.point_size; - dst.curve_size = src.curve_size; + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size); - CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size); + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_num); dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets)); @@ -136,17 +136,17 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre CustomData_blend_write(writer, &curves->geometry.point_data, players, - curves->geometry.point_size, + curves->geometry.point_num, CD_MASK_ALL, &curves->id); CustomData_blend_write(writer, &curves->geometry.curve_data, clayers, - curves->geometry.curve_size, + curves->geometry.curve_num, CD_MASK_ALL, &curves->id); - BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets); + BLO_write_int32_array(writer, curves->geometry.curve_num + 1, curves->geometry.curve_offsets); BLO_write_pointer_array(writer, curves->totcol, curves->mat); if (curves->adt) { @@ -169,11 +169,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) BKE_animdata_blend_read_data(reader, curves->adt); /* Geometry */ - CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); - CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size); + CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); + CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); update_custom_data_pointers(*curves); - BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets); + BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__); @@ -379,4 +379,11 @@ Curves *curves_new_nomain_single(const int points_num, const CurveType type) return curves; } +Curves *curves_new_nomain(CurvesGeometry curves) +{ + Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + bke::CurvesGeometry::wrap(curves_id->geometry) = std::move(curves); + return curves_id; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 4dd0fad57d4..0fd58a52f81 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -46,10 +46,10 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) { } -CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) +CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) { - this->point_size = point_size; - this->curve_size = curve_size; + this->point_num = point_num; + this->curve_num = curve_num; CustomData_reset(&this->point_data); CustomData_reset(&this->curve_data); @@ -57,16 +57,16 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) CD_PROP_FLOAT3, CD_DEFAULT, nullptr, - this->point_size, + this->point_num, ATTR_POSITION.c_str()); - this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__); + this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_num + 1, sizeof(int), __func__); this->update_customdata_pointers(); this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); /* Fill the type counts with the default so they're in a valid state. */ - this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_size; + this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num; } /** @@ -74,15 +74,15 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) */ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) { - CustomData_free(&dst.point_data, dst.point_size); - CustomData_free(&dst.curve_data, dst.curve_size); - dst.point_size = src.point_size; - dst.curve_size = src.curve_size; - CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size); - CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size); + CustomData_free(&dst.point_data, dst.point_num); + CustomData_free(&dst.curve_data, dst.curve_num); + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num); MEM_SAFE_FREE(dst.curve_offsets); - dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__); + dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_num + 1, sizeof(int), __func__); dst.offsets_for_write().copy_from(src.offsets()); dst.tag_topology_changed(); @@ -94,7 +94,7 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { copy_curves_geometry(*this, other); } @@ -110,15 +110,15 @@ CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) /* The source should be empty, but in a valid state so that using it further will work. */ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) { - dst.point_size = src.point_size; + dst.point_num = src.point_num; std::swap(dst.point_data, src.point_data); - CustomData_free(&src.point_data, src.point_size); - src.point_size = 0; + CustomData_free(&src.point_data, src.point_num); + src.point_num = 0; - dst.curve_size = src.curve_size; + dst.curve_num = src.curve_num; std::swap(dst.curve_data, src.curve_data); - CustomData_free(&src.curve_data, src.curve_size); - src.curve_size = 0; + CustomData_free(&src.curve_data, src.curve_num); + src.curve_num = 0; std::swap(dst.curve_offsets, src.curve_offsets); MEM_SAFE_FREE(src.curve_offsets); @@ -130,7 +130,7 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { move_curves_geometry(*this, other); } @@ -145,8 +145,8 @@ CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other) CurvesGeometry::~CurvesGeometry() { - CustomData_free(&this->point_data, this->point_size); - CustomData_free(&this->curve_data, this->curve_size); + CustomData_free(&this->point_data, this->point_num); + CustomData_free(&this->curve_data, this->curve_num); MEM_SAFE_FREE(this->curve_offsets); MEM_delete(this->runtime); this->runtime = nullptr; @@ -158,7 +158,7 @@ CurvesGeometry::~CurvesGeometry() /** \name Accessors * \{ */ -static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) +static int domain_num(const CurvesGeometry &curves, const AttributeDomain domain) { return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num(); } @@ -180,15 +180,15 @@ static VArray<T> get_varray_attribute(const CurvesGeometry &curves, const StringRefNull name, const T default_value) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); const CustomData &custom_data = domain_custom_data(curves, domain); const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str()); if (data != nullptr) { - return VArray<T>::ForSpan(Span<T>(data, size)); + return VArray<T>::ForSpan(Span<T>(data, num)); } - return VArray<T>::ForSingle(default_value, size); + return VArray<T>::ForSingle(default_value, num); } template<typename T> @@ -196,7 +196,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, const AttributeDomain domain, const StringRefNull name) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomData &custom_data = domain_custom_data(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); @@ -204,7 +204,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, if (data == nullptr) { return {}; } - return {data, size}; + return {data, num}; } template<typename T> @@ -213,19 +213,19 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves, const StringRefNull name, const T default_value = T()) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); CustomData &custom_data = domain_custom_data(curves, domain); T *data = (T *)CustomData_duplicate_referenced_layer_named( - &custom_data, type, name.c_str(), size); + &custom_data, type, name.c_str(), num); if (data != nullptr) { - return {data, size}; + return {data, num}; } data = (T *)CustomData_add_layer_named( - &custom_data, type, CD_CALLOC, nullptr, size, name.c_str()); - MutableSpan<T> span = {data, size}; - if (size > 0 && span.first() != default_value) { + &custom_data, type, CD_CALLOC, nullptr, num, name.c_str()); + MutableSpan<T> span = {data, num}; + if (num > 0 && span.first() != default_value) { span.fill(default_value); } return span; @@ -301,22 +301,22 @@ void CurvesGeometry::update_curve_types() Span<float3> CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_size}; + return {(const float3 *)this->position, this->point_num}; } MutableSpan<float3> CurvesGeometry::positions_for_write() { this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size); - return {(float3 *)this->position, this->point_size}; + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); + return {(float3 *)this->position, this->point_num}; } Span<int> CurvesGeometry::offsets() const { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } MutableSpan<int> CurvesGeometry::offsets_for_write() { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } VArray<bool> CurvesGeometry::cyclic() const @@ -444,12 +444,12 @@ MutableSpan<float2> CurvesGeometry::surface_triangle_coords_for_write() /** \name Evaluation * \{ */ -template<typename SizeFn> void build_offsets(MutableSpan<int> offsets, const SizeFn &size_fn) +template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn) { int offset = 0; for (const int i : offsets.drop_back(1).index_range()) { offsets[i] = offset; - offset += size_fn(i); + offset += count_fn(i); } offsets.last() = offset; } @@ -472,7 +472,7 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, const IndexRange points = curves.points_for_curve(curve_index); switch (types[curve_index]) { case CURVE_TYPE_CATMULL_ROM: - return curves::catmull_rom::calculate_evaluated_size( + return curves::catmull_rom::calculate_evaluated_num( points.size(), cyclic[curve_index], resolution[curve_index]); case CURVE_TYPE_POLY: return points.size(); @@ -484,11 +484,11 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, bezier_evaluated_offsets.slice(points)); return bezier_evaluated_offsets[points.last()]; case CURVE_TYPE_NURBS: - return curves::nurbs::calculate_evaluated_size(points.size(), - nurbs_orders[curve_index], - cyclic[curve_index], - resolution[curve_index], - KnotsMode(nurbs_knots_modes[curve_index])); + return curves::nurbs::calculate_evaluated_num(points.size(), + nurbs_orders[curve_index], + cyclic[curve_index], + resolution[curve_index], + KnotsMode(nurbs_knots_modes[curve_index])); } BLI_assert_unreachable(); return 0; @@ -587,13 +587,13 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const const bool is_cyclic = cyclic[curve_index]; const KnotsMode mode = KnotsMode(knots_modes[curve_index]); - if (!curves::nurbs::check_valid_size_and_order(points.size(), order, is_cyclic, mode)) { + if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) { basis_caches[curve_index].invalid = true; continue; } - const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic); - Array<float> knots(knots_size); + const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic); + Array<float> knots(knots_num); curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); curves::nurbs::calculate_basis_cache(points.size(), evaluated_points.size(), @@ -914,8 +914,8 @@ void CurvesGeometry::ensure_evaluated_lengths() const threading::isolate_task([&]() { /* Use an extra length value for the final cyclic segment for a consistent size * (see comment on #evaluated_length_cache). */ - const int total_size = this->evaluated_points_num() + this->curves_num(); - this->runtime->evaluated_length_cache.resize(total_size); + const int total_num = this->evaluated_points_num() + this->curves_num(); + this->runtime->evaluated_length_cache.resize(total_num); MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache; Span<float3> evaluated_positions = this->evaluated_positions(); @@ -944,13 +944,13 @@ void CurvesGeometry::ensure_evaluated_lengths() const void CurvesGeometry::resize(const int points_num, const int curves_num) { - if (points_num != this->point_size) { + if (points_num != this->point_num) { CustomData_realloc(&this->point_data, points_num); - this->point_size = points_num; + this->point_num = points_num; } - if (curves_num != this->curve_size) { + if (curves_num != this->curve_num) { CustomData_realloc(&this->curve_data, curves_num); - this->curve_size = curves_num; + this->curve_num = curves_num; this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc new file mode 100644 index 00000000000..78c2382b62f --- /dev/null +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_curves_utils.hh" + +namespace blender::bke::curves { + +void fill_curve_counts(const bke::CurvesGeometry &curves, + const Span<IndexRange> curve_ranges, + MutableSpan<int> counts) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { + for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { + threading::parallel_for(curves_range, 4096, [&](IndexRange range) { + for (const int i : range) { + counts[i] = curves.points_for_curve(i).size(); + } + }); + } + }); +} + +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset) +{ + int offset = start_offset; + for (const int i : counts_to_offsets.index_range().drop_back(1)) { + const int count = counts_to_offsets[i]; + BLI_assert(count > 0); + counts_to_offsets[i] = offset; + offset += count; + } + counts_to_offsets.last() = offset; +} + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index f409389e463..a28afc8ddca 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -114,7 +114,7 @@ void CurveComponentLegacy::ensure_owns_direct_data() /** \name Attribute Access Helper Functions * \{ */ -int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const +int CurveComponentLegacy::attribute_domain_num(const AttributeDomain domain) const { if (curve_ == nullptr) { return 0; @@ -251,8 +251,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize(const IndexMask mask, MutableSpan<T> r_span) const final { - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -273,8 +273,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final { T *dst = r_span.data(); - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -415,7 +415,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0; } }; @@ -495,8 +495,8 @@ static void point_attribute_materialize(Span<Span<T>> data, const IndexMask mask, MutableSpan<T> r_span) { - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -541,8 +541,8 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, MutableSpan<T> r_span) { T *dst = r_span.data(); - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -589,13 +589,13 @@ static GVArray varray_from_initializer(const AttributeInit &initializer, case AttributeInit::Type::VArray: return static_cast<const AttributeInitVArray &>(initializer).varray; case AttributeInit::Type::MoveArray: - int total_size = 0; + int total_num = 0; for (const SplinePtr &spline : splines) { - total_size += spline->size(); + total_num += spline->size(); } return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type), static_cast<const AttributeInitMove &>(initializer).data, - total_size)); + total_num)); } BLI_assert_unreachable(); return {}; @@ -1168,7 +1168,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { } return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && - component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 7c4a7db6462..b565143d08f 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -308,7 +308,7 @@ bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +int CurveComponent::attribute_domain_num(const AttributeDomain domain) const { if (curves_ == nullptr) { return 0; diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0dc6f486d28..e56a7ca4dd8 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -79,7 +79,7 @@ void InstancesComponent::add_instance(const int instance_handle, const float4x4 BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - attributes_.reallocate(this->instances_amount()); + attributes_.reallocate(this->instances_num()); } blender::Span<int> InstancesComponent::instance_reference_handles() const @@ -183,7 +183,7 @@ void InstancesComponent::remove_unused_references() using namespace blender; using namespace blender::bke; - const int tot_instances = this->instances_amount(); + const int tot_instances = this->instances_num(); const int tot_references_before = references_.size(); if (tot_instances == 0) { @@ -258,12 +258,12 @@ void InstancesComponent::remove_unused_references() }); } -int InstancesComponent::instances_amount() const +int InstancesComponent::instances_num() const { return instance_transforms_.size(); } -int InstancesComponent::references_amount() const +int InstancesComponent::references_num() const { return references_.size(); } @@ -358,7 +358,7 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const } } else { - almost_unique_ids_.reinitialize(this->instances_amount()); + almost_unique_ids_.reinitialize(this->instances_num()); for (const int i : almost_unique_ids_.index_range()) { almost_unique_ids_[i] = i; } @@ -366,12 +366,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } -int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const +int InstancesComponent::attribute_domain_num(const AttributeDomain domain) const { if (domain != ATTR_DOMAIN_INSTANCE) { return 0; } - return this->instances_amount(); + return this->instances_num(); } blender::bke::CustomDataAttributes &InstancesComponent::attributes() diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index fb39861d3e7..5ac9a03f43c 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -169,7 +169,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, /** \name Attribute Access * \{ */ -int MeshComponent::attribute_domain_size(const AttributeDomain domain) const +int MeshComponent::attribute_domain_num(const AttributeDomain domain) const { if (mesh_ == nullptr) { return 0; @@ -839,20 +839,20 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -static GVArray make_derived_read_attribute(const void *data, const int domain_size) +static GVArray make_derived_read_attribute(const void *data, const int domain_num) { return VArray<ElemT>::template ForDerivedSpan<StructT, GetFunc>( - Span<StructT>((const StructT *)data, domain_size)); + Span<StructT>((const StructT *)data, domain_num)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), void (*SetFunc)(StructT &, ElemT)> -static GVMutableArray make_derived_write_attribute(void *data, const int domain_size) +static GVMutableArray make_derived_write_attribute(void *data, const int domain_num) { return VMutableArray<ElemT>::template ForDerivedSpan<StructT, GetFunc, SetFunc>( - MutableSpan<StructT>((StructT *)data, domain_size)); + MutableSpan<StructT>((StructT *)data, domain_num)); } static float3 get_vertex_position(const MVert &vert) @@ -1160,7 +1160,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 400e0ea5e15..6de123c7cb9 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -104,7 +104,7 @@ void PointCloudComponent::ensure_owns_direct_data() /** \name Attribute Access * \{ */ -int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const +int PointCloudComponent::attribute_domain_num(const AttributeDomain domain) const { if (pointcloud_ == nullptr) { return 0; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 2bd8b643899..40e36ced199 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -272,7 +272,7 @@ bool GeometrySet::has_pointcloud() const bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); - return component != nullptr && component->instances_amount() >= 1; + return component != nullptr && component->instances_num() >= 1; } bool GeometrySet::has_volume() const diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index dfa820519a5..3d894f47ae0 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -721,6 +721,9 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) imapf_src = imapf_src->next) { ImagePackedFile *imapf_dst = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)")); + + imapf_dst->view = imapf_src->view; + imapf_dst->tile_number = imapf_src->tile_number; STRNCPY(imapf_dst->filepath, imapf_src->filepath); if (imapf_src->packedfile) { @@ -1197,7 +1200,8 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) } /** Pack image buffer to memory as PNG or EXR. */ -static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath) +static bool image_memorypack_imbuf( + Image *ima, ImBuf *ibuf, int view, int tile_number, const char *filepath) { ibuf->ftype = (ibuf->rect_float) ? IMB_FTYPE_OPENEXR : IMB_FTYPE_PNG; @@ -1219,6 +1223,8 @@ static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath imapf = static_cast<ImagePackedFile *>(MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile")); STRNCPY(imapf->filepath, filepath); imapf->packedfile = pf; + imapf->view = view; + imapf->tile_number = tile_number; BLI_addtail(&ima->packedfiles, imapf); ibuf->encodedbuffer = nullptr; @@ -1234,42 +1240,47 @@ bool BKE_image_memorypack(Image *ima) image_free_packedfiles(ima); - if (BKE_image_is_multiview(ima)) { - /* Store each view as a separate packed files with R_IMF_VIEWS_INDIVIDUAL. */ - ImageView *iv; - int i; + const int tot_viewfiles = image_num_viewfiles(ima); + const bool is_tiled = (ima->source == IMA_SRC_TILED); + const bool is_multiview = BKE_image_is_multiview(ima); - for (i = 0, iv = static_cast<ImageView *>(ima->views.first); iv; - iv = static_cast<ImageView *>(iv->next), i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, nullptr); + ImageUser iuser{}; + BKE_imageuser_default(&iuser); + char tiled_filepath[FILE_MAX]; + for (int view = 0; view < tot_viewfiles; view++) { + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int index = (is_multiview || is_tiled) ? view : IMA_NO_INDEX; + int entry = is_tiled ? tile->tile_number : 0; + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, nullptr); if (!ibuf) { ok = false; break; } - /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ - if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { - const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; - BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], ""); + const char *filepath = ibuf->name; + if (is_tiled) { + iuser.tile = tile->tile_number; + BKE_image_user_file_path(&iuser, ima, tiled_filepath); + filepath = tiled_filepath; + } + else if (is_multiview) { + ImageView *iv = static_cast<ImageView *>(BLI_findlink(&ima->views, view)); + /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; + BLI_path_suffix(iv->filepath, FILE_MAX, suffix[view], ""); + } + filepath = iv->filepath; } - ok = ok && image_memorypack_imbuf(ima, ibuf, iv->filepath); + ok = ok && image_memorypack_imbuf(ima, ibuf, view, tile->tile_number, filepath); IMB_freeImBuf(ibuf); } - - ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } - else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); - if (ibuf) { - ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); - IMB_freeImBuf(ibuf); - } - else { - ok = false; - } + if (is_multiview) { + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } if (ok && ima->source == IMA_SRC_GENERATED) { @@ -1284,27 +1295,24 @@ void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) { const int tot_viewfiles = image_num_viewfiles(ima); - if (tot_viewfiles == 1) { - ImagePackedFile *imapf = static_cast<ImagePackedFile *>( - MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); - BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, ima->filepath, basepath); - if (imapf->packedfile) { - STRNCPY(imapf->filepath, ima->filepath); - } - else { - BLI_freelinkN(&ima->packedfiles, imapf); - } - } - else { - for (ImageView *iv = static_cast<ImageView *>(ima->views.first); iv; iv = iv->next) { + ImageUser iuser{}; + BKE_imageuser_default(&iuser); + for (int view = 0; view < tot_viewfiles; view++) { + iuser.view = view; + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + iuser.tile = tile->tile_number; + char filepath[FILE_MAX]; + BKE_image_user_file_path(&iuser, ima, filepath); + ImagePackedFile *imapf = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, iv->filepath, basepath); + imapf->packedfile = BKE_packedfile_new(reports, filepath, basepath); + imapf->view = view; + imapf->tile_number = tile->tile_number; if (imapf->packedfile) { - STRNCPY(imapf->filepath, iv->filepath); + STRNCPY(imapf->filepath, filepath); } else { BLI_freelinkN(&ima->packedfiles, imapf); @@ -1323,11 +1331,16 @@ void BKE_image_packfiles_from_mem(ReportList *reports, if (tot_viewfiles != 1) { BKE_report(reports, RPT_ERROR, "Cannot pack multiview images from raw data currently..."); } + else if (ima->source == IMA_SRC_TILED) { + BKE_report(reports, RPT_ERROR, "Cannot pack tiled images from raw data currently..."); + } else { ImagePackedFile *imapf = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), __func__)); BLI_addtail(&ima->packedfiles, imapf); imapf->packedfile = BKE_packedfile_new_from_memory(data, data_len); + imapf->view = 0; + imapf->tile_number = 1001; STRNCPY(imapf->filepath, ima->filepath); } } @@ -2950,8 +2963,9 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) /* try to repack file */ if (BKE_image_has_packedfile(ima)) { const int tot_viewfiles = image_num_viewfiles(ima); + const int tot_files = tot_viewfiles * BLI_listbase_count(&ima->tiles); - if (tot_viewfiles != BLI_listbase_count_at_most(&ima->packedfiles, tot_viewfiles + 1)) { + if (tot_files != BLI_listbase_count_at_most(&ima->packedfiles, tot_files + 1)) { /* in case there are new available files to be loaded */ image_free_packedfiles(ima); BKE_image_packfiles(nullptr, ima, ID_BLEND_PATH(bmain, &ima->id)); @@ -3926,18 +3940,23 @@ static ImBuf *load_image_single(Image *ima, int flag = IB_rect | IB_multilayer; *r_cache_ibuf = true; + const int tile_number = image_get_tile_number_from_iuser(ima, iuser); /* is there a PackedFile with this image ? */ if (has_packed && !is_sequence) { - ImagePackedFile *imapf = static_cast<ImagePackedFile *>( - BLI_findlink(&ima->packedfiles, view_id)); - if (imapf->packedfile) { - flag |= imbuf_alpha_flags_for_image(ima); - ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, - imapf->packedfile->size, - flag, - ima->colorspace_settings.name, - "<packed data>"); + flag |= imbuf_alpha_flags_for_image(ima); + + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { + if (imapf->view == view_id && imapf->tile_number == tile_number) { + if (imapf->packedfile) { + ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, + imapf->packedfile->size, + flag, + ima->colorspace_settings.name, + "<packed data>"); + } + break; + } } } else { @@ -3996,6 +4015,8 @@ static ImBuf *load_image_single(Image *ima, BLI_addtail(&ima->packedfiles, imapf); STRNCPY(imapf->filepath, filepath); + imapf->view = view_id; + imapf->tile_number = tile_number; imapf->packedfile = BKE_packedfile_new( nullptr, filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); } @@ -5103,7 +5124,7 @@ bool BKE_image_has_packedfile(const Image *ima) return (BLI_listbase_is_empty(&ima->packedfiles) == false); } -bool BKE_image_has_filepath(Image *ima) +bool BKE_image_has_filepath(const Image *ima) { /* This could be improved to detect cases like //../../, currently path * remapping empty file paths empty. */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index a2338eb9b39..9dc64365f0c 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1384,7 +1384,21 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } if (id->override_library->hierarchy_root != NULL) { - continue; + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id->override_library->hierarchy_root) || + id->override_library->hierarchy_root->lib != id->lib) { + CLOG_ERROR( + &LOG, + "Existing override hierarchy root ('%s') for ID '%s' is invalid, will try to find a " + "new valid one", + id->override_library->hierarchy_root != NULL ? + id->override_library->hierarchy_root->name : + "<NONE>", + id->name); + id->override_library->hierarchy_root = NULL; + } + else { + continue; + } } BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1402,7 +1416,7 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } - lib_override_root_hierarchy_set(bmain, id_root, id_root, NULL); + lib_override_root_hierarchy_set(bmain, id_root, id, NULL); BLI_assert(id->override_library->hierarchy_root != NULL); } @@ -1451,6 +1465,7 @@ static void lib_override_library_remap(Main *bmain, remapper, ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); BKE_id_remapper_free(remapper); + BLI_linklist_free(nomain_ids, NULL); } static bool lib_override_library_resync(Main *bmain, @@ -2058,6 +2073,10 @@ static bool lib_override_resync_tagging_finalize_recurse( CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name); + if (id_root->override_library == NULL) { + BLI_assert(0); + } + LinkNodePair **id_resync_roots_p; if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) { *id_resync_roots_p = MEM_callocN(sizeof(**id_resync_roots_p), __func__); diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 03e03dacfbc..b9ed783fa8c 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -502,13 +502,13 @@ BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img) } if (img) { - const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(img->x, img->y); + data = MEM_mallocN(data_size, __func__); IMB_rect_from_float(img); /* Just in case... */ data->width = img->x; data->height = img->y; - memcpy(data->rect, img->rect, sz - sizeof(*data)); + memcpy(data->rect, img->rect, data_size - sizeof(*data)); } if (bmain) { diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 628f59ae449..4e3544d0f60 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -1577,6 +1577,19 @@ void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth) } } +void BKE_mesh_auto_smooth_flag_set(Mesh *me, + const bool use_auto_smooth, + const float auto_smooth_angle) +{ + if (use_auto_smooth) { + me->flag |= ME_AUTOSMOOTH; + me->smoothresh = auto_smooth_angle; + } + else { + me->flag &= ~ME_AUTOSMOOTH; + } +} + int poly_find_loop_from_vert(const MPoly *poly, const MLoop *loopstart, uint vert) { for (int j = 0; j < poly->totloop; j++, loopstart++) { diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index e7ed100ed03..7c96c463339 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -237,14 +237,14 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose) for (ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_packedfile(ima) == false && !ID_IS_LINKED(ima)) { - if (ima->source == IMA_SRC_FILE) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_TILED)) { BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot++; } - else if (BKE_image_has_multiple_ibufs(ima) && verbose) { + else if (ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE) && verbose) { BKE_reportf(reports, RPT_WARNING, - "Image '%s' skipped, movies, image sequences and packed files not supported", + "Image '%s' skipped, packing movies or image sequences not supported", ima->id.name + 2); } } @@ -494,15 +494,22 @@ static void unpack_generate_paths(const char *name, if (tempname[0] == '\0') { /* NOTE: we generally do not have any real way to re-create extension out of data. */ - BLI_strncpy(tempname, id->name + 2, sizeof(tempname)); + const size_t len = BLI_strncpy_rlen(tempname, id->name + 2, sizeof(tempname)); printf("%s\n", tempname); - /* For images we can add the file extension based on the file magic. */ + /* For images ensure that the temporary filename contains tile number information as well as + * a file extension based on the file magic. */ if (id_type == ID_IM) { - ImagePackedFile *imapf = ((Image *)id)->packedfiles.last; + Image *ima = (Image *)id; + ImagePackedFile *imapf = ima->packedfiles.last; if (imapf != NULL && imapf->packedfile != NULL) { const PackedFile *pf = imapf->packedfile; enum eImbFileType ftype = IMB_ispic_type_from_memory((const uchar *)pf->data, pf->size); + if (ima->source == IMA_SRC_TILED) { + char tile_number[6]; + BLI_snprintf(tile_number, sizeof(tile_number), ".%d", imapf->tile_number); + BLI_strncpy(tempname + len, tile_number, sizeof(tempname) - len); + } if (ftype != IMB_FTYPE_NONE) { const int imtype = BKE_ftype_to_imtype(ftype, NULL); BKE_image_path_ensure_ext_from_imtype(tempname, imtype); @@ -639,6 +646,11 @@ int BKE_packedfile_unpack_image(Main *bmain, /* keep the new name in the image for non-pack specific reasons */ if (how != PF_REMOVE) { BLI_strncpy(ima->filepath, new_file_path, sizeof(imapf->filepath)); + if (ima->source == IMA_SRC_TILED) { + /* Ensure that the Image filepath is kept in a tokenized format. */ + char *filename = (char *)BLI_path_basename(ima->filepath); + BKE_image_ensure_tile_token(filename); + } } MEM_freeN(new_file_path); } diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index 5623cac44ac..8b3fcffd9cd 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -71,7 +71,7 @@ static void extract_barycentric_pixels(UDIMTilePixels &tile_data, int x; for (x = minx; x < maxx; x++) { - float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y); + float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y); float3 barycentric_weights; barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights); diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 7704a74841a..e8c7aff75d1 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -116,15 +116,15 @@ void Spline::reverse() this->mark_cache_invalid(); } -int Spline::evaluated_edges_size() const +int Spline::evaluated_edges_num() const { - const int eval_size = this->evaluated_points_size(); - if (eval_size < 2) { + const int eval_num = this->evaluated_points_num(); + if (eval_num < 2) { /* Two points are required for an edge. */ return 0; } - return this->is_cyclic_ ? eval_size : eval_size - 1; + return this->is_cyclic_ ? eval_num : eval_num - 1; } float Spline::length() const @@ -133,11 +133,11 @@ float Spline::length() const return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last(); } -int Spline::segments_size() const +int Spline::segments_num() const { - const int size = this->size(); + const int num = this->size(); - return is_cyclic_ ? size : size - 1; + return is_cyclic_ ? num : num - 1; } bool Spline::is_cyclic() const @@ -177,7 +177,7 @@ Span<float> Spline::evaluated_lengths() const return evaluated_lengths_cache_; } - const int total = evaluated_edges_size(); + const int total = evaluated_edges_num(); evaluated_lengths_cache_.resize(total); if (total != 0) { Span<float3> positions = this->evaluated_positions(); @@ -242,8 +242,8 @@ Span<float3> Spline::evaluated_tangents() const return evaluated_tangents_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_tangents_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_tangents_cache_.resize(eval_num); Span<float3> positions = this->evaluated_positions(); @@ -369,8 +369,8 @@ Span<float3> Spline::evaluated_normals() const return evaluated_normals_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_normals_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_normals_cache_.resize(eval_num); Span<float3> tangents = this->evaluated_tangents(); MutableSpan<float3> normals = evaluated_normals_cache_; @@ -410,7 +410,7 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); const int index = offset - lengths.begin(); - const int next_index = (index == this->evaluated_points_size() - 1) ? 0 : index + 1; + const int next_index = (index == this->evaluated_points_num() - 1) ? 0 : index + 1; const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; const float length_in_segment = length - previous_length; @@ -420,30 +420,30 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const return LookupResult{index, next_index, factor}; } -Array<float> Spline::sample_uniform_index_factors(const int samples_size) const +Array<float> Spline::sample_uniform_index_factors(const int samples_num) const { const Span<float> lengths = this->evaluated_lengths(); - BLI_assert(samples_size > 0); - Array<float> samples(samples_size); + BLI_assert(samples_num > 0); + Array<float> samples(samples_num); samples[0] = 0.0f; - if (samples_size == 1) { + if (samples_num == 1) { return samples; } const float total_length = this->length(); - const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1)); + const float sample_length = total_length / (samples_num - (is_cyclic_ ? 0 : 1)); /* Store the length at the previous evaluated point in a variable so it can * start out at zero (the lengths array doesn't contain 0 for the first point). */ float prev_length = 0.0f; int i_sample = 1; - for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) { + for (const int i_evaluated : IndexRange(this->evaluated_edges_num())) { const float length = lengths[i_evaluated]; /* Add every sample that fits in this evaluated edge. */ - while ((sample_length * i_sample) < length && i_sample < samples_size) { + while ((sample_length * i_sample) < length && i_sample < samples_num) { const float factor = (sample_length * i_sample - prev_length) / (length - prev_length); samples[i_sample] = i_evaluated + factor; i_sample++; @@ -454,8 +454,8 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const /* Zero lengths or float inaccuracies can cause invalid values, or simply * skip some, so set the values that weren't completed in the main loop. */ - for (const int i : IndexRange(i_sample, samples_size - i_sample)) { - samples[i] = float(samples_size); + for (const int i : IndexRange(i_sample, samples_num - i_sample)) { + samples[i] = float(samples_num); } if (!is_cyclic_) { @@ -468,23 +468,23 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const { - const int eval_size = this->evaluated_points_size(); + const int eval_num = this->evaluated_points_num(); if (is_cyclic_) { - if (index_factor < eval_size) { + if (index_factor < eval_num) { const int index = std::floor(index_factor); - const int next_index = (index < eval_size - 1) ? index + 1 : 0; + const int next_index = (index < eval_num - 1) ? index + 1 : 0; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 1, 0, 1.0f}; + return LookupResult{eval_num - 1, 0, 1.0f}; } - if (index_factor < eval_size - 1) { + if (index_factor < eval_num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 2, eval_size - 1, 1.0f}; + return LookupResult{eval_num - 2, eval_num - 1, 1.0f}; } void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const @@ -504,7 +504,7 @@ void Spline::sample_with_index_factors(const GVArray &src, Span<float> index_factors, GMutableSpan dst) const { - BLI_assert(src.size() == this->evaluated_points_size()); + BLI_assert(src.size() == this->evaluated_points_num()); blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 8e207f93bf5..80515d0ef37 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -335,7 +335,7 @@ void BezierSpline::mark_cache_invalid() auto_handles_dirty_ = true; } -int BezierSpline::evaluated_points_size() const +int BezierSpline::evaluated_points_num() const { BLI_assert(this->size() > 0); return this->control_point_offsets().last(); @@ -502,12 +502,12 @@ Span<float> BezierSpline::evaluated_mappings() const return evaluated_mapping_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_mapping_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_mapping_cache_.resize(eval_num); MutableSpan<float> mappings = evaluated_mapping_cache_; - if (eval_size == 1) { + if (eval_num == 1) { mappings.first() = 0.0f; mapping_cache_dirty_ = false; return mappings; @@ -517,7 +517,7 @@ Span<float> BezierSpline::evaluated_mappings() const blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings); + calculate_mappings_linear_resolution(offsets, num, resolution_, is_cyclic_, mappings); }); mapping_cache_dirty_ = false; @@ -535,15 +535,15 @@ Span<float3> BezierSpline::evaluated_positions() const return evaluated_position_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); MutableSpan<float3> positions = evaluated_position_cache_; - if (size == 1) { + if (num == 1) { /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ - BLI_assert(eval_size == 1); + BLI_assert(eval_num == 1); positions.first() = positions_.first(); position_cache_dirty_ = false; return positions; @@ -556,7 +556,7 @@ Span<float3> BezierSpline::evaluated_positions() const const int grain_size = std::max(512 / resolution_, 1); blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - blender::threading::parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { + blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) { for (const int i : range) { this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); } @@ -564,7 +564,7 @@ Span<float3> BezierSpline::evaluated_positions() const }); if (is_cyclic_) { this->evaluate_segment( - size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1])); + num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 1])); } else { /* Since evaluating the bezier segment doesn't add the final point, @@ -579,23 +579,23 @@ Span<float3> BezierSpline::evaluated_positions() const BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( const float index_factor) const { - const int size = this->size(); + const int num = this->size(); if (is_cyclic_) { - if (index_factor < size) { + if (index_factor < num) { const int index = std::floor(index_factor); - const int next_index = (index < size - 1) ? index + 1 : 0; + const int next_index = (index < num - 1) ? index + 1 : 0; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 1, 0, 1.0f}; + return InterpolationData{num - 1, 0, 1.0f}; } - if (index_factor < size - 1) { + if (index_factor < num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 2, size - 1, 1.0f}; + return InterpolationData{num - 2, num - 1, 1.0f}; } /* Use a spline argument to avoid adding this to the header. */ @@ -605,7 +605,7 @@ static void interpolate_to_evaluated_impl(const BezierSpline &spline, MutableSpan<T> dst) { BLI_assert(src.size() == spline.size()); - BLI_assert(dst.size() == spline.evaluated_points_size()); + BLI_assert(dst.size() == spline.evaluated_points_num()); Span<float> mappings = spline.evaluated_mappings(); for (const int i : dst.index_range()) { @@ -627,8 +627,8 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const return src; } - const int eval_size = this->evaluated_points_size(); - if (eval_size == 1) { + const int eval_num = this->evaluated_points_num(); + if (eval_num == 1) { return src; } @@ -636,7 +636,7 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(eval_size); + Array<T> values(eval_num); interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 9d1d5a53a43..a7eeb82d854 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -124,19 +124,19 @@ void NURBSpline::mark_cache_invalid() length_cache_dirty_ = true; } -int NURBSpline::evaluated_points_size() const +int NURBSpline::evaluated_points_num() const { - if (!this->check_valid_size_and_order()) { + if (!this->check_valid_num_and_order()) { return 0; } - return resolution_ * this->segments_size(); + return resolution_ * this->segments_num(); } void NURBSpline::correct_end_tangents() const { } -bool NURBSpline::check_valid_size_and_order() const +bool NURBSpline::check_valid_num_and_order() const { if (this->size() < order_) { return false; @@ -152,10 +152,10 @@ bool NURBSpline::check_valid_size_and_order() const return true; } -int NURBSpline::knots_size() const +int NURBSpline::knots_num() const { - const int size = this->size() + order_; - return is_cyclic_ ? size + order_ - 1 : size; + const int num = this->size() + order_; + return is_cyclic_ ? num + order_ - 1 : num; } void NURBSpline::calculate_knots() const @@ -173,7 +173,7 @@ void NURBSpline::calculate_knots() const * Covers both Cyclic and EndPoint cases. */ const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0); - knots_.resize(this->knots_size()); + knots_.resize(this->knots_num()); MutableSpan<float> knots = knots_; int r = head; @@ -203,13 +203,13 @@ void NURBSpline::calculate_knots() const Span<float> NURBSpline::knots() const { if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } std::lock_guard lock{knots_mutex_}; if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } @@ -221,7 +221,7 @@ Span<float> NURBSpline::knots() const } static void calculate_basis_for_point(const float parameter, - const int size, + const int num, const int degree, const Span<float> knots, MutableSpan<float> r_weights, @@ -231,7 +231,7 @@ static void calculate_basis_for_point(const float parameter, int start = 0; int end = 0; - for (const int i : IndexRange(size + degree)) { + for (const int i : IndexRange(num + degree)) { const bool knots_equal = knots[i] == knots[i + 1]; if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) { continue; @@ -248,7 +248,7 @@ static void calculate_basis_for_point(const float parameter, for (const int i_order : IndexRange(2, degree)) { if (end + i_order >= knots.size()) { - end = size + degree - i_order; + end = num + degree - i_order; } for (const int i : IndexRange(end - start + 1)) { const int knot_index = start + i; @@ -284,16 +284,16 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const return basis_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); const int order = this->order(); const int degree = order - 1; - basis_cache_.weights.resize(eval_size * order); - basis_cache_.start_indices.resize(eval_size); + basis_cache_.weights.resize(eval_num * order); + basis_cache_.start_indices.resize(eval_num); - if (eval_size == 0) { + if (eval_num == 0) { return basis_cache_; } @@ -303,14 +303,14 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const const Span<float> control_weights = this->weights(); const Span<float> knots = this->knots(); - const int last_control_point_index = is_cyclic_ ? size + degree : size; + const int last_control_point_index = is_cyclic_ ? num + degree : num; const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / this->evaluated_edges_size(); - for (const int i : IndexRange(eval_size)) { + const float step = (end - start) / this->evaluated_edges_num(); + for (const int i : IndexRange(eval_num)) { /* Clamp parameter due to floating point inaccuracy. */ - const float parameter = std::clamp(start + step * i, knots[0], knots[size + degree]); + const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]); MutableSpan<float> point_weights = basis_weights.slice(i * order, order); @@ -318,7 +318,7 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); for (const int j : point_weights.index_range()) { - const int point_index = (basis_start_indices[i] + j) % size; + const int point_index = (basis_start_indices[i] + j) % num; point_weights[j] *= control_weights[point_index]; } } @@ -333,7 +333,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const blender::VArray<T> &src, MutableSpan<T> dst) { - const int size = src.size(); + const int num = src.size(); blender::attribute_math::DefaultMixer<T> mixer(dst); for (const int i : dst.index_range()) { @@ -341,7 +341,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const int start_index = basis_cache.start_indices[i]; for (const int j : point_weights.index_range()) { - const int point_index = (start_index + j) % size; + const int point_index = (start_index + j) % num; mixer.mix_in(i, src[point_index], point_weights[j]); } } @@ -363,7 +363,7 @@ GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(this->evaluated_points_size()); + Array<T> values(this->evaluated_points_num()); interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } @@ -383,8 +383,8 @@ Span<float3> NURBSpline::evaluated_positions() const return evaluated_position_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); /* TODO: Avoid copying the evaluated data from the temporary array. */ VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span()); diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 122f7f6c059..c3cc268c81c 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -76,7 +76,7 @@ void PolySpline::mark_cache_invalid() length_cache_dirty_ = true; } -int PolySpline::evaluated_points_size() const +int PolySpline::evaluated_points_num() const { return this->size(); } diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 83772f153d9..3692a3cc4f7 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -77,7 +77,7 @@ static bool is_subdivision_evaluation_possible_on_gpu(void) return false; } - if (GPU_max_shader_storage_buffer_bindings() < MAX_GPU_SUBDIV_SSBOS) { + if (GPU_max_compute_shader_storage_blocks() < MAX_GPU_SUBDIV_SSBOS) { return false; } diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 3ce2b90e729..9a24b09fc66 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -178,7 +178,7 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries); * Convert given entry's size into human-readable strings. */ void BLI_filelist_entry_size_to_string(const struct stat *st, - uint64_t sz, + uint64_t st_size_fallback, bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN]); /** diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh index 6fe1c6513a2..f13641c3a65 100644 --- a/source/blender/blenlib/BLI_length_parameterize.hh +++ b/source/blender/blenlib/BLI_length_parameterize.hh @@ -17,7 +17,7 @@ namespace blender::length_parameterize { * Return the size of the necessary lengths array for a group of points, taking into account the * possible last cyclic segment. * - * \note This is the same as #bke::curves::curve_segment_size. + * \note This is the same as #bke::curves::curve_segment_num. */ inline int lengths_num(const int points_num, const bool cyclic) { diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 7d10e52f699..192ad482a69 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -7,6 +7,7 @@ * \ingroup bli */ +#include "BLI_math_base.h" #include "BLI_utildefines.h" #include "DNA_vec_types.h" diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 45abac33795..15926e8f2d2 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -307,7 +307,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, bool base_10) A * * Length of 7 is the maximum of the resulting string, for example, `-15.5K\0`. */ -void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format) ATTR_NONNULL(); +void BLI_str_format_decimal_unit(char dst[7], int number_to_format) ATTR_NONNULL(); /** * Compare two strings without regard to case. * @@ -343,8 +343,16 @@ int BLI_strcmp_ignore_pad(const char *str1, const char *str2, char pad) ATTR_WAR */ size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/** + * String case conversion, not affected by locale. + */ + void BLI_str_tolower_ascii(char *str, size_t len) ATTR_NONNULL(); void BLI_str_toupper_ascii(char *str, size_t len) ATTR_NONNULL(); + +char BLI_tolower_ascii(const char c); +char BLI_toupper_ascii(const char c); + /** * Strip white-space from end of the string. */ diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index d87a86ce696..904dea66f7a 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -77,17 +77,19 @@ Value parallel_reduce(IndexRange range, const Reduction &reduction) { #ifdef WITH_TBB - return tbb::parallel_reduce( - tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), - identity, - [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { - return function(IndexRange(subrange.begin(), subrange.size()), ident); - }, - reduction); + if (range.size() >= grain_size) { + return tbb::parallel_reduce( + tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), + identity, + [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { + return function(IndexRange(subrange.begin(), subrange.size()), ident); + }, + reduction); + } #else UNUSED_VARS(grain_size, reduction); - return function(range, identity); #endif + return function(range, identity); } /** diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index 76fc5b6342a..c6178ebb3a0 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -237,7 +237,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_ } void BLI_filelist_entry_size_to_string(const struct stat *st, - const uint64_t sz, + const uint64_t st_size_fallback, /* Used to change MB -> M, etc. - is that really useful? */ const bool UNUSED(compact), char r_size[FILELIST_DIRENTRY_SIZE_LEN]) @@ -247,7 +247,7 @@ void BLI_filelist_entry_size_to_string(const struct stat *st, * will buy us some time until files get bigger than 4GB or until * everyone starts using __USE_FILE_OFFSET64 or equivalent. */ - double size = (double)(st ? st->st_size : sz); + double size = (double)(st ? st->st_size : st_size_fallback); #ifdef WIN32 BLI_str_format_byte_unit(r_size, size, false); #else diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 74559751d91..976b9a5cd02 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -914,14 +914,22 @@ size_t BLI_strnlen(const char *s, const size_t maxlen) /** \name String Case Conversion * \{ */ +char BLI_tolower_ascii(const char c) +{ + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char BLI_toupper_ascii(const char c) +{ + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + void BLI_str_tolower_ascii(char *str, const size_t len) { size_t i; for (i = 0; (i < len) && str[i]; i++) { - if (str[i] >= 'A' && str[i] <= 'Z') { - str[i] += 'a' - 'A'; - } + str[i] = BLI_tolower_ascii(str[i]); } } @@ -930,9 +938,7 @@ void BLI_str_toupper_ascii(char *str, const size_t len) size_t i; for (i = 0; (i < len) && str[i]; i++) { - if (str[i] >= 'a' && str[i] <= 'z') { - str[i] -= 'a' - 'A'; - } + str[i] = BLI_toupper_ascii(str[i]); } } @@ -1149,7 +1155,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base BLI_strncpy(dst + len, base_10 ? units_base_10[order] : units_base_2[order], dst_len - len); } -void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format) +void BLI_str_format_decimal_unit(char dst[7], int number_to_format) { float number_to_format_converted = number_to_format; int order = 0; diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index bfd297214c0..4f6f4a5c413 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -663,7 +663,7 @@ TEST(path_util, PathContains) EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside")) << "A path contains its subdirectory"; EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside")) << "Windows paths should be supported as well"; @@ -672,7 +672,7 @@ TEST(path_util, PathContains) EXPECT_FALSE(BLI_path_contains("/some/path", "/")) << "Root directory not be contained in a subdirectory"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library")) << "Just sharing a suffix is not enough, path semantics should be followed"; EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) diff --git a/source/blender/blenlib/tests/BLI_string_test.cc b/source/blender/blenlib/tests/BLI_string_test.cc index 6c16af5767c..eaaa65dd39f 100644 --- a/source/blender/blenlib/tests/BLI_string_test.cc +++ b/source/blender/blenlib/tests/BLI_string_test.cc @@ -420,98 +420,98 @@ TEST(string, StrFormatByteUnits) EXPECT_STREQ("-8191.8472 PiB", size_str); } -/* BLI_str_format_attribute_domain_size */ -TEST(string, StrFormatAttributeDomainSize) +/* BLI_str_format_decimal_unit */ +TEST(string, StrFormatDecimalUnits) { char size_str[7]; int size; - BLI_str_format_attribute_domain_size(size_str, size = 0); + BLI_str_format_decimal_unit(size_str, size = 0); EXPECT_STREQ("0", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1); + BLI_str_format_decimal_unit(size_str, size = 1); EXPECT_STREQ("1", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10); + BLI_str_format_decimal_unit(size_str, size = 10); EXPECT_STREQ("10", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15); + BLI_str_format_decimal_unit(size_str, size = 15); EXPECT_STREQ("15", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100); + BLI_str_format_decimal_unit(size_str, size = 100); EXPECT_STREQ("100", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155); + BLI_str_format_decimal_unit(size_str, size = 155); EXPECT_STREQ("155", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000); + BLI_str_format_decimal_unit(size_str, size = 1000); EXPECT_STREQ("1.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1555); + BLI_str_format_decimal_unit(size_str, size = 1555); EXPECT_STREQ("1.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10000); + BLI_str_format_decimal_unit(size_str, size = 10000); EXPECT_STREQ("10.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15555); + BLI_str_format_decimal_unit(size_str, size = 15555); EXPECT_STREQ("15.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000); + BLI_str_format_decimal_unit(size_str, size = 100000); EXPECT_STREQ("100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000); + BLI_str_format_decimal_unit(size_str, size = 100000); EXPECT_STREQ("100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155555); + BLI_str_format_decimal_unit(size_str, size = 155555); EXPECT_STREQ("156K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000000); + BLI_str_format_decimal_unit(size_str, size = 1000000); EXPECT_STREQ("1.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1555555); + BLI_str_format_decimal_unit(size_str, size = 1555555); EXPECT_STREQ("1.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10000000); + BLI_str_format_decimal_unit(size_str, size = 10000000); EXPECT_STREQ("10.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15555555); + BLI_str_format_decimal_unit(size_str, size = 15555555); EXPECT_STREQ("15.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000000); + BLI_str_format_decimal_unit(size_str, size = 100000000); EXPECT_STREQ("100M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155555555); + BLI_str_format_decimal_unit(size_str, size = 155555555); EXPECT_STREQ("156M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000000000); + BLI_str_format_decimal_unit(size_str, size = 1000000000); EXPECT_STREQ("1.0B", size_str); /* Largest possible value. */ - BLI_str_format_attribute_domain_size(size_str, size = INT32_MAX); + BLI_str_format_decimal_unit(size_str, size = INT32_MAX); EXPECT_STREQ("2.1B", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -0); + BLI_str_format_decimal_unit(size_str, size = -0); EXPECT_STREQ("0", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1); + BLI_str_format_decimal_unit(size_str, size = -1); EXPECT_STREQ("-1", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10); + BLI_str_format_decimal_unit(size_str, size = -10); EXPECT_STREQ("-10", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15); + BLI_str_format_decimal_unit(size_str, size = -15); EXPECT_STREQ("-15", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100); + BLI_str_format_decimal_unit(size_str, size = -100); EXPECT_STREQ("-100", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155); + BLI_str_format_decimal_unit(size_str, size = -155); EXPECT_STREQ("-155", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000); + BLI_str_format_decimal_unit(size_str, size = -1000); EXPECT_STREQ("-1.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1555); + BLI_str_format_decimal_unit(size_str, size = -1555); EXPECT_STREQ("-1.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10000); + BLI_str_format_decimal_unit(size_str, size = -10000); EXPECT_STREQ("-10.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15555); + BLI_str_format_decimal_unit(size_str, size = -15555); EXPECT_STREQ("-15.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100000); + BLI_str_format_decimal_unit(size_str, size = -100000); EXPECT_STREQ("-100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155555); + BLI_str_format_decimal_unit(size_str, size = -155555); EXPECT_STREQ("-156K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000000); + BLI_str_format_decimal_unit(size_str, size = -1000000); EXPECT_STREQ("-1.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1555555); + BLI_str_format_decimal_unit(size_str, size = -1555555); EXPECT_STREQ("-1.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10000000); + BLI_str_format_decimal_unit(size_str, size = -10000000); EXPECT_STREQ("-10.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15555555); + BLI_str_format_decimal_unit(size_str, size = -15555555); EXPECT_STREQ("-15.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100000000); + BLI_str_format_decimal_unit(size_str, size = -100000000); EXPECT_STREQ("-100M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155555555); + BLI_str_format_decimal_unit(size_str, size = -155555555); EXPECT_STREQ("-156M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000000000); + BLI_str_format_decimal_unit(size_str, size = -1000000000); EXPECT_STREQ("-1.0B", size_str); /* Smallest possible value. */ - BLI_str_format_attribute_domain_size(size_str, size = -INT32_MAX); + BLI_str_format_decimal_unit(size_str, size = -INT32_MAX); EXPECT_STREQ("-2.1B", size_str); } diff --git a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc index 0ff488202c2..09bb1e7239f 100644 --- a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc +++ b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc @@ -57,23 +57,23 @@ static void str_ghash_tests(GHash *ghash, const char *id) printf("\n========== STARTING %s ==========\n", id); #ifdef TEXT_CORPUS_PATH - size_t sz = 0; + size_t data_size = 0; char *data; { struct stat st; if (stat(TEXT_CORPUS_PATH, &st) == 0) - sz = st.st_size; + data_size = st.st_size; } - if (sz != 0) { + if (data_size != 0) { FILE *f = fopen(TEXT_CORPUS_PATH, "r"); - data = (char *)MEM_mallocN(sz + 1, __func__); - if (fread(data, sizeof(*data), sz, f) != sz) { + data = (char *)MEM_mallocN(data_size + 1, __func__); + if (fread(data, sizeof(*data), data_size, f) != data_size) { printf("ERROR in reading file %s!", TEXT_CORPUS_PATH); MEM_freeN(data); data = BLI_strdup(words10k); } - data[sz] = '\0'; + data[data_size] = '\0'; fclose(f); } else { diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6ba97caa0ae..16bad11629a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1477,14 +1477,14 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) const int width = fd_data[0]; const int height = fd_data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + data = MEM_mallocN(data_size, __func__); if (data) { - BLI_assert((sz - sizeof(*data)) == + BLI_assert((data_size - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2))); data->width = width; data->height = height; - memcpy(data->rect, &fd_data[2], sz - sizeof(*data)); + memcpy(data->rect, &fd_data[2], data_size - sizeof(*data)); } } } @@ -3857,14 +3857,14 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) const int width = data[0]; const int height = data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - bfd->main->blen_thumb = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + bfd->main->blen_thumb = MEM_mallocN(data_size, __func__); - BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) == + BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2))); bfd->main->blen_thumb->width = width; bfd->main->blen_thumb->height = height; - memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb)); + memcpy(bfd->main->blen_thumb->rect, &data[2], data_size - sizeof(*bfd->main->blen_thumb)); } } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 7fd72fec3d0..de47fe49946 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -3034,5 +3034,16 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) brush->curves_sculpt_settings->points_per_curve = 8; } } + + /* UDIM Packing. */ + if (!DNA_struct_elem_find(fd->filesdna, "ImagePackedFile", "int", "tile_number")) { + for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + int view; + LISTBASE_FOREACH_INDEX (ImagePackedFile *, imapf, &ima->packedfiles, view) { + imapf->view = view; + imapf->tile_number = 1001; + } + } + } } } diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 573a740dac8..725751d15af 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -112,7 +112,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, double *X, *Y, *W; const unsigned int src_width = src->get_width(); const unsigned int src_height = src->get_height(); - unsigned int x, y, sz; + unsigned int x, y, src_dim_max; unsigned int i; float *buffer = src->get_buffer(); const uint8_t num_channels = src->get_num_channels(); @@ -202,10 +202,10 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, (void)0 /* Intermediate buffers. */ - sz = MAX2(src_width, src_height); - X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf"); - Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf"); - W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf"); + src_dim_max = MAX2(src_width, src_height); + X = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss X buf"); + Y = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss Y buf"); + W = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss W buf"); if (xy & 1) { /* H. */ int offset; for (y = 0; y < src_height; y++) { diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 4f562dd9804..0b9909a904b 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -95,7 +95,7 @@ typedef struct EEVEE_LightBake { /** Target layer to store the data to. */ int layer; /** Sample count for the convolution. */ - float samples_ct, invsamples_ct; + float samples_count, invsamples_count; /** Sampling bias during convolution step. */ float lod_factor; /** Max cube-map LOD to sample when convolving. */ @@ -282,14 +282,14 @@ static void irradiance_pool_size_get(int visibility_size, int total_samples, int (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); /* The irradiance itself take one layer, hence the +1 */ - int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + int layer_count = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); - int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + int texel_count = (int)ceilf((float)total_samples / (float)(layer_count - 1)); r_size[0] = visibility_size * - max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + max_ii(1, min_ii(texel_count, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); r_size[1] = visibility_size * - max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_ct; + max_ii(1, (texel_count / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[2] = layer_count; } static bool EEVEE_lightcache_validate(const LightCache *light_cache, diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 79b84bfba4c..a4bd789438d 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -64,7 +64,7 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD DRW_PASS_CREATE(psl->lookdev_diffuse_pass, state); grp = DRW_shgroup_create(sh, psl->lookdev_diffuse_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, -1.0f, false, false); DRW_shgroup_add_material_resources(grp, gpumat); DRW_shgroup_call(grp, sphere, NULL); } @@ -75,7 +75,7 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD DRW_PASS_CREATE(psl->lookdev_glossy_pass, state); grp = DRW_shgroup_create(sh, psl->lookdev_glossy_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, -1.0f, false, false); DRW_shgroup_add_material_resources(grp, gpumat); DRW_shgroup_call(grp, sphere, NULL); } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index a81d3e56673..efd27c19654 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -67,6 +67,7 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, EEVEE_Data *vedata, const int *ssr_id, const float *refract_depth, + const float alpha_clip_threshold, bool use_ssrefraction, bool use_alpha_blend) { @@ -91,6 +92,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block_ref(shgrp, "renderpass_block", &pd->renderpass_ubo); + DRW_shgroup_uniform_float_copy(shgrp, "alphaClipThreshold", alpha_clip_threshold); + DRW_shgroup_uniform_int_copy(shgrp, "outputSssId", 1); DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); if (use_diffuse || use_glossy || use_refract) { @@ -480,6 +483,8 @@ BLI_INLINE void material_shadow(EEVEE_Data *vedata, /* Shadow Pass */ const bool use_shadow_shader = ma->use_nodes && ma->nodetree && ELEM(ma->blend_shadow, MA_BS_CLIP, MA_BS_HASHED); + float alpha_clip_threshold = (ma->blend_shadow == MA_BS_CLIP) ? ma->alpha_threshold : -1.0f; + int mat_options = VAR_MAT_MESH | VAR_MAT_DEPTH; SET_FLAG_FROM_TEST(mat_options, use_shadow_shader, VAR_MAT_HASH); SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR); @@ -503,7 +508,8 @@ BLI_INLINE void material_shadow(EEVEE_Data *vedata, } else { *grp_p = grp = DRW_shgroup_create(sh, psl->shadow_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources( + grp, gpumat, sldata, vedata, NULL, NULL, alpha_clip_threshold, false, false); } DRW_shgroup_add_material_resources(grp, gpumat); @@ -533,6 +539,7 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, const bool use_ssrefract = use_gpumat && ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && ((effects->enabled_effects & EFFECT_REFRACT) != 0); const bool use_depth_shader = use_gpumat && ELEM(ma->blend_method, MA_BM_CLIP, MA_BM_HASHED); + float alpha_clip_threshold = (ma->blend_method == MA_BM_CLIP) ? ma->alpha_threshold : -1.0f; /* HACK: Assume the struct will never be smaller than our variations. * This allow us to only keep one ghash and avoid bigger keys comparisons/hashing. */ @@ -581,7 +588,8 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, } else { *grp_p = grp = DRW_shgroup_create(sh, depth_ps); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, false); + EEVEE_material_bind_resources( + grp, gpumat, sldata, vedata, NULL, NULL, alpha_clip_threshold, false, false); } DRW_shgroup_add_material_resources(grp, gpumat); @@ -630,8 +638,15 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata, } else { *grp_p = grp = DRW_shgroup_create(sh, shading_pass); - EEVEE_material_bind_resources( - grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, false); + EEVEE_material_bind_resources(grp, + gpumat, + sldata, + vedata, + &ssr_id, + &ma->refract_depth, + alpha_clip_threshold, + use_ssrefract, + false); } DRW_shgroup_add_material_resources(grp, gpumat); @@ -677,7 +692,7 @@ static EeveeMaterialCache material_transparent(EEVEE_Data *vedata, DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->transparent_pass); - EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, false, true); + EEVEE_material_bind_resources(grp, gpumat, sldata, vedata, NULL, NULL, -1.0f, false, true); DRW_shgroup_add_material_resources(grp, gpumat); cur_state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; @@ -699,7 +714,7 @@ static EeveeMaterialCache material_transparent(EEVEE_Data *vedata, psl->transparent_pass); EEVEE_material_bind_resources( - grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, use_ssrefract, true); + grp, gpumat, sldata, vedata, &ssr_id, &ma->refract_depth, -1.0f, use_ssrefract, true); DRW_shgroup_add_material_resources(grp, gpumat); cur_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 3a86640134c..6b9dc6fb017 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -1141,6 +1141,7 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, EEVEE_Data *vedata, const int *ssr_id, const float *refract_depth, + const float alpha_clip_threshold, bool use_ssrefraction, bool use_alpha_blend); /* eevee_lights.c */ diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl index fa94b5ed272..0f5290a7c07 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl @@ -5,7 +5,7 @@ #pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl) #pragma BLENDER_REQUIRE(renderpass_lib.glsl) -#ifdef USE_SHADER_TO_RGBA +#if defined(USE_SHADER_TO_RGBA) || defined(USE_ALPHA_BLEND) bool do_sss = false; bool do_ssr = false; #else @@ -309,16 +309,19 @@ vec4 closure_to_rgba(Closure closure) return vec4(closure.radiance, 1.0 - saturate(avg(closure.transmittance))); } -Closure closure_add(Closure cl1, Closure cl2) +Closure closure_add(inout Closure cl1, inout Closure cl2) { Closure cl; cl.radiance = cl1.radiance + cl2.radiance; cl.transmittance = cl1.transmittance + cl2.transmittance; cl.holdout = cl1.holdout + cl2.holdout; + /* Make sure each closure is only added once to the result. */ + cl1 = CLOSURE_DEFAULT; + cl2 = CLOSURE_DEFAULT; return cl; } -Closure closure_mix(Closure cl1, Closure cl2, float fac) +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac) { /* Weights have already been applied. */ return closure_add(cl1, cl2); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl index e450b8ad3c8..5e34d654cfd 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl @@ -92,7 +92,7 @@ vec4 closure_to_rgba(Closure closure) return vec4(0.0); } -Closure closure_mix(Closure cl1, Closure cl2, float fac) +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac) { Closure cl; cl.absorption = mix(cl1.absorption, cl2.absorption, fac); @@ -102,7 +102,7 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac) return cl; } -Closure closure_add(Closure cl1, Closure cl2) +Closure closure_add(inout Closure cl1, inout Closure cl2) { Closure cl; cl.absorption = cl1.absorption + cl2.absorption; diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl index 492b78a20b6..21d347942ca 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl @@ -49,7 +49,7 @@ struct Closure { #ifndef GPU_METAL /* Prototype */ Closure nodetree_exec(); -vec4 closure_to_rgba(Closure); +vec4 closure_to_rgba(Closure cl); void output_aov(vec4 color, float value, uint hash); vec3 coordinate_camera(vec3 P); vec3 coordinate_screen(vec3 P); @@ -87,8 +87,8 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection clearcoat, ClosureRefraction refraction); -Closure closure_add(Closure cl1, Closure cl2); -Closure closure_mix(Closure cl1, Closure cl2, float fac); +Closure closure_add(inout Closure cl1, inout Closure cl2); +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac); float ambient_occlusion_eval(vec3 normal, float distance, diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index c8eea8d7860..15c68dc5829 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -11,6 +11,8 @@ #pragma BLENDER_REQUIRE(surface_lib.glsl) #ifdef USE_ALPHA_HASH +/* A value of -1.0 will disable alpha clip and use alpha hash. */ +uniform float alphaClipThreshold; /* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */ float hash(vec2 a) @@ -76,9 +78,16 @@ void main() float opacity = saturate(1.0 - avg(cl.transmittance)); - /* Hashed Alpha Testing */ - if (opacity < hashed_alpha_threshold(worldPosition)) { - discard; + if (alphaClipThreshold == -1.0) { + /* NOTE: uniform control flow required for dFdx(). */ + if (opacity < hashed_alpha_threshold(worldPosition)) { + discard; + } + } + else { + if (opacity <= alphaClipThreshold) { + discard; + } } #endif } diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 1f2f7cb65cc..696e5d4c97b 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -112,6 +112,7 @@ GlobalData init_globals(void) surf.N = -surf.N; } # ifdef HAIR_SHADER + vec3 V = cameraVec(surf.P); /* Shade as a cylinder. */ vec3 B = normalize(cross(worldNormal, hairTangent)); float cos_theta; @@ -125,7 +126,10 @@ GlobalData init_globals(void) } float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta); - surf.T = hairTangent; + surf.curve_T = -hairTangent; + /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ + surf.curve_B = cross(V, surf.curve_T); + surf.curve_N = safe_normalize(cross(surf.curve_T, surf.curve_B)); surf.is_strand = true; surf.hair_time = hairTime; surf.hair_thickness = hairThickness; @@ -134,7 +138,7 @@ GlobalData init_globals(void) surf.barycentric_coords = hair_resolve_barycentric(hairBary); # endif # else - surf.T = vec3(0.0); + surf.curve_T = surf.curve_B = surf.curve_N = vec3(0.0); surf.is_strand = false; surf.hair_time = 0.0; surf.hair_thickness = 0.0; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index b574e8cdb4c..ce863bdf660 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -57,3 +57,23 @@ float F_eta(float a, float b) { return 0.0; } + +vec3 coordinate_camera(vec3 P) +{ + return vec3(0.0); +} + +vec3 coordinate_screen(vec3 P) +{ + return vec3(0.0); +} + +vec3 coordinate_reflect(vec3 P, vec3 N) +{ + return vec3(0.0); +} + +vec3 coordinate_incoming(vec3 P) +{ + return vec3(0.0); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl index abecb373995..3c5acf62e30 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl @@ -164,7 +164,7 @@ float attr_load_float(samplerBuffer cd_buf) * \{ */ # ifndef OBINFO_LIB -# error "draw_object_infos is mandatory for volume objects" +# error draw_object_infos is mandatory for volume objects # endif vec3 g_orco; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index 11f93ad0d14..708bd153e84 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -18,7 +18,7 @@ void main() ViewMatrixInverse[3].xyz, ViewMatrixInverse[2].xyz, interp.P, - T, + interp.curves_tangent, interp.curves_binormal, interp.curves_time, interp.curves_thickness, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 06191a5c007..277b2e35d8b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -9,6 +9,7 @@ float g_holdout; /* The Closure type is never used. Use float as dummy type. */ #define Closure float +#define CLOSURE_DEFAULT 0.0 /* Sampled closure parameters. */ ClosureDiffuse g_diffuse_data; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl index 0d8644c9901..30b48edaa78 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl @@ -42,6 +42,12 @@ void init_globals_curves() float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta); + /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ + vec3 V = cameraVec(g_data.P); + g_data.curve_T = -interp.curves_tangent; + g_data.curve_B = cross(V, g_data.curve_T); + g_data.curve_N = safe_normalize(cross(g_data.curve_T, g_data.curve_B)); + g_data.is_strand = true; g_data.hair_time = interp.curves_time; g_data.hair_thickness = interp.curves_thickness; @@ -94,6 +100,7 @@ void init_interface() interp.P = vec3(0.0); interp.N = vec3(0.0); interp.barycentric_coords = vec2(0.0); + interp.curves_tangent = vec3(0.0); interp.curves_binormal = vec3(0.0); interp.curves_time = 0.0; interp.curves_time_width = 0.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index cd297de5719..ccd67360073 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -54,6 +54,7 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") .smooth(Type::VEC3, "P") .smooth(Type::VEC3, "N") .smooth(Type::VEC2, "barycentric_coords") + .smooth(Type::VEC3, "curves_tangent") .smooth(Type::VEC3, "curves_binormal") .smooth(Type::FLOAT, "curves_time") .smooth(Type::FLOAT, "curves_time_width") diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 19afdb3de5a..c7ef8677336 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -61,10 +61,10 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, /* Depth need to be remapped to [0..1] range. */ pix_z = MEM_dupallocN(pix_z); - int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + int pix_num = rpass_z_src->rectx * rpass_z_src->recty; if (DRW_view_is_persp_get(view)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); } @@ -74,7 +74,7 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, float near = DRW_view_near_distance_get(view); float far = DRW_view_far_distance_get(view); float range_inv = 1.0f / fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (pix_z[i] + near) * range_inv; pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); } @@ -172,11 +172,11 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert GPU depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -192,7 +192,7 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh index cb84c7f14ad..358e6fd3bd9 100644 --- a/source/blender/draw/engines/image/image_instance_data.hh +++ b/source/blender/draw/engines/image/image_instance_data.hh @@ -75,8 +75,10 @@ struct IMAGE_InstanceData { TextureInfo &info = texture_infos[i]; const bool is_allocated = info.texture != nullptr; const bool is_visible = info.visible; - const bool should_be_freed = !is_visible && is_allocated; - const bool should_be_created = is_visible && !is_allocated; + const bool resolution_changed = assign_if_different(info.last_viewport_size, + float2(DRW_viewport_size_get())); + const bool should_be_freed = is_allocated && (!is_visible || resolution_changed); + const bool should_be_created = is_visible && (!is_allocated || resolution_changed); if (should_be_freed) { GPU_texture_free(info.texture); diff --git a/source/blender/draw/engines/image/image_texture_info.hh b/source/blender/draw/engines/image/image_texture_info.hh index cd51cdaff3c..9a75941c533 100644 --- a/source/blender/draw/engines/image/image_texture_info.hh +++ b/source/blender/draw/engines/image/image_texture_info.hh @@ -46,6 +46,8 @@ struct TextureInfo { */ GPUTexture *texture; + float2 last_viewport_size = float2(0.0f, 0.0f); + ~TextureInfo() { if (batch != nullptr) { diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 072d32c41cd..9531b0dd983 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -297,7 +297,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) } const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - int line_ct = gridlines * 4 + 2; + const int line_count = gridlines * 4 + 2; DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; state |= (grid_xray) ? DRW_STATE_DEPTH_ALWAYS : DRW_STATE_DEPTH_LESS_EQUAL; @@ -311,8 +311,8 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); - DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); - DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_count / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_count); } } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 1279682e899..e5dcf6c5624 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -115,11 +115,11 @@ static void workbench_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert ogl depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -135,7 +135,7 @@ static void workbench_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 2c902e9b627..ce7773e7439 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -124,12 +124,12 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, double noise_ofs; BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); float dim[3], step_length, max_slice; - float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); + float slice_count[3] = {fds->res[0], fds->res[1], fds->res[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, fds->slice_per_voxel)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); BKE_object_dimensions_get(ob, dim); - invert_v3(slice_ct); - mul_v3_v3(dim, slice_ct); + invert_v3(slice_count); + mul_v3_v3(dim, slice_count); step_length = len_v3(dim); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); @@ -273,12 +273,12 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, float step_length, max_slice; int resolution[3]; GPU_texture_get_mipmap_size(grid->texture, 0, resolution); - float slice_ct[3] = {resolution[0], resolution[1], resolution[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); - invert_v3(slice_ct); - mul_v3_v3(slice_ct, world_size); - step_length = len_v3(slice_ct); + float slice_count[3] = {resolution[0], resolution[1], resolution[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, 5.0f)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); + invert_v3(slice_count); + mul_v3_v3(slice_count, world_size); + step_length = len_v3(slice_count); /* Set uniforms. */ grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 712118e8282..572b87282e9 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -430,12 +430,12 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, DRW_shgroup_call_ex(shgroup, ob, NULL, geom, true, NULL) void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_num); /** * A count of 0 instance will use the default number of instance in the batch. */ void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num); void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 4567e470146..cb6006e303a 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -328,7 +328,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], bool do_final, bool do_uvedit, - bool use_subsurf_fdots, const Scene *scene, const struct ToolSettings *ts, bool use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 8a7b4fc9703..ec544d8e786 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -563,7 +563,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -687,7 +686,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshRenderData *mr = mesh_render_data_create( object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_subsurf_fdots = mr->me && mr->me->runtime.subsurf_face_dot_tags != nullptr; mr->use_final_mesh = do_final; #ifdef DEBUG_TIME @@ -919,7 +918,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -935,7 +933,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, obmat, do_final, do_uvedit, - use_subsurf_fdots, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 7b8f34b999c..ebcdabe4942 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -108,7 +108,7 @@ static void curve_eval_render_wire_verts_edges_len_get(const blender::bke::Curve const blender::VArray<bool> cyclic = curves.cyclic(); for (const int i : curves.curves_range()) { const IndexRange points = curves.evaluated_points_for_curve(i); - *r_edge_len += blender::bke::curves::curve_segment_size(points.size(), cyclic[i]); + *r_edge_len += blender::bke::curves::curve_segment_num(points.size(), cyclic[i]); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index f2742f3bcc7..1896df7c650 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -37,6 +37,7 @@ using blender::float3; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; /* ---------------------------------------------------------------------- */ @@ -147,46 +148,50 @@ static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_ca return; } - curves_cache.strands_len = curves.geometry.curve_size; - curves_cache.elems_len = curves.geometry.point_size + curves.geometry.curve_size; - curves_cache.point_len = curves.geometry.point_size; + curves_cache.strands_len = curves.geometry.curve_num; + curves_cache.elems_len = curves.geometry.point_num + curves.geometry.curve_num; + curves_cache.point_len = curves.geometry.point_num; } -static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, - GPUVertBufRaw &attr_step, - GPUVertBufRaw &length_step) +struct PositionAndParameter { + float3 position; + float parameter; +}; + +static void curves_batch_cache_fill_segments_proc_pos( + const Curves &curves_id, + MutableSpan<PositionAndParameter> posTime_data, + MutableSpan<float> hairLength_data) { /* TODO: use hair radius layer if available. */ - const int curve_size = curves_id.geometry.curve_size; + const int curve_num = curves_id.geometry.curve_num; const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_id.geometry); Span<float3> positions = curves.positions(); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range = curves.points_for_curve(i); + for (const int i_curve : IndexRange(curve_num)) { + const IndexRange points = curves.points_for_curve(i_curve); + + Span<float3> curve_positions = positions.slice(points); + MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - Span<float3> curve_positions = positions.slice(curve_range); float total_len = 0.0f; - float *seg_data_first; - for (const int i_curve : curve_positions.index_range()) { - float *seg_data = (float *)GPU_vertbuf_raw_step(&attr_step); - copy_v3_v3(seg_data, curve_positions[i_curve]); - if (i_curve == 0) { - seg_data_first = seg_data; - } - else { - total_len += blender::math::distance(curve_positions[i_curve - 1], - curve_positions[i_curve]); + for (const int i_point : curve_positions.index_range()) { + if (i_point > 0) { + total_len += blender::math::distance(curve_positions[i_point - 1], + curve_positions[i_point]); } - seg_data[3] = total_len; + curve_posTime_data[i_point].position = curve_positions[i_point]; + curve_posTime_data[i_point].parameter = total_len; } + hairLength_data[i_curve] = total_len; + /* Assign length value. */ - *(float *)GPU_vertbuf_raw_step(&length_step) = total_len; if (total_len > 0.0f) { + const float factor = 1.0f / total_len; /* Divide by total length to have a [0-1] number. */ - for ([[maybe_unused]] const int i_curve : curve_positions.index_range()) { - seg_data_first[3] /= total_len; - seg_data_first += 4; + for (const int i_point : curve_positions.index_range()) { + curve_posTime_data[i_point].parameter *= factor; } } } @@ -199,26 +204,26 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { /* Initialize vertex format. */ GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "pos"); cache.proc_point_buf = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len); - GPUVertBufRaw point_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_point_buf, pos_id, &point_step); + MutableSpan posTime_data{ + reinterpret_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), + cache.point_len}; GPUVertFormat length_format = {0}; - uint length_id = GPU_vertformat_attr_add( - &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); cache.proc_length_buf = GPU_vertbuf_create_with_format(&length_format); GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len); - GPUVertBufRaw length_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_length_buf, length_id, &length_step); + MutableSpan hairLength_data{ + reinterpret_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), cache.strands_len}; - curves_batch_cache_fill_segments_proc_pos(curves, point_step, length_step); + curves_batch_cache_fill_segments_proc_pos(curves, posTime_data, hairLength_data); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache.proc_point_buf); @@ -307,7 +312,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, const int res, GPUIndexBufBuilder &elb) { - const int curves_num = curves.geometry.curve_size; + const int curves_num = curves.geometry.curve_num; uint curr_point = 0; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 917216b92e6..7dc0244275d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -708,12 +708,18 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, case CD_MCOL: case CD_PROP_BYTE_COLOR: case CD_PROP_COLOR: { + /* First check Color attributes, when not found check mesh attributes. Geometry nodes + * can generate those layers. */ int vcol_bit = mesh_cd_calc_gpu_layers_vcol_used(&me_query, cd_vdata, cd_ldata, name); if (vcol_bit != -1) { cd_used.vcol |= 1UL << (uint)vcol_bit; + break; } + if (layer != -1 && domain != ATTR_DOMAIN_NUM) { + drw_mesh_attributes_add_request(attributes, type, layer, domain); + } break; } case CD_PROP_FLOAT3: @@ -1231,11 +1237,11 @@ static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Me &me_query.id, render, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); if (active_i >= 0) { - cache->cd_used.vcol |= 1UL << (uint)active_i; + cache->cd_needed.vcol |= 1UL << (uint)active_i; } if (render_i >= 0) { - cache->cd_used.vcol |= 1UL << (uint)render_i; + cache->cd_needed.vcol |= 1UL << (uint)render_i; } } @@ -2128,8 +2134,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX); - const bool use_subsurf_fdots = me->runtime.subsurf_face_dot_tags != NULL; - if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, @@ -2142,7 +2146,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, false, true, - false, scene, ts, true); @@ -2160,7 +2163,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, false, false, - use_subsurf_fdots, scene, ts, true); @@ -2178,7 +2180,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, true, false, - use_subsurf_fdots, ts, use_hide); } @@ -2199,7 +2200,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, true, false, - use_subsurf_fdots, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 7d245f4158b..8eb0509d615 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1904,7 +1904,6 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool /*use_subsurf_fdots*/, const ToolSettings *ts, const bool /*use_hide*/, OpenSubdiv_EvaluatorCache *evaluator_cache) @@ -2103,7 +2102,6 @@ void DRW_create_subdivision(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const ToolSettings *ts, const bool use_hide) { @@ -2128,7 +2126,6 @@ void DRW_create_subdivision(const Scene *scene, obmat, do_final, do_uvedit, - use_subsurf_fdots, ts, use_hide, g_evaluator_cache)) { diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 88118361115..2edf596ac63 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -209,7 +209,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; + float hair_rad_shape = 0.0f; float hair_rad_root = 0.005f; float hair_rad_tip = 0.0f; bool hair_close_tip = true; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index bbf40b72c5a..e26502bb066 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -530,7 +530,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s dst->view_data_active = dst->vmempool->view_data[view]; dst->resource_handle = 0; dst->pass_handle = 0; - dst->primary_view_ct = 0; + dst->primary_view_num = 0; drw_viewport_data_reset(dst->vmempool); @@ -1314,7 +1314,7 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d); - if (G.is_rendering) { + if (G.is_rendering && U.experimental.use_draw_manager_acquire_lock) { return; } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 7a9585262ff..2d0837370b2 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -617,7 +617,7 @@ typedef struct DRWManager { DRWView *view_default; DRWView *view_active; DRWView *view_previous; - uint primary_view_ct; + uint primary_view_num; /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ ViewInfos view_storage_cpy; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index b5432da0957..b0d8017940f 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -937,25 +937,25 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, } void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct) + DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct); + drw_command_draw_range(shgroup, geom, handle, v_sta, v_num); } void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct) + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); + drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_num); } void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, @@ -1905,8 +1905,8 @@ DRWView *DRW_view_create(const float viewmat[4][4], { DRWView *view = BLI_memblock_alloc(DST.vmempool->views); - if (DST.primary_view_ct < MAX_CULLED_VIEWS) { - view->culling_mask = 1u << DST.primary_view_ct++; + if (DST.primary_view_num < MAX_CULLED_VIEWS) { + view->culling_mask = 1u << DST.primary_view_num++; } else { BLI_assert(0); diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 6f571084620..2d24d07e037 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -194,7 +194,6 @@ void DRW_create_subdivision(const struct Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const ToolSettings *ts, const bool use_hide); diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc index 0e55d28f6df..3dc28dc9a9a 100644 --- a/source/blender/draw/intern/draw_view_data.cc +++ b/source/blender/draw/intern/draw_view_data.cc @@ -88,7 +88,7 @@ void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewpo }); } -static void draw_viewport_engines_data_clear(ViewportEngineData *data) +static void draw_viewport_engines_data_clear(ViewportEngineData *data, bool clear_instance_data) { DrawEngineType *engine_type = data->engine_type->draw_engine; const DrawEngineDataSize *data_size = engine_type->vedata_size; @@ -103,7 +103,7 @@ static void draw_viewport_engines_data_clear(ViewportEngineData *data) MEM_SAFE_FREE(data->stl->storage[i]); } - if (data->instance_data) { + if (clear_instance_data && data->instance_data) { BLI_assert(engine_type->instance_free != nullptr); engine_type->instance_free(data->instance_data); data->instance_data = nullptr; @@ -120,7 +120,7 @@ static void draw_viewport_engines_data_clear(ViewportEngineData *data) } } -static void draw_view_data_clear(DRWViewData *view_data) +static void draw_view_data_clear(DRWViewData *view_data, bool free_instance_data) { GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.default_fb); GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.overlay_fb); @@ -137,23 +137,20 @@ static void draw_view_data_clear(DRWViewData *view_data) GPU_TEXTURE_FREE_SAFE(view_data->dtxl.depth_in_front); for (ViewportEngineData &engine : view_data->engines) { - draw_viewport_engines_data_clear(&engine); + draw_viewport_engines_data_clear(&engine, free_instance_data); } - - view_data->texture_list_size[0] = view_data->texture_list_size[1] = 0; - view_data->cache_time = 0.0f; } void DRW_view_data_free(DRWViewData *view_data) { - draw_view_data_clear(view_data); + draw_view_data_clear(view_data, true); delete view_data; } void DRW_view_data_texture_list_size_validate(DRWViewData *view_data, const int size[2]) { if (!equals_v2v2_int(view_data->texture_list_size, size)) { - draw_view_data_clear(view_data); + draw_view_data_clear(view_data, false); copy_v2_v2_int(view_data->texture_list_size, size); } } @@ -195,7 +192,7 @@ void DRW_view_data_free_unused(DRWViewData *view_data) { for (ViewportEngineData &engine : view_data->engines) { if (view_data->enabled_engines.first_index_of_try(&engine) == -1) { - draw_viewport_engines_data_clear(&engine); + draw_viewport_engines_data_clear(&engine, false); } } } diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 58d093c678d..786204a52ed 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -168,11 +168,11 @@ void draw_keyframe_shape(float x, /* Common attributes shared between the draw calls. */ typedef struct DrawKeylistUIData { float alpha; - float icon_sz; - float half_icon_sz; - float smaller_sz; - float ipo_sz; - float gpencil_sz; + float icon_size; + float half_icon_size; + float smaller_size; + float ipo_size; + float gpencil_size; float screenspace_margin; float sel_color[4]; float unsel_color[4]; @@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, /* TODO: allow this opacity factor to be themed? */ ctx->alpha = channel_locked ? 0.25f : 1.0f; - ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; - ctx->half_icon_sz = 0.5f * ctx->icon_sz; - ctx->smaller_sz = 0.35f * ctx->icon_sz; - ctx->ipo_sz = 0.1f * ctx->icon_sz; - ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->icon_size = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_size = 0.5f * ctx->icon_size; + ctx->smaller_size = 0.35f * ctx->icon_size; + ctx->ipo_size = 0.1f * ctx->icon_size; + ctx->gpencil_size = ctx->smaller_size * 0.8f; ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; @@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), - .ymin = ypos - ctx->gpencil_sz, - .ymax = ypos + ctx->gpencil_sz, + .ymin = ypos - ctx->gpencil_size, + .ymax = ypos + ctx->gpencil_size, }, true, 0.25f * (float)UI_UNIT_X, @@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->smaller_sz, - .ymax = ypos + ctx->smaller_sz, + .ymin = ypos - ctx->smaller_size, + .ymax = ypos + ctx->smaller_size, }, true, 3.0f, @@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->half_icon_sz, - .ymax = ypos + ctx->half_icon_sz, + .ymin = ypos - ctx->half_icon_size, + .ymax = ypos + ctx->half_icon_size, }, true, 3.0f, @@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->ipo_sz, - .ymax = ypos + ctx->ipo_sz, + .ymin = ypos - ctx->ipo_size, + .ymax = ypos + ctx->ipo_size, }, true, 3.0f, @@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx, draw_keyframe_shape(ak->cfra, ypos, - ctx->icon_sz, + ctx->icon_size, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 7d9e663c444..552ef1d96c8 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -20,7 +20,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi MutableSpan<float3> positions = curves.positions_for_write(); float *radius_data = (float *)CustomData_add_layer_named( - &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius"); MutableSpan<float> radii{radius_data, curves.points_num()}; for (const int i : offsets.index_range()) { diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index c367072e6e7..e9fcd2bd5fe 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -164,8 +164,16 @@ void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); +/** + * Return the File Browser area in which \a file_operator is active. + */ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win, const struct wmOperator *file_operator); +/** + * Check if there is any area in \a win that acts as a modal File Browser (#SpaceFile.op is set) + * and return it. + */ +struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win); /* TODO: Maybe we should move this to BLI? * On the other hand, it's using defines from space-file area, so not sure... */ diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 54d36f44f82..80a75da27f8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -266,6 +266,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene, int *r_verts_len); void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy); void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l); struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d1a6501408c..ea1095b26ff 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -164,7 +164,7 @@ DEF_ICON(NLA) DEF_ICON(PREFERENCES) DEF_ICON(TIME) DEF_ICON(NODETREE) -DEF_ICON_BLANK(181) +DEF_ICON(GEOMETRY_NODES) DEF_ICON(CONSOLE) DEF_ICON_BLANK(183) DEF_ICON(TRACKER) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 301171a284d..a996d1e0e76 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -184,50 +184,50 @@ enum { /** #uiBut.flag general state flags. */ enum { - /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */ - UI_BUT_ICON_SUBMENU = 1 << 7, - UI_BUT_ICON_PREVIEW = 1 << 8, + /* WARNING: the first 10 flags are internal (see #UI_SELECT definition). */ + UI_BUT_ICON_SUBMENU = 1 << 10, + UI_BUT_ICON_PREVIEW = 1 << 11, - UI_BUT_NODE_LINK = 1 << 9, - UI_BUT_NODE_ACTIVE = 1 << 10, - UI_BUT_DRAG_LOCK = 1 << 11, + UI_BUT_NODE_LINK = 1 << 12, + UI_BUT_NODE_ACTIVE = 1 << 13, + UI_BUT_DRAG_LOCK = 1 << 14, /** Grayed out and un-editable. */ - UI_BUT_DISABLED = 1 << 12, + UI_BUT_DISABLED = 1 << 15, - UI_BUT_ANIMATED = 1 << 13, - UI_BUT_ANIMATED_KEY = 1 << 14, - UI_BUT_DRIVEN = 1 << 15, - UI_BUT_REDALERT = 1 << 16, + UI_BUT_ANIMATED = 1 << 16, + UI_BUT_ANIMATED_KEY = 1 << 17, + UI_BUT_DRIVEN = 1 << 18, + UI_BUT_REDALERT = 1 << 19, /** Grayed out but still editable. */ - UI_BUT_INACTIVE = 1 << 17, - UI_BUT_LAST_ACTIVE = 1 << 18, - UI_BUT_UNDO = 1 << 19, - UI_BUT_IMMEDIATE = 1 << 20, - UI_BUT_NO_UTF8 = 1 << 21, + UI_BUT_INACTIVE = 1 << 20, + UI_BUT_LAST_ACTIVE = 1 << 21, + UI_BUT_UNDO = 1 << 22, + UI_BUT_IMMEDIATE = 1 << 23, + UI_BUT_NO_UTF8 = 1 << 24, /** For popups, pressing return activates this button, overriding the highlighted button. * For non-popups this is just used as a display hint for the user to let them * know the action which is activated when pressing return (file selector for eg). */ - UI_BUT_ACTIVE_DEFAULT = 1 << 23, + UI_BUT_ACTIVE_DEFAULT = 1 << 25, /** This but is "inside" a list item (currently used to change theme colors). */ - UI_BUT_LIST_ITEM = 1 << 24, + UI_BUT_LIST_ITEM = 1 << 26, /** edit this button as well as the active button (not just dragging) */ - UI_BUT_DRAG_MULTI = 1 << 25, + UI_BUT_DRAG_MULTI = 1 << 27, /** Use for popups to start editing the button on initialization. */ - UI_BUT_ACTIVATE_ON_INIT = 1 << 26, + UI_BUT_ACTIVATE_ON_INIT = 1 << 28, /** #uiBut.str contains #UI_SEP_CHAR, used for key shortcuts */ - UI_BUT_HAS_SEP_CHAR = 1 << 27, + UI_BUT_HAS_SEP_CHAR = 1 << 29, /** Don't run updates while dragging (needed in rare cases). */ - UI_BUT_UPDATE_DELAY = 1 << 28, + UI_BUT_UPDATE_DELAY = 1u << 30, /** When widget is in text-edit mode, update value on each char stroke. */ - UI_BUT_TEXTEDIT_UPDATE = 1 << 29, + UI_BUT_TEXTEDIT_UPDATE = 1ul << 31, /** Show 'x' icon to clear/unlink value of text or search button. */ - UI_BUT_VALUE_CLEAR = 1 << 30, + UI_BUT_VALUE_CLEAR = 1ul << 32, /** RNA property of the button is overridden from linked reference data. */ - UI_BUT_OVERRIDDEN = 1u << 31u, + UI_BUT_OVERRIDDEN = 1ul << 33, }; /* Default font size for normal text. */ @@ -462,7 +462,7 @@ enum { void UI_draw_widget_scroll(struct uiWidgetColors *wcol, const struct rcti *rect, const struct rcti *slider, - int state); + uint64_t state); /** * Shortening string helper. @@ -887,9 +887,9 @@ uiBut *UI_but_active_drop_name_button(const struct bContext *C); bool UI_but_active_drop_name(const struct bContext *C); bool UI_but_active_drop_color(struct bContext *C); -void UI_but_flag_enable(uiBut *but, int flag); -void UI_but_flag_disable(uiBut *but, int flag); -bool UI_but_flag_is_set(uiBut *but, int flag); +void UI_but_flag_enable(uiBut *but, uint64_t flag); +void UI_but_flag_disable(uiBut *but, uint64_t flag); +bool UI_but_flag_is_set(uiBut *but, uint64_t flag); void UI_but_drawflag_enable(uiBut *but, int flag); void UI_but_drawflag_disable(uiBut *but, int flag); @@ -1682,7 +1682,7 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + uint64_t state, uint8_t name_prefix_offset); /** diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index b7098c26bcd..effd3a64fcd 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -848,7 +848,7 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) BLI_assert(oldbut->active); /* flags from the buttons we want to refresh, may want to add more here... */ - const int flag_copy = UI_BUT_REDALERT | UI_HAS_ICON | UI_SELECT_DRAW; + const uint64_t flag_copy = UI_BUT_REDALERT | UI_HAS_ICON | UI_SELECT_DRAW; const int drawflag_copy = 0; /* None currently. */ /* still stuff needs to be copied */ @@ -991,7 +991,7 @@ static bool ui_but_update_from_old_block(const bContext *C, found_active = true; } else { - int flag_copy = UI_BUT_DRAG_MULTI; + uint64_t flag_copy = UI_BUT_DRAG_MULTI; /* Stupid special case: The active button may be inside (as in, overlapped on top) a tree-row * button which we also want to keep highlighted then. */ @@ -4219,7 +4219,7 @@ static uiBut *ui_def_but(uiBlock *block, return but; } -void ui_def_but_icon(uiBut *but, const int icon, const int flag) +void ui_def_but_icon(uiBut *but, const int icon, const uint64_t flag) { if (icon) { ui_icon_ensure_deferred(static_cast<const bContext *>(but->block->evil_C), @@ -5806,17 +5806,17 @@ void UI_block_flag_disable(uiBlock *block, int flag) block->flag &= ~flag; } -void UI_but_flag_enable(uiBut *but, int flag) +void UI_but_flag_enable(uiBut *but, uint64_t flag) { but->flag |= flag; } -void UI_but_flag_disable(uiBut *but, int flag) +void UI_but_flag_disable(uiBut *but, uint64_t flag) { but->flag &= ~flag; } -bool UI_but_flag_is_set(uiBut *but, int flag) +bool UI_but_flag_is_set(uiBut *but, uint64_t flag) { return (but->flag & flag) != 0; } diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index e838ce37d8e..b77d48552c7 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -144,7 +144,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but) return; } - const int flag = but_anim->flag; + const uint64_t flag = but_anim->flag; if (flag & UI_BUT_DRIVEN) { but->icon = ICON_DECORATE_DRIVER; @@ -162,7 +162,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but) but->icon = ICON_DECORATE; } - const int flag_copy = (UI_BUT_DISABLED | UI_BUT_INACTIVE); + const uint64_t flag_copy = (UI_BUT_DISABLED | UI_BUT_INACTIVE); but->flag = (but->flag & ~flag_copy) | (flag & flag_copy); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index c09ff68bbca..e8c5f2bb322 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -74,7 +74,8 @@ enum { UI_SELECT_DRAW = (1 << 5), /** Property search filter is active and the button does not match. */ UI_SEARCH_FILTER_NO_MATCH = (1 << 6), - /* WARNING: rest of #uiBut.flag in UI_interface.h */ + + /* WARNING: rest of #uiBut.flag in UI_interface.h (starting at `1 << 10`). */ }; /** #uiBut.dragflag */ @@ -147,7 +148,8 @@ struct uiBut { /** Pointer back to the layout item holding this button. */ uiLayout *layout; - int flag, drawflag; + uint64_t flag; + int drawflag; eButType type; eButPointerType pointype; short bit, bitnr, retval, strwidth, alignnr; @@ -709,7 +711,7 @@ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) extern void ui_but_set_string_interactive(struct bContext *C, uiBut *but, const char *value); extern uiBut *ui_but_drag_multi_edit_get(uiBut *but); -void ui_def_but_icon(uiBut *but, int icon, int flag); +void ui_def_but_icon(uiBut *but, int icon, uint64_t flag); /** * Avoid using this where possible since it's better not to ask for an icon in the first place. */ @@ -1216,14 +1218,14 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + uint64_t state, uiMenuItemSeparatorType separator_type, int *r_xmax); void ui_draw_preview_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + uint64_t state, eFontStyle_Align text_align); /** * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on @@ -1424,8 +1426,8 @@ uiBlock *ui_block_find_mouse_over(const struct ARegion *region, bool only_clip); uiBut *ui_region_find_first_but_test_flag(struct ARegion *region, - int flag_include, - int flag_exclude); + uint64_t flag_include, + uint64_t flag_exclude); uiBut *ui_region_find_active_but(struct ARegion *region) ATTR_WARN_UNUSED_RESULT; bool ui_region_contains_point_px(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c1bb2ed6d18..198195f1d9a 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -5316,7 +5316,7 @@ static void ui_item_align(uiLayout *litem, short nr) } } -static void ui_item_flag(uiLayout *litem, int flag) +static void ui_item_flag(uiLayout *litem, uint64_t flag) { LISTBASE_FOREACH_BACKWARD (uiItem *, item, &litem->items) { if (item->type == ITEM_BUTTON) { diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 337b2852d57..b1619df7e4c 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -699,7 +699,9 @@ uiBut *ui_region_find_active_but(ARegion *region) return nullptr; } -uiBut *ui_region_find_first_but_test_flag(ARegion *region, int flag_include, int flag_exclude) +uiBut *ui_region_find_first_but_test_flag(ARegion *region, + uint64_t flag_include, + uint64_t flag_exclude) { LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index bc497e2647c..016abb2f021 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -58,7 +58,7 @@ struct uiSearchItems { char **names; void **pointers; int *icons; - int *states; + uint64_t *states; uint8_t *name_prefix_offsets; /** Is there any item with an icon? */ @@ -94,7 +94,7 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + uint64_t state, const uint8_t name_prefix_offset) { /* hijack for autocomplete */ @@ -556,7 +556,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) if (data->preview) { /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const uint64_t state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; /* ensure icon is up-to-date */ ui_icon_ensure_deferred(C, data->items.icons[a], data->preview); @@ -590,7 +590,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0; /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const uint64_t state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; char *name = data->items.names[a]; int icon = data->items.icons[a]; char *name_sep_test = nullptr; @@ -847,7 +847,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.names = (char **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.pointers = (void **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.icons = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); - data->items.states = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); + data->items.states = (uint64_t *)MEM_callocN(data->items.maxitem * sizeof(uint64_t), __func__); data->items.name_prefix_offsets = nullptr; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { data->items.names[i] = (char *)MEM_callocN(data->items.maxstrlen + 1, __func__); @@ -913,7 +913,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* widget itself */ /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */ { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const uint64_t state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; wmOperatorType *ot = static_cast<wmOperatorType *>(data->items.pointers[a]); char text_pre[128]; diff --git a/source/blender/editors/interface/interface_template_search_menu.cc b/source/blender/editors/interface/interface_template_search_menu.cc index c3021028b97..854e3dace3b 100644 --- a/source/blender/editors/interface/interface_template_search_menu.cc +++ b/source/blender/editors/interface/interface_template_search_menu.cc @@ -87,7 +87,7 @@ struct MenuSearch_Item { /** Support a single level sub-menu nesting (for operator buttons that expand). */ const char *drawstr_submenu; int icon; - int state; + uint64_t state; MenuSearch_Parent *menu_parent; MenuType *mt; diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index 993ccdf92f7..e68d8f12a1f 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -494,7 +494,7 @@ static bool add_collection_search_item(CollItemSearch *cis, cis->name, cis->data, cis->iconid, - cis->has_sep_char ? (int)UI_BUT_HAS_SEP_CHAR : 0, + cis->has_sep_char ? (uint64_t)UI_BUT_HAS_SEP_CHAR : 0, name_prefix_offset); } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 98ecf91adbc..2801930ff1d 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -256,10 +256,10 @@ typedef struct uiWidgetType { /* converted colors for state */ uiWidgetColors wcol; - void (*state)(struct uiWidgetType *, int state, int drawflag, eUIEmbossType emboss); - void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); + void (*state)(struct uiWidgetType *, uint64_t state, int drawflag, eUIEmbossType emboss); + void (*draw)(uiWidgetColors *, rcti *, uint64_t state, int roundboxalign, const float zoom); void (*custom)( - uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); + uiBut *, uiWidgetColors *, rcti *, uint64_t state, int roundboxalign, const float zoom); void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *); } uiWidgetType; @@ -1289,7 +1289,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol) #define PREVIEW_PAD 4 -static float widget_alpha_factor(const int state) +static float widget_alpha_factor(const uint64_t state) { if (state & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { if (state & UI_SEARCH_FILTER_NO_MATCH) { @@ -2446,7 +2446,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, * \{ */ /* put all widget colors on half alpha, use local storage */ -static void ui_widget_color_disabled(uiWidgetType *wt, const int state) +static void ui_widget_color_disabled(uiWidgetType *wt, const uint64_t state) { static uiWidgetColors wcol_theme_s; @@ -2473,7 +2473,7 @@ static void widget_active_color(uiWidgetColors *wcol) } static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state, - int state, + uint64_t state, int drawflag, const eUIEmbossType emboss) { @@ -2501,7 +2501,7 @@ static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wco } /* copy colors from theme, and set changes in it based on state */ -static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state(uiWidgetType *wt, uint64_t state, int drawflag, eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; @@ -2600,7 +2600,10 @@ static float widget_radius_from_rcti(const rcti *rect, const uiWidgetColors *wco * \{ */ /* sliders use special hack which sets 'item' as inner when drawing filling */ -static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_numslider(uiWidgetType *wt, + uint64_t state, + int drawflag, + eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; @@ -2624,7 +2627,7 @@ static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eU /* labels use theme colors for text */ static void widget_state_option_menu(uiWidgetType *wt, - int state, + uint64_t state, int drawflag, eUIEmbossType emboss) { @@ -2644,7 +2647,7 @@ static void widget_state_option_menu(uiWidgetType *wt, } static void widget_state_nothing(uiWidgetType *wt, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(drawflag), eUIEmbossType UNUSED(emboss)) { @@ -2653,7 +2656,7 @@ static void widget_state_nothing(uiWidgetType *wt, /* special case, button that calls pulldown */ static void widget_state_pulldown(uiWidgetType *wt, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(drawflag), eUIEmbossType UNUSED(emboss)) { @@ -2662,7 +2665,7 @@ static void widget_state_pulldown(uiWidgetType *wt, /* special case, pie menu items */ static void widget_state_pie_menu_item(uiWidgetType *wt, - int state, + uint64_t state, int UNUSED(drawflag), eUIEmbossType UNUSED(emboss)) { @@ -2697,7 +2700,7 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, /* special case, menu items */ static void widget_state_menu_item(uiWidgetType *wt, - int state, + uint64_t state, int UNUSED(drawflag), eUIEmbossType UNUSED(emboss)) { @@ -2787,7 +2790,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r } static void widget_menu_back( - uiWidgetColors *wcol, rcti *rect, int flag, int direction, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t flag, int direction, const float zoom) { uiWidgetBase wtb; int roundboxalign = UI_CNR_ALL; @@ -3321,8 +3324,12 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) #define NUM_BUT_PADDING_FACTOR 0.425f -static void widget_numbut_draw( - uiWidgetColors *wcol, rcti *rect, const float zoom, int state, int roundboxalign, bool emboss) +static void widget_numbut_draw(uiWidgetColors *wcol, + rcti *rect, + const float zoom, + uint64_t state, + int roundboxalign, + bool emboss) { const float rad = widget_radius_from_zoom(zoom, wcol); const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f); @@ -3423,13 +3430,13 @@ static void widget_numbut_draw( } static void widget_numbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t state, int roundboxalign, const float zoom) { widget_numbut_draw(wcol, rect, zoom, state, roundboxalign, false); } static void widget_menubut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t UNUSED(state), int roundboxalign, const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3454,7 +3461,7 @@ static void widget_menubut( static void widget_menubut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; @@ -3476,14 +3483,17 @@ static void widget_menubut_embossn(const uiBut *UNUSED(but), static void widget_numbut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int state, + uint64_t state, int roundboxalign, const float zoom) { widget_numbut_draw(wcol, rect, zoom, state, roundboxalign, true); } -void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state) +void UI_draw_widget_scroll(uiWidgetColors *wcol, + const rcti *rect, + const rcti *slider, + uint64_t state) { uiWidgetBase wtb; bool outline = false; @@ -3573,7 +3583,7 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s static void widget_scroll(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int state, + uint64_t state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3635,7 +3645,7 @@ static void widget_scroll(uiBut *but, static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int roundboxalign, const float zoom) { @@ -3669,7 +3679,7 @@ static void widget_progressbar(uiBut *but, static void widget_treerow_exec(uiWidgetColors *wcol, rcti *rect, - int state, + uint64_t state, int UNUSED(roundboxalign), int indentation, const float zoom) @@ -3690,8 +3700,12 @@ static void widget_treerow_exec(uiWidgetColors *wcol, BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); } -static void widget_treerow( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_treerow(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + uint64_t state, + int roundboxalign, + const float zoom) { uiButTreeRow *tree_row = (uiButTreeRow *)but; BLI_assert(but->type == UI_BTYPE_TREEROW); @@ -3701,7 +3715,7 @@ static void widget_treerow( static void widget_nodesocket(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3737,8 +3751,12 @@ static void widget_nodesocket(uiBut *but, copy_v3_v3_uchar(wcol->outline, old_outline); } -static void widget_numslider( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numslider(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + uint64_t state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb, wtb1; widget_init(&wtb); @@ -3850,8 +3868,12 @@ static void widget_numslider( /* I think 3 is sufficient border to indicate keyed status */ #define SWATCH_KEYED_BORDER 3 -static void widget_swatch( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_swatch(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + uint64_t state, + int roundboxalign, + const float zoom) { BLI_assert(but->type == UI_BTYPE_COLOR); uiButColor *color_but = (uiButColor *)but; @@ -3938,7 +3960,7 @@ static void widget_swatch( static void widget_unitvec(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -3946,8 +3968,12 @@ static void widget_unitvec(uiBut *but, ui_draw_but_UNITVEC(but, wcol, rect, rad); } -static void widget_icon_has_anim( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_icon_has_anim(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + uint64_t state, + int roundboxalign, + const float zoom) { if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && but->emboss != UI_EMBOSS_NONE) { @@ -3971,7 +3997,7 @@ static void widget_icon_has_anim( } static void widget_textbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t state, int roundboxalign, const float zoom) { if (state & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); @@ -3989,7 +4015,7 @@ static void widget_textbut( static void widget_preview_tile(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3999,7 +4025,7 @@ static void widget_preview_tile(uiBut *but, } static void widget_menuiconbut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t UNUSED(state), int roundboxalign, const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4012,7 +4038,7 @@ static void widget_menuiconbut( } static void widget_pulldownbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t state, int roundboxalign, const float zoom) { float back[4]; UI_GetThemeColor4fv(TH_BACK, back); @@ -4042,7 +4068,7 @@ static void widget_pulldownbut( static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4066,7 +4092,7 @@ static void widget_menu_itembut(uiWidgetColors *wcol, static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4088,7 +4114,7 @@ static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4114,7 +4140,7 @@ static void widget_menu_radial_itembut(uiBut *but, static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4131,7 +4157,7 @@ static void widget_list_itembut(uiWidgetColors *wcol, static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, - int state, + uint64_t state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -4177,7 +4203,10 @@ static void widget_optionbut(uiWidgetColors *wcol, } /* labels use Editor theme colors for text */ -static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_label(uiWidgetType *wt, + uint64_t state, + int drawflag, + eUIEmbossType emboss) { if (state & UI_BUT_LIST_ITEM) { /* Override default label theme's colors. */ @@ -4204,7 +4233,7 @@ static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmb } static void widget_radiobut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t UNUSED(state), int roundboxalign, const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4218,7 +4247,7 @@ static void widget_radiobut( static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + uint64_t UNUSED(state), int roundboxalign, const float zoom) { @@ -4245,7 +4274,7 @@ static void widget_box(uiBut *but, } static void widget_but( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t UNUSED(state), int roundboxalign, const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4272,7 +4301,7 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), #endif static void widget_roundbut_exec( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t state, int roundboxalign, const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4291,7 +4320,7 @@ static void widget_roundbut_exec( } static void widget_tab( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) + uiWidgetColors *wcol, rcti *rect, uint64_t state, int roundboxalign, const float zoom) { const float rad = widget_radius_from_zoom(zoom, wcol); const bool is_active = (state & UI_SELECT); @@ -4922,7 +4951,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu const int roundboxalign = widget_roundbox_set(but, rect); /* Mask out flags re-used for local state. */ - int state = but->flag & ~UI_STATE_FLAGS_ALL; + uint64_t state = but->flag & ~UI_STATE_FLAGS_ALL; const int drawflag = but->drawflag; if (state & UI_SELECT_DRAW) { @@ -5335,7 +5364,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + uint64_t state, uiMenuItemSeparatorType separator_type, int *r_xmax) { @@ -5528,7 +5557,7 @@ void ui_draw_preview_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + uint64_t state, eFontStyle_Align text_align) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM_UNPADDED); diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index 66171dc13e4..94190c0a39d 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -1532,7 +1532,7 @@ void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom) uiWidgetColors wcol = btheme->tui.wcol_scroll; const float alpha_fac = v2d->alpha_hor / 255.0f; rcti slider; - int state; + uint64_t state; slider.xmin = scrollers.hor_min; slider.xmax = scrollers.hor_max; @@ -1566,7 +1566,7 @@ void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom) uiWidgetColors wcol = btheme->tui.wcol_scroll; rcti slider; const float alpha_fac = v2d->alpha_vert / 255.0f; - int state; + uint64_t state; slider.xmin = vert.xmin; slider.xmax = vert.xmax; diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 418a399db28..ef093a01ff8 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../../depsgraph ../../io/alembic ../../io/collada + ../../io/common ../../io/gpencil ../../io/usd ../../io/wavefront_obj diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 3345d422bd1..886586ff236 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -29,6 +29,7 @@ #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #include "IO_wavefront_obj.h" #include "io_obj.h" @@ -59,6 +60,15 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = { "Export objects as they appear in the viewport"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem io_obj_path_mode[] = { + {PATH_REFERENCE_AUTO, "AUTO", 0, "Auto", "Use Relative paths with subdirectories only"}, + {PATH_REFERENCE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Always write absolute paths"}, + {PATH_REFERENCE_RELATIVE, "RELATIVE", 0, "Relative", "Write relative paths where possible"}, + {PATH_REFERENCE_MATCH, "MATCH", 0, "Match", "Match Absolute/Relative setting with input path"}, + {PATH_REFERENCE_STRIP, "STRIP", 0, "Strip", "Write filename only"}, + {PATH_REFERENCE_COPY, "COPY", 0, "Copy", "Copy the file to the destination path"}, + {0, NULL, 0, NULL, NULL}}; + static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -87,6 +97,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } struct OBJExportParams export_params; + export_params.file_base_for_tests[0] = '\0'; RNA_string_get(op->ptr, "filepath", export_params.filepath); export_params.blen_filepath = CTX_data_main(C)->filepath; export_params.export_animation = RNA_boolean_get(op->ptr, "export_animation"); @@ -103,6 +114,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv"); export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals"); export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials"); + export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs"); @@ -119,9 +131,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) { - const bool export_animation = RNA_boolean_get(imfptr, "export_animation"); const bool export_smooth_groups = RNA_boolean_get(imfptr, "export_smooth_groups"); + const bool export_materials = RNA_boolean_get(imfptr, "export_materials"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -150,6 +162,9 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE); uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE); + sub = uiLayoutColumn(sub, false); + uiLayoutSetEnabled(sub, export_materials); + uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE); /* Options for what to write. */ box = uiLayoutBox(layout); @@ -162,6 +177,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); + /* Grouping options. */ box = uiLayoutBox(layout); uiItemL(box, IFACE_("Grouping"), ICON_GROUP); col = uiLayoutColumn(box, false); @@ -231,6 +247,8 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op) void WM_OT_obj_export(struct wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Export Wavefront OBJ"; ot->description = "Save the scene to a Wavefront OBJ file"; ot->idname = "WM_OT_obj_export"; @@ -244,7 +262,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot) ot->flag |= OPTYPE_PRESET; WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, @@ -320,6 +338,12 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Materials", "Export MTL library. There must be a Principled-BSDF node for image textures to " "be exported to the MTL file"); + RNA_def_enum(ot->srna, + "path_mode", + io_obj_path_mode, + PATH_REFERENCE_AUTO, + "Path Mode", + "Method used to reference paths"); RNA_def_boolean(ot->srna, "export_triangulated_mesh", false, @@ -358,6 +382,10 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Every smooth-shaded face is assigned group \"1\" and every flat-shaded face \"off\""); RNA_def_boolean( ot->srna, "smooth_group_bitflags", false, "Generate Bitflags for Smooth Groups", ""); + + /* Only show .obj or .mtl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -415,6 +443,8 @@ static void wm_obj_import_draw(bContext *C, wmOperator *op) void WM_OT_obj_import(struct wmOperatorType *ot) { + PropertyRNA *prop; + ot->name = "Import Wavefront OBJ"; ot->description = "Load a Wavefront OBJ scene"; ot->idname = "WM_OT_obj_import"; @@ -425,7 +455,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) ot->ui = wm_obj_import_draw; WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, @@ -453,4 +483,8 @@ void WM_OT_obj_import(struct wmOperatorType *ot) false, "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + + /* Only show .obj or .mtl files by default. */ + prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); } diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 97ca9b026ec..609230eefea 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -190,6 +190,20 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE); } +static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + if (!BLI_path_extension_check_n(filepath, ".usd", ".usda", ".usdc", NULL)) { + BLI_path_extension_ensure(filepath, FILE_MAX, ".usdc"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + + return false; +} + void WM_OT_usd_export(struct wmOperatorType *ot) { ot->name = "Export USD"; @@ -200,6 +214,7 @@ void WM_OT_usd_export(struct wmOperatorType *ot) ot->exec = wm_usd_export_exec; ot->poll = WM_operator_winactive; ot->ui = wm_usd_export_draw; + ot->check = wm_usd_export_check; ot->flag = OPTYPE_REGISTER; /* No UNDO possible. */ diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index ee40431c101..5d8cf176b87 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -190,6 +190,22 @@ typedef struct KnifeBVH { } KnifeBVH; +/** Additional per-object data. */ +typedef struct KnifeObjectInfo { + const float (*cagecos)[3]; + + /** + * Optionally allocate triangle indices, these are needed for non-interactive knife + * projection as multiple cuts are made without the BVH being updated. + * Using these indices the it's possible to access `cagecos` even if the face has been cut + * and the loops in `em->looptris` no longer refer to the original triangles, see: + */ + const int (*tri_indices)[3]; + + /** Only assigned for convenient access. */ + BMEditMesh *em; +} KnifeObjectInfo; + /* struct for properties used while drawing */ typedef struct KnifeTool_OpData { ARegion *region; /* Region that knifetool was activated in. */ @@ -203,6 +219,9 @@ typedef struct KnifeTool_OpData { Object **objects; uint objects_len; + /** Array `objects_len` length of additional per-object data. */ + KnifeObjectInfo *objects_info; + MemArena *arena; /* Reused for edge-net filling. */ @@ -220,7 +239,6 @@ typedef struct KnifeTool_OpData { GHash *facetrimap; KnifeBVH bvh; - const float (**cagecos)[3]; BLI_mempool *kverts; BLI_mempool *kedges; @@ -1134,6 +1152,52 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k /** \} */ /* -------------------------------------------------------------------- */ +/** \name Knife Object Info Accessors (#KnifeObjectInfo) + * \{ */ + +static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + int tri_index_buf[3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + if (obinfo->tri_indices) { + return obinfo->tri_indices[tri_index]; + } + for (int i = 0; i < 3; i++) { + tri_index_buf[i] = BM_elem_index_get(obinfo->em->looptris[tri_index][i]->v); + } + return tri_index_buf; +} + +static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + int tri_ind_buf[3]; + const int *tri_ind = knife_bm_tri_index_get(kcd, base_index, tri_index, tri_ind_buf); + for (int i = 0; i < 3; i++) { + copy_v3_v3(cos[i], obinfo->cagecos[tri_ind[i]]); + } +} + +static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + knife_bm_tri_cagecos_get(kcd, base_index, tri_index, cos); + const Object *ob = kcd->objects[base_index]; + for (int i = 0; i < 3; i++) { + mul_m4_v3(ob->obmat, cos[i]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Knife BVH Utils * \{ */ @@ -1219,16 +1283,7 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) if (!test_fn_ret) { continue; } - - copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]); - copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]); - copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]); - - /* Convert to world-space. */ - mul_m4_v3(ob->obmat, cos[0]); - mul_m4_v3(ob->obmat, cos[1]); - mul_m4_v3(ob->obmat, cos[2]); - + knife_bm_tri_cagecos_get_worldspace(kcd, b, i, cos); BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); } @@ -1282,12 +1337,7 @@ static void knife_bvh_raycast_cb(void *userdata, } } - copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]); - copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]); - copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]); - mul_m4_v3(ob->obmat, tri_cos[0]); - mul_m4_v3(ob->obmat, tri_cos[1]); - mul_m4_v3(ob->obmat, tri_cos[2]); + knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); isect = (ray->radius > 0.0f ? @@ -1684,7 +1734,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->cagecos[base_index][BM_elem_index_get(v)]; + cageco = kcd->objects_info[base_index].cagecos[BM_elem_index_get(v)]; } else { cageco = v->co; @@ -2545,12 +2595,8 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, if (tri[0]->f != f) { break; } - copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]); - copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]); - copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]); - mul_m4_v3(ob->obmat, lv[0]); - mul_m4_v3(ob->obmat, lv[1]); - mul_m4_v3(ob->obmat, lv[2]); + + knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, lv); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with @@ -2605,9 +2651,10 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd) ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - if (kcd->cagecos[b]) { + const float(*cagecos)[3] = kcd->objects_info[b].cagecos; + if (cagecos) { for (int i = 0; i < em->bm->totvert; i++) { - copy_v3_v3(ws, kcd->cagecos[b][i]); + copy_v3_v3(ws, cagecos[i]); mul_m4_v3(ob->obmat, ws); minmax_v3v3_v3(min, max, ws); } @@ -3961,10 +4008,13 @@ static void knifetool_undo(KnifeTool_OpData *kcd) /** \} */ /* -------------------------------------------------------------------- */ -/** \name #KnifeTool_OpData (#op->customdata) Init and Free +/** \name #KnifeObjectInfo (#kcd->objects_info) Init and Free * \{ */ -static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index) +static void knifetool_init_obinfo(KnifeTool_OpData *kcd, + Object *ob, + uint base_index, + bool use_tri_indices) { Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); @@ -3973,18 +4023,36 @@ static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_ BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc( + KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + obinfo->em = em_eval; + obinfo->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); + + if (use_tri_indices) { + BMLoop *(*looptris)[3] = em_eval->looptris; + int(*tri_indices)[3] = MEM_mallocN(sizeof(int[3]) * em_eval->tottri, __func__); + for (int i = 0; i < em_eval->tottri; i++) { + BMLoop **tri = looptris[i]; + tri_indices[i][0] = BM_elem_index_get(tri[0]->v); + tri_indices[i][1] = BM_elem_index_get(tri[1]->v); + tri_indices[i][2] = BM_elem_index_get(tri[2]->v); + } + obinfo->tri_indices = tri_indices; + } } -static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index) +static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint base_index) { - if (kcd->cagecos[base_index]) { - MEM_freeN((void *)kcd->cagecos[base_index]); - kcd->cagecos[base_index] = NULL; - } + MEM_SAFE_FREE(kcd->objects_info[base_index].cagecos); + MEM_SAFE_FREE(kcd->objects_info[base_index].tri_indices); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #KnifeTool_OpData (#op->customdata) Init and Free + * \{ */ + static void knife_init_colors(KnifeColors *colors) { /* Possible BMESH_TODO: add explicit themes or calculate these by @@ -4018,6 +4086,10 @@ static void knifetool_init(bContext *C, const float angle_snapping_increment, const bool is_interactive) { + /* Needed so multiple non-interactive cuts (also called knife-project) + * doesn't access indices of loops that were created by cutting, see: T97153. */ + bool use_tri_indices = !is_interactive; + kcd->vc = *vc; Scene *scene = vc->scene; @@ -4031,11 +4103,11 @@ static void knifetool_init(bContext *C, Object *ob; BMEditMesh *em; - kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos"); + kcd->objects_info = MEM_callocN(sizeof(*kcd->objects_info) * kcd->objects_len, "knife cagecos"); for (uint b = 0; b < kcd->objects_len; b++) { ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - knifetool_init_cagecos(kcd, ob, b); + knifetool_init_obinfo(kcd, ob, b, use_tri_indices); /* Can't usefully select resulting edges in face mode. */ kcd->select_result = (em->selectmode != SCE_SELECT_FACE); @@ -4139,9 +4211,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) /* Knife BVH cleanup. */ for (int i = 0; i < kcd->objects_len; i++) { - knifetool_free_cagecos(kcd, i); + knifetool_free_obinfo(kcd, i); } - MEM_freeN((void *)kcd->cagecos); + MEM_freeN((void *)kcd->objects_info); knife_bvh_free(kcd); /* Line-hits cleanup. */ @@ -4391,7 +4463,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; - case KNF_MODAL_CONFIRM: + case KNF_MODAL_CONFIRM: { + const bool changed = (kcd->totkvert != 0); /* finish */ ED_region_tag_redraw(kcd->region); @@ -4400,11 +4473,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); /* Cancel to prevent undo push for empty cuts. */ - if (kcd->totkvert == 0) { + if (!changed) { return OPERATOR_CANCELLED; } - return OPERATOR_FINISHED; + } case KNF_MODAL_UNDO: if (BLI_stack_is_empty(kcd->undostack)) { ED_region_tag_redraw(kcd->region); diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index db8860efdd8..b7deade5146 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -2116,6 +2116,22 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool object_curves_empty_hair_add_poll(bContext *C) +{ + if (!U.experimental.use_new_curves_type) { + return false; + } + if (!ED_operator_objectmode(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + if (ob == nullptr || ob->type != OB_MESH) { + CTX_wm_operator_poll_msg_set(C, "No active mesh object"); + return false; + } + return true; +} + void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) { ot->name = "Add Empty Curves"; @@ -2123,7 +2139,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_curves_empty_hair_add"; ot->exec = object_curves_empty_hair_add_exec; - ot->poll = object_curves_add_poll; + ot->poll = object_curves_empty_hair_add_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2762,9 +2778,32 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif + {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"}, {0, nullptr, 0, nullptr, nullptr}, }; +static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *items = nullptr; + int items_num = 0; + for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) { + if (item->value == OB_CURVES) { + if (U.experimental.use_new_curves_type) { + RNA_enum_item_add(&items, &items_num, item); + } + } + else { + RNA_enum_item_add(&items, &items_num, item); + } + } + RNA_enum_item_end(&items, &items_num); + *r_free = true; + return items; +} + static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == nullptr) { @@ -3065,6 +3104,50 @@ static int object_convert_exec(bContext *C, wmOperator *op) } ob_gpencil->actcol = actcol; } + else if (target == OB_CURVES) { + ob->flag |= OB_DONE; + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + GeometrySet geometry; + if (ob_eval->runtime.geometry_set_eval != nullptr) { + geometry = *ob_eval->runtime.geometry_set_eval; + } + + if (geometry.has_curves()) { + if (keep_original) { + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); + newob = basen->object; + + /* Decrement original curve's usage count. */ + Curve *legacy_curve = static_cast<Curve *>(newob->data); + id_us_min(&legacy_curve->id); + + /* Make a copy of the curve. */ + newob->data = BKE_id_copy(bmain, &legacy_curve->id); + } + else { + newob = ob; + } + + const CurveComponent &curve_component = *geometry.get_component_for_read<CurveComponent>(); + const Curves *curves_eval = curve_component.get_for_read(); + Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2)); + + newob->data = new_curves; + newob->type = OB_CURVES; + + blender::bke::CurvesGeometry::wrap( + new_curves->geometry) = blender::bke::CurvesGeometry::wrap(curves_eval->geometry); + BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id); + + BKE_object_free_derived_caches(newob); + BKE_object_free_modifiers(newob, 0); + } + else { + BKE_reportf( + op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2); + } + } else if (ob->type == OB_MESH && target == OB_POINTCLOUD) { ob->flag |= OB_DONE; @@ -3480,6 +3563,7 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); + RNA_def_enum_funcs(ot->prop, convert_target_items_fn); RNA_def_boolean(ot->srna, "keep_original", false, diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index cb0e76c11e4..c9626e674f2 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -17,6 +17,7 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_math_rotation.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -86,6 +87,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "UI_interface_icons.h" @@ -1461,6 +1463,8 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1508,6 +1512,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } @@ -1577,6 +1582,25 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, + "use_auto_smooth", + false, + "Auto Smooth", + "Enable automatic smooth based on smooth/sharp faces/edges and angle between faces"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_property(ot->srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(30.0f)); + RNA_def_property_ui_text(prop, + "Angle", + "Maximum angle between face normals that will be considered as smooth" + "(unused if custom split normals data are available)"); } /** \} */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 5ef8e573e27..76dcfbd8d36 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1964,7 +1964,7 @@ static void single_objectdata_action_users( ID *id_act = (ID *)adt->action; if (single_data_needs_duplication(id_act)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); + BKE_animdata_duplicate_id_action(bmain, id_obdata, USER_DUP_ACT | USER_DUP_LINKED_ID); } } } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 1fdecf47bbd..f214efb44be 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -18,6 +18,7 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" @@ -105,10 +106,11 @@ struct AddOperationExecutor { bool use_front_face_; bool interpolate_length_; bool interpolate_shape_; + bool interpolate_point_count_; bool use_interpolation_; float new_curve_length_; int add_amount_; - int points_per_curve_; + int constant_points_per_curve_; /** Various matrices to convert between coordinate spaces. */ float4x4 curves_to_world_mat_; @@ -129,6 +131,15 @@ struct AddOperationExecutor { Vector<int> looptri_indices; }; + struct NeighborInfo { + /* Curve index of the neighbor. */ + int index; + /* The weights of all neighbors of a new curve add up to 1. */ + float weight; + }; + static constexpr int max_neighbors = 5; + using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) { self_ = &self; @@ -172,10 +183,12 @@ struct AddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); - points_per_curve_ = std::max(2, brush_settings_->points_per_curve); + constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - use_interpolation_ = interpolate_length_ || interpolate_shape_; + interpolate_point_count_ = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + use_interpolation_ = interpolate_length_ || interpolate_shape_ || interpolate_point_count_; new_curve_length_ = brush_settings_->curve_length; tot_old_curves_ = curves_->curves_num(); @@ -215,18 +228,28 @@ struct AddOperationExecutor { return; } + Array<NeighborsVector> neighbors_per_curve; if (use_interpolation_) { this->ensure_curve_roots_kdtree(); + neighbors_per_curve = this->find_curve_neighbors(added_points); } + /* Resize to add the new curves, building the offsets in the array owned by the curves. */ const int tot_added_curves = added_points.bary_coords.size(); - const int tot_added_points = tot_added_curves * points_per_curve_; + curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); + if (interpolate_point_count_) { + this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); + } + else { + this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); + } - curves_->resize(curves_->points_num() + tot_added_points, - curves_->curves_num() + tot_added_curves); + /* Resize to add the correct point count calculated as part of building the offsets. */ + curves_->resize(curves_->offsets().last(), curves_->curves_num()); - threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, - [&]() { this->initialize_attributes(added_points); }); + this->initialize_attributes(added_points, neighbors_per_curve); + + curves_->update_curve_types(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); ED_region_tag_redraw(region_); @@ -578,33 +601,42 @@ struct AddOperationExecutor { } } - void initialize_curve_offsets(const int tot_added_curves) + void initialize_curve_offsets_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) { - MutableSpan<int> offsets = curves_->offsets_for_write(); - threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + + attribute_math::DefaultMixer<int> mixer{new_offsets}; + threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { + for (const int i : curves_range) { + if (neighbors_per_curve[i].is_empty()) { + mixer.mix_in(i, constant_points_per_curve_, 1.0f); + } + else { + for (const NeighborInfo &neighbor : neighbors_per_curve[i]) { + const int neighbor_points_num = curves_->points_for_curve(neighbor.index).size(); + mixer.mix_in(i, neighbor_points_num, neighbor.weight); + } + } } }); - } + mixer.finalize(); - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); + } - void initialize_attributes(const AddedPoints &added_points) + void initialize_curve_offsets_without_interpolation(const int points_per_curve) { - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - neighbors_per_curve = this->find_curve_neighbors(added_points); + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + int offset = tot_old_points_; + for (const int i : new_offsets.index_range()) { + new_offsets[i] = offset; + offset += points_per_curve; } + } + void initialize_attributes(const AddedPoints &added_points, + const Span<NeighborsVector> neighbors_per_curve) + { Array<float> new_lengths_cu(added_points.bary_coords.size()); if (interpolate_length_) { this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); @@ -733,15 +765,14 @@ struct AddOperationExecutor { threading::parallel_for( added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { - const int first_point_i = tot_old_points_ + i * points_per_curve_; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float3 &root_cu = added_points.positions_cu[i]; const float length = lengths_cu[i]; const float3 &normal_su = normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 tip_cu = root_cu + length * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); } }); } @@ -762,23 +793,22 @@ struct AddOperationExecutor { added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float length_cu = new_lengths_cu[i]; const float3 &normal_su = new_normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 &root_cu = added_points.positions_cu[i]; - const int first_point_i = tot_old_points_ + i * points_per_curve_; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); continue; } - positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); + positions_cu.slice(points).fill(root_cu); for (const NeighborInfo &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; @@ -812,8 +842,8 @@ struct AddOperationExecutor { const float neighbor_length_cu = neighbor_spline.length(); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; - for (const int j : IndexRange(points_per_curve_)) { + const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; + for (const int j : IndexRange(points.size())) { const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( j * resample_factor); const float index_factor = lookup.evaluated_index + lookup.factor; @@ -823,7 +853,7 @@ struct AddOperationExecutor { const float3 relative_coord = p - neighbor_root_cu; float3 rotated_relative_coord = relative_coord; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; + positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; } } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 1303da71435..02e992029ff 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -7,6 +7,7 @@ */ #include <float.h> +#include <limits.h> #include <math.h> #include <stdio.h> #include <string.h> @@ -35,12 +36,17 @@ #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_defs.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" +#include "DNA_object_enums.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_camera.h" #include "BKE_colorband.h" @@ -61,6 +67,8 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -76,12 +84,18 @@ #include "GPU_capabilities.h" #include "GPU_init_exit.h" +#include "NOD_shader.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "IMB_colormanagement.h" @@ -6459,6 +6473,38 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) return ima; } +static CustomDataLayer *proj_paint_color_attribute_create(wmOperator *op, Object *ob) +{ + char name[MAX_NAME] = ""; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + AttributeDomain domain = ATTR_DOMAIN_POINT; + CustomDataType type = CD_PROP_COLOR; + + if (op) { + RNA_string_get(op->ptr, "name", name); + RNA_float_get_array(op->ptr, "color", color); + domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); + } + + ID *id = (ID *)ob->data; + CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); + + if (!layer) { + return NULL; + } + + BKE_id_attributes_active_color_set(id, layer); + + if (!BKE_id_attributes_render_color_get(id)) { + BKE_id_attributes_render_color_set(id, layer); + } + + BKE_object_attributes_active_color_fill(ob, color, false); + + return layer; +} + static void proj_paint_default_color(wmOperator *op, int type, Material *ma) { if (RNA_struct_property_is_set(op->ptr, "color")) { @@ -6516,6 +6562,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Material *ma; Image *ima = NULL; + CustomDataLayer *layer = NULL; if (!ob) { return false; @@ -6528,7 +6575,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) int type = RNA_enum_get(op->ptr, "type"); bool is_data = (type > LAYER_BASE_COLOR); - bNode *imanode; + bNode *new_node; bNodeTree *ntree = ma->nodetree; if (!ntree) { @@ -6538,17 +6585,36 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - - ima = proj_paint_image_create(op, bmain, is_data); - imanode->id = &ima->id; - - nodeSetActive(ntree, imanode); + const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ? + (ePaintCanvasSource)RNA_enum_get(op->ptr, + "slot_type") : + PAINT_CANVAS_SOURCE_IMAGE; + + /* Create a new node. */ + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + ima = proj_paint_image_create(op, bmain, is_data); + new_node->id = &ima->id; + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_ATTRIBUTE); + if ((layer = proj_paint_color_attribute_create(op, ob))) { + BLI_strncpy_utf8( + ((NodeShaderAttribute *)new_node->storage)->name, layer->name, MAX_NAME); + } + break; + } + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + return false; + } + nodeSetActive(ntree, new_node); /* Connect to first available principled BSDF node. */ bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + bNode *out_node = new_node; if (in_node != NULL) { bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); @@ -6611,6 +6677,11 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); } + if (layer) { + BKE_texpaint_slot_refresh_cache(scene, ma, ob); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); + } DEG_id_tag_update(&ntree->id, 0); DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); @@ -6678,13 +6749,51 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, int type = get_texture_layer_type(op, "type"); proj_paint_default_color(op, type, ma); - char imagename[MAX_ID_NAME - 2]; - get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); - RNA_string_set(op->ptr, "name", imagename); + char name[MAX_NAME]; + get_default_texture_layer_name_for_object(ob, type, (char *)&name, sizeof(name)); + RNA_string_set(op->ptr, "name", name); return WM_operator_props_dialog_popup(C, op, 300); } +static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + Object *ob = ED_object_active_context(C); + ePaintCanvasSource slot_type = PAINT_CANVAS_SOURCE_IMAGE; + + if (ob->mode == OB_MODE_SCULPT) { + slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type"); + uiItemR(layout, op->ptr, "slot_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + } + + uiItemR(layout, op->ptr, "name", 0, NULL, ICON_NONE); + + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + + uiItemR(layout, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "float", 0, NULL, ICON_NONE); + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + uiItemR(layout, op->ptr, "domain", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "data_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + break; + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + break; + } + + uiItemR(layout, op->ptr, "color", 0, NULL, ICON_NONE); +} + #define IMA_DEF_NAME N_("Untitled") void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) @@ -6692,40 +6801,92 @@ void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) PropertyRNA *prop; static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + static const EnumPropertyItem slot_type_items[3] = { + {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""}, + {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem domain_items[3] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem attribute_type_items[3] = { + {CD_PROP_COLOR, "COLOR", 0, "Color", ""}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; + ot->name = "Add Paint Slot"; + ot->description = "Add a paint slot"; ot->idname = "PAINT_OT_add_texture_paint_slot"; /* api callbacks */ ot->invoke = texture_paint_add_texture_paint_slot_invoke; ot->exec = texture_paint_add_texture_paint_slot_exec; ot->poll = ED_operator_object_active_editable_mesh; + ot->ui = texture_paint_add_texture_paint_slot_ui; /* flags */ ot->flag = OPTYPE_UNDO; - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + /* Shared Properties */ + prop = RNA_def_enum(ot->srna, + "type", + layer_type_items, + 0, + "Material Layer Type", + "Material layer type of new paint slot"); RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_enum( + ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot"); + + prop = RNA_def_string( + ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_color( ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); + + /* Image Properties */ + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, "Generated Type", "Fill the image with a grid for UV map testing"); + RNA_def_boolean( ot->srna, "float", 0, "32-bit Float", "Create image with 32-bit floating-point bit depth"); + + /* Color Attribute Properties */ + RNA_def_enum(ot->srna, + "domain", + domain_items, + ATTR_DOMAIN_POINT, + "Domain", + "Type of element that attribute is stored on"); + + RNA_def_enum(ot->srna, + "data_type", + attribute_type_items, + CD_PROP_COLOR, + "Data Type", + "Type of data stored in attribute"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 32b7047c2b0..85ea5d5bfc6 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -5281,7 +5281,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f * canvas it is painting on. (ref. use_sculpt_texture_paint). */ if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { View3D *v3d = CTX_wm_view3d(C); - if (v3d) { + if (v3d->shading.type == OB_SOLID) { v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 09f13ff110d..f71a814aff4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -124,8 +124,8 @@ static void color_filter_task_cb(void *__restrict userdata, } case COLOR_FILTER_HUE: rgb_to_hsv_v(orig_color, hsv_color); - hue = hsv_color[0] + fade; - hsv_color[0] = fabs((hsv_color[0] + fade) - hue); + hue = hsv_color[0]; + hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue, 1); hsv_to_rgb_v(hsv_color, final_color); break; case COLOR_FILTER_SATURATION: @@ -328,8 +328,12 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent { Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + View3D *v3d = CTX_wm_view3d(C); SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; + if (v3d->shading.type == OB_SOLID) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL); if (use_automasking) { diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 2b5a20205bd..5aa1dbef74c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -1043,6 +1043,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; + View3D *v3d = CTX_wm_view3d(C); + if (v3d->shading.type == OB_SOLID) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index a63ed142ed8..2a8a2be8b65 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -761,7 +761,8 @@ static int sound_pack_exec(bContext *C, wmOperator *op) sound->packedfile = BKE_packedfile_new( op->reports, sound->filepath, ID_BLEND_PATH(bmain, &sound->id)); - BKE_sound_load(bmain, sound); + + DEG_id_tag_update_ex(bmain, &sound->id, ID_RECALC_AUDIO); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 9f71d6f77c7..59ecef7d4c6 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -2802,7 +2802,8 @@ int ED_path_extension_type(const char *path) if (BLI_path_extension_check(path, ".zip")) { return FILE_TYPE_ARCHIVE; } - if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) { + if (BLI_path_extension_check_n( + path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) { return FILE_TYPE_OBJECT_IO; } if (BLI_path_extension_check_array(path, imb_ext_image)) { diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 011506368ee..ce36e3e4e4f 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1364,3 +1364,21 @@ ScrArea *ED_fileselect_handler_area_find(const wmWindow *win, const wmOperator * return NULL; } + +ScrArea *ED_fileselect_handler_area_find_any_with_op(const wmWindow *win) +{ + const bScreen *screen = WM_window_get_active_screen(win); + + ED_screen_areas_iter (win, screen, area) { + if (area->spacetype != SPACE_FILE) { + continue; + } + + const SpaceFile *sfile = area->spacedata.first; + if (sfile->op) { + return area; + } + } + + return NULL; +} diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index aa77aab2283..0cfb924e618 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -2316,6 +2316,14 @@ static bool image_has_valid_path(Image *ima) return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/'); } +static bool image_should_pack_during_save_all(const Image *ima) +{ + /* Images without a filepath (implied with IMA_SRC_GENERATED) should + * be packed during a save_all operation. */ + return (ima->source == IMA_SRC_GENERATED) || + (ima->source == IMA_SRC_TILED && !BKE_image_has_filepath(ima)); +} + bool ED_image_should_save_modified(const Main *bmain) { ReportList reports; @@ -2339,7 +2347,7 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { if (!ID_IS_LINKED(ima)) { num_saveable_images++; } @@ -2396,7 +2404,7 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { BKE_image_memorypack(ima); } else if (is_format_writable) { @@ -3040,9 +3048,8 @@ static bool image_pack_test(bContext *C, wmOperator *op) return false; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Packing movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported"); return false; } @@ -3110,9 +3117,8 @@ static int image_unpack_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -3144,9 +3150,8 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 8b059b33a9a..40082b08806 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -58,14 +58,12 @@ * --> Most channels are now selection only. */ -static int mouse_nla_channels( - bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - View2D *v2d = &ac->region->v2d; int notifierFlags = 0; /* get the channel that was clicked on */ @@ -203,47 +201,8 @@ static int mouse_nla_channels( } case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)ale->data; - AnimData *adt = ale->adt; - short offset; - - /* offset for start of channel (on LHS of channel-list) */ - if (ale->id) { - /* special exception for materials and particles */ - if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) { - offset = 21 + NLACHANNEL_BUTTON_WIDTH; - } - else { - offset = 14; - } - } - else { - offset = 0; - } - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - /* toggle protection (only if there's a toggle there) */ - nlt->flag ^= NLATRACK_PROTECTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - } - else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) { - /* toggle mute */ - nlt->flag ^= NLATRACK_MUTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) { - /* toggle 'solo' */ - BKE_nlatrack_solo_toggle(adt, nlt); - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (nlaedit_is_tweakmode_on(ac) == 0) { + if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this F-Curve only */ @@ -269,61 +228,40 @@ static int mouse_nla_channels( case ANIMTYPE_NLAACTION: { AnimData *adt = BKE_animdata_from_id(ale->id); - /* button region... */ - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - if (nlaedit_is_tweakmode_on(ac) == 0) { - /* 'push-down' action - only usable when not in tweak-mode */ - /* TODO: make this use the operator instead of calling the function directly - * however, calling the operator requires that we supply the args, - * and that works with proper buttons only */ - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id)); - } - else { - /* When in tweak-mode, this button becomes the toggle for mapped editing. */ - adt->flag ^= ADT_NLA_EDIT_NOMAP; - } + /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block + * - this is useful when there's no clear divider, and makes more sense in + * the case of users trying to use this to change actions + * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection + * while in tweak-mode is really evil! + * - we disable "solo" flags too, to make it easier to work with stashed actions + * with less trouble + */ + if (nlaedit_is_tweakmode_on(ac)) { + /* Exit tweak-mode immediately. */ + nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; ale->update |= ANIM_UPDATE_DEPS; } - /* OR rest of name... */ else { - /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block - * - this is useful when there's no clear divider, and makes more sense in - * the case of users trying to use this to change actions - * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection - * while in tweak-mode is really evil! - * - we disable "solo" flags too, to make it easier to work with stashed actions - * with less trouble - */ - if (nlaedit_is_tweakmode_on(ac)) { - /* Exit tweak-mode immediately. */ - nlaedit_disable_tweakmode(ac, true); - - /* changes to NLA-Action occurred */ - notifierFlags |= ND_NLA_ACTCHANGE; - ale->update |= ANIM_UPDATE_DEPS; + /* select/deselect */ + if (selectmode == SELECT_INVERT) { + /* inverse selection status of this AnimData block only */ + adt->flag ^= ADT_UI_SELECTED; } else { - /* select/deselect */ - if (selectmode == SELECT_INVERT) { - /* inverse selection status of this AnimData block only */ - adt->flag ^= ADT_UI_SELECTED; - } - else { - /* select AnimData block by itself */ - ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); - adt->flag |= ADT_UI_SELECTED; - } - - /* set active? */ - if (adt->flag & ADT_UI_SELECTED) { - adt->flag |= ADT_UI_ACTIVE; - } + /* select AnimData block by itself */ + ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); + adt->flag |= ADT_UI_SELECTED; + } - notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); + /* set active? */ + if (adt->flag & ADT_UI_SELECTED) { + adt->flag |= ADT_UI_ACTIVE; } + + notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } @@ -386,7 +324,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv &channel_index); /* handle mouse-click in the relevant channel then */ - notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode); + notifierFlags = mouse_nla_channels(C, &ac, channel_index, selectmode); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL); diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 4247d5a1fbc..dfc0beb13fc 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -139,7 +139,7 @@ static void get_context_path_node_geometry(const bContext &C, Object *object = CTX_data_active_object(&C); ui::context_path_add_generic(path, RNA_Object, object); ModifierData *modifier = BKE_object_active_modifier(object); - ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_GEOMETRY_NODES); context_path_add_node_tree_and_node_groups(snode, path); } } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9076b17a926..f5048e0cc67 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -895,9 +895,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), - to_string(mesh_info.tot_verts).c_str(), - to_string(mesh_info.tot_edges).c_str(), - to_string(mesh_info.tot_faces).c_str()); + to_string(mesh_info.verts_num).c_str(), + to_string(mesh_info.edges_num).c_str(), + to_string(mesh_info.faces_num).c_str()); ss << line << line_end; break; } @@ -908,7 +908,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Point Cloud: %s points"), - to_string(pointcloud_info.tot_points).c_str()); + to_string(pointcloud_info.points_num).c_str()); ss << line << line_end; break; } @@ -918,7 +918,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Curve: %s splines"), - to_string(curve_info.tot_splines).c_str()); + to_string(curve_info.splines_num).c_str()); ss << line << line_end; break; } @@ -928,7 +928,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Instances: %s"), - to_string(instances_info.tot_instances).c_str()); + to_string(instances_info.instances_num).c_str()); ss << line << line_end; break; } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 2d7972e2291..fb2f1bf3751 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -66,7 +66,9 @@ namespace blender::ed::space_node { #define USE_ESC_COMPO -/* ***************** composite job manager ********************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Job Manager + * \{ */ enum { COM_RECALC_COMPOSITE = 1, @@ -293,6 +295,12 @@ static void compo_startjob(void *cjv, } // namespace blender::ed::space_node +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Composite Job C API + * \{ */ + void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) { using namespace blender::ed::space_node; @@ -336,9 +344,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene WM_jobs_start(CTX_wm_manager(C), wm_job); } +/** \} */ + namespace blender::ed::space_node { -/* ***************************************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Poll & Utility Functions + * \{ */ bool composite_node_active(bContext *C) { @@ -388,8 +400,14 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) } } +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Node Editor Public API Functions + * \{ */ + void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) { if (C != nullptr) { @@ -783,9 +801,13 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) // node_update_nodetree(C, ntree, 0.0f, 0.0f); } +/** \} */ + namespace blender::ed::space_node { -/* ***************** generic operator functions for nodes ***************** */ +/* -------------------------------------------------------------------- */ +/** \name Generic Operator Functions for Nodes + * \{ */ #if 0 /* UNUSED */ @@ -861,7 +883,11 @@ static void edit_node_properties_get( } #endif -/* ************************** Node generic ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Generic + * \{ */ /* is rct in visible part of node? */ static bNode *visible_node(SpaceNode &snode, const rctf &rct) @@ -874,7 +900,11 @@ static bNode *visible_node(SpaceNode &snode, const rctf &rct) return nullptr; } -/* ********************** size widget operator ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Size Widget Operator + * \{ */ struct NodeSizeWidget { float mxstart, mystart; @@ -1077,7 +1107,11 @@ void NODE_OT_resize(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING; } -/* ********************** hidden sockets ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hidden Sockets + * \{ */ bool node_has_hidden_sockets(bNode *node) { @@ -1211,7 +1245,11 @@ bool node_find_indicated_socket(SpaceNode &snode, return false; } -/* ****************** Link Dimming *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Dimming + * \{ */ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) { @@ -1237,7 +1275,11 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link) return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f; } -/* ****************** Duplicate *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Duplicate Operator + * \{ */ static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map, bNode *node) @@ -1422,8 +1464,7 @@ void node_select_all(ListBase *lb, int action) } } -/* ******************************** */ -/* XXX some code needing updating to operators. */ +/* XXX: some code needing updating to operators. */ /* goes over all scenes, reads render layers */ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1526,7 +1567,11 @@ void NODE_OT_render_changed(wmOperatorType *ot) ot->flag = 0; } -/* ****************** Hide operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hide Operator + * \{ */ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) { @@ -1722,7 +1767,11 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Mute operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Mute Operator + * \{ */ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1758,7 +1807,11 @@ void NODE_OT_mute_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete operator ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete Operator + * \{ */ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1793,7 +1846,11 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Switch View ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Switch View + * \{ */ static bool node_switch_view_poll(bContext *C) { @@ -1837,7 +1894,12 @@ void NODE_OT_switch_view_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete with reconnect ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete with Reconnect Operator + * \{ */ + static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1872,7 +1934,11 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** File Output Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node File Output Add Socket Operator + * \{ */ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) { @@ -1922,7 +1988,11 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot) ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); } -/* ****************** Multi File Output Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Remove Socket Operator + * \{ */ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1968,7 +2038,11 @@ void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Multi File Output Move Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Move Socket Node + * \{ */ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) { @@ -2040,7 +2114,11 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); } -/* ****************** Copy Node Color ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy Node Color Operator + * \{ */ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2085,7 +2163,11 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Copy to clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy to Clipboard Operator + * \{ */ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2163,7 +2245,11 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Paste from clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Paste from Clipboard + * \{ */ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { @@ -2287,7 +2373,11 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Add interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Add Interface Socket Operator + * \{ */ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) { @@ -2357,7 +2447,11 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Remove interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Remove Interface Socket Operator + * \{ */ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) { @@ -2403,7 +2497,11 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Change interface socket type operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Change Interface Socket Type Operator + * \{ */ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) { @@ -2503,7 +2601,11 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot) ot->prop = prop; } -/********************** Move interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Move Interface Socket Operator + * \{ */ static const EnumPropertyItem move_direction_items[] = { {1, "UP", 0, "Up", ""}, @@ -2577,7 +2679,11 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/* ********************** Shader Script Update ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Shader Script Update + * \{ */ static bool node_shader_script_update_poll(bContext *C) { @@ -2722,7 +2828,11 @@ void NODE_OT_shader_script_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************** Viewer border ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Viewer Border + * \{ */ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *region, @@ -2844,7 +2954,11 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Add Socket + * \{ */ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2888,7 +3002,11 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Remove Socket + * \{ */ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2933,4 +3051,7 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ + } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 1d0097068f1..147cbc5a05c 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -193,11 +193,6 @@ static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) return is_position_over_node_or_socket(*snode, mouse); } -static void node_toggle(bNode *node) -{ - nodeSetSelected(node, !(node->flag & SELECT)); -} - void node_socket_select(bNode *node, bNodeSocket &sock) { sock.flag |= SELECT; @@ -510,10 +505,10 @@ void node_select_single(bContext &C, bNode &node) WM_event_add_notifier(&C, NC_NODE | NA_SELECTED, nullptr); } -static int node_mouse_select(bContext *C, - wmOperator *op, - const int mval[2], - bool wait_to_deselect_others) +static bool node_mouse_select(bContext *C, + wmOperator *op, + const int mval[2], + struct SelectPick_Params *params) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); @@ -525,36 +520,38 @@ static int node_mouse_select(bContext *C, bNodeSocket *sock = nullptr; bNodeSocket *tsock; float cursor[2]; - int ret_value = OPERATOR_CANCELLED; - const bool extend = RNA_boolean_get(op->ptr, "extend"); /* always do socket_select when extending selection. */ - const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - /* These cases are never modal. */ - if (extend || socket_select) { - wait_to_deselect_others = false; - } + const bool socket_select = (params->sel_op == SEL_OP_XOR) || + RNA_boolean_get(op->ptr, "socket_select"); + bool changed = false; + bool found = false; + bool node_was_selected = false; /* get mouse coordinates in view2d space */ UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); /* first do socket selection, these generally overlap with nodes. */ if (socket_select) { + /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */ + const bool extend = (params->sel_op == SEL_OP_XOR); if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { + found = true; + node_was_selected = node->flag & SELECT; + /* NOTE: SOCK_IN does not take into account the extend case... * This feature is not really used anyway currently? */ node_socket_toggle(node, *sock, true); - ret_value = OPERATOR_FINISHED; + changed = true; } else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { + found = true; + node_was_selected = node->flag & SELECT; + if (sock->flag & SELECT) { if (extend) { node_socket_deselect(node, *sock, true); - } - else { - ret_value = OPERATOR_FINISHED; + changed = true; } } else { @@ -566,6 +563,7 @@ static int node_mouse_select(bContext *C, continue; } node_socket_deselect(node, *tsock, true); + changed = true; } } if (!extend) { @@ -575,69 +573,71 @@ static int node_mouse_select(bContext *C, } for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) { node_socket_deselect(tnode, *tsock, true); + changed = true; } } } node_socket_select(node, *sock); - ret_value = OPERATOR_FINISHED; + changed = true; } } } if (!sock) { + /* find the closest visible node */ node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]); + found = (node != nullptr); + node_was_selected = node && (node->flag & SELECT); - if (extend) { - if (node != nullptr) { - /* If node is selected but not active, we want to make it active, - * but not toggle (deselect) it. */ - if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { - node_toggle(node); - } - ret_value = OPERATOR_FINISHED; + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (node->flag & SELECT)) { + found = false; } - } - else if (deselect_all && node == nullptr) { - /* Rather than deselecting others, users may want to drag to box-select (drag from empty - * space) or tweak-translate an already selected item. If these cases may apply, delay - * deselection. */ - if (wait_to_deselect_others) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - /* Deselect in empty space. */ + else if (found || params->deselect_all) { + /* Deselect everything. */ for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { nodeSetSelected(tnode, false); } - ret_value = OPERATOR_FINISHED; + changed = true; } } - else if (node != nullptr) { - /* When clicking on an already selected node, we want to wait to deselect - * others and allow the user to start moving the node without that. */ - if (wait_to_deselect_others && (node->flag & SELECT)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - nodeSetSelected(node, true); - for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode != node) { - nodeSetSelected(tnode, false); - } + if (found) { + switch (params->sel_op) { + case SEL_OP_ADD: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_SUB: { + nodeSetSelected(node, false); + break; + } + case SEL_OP_XOR: { + /* Check active so clicking on an inactive node activates it. */ + bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE); + nodeSetSelected(node, !is_selected); + break; + } + case SEL_OP_SET: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } - - ret_value = OPERATOR_FINISHED; } + + changed = true; } } /* update node order */ - if (ret_value != OPERATOR_CANCELLED) { + if (changed || found) { bool active_texture_changed = false; bool viewer_node_changed = false; - if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) { + if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) { viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER; ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed); } @@ -654,23 +654,35 @@ static int node_mouse_select(bContext *C, WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } - return ret_value; + return changed || found; } static int node_select_exec(bContext *C, wmOperator *op) { - const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - /* get settings from RNA properties for operator */ int mval[2]; - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); + RNA_int_get_array(op->ptr, "location", mval); + + struct SelectPick_Params params = {}; + ED_select_pick_params_from_operator(op, ¶ms); /* perform the select */ - const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); + const bool changed = node_mouse_select(C, op, mval, ¶ms); - /* allow tweak event to work too */ - return ret_value | OPERATOR_PASS_THROUGH; + if (changed) { + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + } + /* Nothing selected, just passthrough. */ + return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED; +} + +static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_int_set_array(op->ptr, "location", event->mval); + + const int retval = node_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void NODE_OT_select(wmOperatorType *ot) @@ -684,24 +696,28 @@ void NODE_OT_select(wmOperatorType *ot) /* api callbacks */ ot->exec = node_select_exec; - ot->invoke = WM_generic_select_invoke; - ot->modal = WM_generic_select_modal; + ot->invoke = node_select_invoke; ot->poll = ED_operator_node_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - WM_operator_properties_generic_select(ot); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_int_vector(ot->srna, + "location", + 2, + NULL, + INT_MIN, + INT_MAX, + "Location", + "Mouse location", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index ff99416c213..57b0e2022ff 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -1809,6 +1809,25 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>( te); + if (!override_elem.is_rna_path_valid) { + uiBut *but = uiDefBut(block, + UI_BTYPE_LABEL, + 0, + override_elem.rna_path.c_str(), + x + pad_x, + te->ys + pad_y, + item_max_width, + item_height, + NULL, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_flag_enable(but, UI_BUT_REDALERT); + continue; + } + PointerRNA *ptr = &override_elem.override_rna_ptr; PropertyRNA *prop = &override_elem.override_rna_prop; const PropertyType prop_type = RNA_property_type(prop); @@ -1936,8 +1955,9 @@ static bool outliner_draw_overrides_warning_buts(uiBlock *block, break; } case TSE_LIBRARY_OVERRIDE: { - const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata)); - if (!is_rna_path_valid) { + TreeElementOverridesProperty &te_override_prop = + *tree_element_cast<TreeElementOverridesProperty>(te); + if (!te_override_prop.is_rna_path_valid) { item_has_warnings = true; if (do_draw) { tip = TIP_( diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index d6c5901b546..f4e28af3fca 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -334,8 +334,16 @@ static void do_item_rename(ARegion *region, add_textbut = true; } } - else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { - BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + else if (te->idcode == ID_LI) { + if (reinterpret_cast<Library *>(tselem->id)->parent) { + BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + } + else { + BKE_report( + reports, + RPT_WARNING, + "Library path is not editable from here anymore, please use Relocate operation instead"); + } } else { add_textbut = true; @@ -568,187 +576,6 @@ void OUTLINER_OT_id_delete(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name ID Remap Operator - * \{ */ - -static int outliner_id_remap_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - - const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); - ID *old_id = reinterpret_cast<ID *>( - BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id"))); - ID *new_id = reinterpret_cast<ID *>( - BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id"))); - - /* check for invalid states */ - if (space_outliner == nullptr) { - return OPERATOR_CANCELLED; - } - - if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) { - BKE_reportf(op->reports, - RPT_ERROR_INVALID_INPUT, - "Invalid old/new ID pair ('%s' / '%s')", - old_id ? old_id->name : "Invalid ID", - new_id ? new_id->name : "Invalid ID"); - return OPERATOR_CANCELLED; - } - - if (ID_IS_LINKED(old_id)) { - BKE_reportf(op->reports, - RPT_WARNING, - "Old ID '%s' is linked from a library, indirect usages of this data-block will " - "not be remapped", - old_id->name); - } - - BKE_libblock_remap( - bmain, old_id, new_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); - - BKE_main_lib_objects_recalc_all(bmain); - - /* recreate dependency graph to include new objects */ - DEG_relations_tag_update(bmain); - - /* Free gpu materials, some materials depend on existing objects, - * such as lights so freeing correctly refreshes. */ - GPU_materials_free(bmain); - - WM_event_add_notifier(C, NC_WINDOW, nullptr); - - return OPERATOR_FINISHED; -} - -static bool outliner_id_remap_find_tree_element(bContext *C, - wmOperator *op, - ListBase *tree, - const float y) -{ - LISTBASE_FOREACH (TreeElement *, te, tree) { - if (y > te->ys && y < te->ys + UI_UNIT_Y) { - TreeStoreElem *tselem = TREESTORE(te); - - if ((tselem->type == TSE_SOME_ID) && tselem->id) { - RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name)); - RNA_enum_set_identifier(C, op->ptr, "new_id", tselem->id->name + 2); - RNA_enum_set_identifier(C, op->ptr, "old_id", tselem->id->name + 2); - return true; - } - } - if (outliner_id_remap_find_tree_element(C, op, &te->subtree, y)) { - return true; - } - } - return false; -} - -static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - ARegion *region = CTX_wm_region(C); - float fmval[2]; - - if (!RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "id_type"))) { - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - outliner_id_remap_find_tree_element(C, op, &space_outliner->tree, fmval[1]); - } - - return WM_operator_props_dialog_popup(C, op, 400); -} - -static const EnumPropertyItem *outliner_id_itemf(bContext *C, - PointerRNA *ptr, - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - if (C == nullptr) { - return DummyRNA_NULL_items; - } - - EnumPropertyItem item_tmp = {0}, *item = nullptr; - int totitem = 0; - int i = 0; - - short id_type = (short)RNA_enum_get(ptr, "id_type"); - ID *id = reinterpret_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first); - - for (; id; id = reinterpret_cast<ID *>(id->next)) { - item_tmp.identifier = item_tmp.name = id->name + 2; - item_tmp.value = i++; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - -void OUTLINER_OT_id_remap(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Outliner ID Data Remap"; - ot->idname = "OUTLINER_OT_id_remap"; - - /* callbacks */ - ot->invoke = outliner_id_remap_invoke; - ot->exec = outliner_id_remap_exec; - ot->poll = ED_operator_outliner_active; - - /* Flags. */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); - /* Changing ID type wont make sense, would return early with "Invalid old/new ID pair" anyways. - */ - RNA_def_property_flag(prop, PROP_HIDDEN); - - prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace"); - RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, outliner_id_itemf); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_ENUM_NO_TRANSLATE | PROP_HIDDEN)); - - ot->prop = RNA_def_enum(ot->srna, - "new_id", - DummyRNA_NULL_items, - 0, - "New ID", - "New ID to remap all selected IDs' users to"); - RNA_def_property_enum_funcs_runtime(ot->prop, nullptr, nullptr, outliner_id_itemf); - RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); -} - -void id_remap_fn(bContext *C, - ReportList *UNUSED(reports), - Scene *UNUSED(scene), - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) -{ - wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false); - PointerRNA op_props; - - BLI_assert(tselem->id != nullptr); - - WM_operator_properties_create_ptr(&op_props, ot); - - RNA_enum_set(&op_props, "id_type", GS(tselem->id->name)); - RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2); - - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr); - - WM_operator_properties_free(&op_props); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name ID Copy Operator * \{ */ diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index f3bcb7b0f1e..2fdf327cbee 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -442,13 +442,6 @@ void id_delete_fn(struct bContext *C, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void id_remap_fn(struct bContext *C, - struct ReportList *reports, - struct Scene *scene, - struct TreeElement *te, - struct TreeStoreElem *tsep, - struct TreeStoreElem *tselem, - void *user_data); /** * To retrieve coordinates with redrawing the entire tree. @@ -523,7 +516,6 @@ void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); void OUTLINER_OT_lib_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_operation(struct wmOperatorType *ot); -void OUTLINER_OT_id_remap(struct wmOperatorType *ot); void OUTLINER_OT_id_copy(struct wmOperatorType *ot); void OUTLINER_OT_id_paste(struct wmOperatorType *ot); void OUTLINER_OT_data_operation(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc index 8baac45666e..e053a94c572 100644 --- a/source/blender/editors/space_outliner/outliner_ops.cc +++ b/source/blender/editors/space_outliner/outliner_ops.cc @@ -31,7 +31,6 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_lib_relocate); WM_operatortype_append(OUTLINER_OT_id_operation); WM_operatortype_append(OUTLINER_OT_id_delete); - WM_operatortype_append(OUTLINER_OT_id_remap); WM_operatortype_append(OUTLINER_OT_id_copy); WM_operatortype_append(OUTLINER_OT_id_paste); WM_operatortype_append(OUTLINER_OT_data_operation); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 5da64177e51..f10edc29e37 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -1692,7 +1692,6 @@ enum { OL_OP_SELECT = 1, OL_OP_DESELECT, OL_OP_SELECT_HIERARCHY, - OL_OP_REMAP, OL_OP_RENAME, }; @@ -1700,11 +1699,6 @@ static const EnumPropertyItem prop_object_op_types[] = { {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""}, {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""}, {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, - {OL_OP_REMAP, - "REMAP", - 0, - "Remap Users", - "Make all users of selected data-blocks to use instead a new chosen one"}, {OL_OP_RENAME, "RENAME", 0, "Rename", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -1759,12 +1753,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Deselect Objects"; selection_changed = true; } - else if (event == OL_OP_REMAP) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); - /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth - * trick does not work here). */ - } else if (event == OL_OP_RENAME) { outliner_do_object_operation( C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); @@ -1973,7 +1961,6 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, - OUTLINER_IDOP_REMAP, OUTLINER_IDOP_COPY, OUTLINER_IDOP_PASTE, @@ -1991,11 +1978,6 @@ static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""}, - {OUTLINER_IDOP_REMAP, - "REMAP", - 0, - "Remap Users", - "Make all users of selected data-blocks to use instead current (clicked) one"}, {0, "", 0, nullptr, nullptr}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, "OVERRIDE_LIBRARY_CREATE", @@ -2414,15 +2396,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } break; } - case OUTLINER_IDOP_REMAP: { - if (idlevel > 0) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); - /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth - * trick does not work here). */ - } - break; - } case OUTLINER_IDOP_COPY: { wm->op_undo_depth++; WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); @@ -2527,14 +2500,12 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) enum eOutlinerLibOpTypes { OL_LIB_INVALID = 0, - OL_LIB_RENAME, OL_LIB_DELETE, OL_LIB_RELOCATE, OL_LIB_RELOAD, }; static const EnumPropertyItem outliner_lib_op_type_items[] = { - {OL_LIB_RENAME, "RENAME", 0, "Rename", ""}, {OL_LIB_DELETE, "DELETE", ICON_X, @@ -2566,14 +2537,6 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { - case OL_LIB_RENAME: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); - - WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); - ED_undo_push(C, "Rename Library"); - break; - } case OL_LIB_DELETE: { outliner_do_libdata_operation( C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 857f5577e59..3a039da86c2 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -84,14 +84,13 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t TreeElementOverridesData &override_data) : AbstractTreeElement(legacy_te), override_rna_ptr(override_data.override_rna_ptr), - override_rna_prop(override_data.override_rna_prop) + override_rna_prop(override_data.override_rna_prop), + rna_path(override_data.override_property.rna_path), + is_rna_path_valid(override_data.is_rna_path_valid) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE); legacy_te.name = override_data.override_property.rna_path; - /* Abusing this for now, better way to do it is also pending current refactor of the whole tree - * code to use C++. */ - legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid); } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index a2d1409f193..b42e1c37a0f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -8,6 +8,8 @@ #include "RNA_types.h" +#include "BLI_string_ref.hh" + #include "tree_element.hh" struct ID; @@ -39,6 +41,9 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { PointerRNA override_rna_ptr; PropertyRNA &override_rna_prop; + StringRefNull rna_path; + bool is_rna_path_valid; + public: TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); }; diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 9298eb83b46..469169cf4cc 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -136,7 +136,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) ot->srna, "overlap_shuffle_override", false, - "Override Overlap Shuffle Behaviour", + "Override Overlap Shuffle Behavior", "Use the overlap_mode tool settings to determine how to shuffle overlapping strips"); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 4afa70d9ef6..66eced27b32 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { - if (component_->attribute_domain_size(domain_) == 0) { + if (component_->attribute_domain_num(domain_) == 0) { return; } @@ -110,8 +110,8 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { - const int domain_size = component_->attribute_domain_size(domain_); - if (domain_size == 0) { + const int domain_num = component_->attribute_domain_num(domain_); + if (domain_num == 0) { return {}; } @@ -129,7 +129,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<InstanceReference> references = instances.references(); return std::make_unique<ColumnValues>( column_id.name, - VArray<InstanceReference>::ForFunc(domain_size, + VArray<InstanceReference>::ForFunc(domain_num, [reference_handles, references](int64_t index) { return references[reference_handles[index]]; })); @@ -137,13 +137,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<float4x4> transforms = instances.instance_transforms(); if (STREQ(column_id.name, "Rotation")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].to_euler(); })); } if (STREQ(column_id.name, "Scale")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].scale(); })); } @@ -210,7 +210,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( int GeometryDataSource::tot_rows() const { - return component_->attribute_domain_size(domain_); + return component_->attribute_domain_num(domain_); } /** @@ -524,17 +524,17 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, std::make_unique<GeometryComponentCacheKey>(component)); const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); for (const auto item : fields_to_show.items()) { StringRef name = item.key; const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - GArray<> evaluated_array(field.cpp_type(), domain_size); + GArray<> evaluated_array(field.cpp_type(), domain_num); bke::GeometryComponentFieldContext field_context{component, domain}; - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add_with_destination(field, evaluated_array); field_evaluator.evaluate(); return evaluated_array; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index c4b5228758c..724d0783707 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -144,7 +144,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) /* Using the tree row button instead of a separate right aligned button gives padding * to the right side of the number, which it didn't have with the button. */ char element_count[7]; - BLI_str_format_attribute_domain_size(element_count, *count); + BLI_str_format_decimal_unit(element_count, *count); UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); } } @@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const } if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) { - return component->attribute_domain_size(*domain_); + return component->attribute_domain_num(*domain_); } return 0; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index dbe67bd0d66..018468dcd03 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,6 +479,45 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { bool clipx = true, clipy = true; @@ -517,31 +556,29 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) } if (resize) { - if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] && - t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) { - vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]); - } - else if (max[0] > (base_offset[0] + t->aspect[0]) && - t->center_global[0] < (base_offset[0] + t->aspect[0])) { - vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) / - (t->center_global[0] - max[0]); - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] && - t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) { - vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]); - } - else if (max[1] > (base_offset[1] + t->aspect[1]) && - t->center_global[1] < (base_offset[1] + t->aspect[1])) { - vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) / - (t->center_global[1] - max[1]); - } - else { - clipy = 0; - } + /* Assume no change is required. */ + float scalex = 1.0f; + float scaley = 1.0f; + + /* Update U against the left border. */ + constrain_scale_to_boundary( + t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], + max[0] - t->center_global[0], + &scalex); + + /* Do the same for the V co-ordinate, which is called `y`. */ + constrain_scale_to_boundary( + t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); + constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], + max[1] - t->center_global[1], + &scaley); + + clipx = (scalex != 1.0f); + clipy = (scaley != 1.0f); + vec[0] *= scalex; + vec[1] *= scaley; } else { if (min[0] < base_offset[0]) { diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index e119b264ae7..769fd28c57b 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1020,7 +1020,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) objects, objects_len, t->mval, - true, + t->tsnap.modeSelect == SNAP_NOT_SELECTED, &dist_sq, t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index fade7f47d9c..0505772c668 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -168,8 +168,13 @@ struct SnapObjectContext { /** \name Utilities * \{ */ -/* Mesh used for snapping. - * If nullptr the BMesh should be used. */ +/** + * Mesh used for snapping. + * + * - When the return value is null the `BKE_editmesh_from_object(ob_eval)` should be used. + * - In rare cases there is no evaluated mesh available and a null result doesn't imply an + * edit-mesh, so callers need to account for a null edit-mesh too, see: T96536. + */ static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) { const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); @@ -998,6 +1003,9 @@ static void raycast_obj_fn(SnapObjectContext *sctx, const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); if (me_eval == nullptr) { BMEditMesh *em = BKE_editmesh_from_object(ob_eval); + if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */ + return; + } BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), "Make sure there is only one pointer for looptris"); retval = raycastEditMesh(sctx, @@ -2696,6 +2704,9 @@ static void snap_obj_fn(SnapObjectContext *sctx, const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); if (me_eval == nullptr) { BMEditMesh *em = BKE_editmesh_from_object(ob_eval); + if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */ + return; + } BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), "Make sure there is only one pointer for looptris"); retval = snapEditMesh( diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 34fae2ffb2a..c0ea753ed51 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -267,26 +267,35 @@ static bool uvedit_have_selection_multi(const Scene *scene, return have_select; } +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy) +{ + if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) { + *r_aspx = 1.0f; + *r_aspy = 1.0f; + return; + } + Image *ima; + ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL); + ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); +} + void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) { BMEditMesh *em = BKE_editmesh_from_object(ob); BLI_assert(em != NULL); bool sloppy = true; bool selected = false; - BMFace *efa; - Image *ima; - - efa = BM_mesh_active_face_get(em->bm, sloppy, selected); - - if (efa) { - ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL); - - ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); - } - else { + BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected); + if (!efa) { *r_aspx = 1.0f; *r_aspy = 1.0f; + return; } + + ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy); } static void construct_param_handle_face_add(ParamHandle *handle, @@ -1527,49 +1536,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius) } } -static void correct_uv_aspect(Object *ob, BMEditMesh *em) +static void shrink_loop_uv_by_aspect_ratio(BMFace *efa, + const int cd_loop_uv_offset, + const float aspect_y) { + BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */ + BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */ + BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - BMFace *efa; - float scale, aspx, aspy; + BMIter iter; + BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (aspect_y > 1.0f) { + /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */ + luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y); + } + else { + /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */ + luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y); + } + } +} +static void correct_uv_aspect(Object *ob, BMEditMesh *em) +{ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - + float aspx, aspy; ED_uvedit_get_aspect(ob, &aspx, &aspy); + const float aspect_y = aspx / aspy; + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); + } + } +} - if (aspx == aspy) { +static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em) +{ + const int materials_num = ob->totcol; + if (materials_num == 0) { + /* Without any materials, there is no aspect_y information and nothing to do. */ return; } - if (aspx > aspy) { - scale = aspy / aspx; + float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num); + /* Lazily initialize aspect ratio for materials. */ + copy_vn_fl(material_aspect_y, materials_num, -1.0f); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f; - } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; } - } - else { - scale = aspx / aspy; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int material_index = efa->mat_nr; + if (UNLIKELY(material_index < 0 || material_index >= materials_num)) { + /* The index might be for a material slot which is not currently setup. */ + continue; + } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f; - } + float aspect_y = material_aspect_y[material_index]; + if (aspect_y == -1.0f) { + /* Lazily initialize aspect ratio for materials. */ + float aspx, aspy; + ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy); + aspect_y = aspx / aspy; + material_aspect_y[material_index] = aspect_y; } + + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + continue; + } + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); } } @@ -1613,7 +1661,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) uv_map_clip_correct_properties_ex(ot, true); } -static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) +/** + * \param per_face_aspect: Calculate the aspect ratio per-face, + * otherwise use a single aspect for all UV's based on the material of the active face. + * TODO: using per-face aspect may split UV islands so more advanced UV projection methods + * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves. + * For now keep using a single aspect for all faces in this case. + */ +static void uv_map_clip_correct_multi(Object **objects, + uint objects_len, + wmOperator *op, + bool per_face_aspect) { BMFace *efa; BMLoop *l; @@ -1633,9 +1691,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper BMEditMesh *em = BKE_editmesh_from_object(ob); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* correct for image aspect ratio */ + /* Correct for image aspect ratio. */ if (correct_aspect) { - correct_uv_aspect(ob, em); + if (per_face_aspect) { + correct_uv_aspect_per_face(ob, em); + } + else { + correct_uv_aspect(ob, em); + } } if (scale_to_bounds) { @@ -1678,6 +1741,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper dy = 1.0f / dy; } + if (dx == 1.0f && dy == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; @@ -1702,7 +1770,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper static void uv_map_clip_correct(Object *ob, wmOperator *op) { - uv_map_clip_correct_multi(&ob, 1, op); + uv_map_clip_correct_multi(&ob, 1, op, true); } /** \} */ @@ -2283,7 +2351,9 @@ static int smart_project_exec(bContext *C, wmOperator *op) .use_seams = true, }); - uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */ + const bool per_face_aspect = false; + uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect); } MEM_freeN(objects_changed); @@ -2485,7 +2555,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) } if (changed_multi) { - uv_map_clip_correct_multi(objects, objects_len, op); + uv_map_clip_correct_multi(objects, objects_len, op, true); } MEM_freeN(objects); diff --git a/source/blender/freestyle/intern/geometry/SweepLine.h b/source/blender/freestyle/intern/geometry/SweepLine.h index 1165e1bf064..c170ee4d122 100644 --- a/source/blender/freestyle/intern/geometry/SweepLine.h +++ b/source/blender/freestyle/intern/geometry/SweepLine.h @@ -183,7 +183,7 @@ template<class T1, class T2> struct binary_rule { binary_rule() { } - template<class T3, class T4> binary_rule(const binary_rule<T3, T4> &brother) + template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/) { } virtual ~binary_rule() diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 8716d6c8f67..010c327d482 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -16,15 +16,19 @@ set(INC set(SRC intern/mesh_merge_by_distance.cc + intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc intern/point_merge_by_distance.cc intern/realize_instances.cc + intern/resample_curves.cc intern/uv_parametrizer.c GEO_mesh_merge_by_distance.hh + GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh GEO_point_merge_by_distance.hh GEO_realize_instances.hh + GEO_resample_curves.hh GEO_uv_parametrizer.h ) diff --git a/source/blender/geometry/GEO_mesh_primitive_cuboid.hh b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh new file mode 100644 index 00000000000..d8f16065e2b --- /dev/null +++ b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +struct Mesh; +struct float3; +namespace blender { +namespace bke { +class AttributeIDRef; +} +} // namespace blender + +namespace blender::geometry { + +Mesh *create_cuboid_mesh( + const float3 &size, int verts_x, int verts_y, int verts_z, const bke::AttributeIDRef &uv_id); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh new file mode 100644 index 00000000000..97399ccb0a5 --- /dev/null +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "FN_field.hh" + +#include "BKE_geometry_set.hh" + +struct Curves; + +namespace blender::geometry { + +/** + * Create new curves where the selected curves have been resampled with a number of uniform-length + * samples defined by the count field. Interpolate attributes to the result, with an accuracy that + * depends on the curve's resolution parameter. + * + * \note The values provided by the #count_field are clamped to 1 or greater. + */ +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field); + +/** + * Create new curves resampled to make each segment have the length specified by the + * #segment_length field input, rounded to make the length of each segment the same. + * The accuracy will depend on the curve's resolution parameter. + */ +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field); + +/** + * Evaluate each selected curve to its implicit evaluated points. + */ +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc new file mode 100644 index 00000000000..e41516d0486 --- /dev/null +++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_index_range.hh" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" + +#include "GEO_mesh_primitive_cuboid.hh" + +namespace blender::geometry { + +struct CuboidConfig { + float3 size; + int verts_x; + int verts_y; + int verts_z; + int edges_x; + int edges_y; + int edges_z; + int vertex_count; + int poly_count; + int loop_count; + + CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) + : size(size), + verts_x(verts_x), + verts_y(verts_y), + verts_z(verts_z), + edges_x(verts_x - 1), + edges_y(verts_y - 1), + edges_z(verts_z - 1) + { + BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); + this->vertex_count = this->get_vertex_count(); + this->poly_count = this->get_poly_count(); + this->loop_count = this->poly_count * 4; + } + + private: + int get_vertex_count() + { + const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); + return verts_x * verts_y * verts_z - inner_position_count; + } + + int get_poly_count() + { + return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); + } +}; + +static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) +{ + const float z_bottom = -config.size.z / 2.0f; + const float z_delta = config.size.z / config.edges_z; + + const float x_left = -config.size.x / 2.0f; + const float x_delta = config.size.x / config.edges_x; + + const float y_front = -config.size.y / 2.0f; + const float y_delta = config.size.y / config.edges_y; + + int vert_index = 0; + + for (const int z : IndexRange(config.verts_z)) { + if (ELEM(z, 0, config.edges_z)) { + /* Fill bottom and top. */ + const float z_pos = z_bottom + z_delta * z; + for (const int y : IndexRange(config.verts_y)) { + const float y_pos = y_front + y_delta * y; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + } + else { + for (const int y : IndexRange(config.verts_y)) { + if (ELEM(y, 0, config.edges_y)) { + /* Fill y-sides. */ + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + else { + /* Fill x-sides. */ + const float x_pos = x_left; + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + const float x_pos2 = x_left + x_delta * config.edges_x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); + } + } + } + } +} + +/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. + * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for + * anti-clockwise. + */ +static void define_quad(MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops, + const int poly_index, + const int loop_index, + const int vert_1, + const int vert_2, + const int vert_3, + const int vert_4) +{ + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_1 = loops[loop_index]; + loop_1.v = vert_1; + MLoop &loop_2 = loops[loop_index + 1]; + loop_2.v = vert_2; + MLoop &loop_3 = loops[loop_index + 2]; + loop_3.v = vert_3; + MLoop &loop_4 = loops[loop_index + 3]; + loop_4.v = vert_4; +} + +static void calculate_polys(const CuboidConfig &config, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) +{ + int loop_index = 0; + int poly_index = 0; + + /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ + const int xy_cross_section_vert_count = config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2); + + /* Calculate polys for Bottom faces. */ + int vert_1_start = 0; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + const int vert_1 = vert_1_start + x; + const int vert_2 = vert_1_start + config.verts_x + x; + const int vert_3 = vert_2 + 1; + const int vert_4 = vert_1 + 1; + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + vert_1_start += config.verts_x; + } + + /* Calculate polys for Front faces. */ + vert_1_start = 0; + int vert_2_start = config.verts_x * config.verts_y; + + for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_1_start = vert_2_start; + vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); + } + + /* Calculate polys for Top faces. */ + vert_1_start = config.verts_x * config.verts_y + + (config.verts_z - 2) * (config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2)); + vert_2_start = vert_1_start + config.verts_x; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_2_start += config.verts_x; + vert_1_start += config.verts_x; + } + + /* Calculate polys for Back faces. */ + vert_1_start = config.verts_x * config.edges_y; + vert_2_start = vert_1_start + xy_cross_section_vert_count; + + for (const int z : IndexRange(config.edges_z)) { + if (z == (config.edges_z - 1)) { + vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); + } + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_2_start + x, + vert_2_start + x + 1, + vert_1_start + x + 1); + loop_index += 4; + poly_index++; + } + vert_2_start += xy_cross_section_vert_count; + vert_1_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Left faces. */ + vert_1_start = 0; + vert_2_start = config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1; + int vert_2; + int vert_3; + int vert_4; + + if (z == 0 || y == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_1 += config.verts_x - 2; + vert_4 = vert_1 + 2; + } + + if (y == 0 || z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_2 += config.verts_x - 2; + vert_3 = vert_2 + 2; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Right faces. */ + vert_1_start = config.edges_x; + vert_2_start = vert_1_start + config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1 = vert_1_start; + int vert_2 = vert_2_start; + int vert_3 = vert_2_start + 2; + int vert_4 = vert_1 + config.verts_x; + + if (z == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_4 = vert_1 + 2; + } + + if (z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_3 = vert_2 + 2; + } + + if (y == (config.edges_y - 1)) { + vert_3 = vert_2 + config.verts_x; + vert_4 = vert_1 + config.verts_x; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } +} + +static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + bke::OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>(uv_id, ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); + + int loop_index = 0; + + const float x_delta = 0.25f / static_cast<float>(config.edges_x); + const float y_delta = 0.25f / static_cast<float>(config.edges_y); + const float z_delta = 0.25f / static_cast<float>(config.edges_z); + + /* Calculate bottom face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); + } + } + + /* Calculate front face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); + } + } + + /* Calculate top face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); + } + } + + /* Calculate back face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); + } + } + + /* Calculate left face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); + } + } + + /* Calculate right face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); + } + } + + uv_attribute.save(); +} + +Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z, + const bke::AttributeIDRef &uv_id) +{ + const CuboidConfig config(size, verts_x, verts_y, verts_z); + + Mesh *mesh = BKE_mesh_new_nomain( + config.vertex_count, 0, 0, config.loop_count, config.poly_count); + + calculate_vertices(config, {mesh->mvert, mesh->totvert}); + + calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); + BKE_mesh_calc_edges(mesh, false, false); + + if (uv_id) { + calculate_uvs(config, mesh, uv_id); + } + + return mesh; +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index f3f0e5b1fce..ae07e817c67 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -374,7 +374,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>( - to_type, instances_component.instances_amount()); + to_type, instances_component.instances_num()); conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span()); span = temporary_array->as_span(); gather_info.r_temporary_arrays.append(std::move(temporary_array)); @@ -548,7 +548,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component); const Curves *curves = curve_component.get_for_read(); - if (curves != nullptr && curves->geometry.curve_size > 0) { + if (curves != nullptr && curves->geometry.curve_num > 0) { const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.curves_offsets, @@ -556,8 +556,8 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size; - gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size; + gather_info.r_offsets.curves_offsets.point += curves->geometry.point_num; + gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_num; } break; } @@ -1052,7 +1052,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, VectorSet<const Curves *> &r_curves) { if (const Curves *curves = geometry_set.get_curves_for_read()) { - if (curves->geometry.curve_size != 0) { + if (curves->geometry.curve_num != 0) { r_curves.add(curves); } } @@ -1215,13 +1215,13 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const RealizeCurveTask &last_task = tasks.last(); const Curves &last_curves = *last_task.curve_info->curves; - const int points_size = last_task.start_indices.point + last_curves.geometry.point_size; - const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size; + const int points_num = last_task.start_indices.point + last_curves.geometry.point_num; + const int curves_num = last_task.start_indices.curve + last_curves.geometry.curve_num; /* Allocate new curves data-block. */ - Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size); + Curves *dst_curves_id = bke::curves_new_nomain(points_num, curves_num); bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - dst_curves.offsets_for_write().last() = points_size; + dst_curves.offsets_for_write().last() = points_num; CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); dst_component.replace(dst_curves_id); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc new file mode 100644 index 00000000000..7895225a189 --- /dev/null +++ b/source/blender/geometry/intern/resample_curves.cc @@ -0,0 +1,474 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_length_parameterize.hh" +#include "BLI_task.hh" + +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_fields.hh" + +#include "GEO_resample_curves.hh" + +namespace blender::geometry { + +static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field) +{ + static fn::CustomMF_SI_SO<int, int> max_one_fn( + "Clamp Above One", + [](int value) { return std::max(1, value); }, + fn::CustomMF_presets::AllSpanOrSingle()); + auto clamp_op = std::make_shared<fn::FieldOperation>( + fn::FieldOperation(max_one_fn, {count_field})); + + return fn::Field<int>(std::move(clamp_op)); +} + +static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field) +{ + static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( + "Length Input to Count", + [](const float curve_length, const float sample_length) { + /* Find the number of sampled segments by dividing the total length by + * the sample length. Then there is one more sampled point than segment. */ + const int count = int(curve_length / sample_length) + 1; + return std::max(1, count); + }, + fn::CustomMF_presets::AllSpanOrSingle()); + + auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation( + get_count_fn, + {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field})); + + return fn::Field<int>(std::move(get_count_op)); +} + +/** + * Return true if the attribute should be copied/interpolated to the result curves. + * Don't output attributes that correspond to curve types that have no curves in the result. + */ +static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id, + const std::array<int, CURVE_TYPES_NUM> &type_counts) +{ + if (!attribute_id.is_named()) { + return true; + } + if (ELEM(attribute_id.name(), + "handle_type_left", + "handle_type_right", + "handle_left", + "handle_right")) { + return type_counts[CURVE_TYPE_BEZIER] != 0; + } + if (ELEM(attribute_id.name(), "nurbs_weight")) { + return type_counts[CURVE_TYPE_NURBS] != 0; + } + return true; +} + +/** + * Return true if the attribute should be copied to poly curves. + */ +static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attribute_id) +{ + static const Set<StringRef> no_interpolation{{ + "handle_type_left", + "handle_type_right", + "handle_position_right", + "handle_position_left", + "nurbs_weight", + }}; + return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); +} + +/** + * Retrieve spans from source and result attributes. + */ +static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids, + const CurveComponent &src_component, + CurveComponent &dst_component, + Vector<GSpan> &src, + Vector<GMutableSpan> &dst, + Vector<bke::OutputAttribute> &dst_attributes) +{ + for (const int i : ids.index_range()) { + GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); + BLI_assert(src_attribute); + src.append(src_attribute.get_internal_span()); + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); + bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + ids[i], ATTR_DOMAIN_POINT, data_type); + dst.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } +} + +struct AttributesForInterpolation : NonCopyable, NonMovable { + Vector<GSpan> src; + Vector<GMutableSpan> dst; + + Vector<bke::OutputAttribute> dst_attributes; + + Vector<GSpan> src_no_interpolation; + Vector<GMutableSpan> dst_no_interpolation; +}; + +/** + * Gather a set of all generic attribute IDs to copy to the result curves. + */ +static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, + CurveComponent &dst_component, + AttributesForInterpolation &result) +{ + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap( + dst_component.get_for_write()->geometry); + + VectorSet<bke::AttributeIDRef> ids; + VectorSet<bke::AttributeIDRef> ids_no_interpolation; + src_component.attribute_foreach( + [&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + return true; + } + if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { + return true; + } + if (interpolate_attribute_to_poly_curve(id)) { + ids.add_new(id); + } + else { + ids_no_interpolation.add_new(id); + } + return true; + }); + + /* Position is handled differently since it has non-generic interpolation for Bezier + * curves and because the evaluated positions are cached for each evaluated point. */ + ids.remove_contained("position"); + + retrieve_attribute_spans( + ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); + + /* Attributes that aren't interpolated like Bezier handles still have to be be copied + * to the result when there are any unselected curves of the corresponding type. */ + retrieve_attribute_spans(ids_no_interpolation, + src_component, + dst_component, + result.src_no_interpolation, + result.dst_no_interpolation, + result.dst_attributes); + + dst_curves.update_customdata_pointers(); +} + +/** + * Copy the provided point attribute values between all curves in the #curve_ranges index + * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. + */ +template<typename T> +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> curve_ranges, + const Span<T> src, + const MutableSpan<T> dst) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange src_points = src_curves.points_for_curves(range); + const IndexRange dst_points = dst_curves.points_for_curves(range); + /* The arrays might be large, so a threaded copy might make sense here too. */ + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }); +} +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> unselected_ranges, + const GSpan src, + const GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); + }); +} + +static Curves *resample_to_uniform(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + /* Create the new curves without any points and evaluate the final count directly + * into the offsets array, in order to be accumulated into offsets later. */ + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(count_field, dst_offsets); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + + VArray<bool> curves_cyclic = src_curves.cyclic(); + VArray<int8_t> curve_types = src_curves.curve_types(); + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + src_curves.ensure_evaluated_lengths(); + + /* Sampling arbitrary attributes works by first interpolating them to the curve's standard + * "evaluated points" and then interpolating that result with the uniform samples. This is + * potentially wasteful when down-sampling a curve to many fewer points. There are two possible + * solutions: only sample the necessary points for interpolation, or first sample curve + * parameter/segment indices and evaluate the curve directly. */ + Array<int> sample_indices(dst_curves.points_num()); + Array<float> sample_factors(dst_curves.points_num()); + + /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on + * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a + * time or one curve at a time. */ + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + Vector<std::byte> evaluated_buffer; + + /* Gather uniform samples based on the accumulated lengths of the original curve. */ + for (const int i_curve : sliced_selection) { + const bool cyclic = curves_cyclic[i_curve]; + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::create_uniform_samples( + src_curves.evaluated_lengths_for_curve(i_curve, cyclic), + curves_cyclic[i_curve], + sample_indices.as_mutable_span().slice(dst_points), + sample_factors.as_mutable_span().slice(dst_points)); + } + + /* For every attribute, evaluate attributes from every curve in the range in the original + * curve's "evaluated points", then use linear interpolation to sample to the result. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + + if (curve_types[i_curve] == CURVE_TYPE_POLY) { + length_parameterize::linear_interpolation(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + else { + const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); + evaluated_buffer.clear(); + evaluated_buffer.resize(sizeof(T) * evaluated_size); + MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); + src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); + + length_parameterize::linear_interpolation(evaluated.as_span(), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + } + }); + } + + /* Interpolate the evaluated positions to the resampled curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst_positions.slice(dst_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + return resample_to_uniform(src_component, selection_field, get_count_input_max_one(count_field)); +} + +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field) +{ + return resample_to_uniform( + src_component, selection_field, get_count_input_from_length(segment_length_field)); +} + +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + src_curves.ensure_evaluated_offsets(); + threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); + } + }); + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* Create the correct number of uniform-length samples for every selected curve. */ + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + /* Evaluate generic point attributes directly to the result attributes. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + src_curves.interpolate_to_evaluated( + i_curve, src.slice(src_points), dst.slice(dst_points)); + } + }); + } + + /* Copy the evaluated positions to the selected curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +} // namespace blender::geometry diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index f87c85f808f..69fc26c99e9 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -72,7 +72,6 @@ set(SRC intern/lineart/MOD_lineart.h intern/lineart/lineart_intern.h - ) if(WITH_TBB) diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index dbe2ae0b890..b09bb15ce81 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -2358,18 +2358,21 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], double shift_x, double shift_y, - Object *use_ob) + Mesh *use_mesh) { - const BoundBox *bb = BKE_object_boundbox_get(use_ob); - if (!bb) { - /* For lights and empty stuff there will be no bbox. */ + if (!use_mesh) { return false; } + float mesh_min[3], mesh_max[3]; + INIT_MINMAX(mesh_min, mesh_max); + BKE_mesh_minmax(use_mesh, mesh_min, mesh_max); + BoundBox bb = {0}; + BKE_boundbox_init_from_minmax(&bb, mesh_min, mesh_max); double co[8][4]; double tmp[3]; for (int i = 0; i < 8; i++) { - copy_v3db_v3fl(co[i], bb->vec[i]); + copy_v3db_v3fl(co[i], bb.vec[i]); copy_v3_v3_db(tmp, co[i]); mul_v4_m4v3_db(co[i], model_view_proj, tmp); co[i][0] -= shift_x * 2 * co[i][3]; @@ -2481,13 +2484,6 @@ static void lineart_main_load_geometries( continue; } - if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) { - if (G.debug_value == 4000) { - bound_box_discard_count++; - } - continue; - } - if (use_ob->type == OB_MESH) { use_mesh = BKE_object_get_evaluated_mesh(use_ob); } @@ -2506,6 +2502,17 @@ static void lineart_main_load_geometries( continue; } + if (!lineart_geometry_check_visible( + obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) { + if (ob->type != OB_MESH) { + BKE_id_free(NULL, use_mesh); + } + if (G.debug_value == 4000) { + bound_box_discard_count++; + } + continue; + } + if (ob->type != OB_MESH) { obi->free_use_mesh = true; } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 80cc2ac3013..a2db89f849e 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -536,6 +536,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_2D_widget_info.hh shaders/infos/gpu_shader_3D_depth_only_info.hh shaders/infos/gpu_shader_3D_flat_color_info.hh + shaders/infos/gpu_shader_3D_image_info.hh shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh shaders/infos/gpu_shader_3D_point_info.hh shaders/infos/gpu_shader_3D_polyline_info.hh diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 0d0542aa528..061b850619f 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -30,6 +30,7 @@ int GPU_max_batch_vertices(void); int GPU_max_vertex_attribs(void); int GPU_max_varying_floats(void); int GPU_max_shader_storage_buffer_bindings(void); +int GPU_max_compute_shader_storage_blocks(void); int GPU_extensions_len(void); const char *GPU_extension_get(int i); @@ -40,6 +41,7 @@ bool GPU_mip_render_workaround(void); bool GPU_depth_blitting_workaround(void); bool GPU_use_main_context_workaround(void); bool GPU_use_hq_normals_workaround(void); +bool GPU_clear_viewport_workaround(void); bool GPU_crappy_amd_driver(void); bool GPU_compute_shader_support(void); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 9d34d2fb0ed..461b0dbb8e4 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -282,6 +282,16 @@ typedef enum eGPUBuiltinShader { GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE, GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR, /** + * Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex. + * + * Exposed via Python-API for add-ons. + * + * \param image: uniform sampler2D + * \param texCoord: in vec2 + * \param pos: in vec3 + */ + GPU_SHADER_3D_IMAGE, + /** * Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex. * * \param alpha: uniform float diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 474205ce8c3..b045d908438 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -282,7 +282,7 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl); * \warning Only work for 2D texture for now. * \warning Only clears the MIP 0 of the texture. * \param data_format: data format of the pixel data. - * \note The format is float for uniform textures. + * \note The format is float for UNORM textures. * \param data: 1 pixel worth of data to fill the texture with. */ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data); diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index c6b0d5902fe..bf8dc409cb7 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -55,7 +55,7 @@ typedef struct GPUVertAttr { /* 1 to 4 or 8 or 12 or 16 */ uint comp_len : 5; /* size in bytes, 1 to 64 */ - uint sz : 7; + uint size : 7; /* from beginning of vertex, in bytes */ uint offset : 11; /* up to GPU_VERT_ATTR_MAX_NAMES */ diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index b750dacaca6..4ec215c2d3b 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -142,6 +142,11 @@ bool GPU_use_hq_normals_workaround() return GCaps.use_hq_normals_workaround; } +bool GPU_clear_viewport_workaround() +{ + return GCaps.clear_viewport_workaround; +} + bool GPU_compute_shader_support() { return GCaps.compute_shader_support; @@ -162,6 +167,11 @@ int GPU_max_shader_storage_buffer_bindings() return GCaps.max_shader_storage_buffer_bindings; } +int GPU_max_compute_shader_storage_blocks() +{ + return GCaps.max_compute_shader_storage_blocks; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 4a951eb8458..a17dbe7f8e6 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -36,6 +36,7 @@ struct GPUCapabilities { int max_vertex_attribs = 0; int max_varying_floats = 0; int max_shader_storage_buffer_bindings = 0; + int max_compute_shader_storage_blocks = 0; int extensions_len = 0; const char *(*extension_get)(int); @@ -51,6 +52,7 @@ struct GPUCapabilities { bool use_main_context_workaround = false; bool broken_amd_driver = false; bool use_hq_normals_workaround = false; + bool clear_viewport_workaround = false; /* Vulkan related workarounds. */ /* Metal related workarounds. */ diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index 6f9f9454691..ba0e9d0310a 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -32,6 +32,7 @@ #include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ +#include "BLI_vector.hh" #include "gpu_codegen.h" #include "gpu_material_library.h" @@ -58,18 +59,27 @@ struct GPUCodegenCreateInfo : ShaderCreateInfo { /** Duplicate attribute names to avoid reference the GPUNodeGraph directly. */ char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1]; char var_names[16][8]; + blender::Vector<std::array<char, 32>, 16> sampler_names; + + /* Returns the appended name memory location */ + const char *append_sampler_name(const char name[32]) + { + auto index = sampler_names.append_and_get_index(std::array<char, 32>()); + char *name_buffer = sampler_names[index].data(); + memcpy(name_buffer, name, 32); + return name_buffer; + } }; /** Optional generated interface. */ StageInterfaceInfo *interface_generated = nullptr; /** Optional name buffer containing names referenced by StringRefNull. */ - NameBuffer *name_buffer = nullptr; + NameBuffer name_buffer; GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name){}; ~GPUCodegenCreateInfo() { delete interface_generated; - MEM_delete(name_buffer); }; }; @@ -292,7 +302,6 @@ void GPUCodegen::generate_attribs() GPUCodegenCreateInfo &info = *create_info; - info.name_buffer = MEM_new<GPUCodegenCreateInfo::NameBuffer>("info.name_buffer"); info.interface_generated = new StageInterfaceInfo("codegen_iface", "var_attrs"); StageInterfaceInfo &iface = *info.interface_generated; info.vertex_out(iface); @@ -306,11 +315,11 @@ void GPUCodegen::generate_attribs() BLI_assert_msg(0, "Too many attributes"); break; } - STRNCPY(info.name_buffer->attr_names[slot], attr->input_name); - SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id); + STRNCPY(info.name_buffer.attr_names[slot], attr->input_name); + SNPRINTF(info.name_buffer.var_names[slot], "v%d", attr->id); - blender::StringRefNull attr_name = info.name_buffer->attr_names[slot]; - blender::StringRefNull var_name = info.name_buffer->var_names[slot]; + blender::StringRefNull attr_name = info.name_buffer.attr_names[slot]; + blender::StringRefNull var_name = info.name_buffer.var_names[slot]; eGPUType input_type, iface_type; @@ -352,14 +361,19 @@ void GPUCodegen::generate_resources() /* Textures. */ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) { if (tex->colorband) { - info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->sampler_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, name, Frequency::BATCH); } else if (tex->tiled_mapping_name[0] != '\0') { - info.sampler(0, ImageType::FLOAT_2D_ARRAY, tex->sampler_name, Frequency::BATCH); - info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->tiled_mapping_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH); + + const char *name_mapping = info.name_buffer.append_sampler_name(tex->tiled_mapping_name); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, name_mapping, Frequency::BATCH); } else { - info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_2D, name, Frequency::BATCH); } } diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index c62a6416f92..055207eace8 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -50,11 +50,11 @@ void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf) r_name_buf[0] = '\0'; return; } - size_t sz = 0; + size_t len = 0; for (StringRef &name : stack) { - sz += BLI_snprintf_rlen(r_name_buf + sz, name_buf_len - sz, "%s > ", name.data()); + len += BLI_snprintf_rlen(r_name_buf + len, name_buf_len - len, "%s > ", name.data()); } - r_name_buf[sz - 3] = '\0'; + r_name_buf[len - 3] = '\0'; } bool GPU_debug_group_match(const char *ref) diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 7dc1c739750..69467e5b28a 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -490,7 +490,7 @@ static void immEndVertex() /* and move on to the next vertex */ #endif uchar *data = imm->vertex_data + a->offset; - memcpy(data, data - imm->vertex_format.stride, a->sz); + memcpy(data, data - imm->vertex_format.stride, a->size); /* TODO: consolidate copy of adjacent attributes */ } } diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 54473b3a44d..146461d1dfb 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -232,6 +232,7 @@ void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint ma data_ = indices; index_start_ = 0; index_len_ = indices_len; + is_empty_ = min_index > max_index; #if GPU_TRACK_INDEX_RANGE /* Everything remains 32 bit while building to keep things simple. diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh index 9323a81d393..6ce62ae852e 100644 --- a/source/blender/gpu/intern/gpu_index_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh @@ -45,6 +45,8 @@ class IndexBuf { bool is_init_ = false; /** Is this object only a reference to a subrange of another IndexBuf. */ bool is_subrange_ = false; + /** True if buffer only contains restart indices. */ + bool is_empty_ = false; union { /** Mapped buffer data. non-NULL indicates not yet sent to VRAM. */ @@ -61,9 +63,12 @@ class IndexBuf { void init_subrange(IndexBuf *elem_src, uint start, uint length); void init_build_on_device(uint index_len); + /* Returns render index count (not precise). */ uint32_t index_len_get() const { - return index_len_; + /* Return 0 to bypass drawing for index buffers full of restart indices. + * They can lead to graphical glitches on some systems. (See T96892) */ + return is_empty_ ? 0 : index_len_; } /* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */ size_t size_get() const diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 9253aae8404..e7337073773 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -177,8 +177,8 @@ void GPU_material_free(ListBase *gpumaterial) { LISTBASE_FOREACH (LinkData *, link, gpumaterial) { GPUMaterial *material = link->data; - GPU_material_free_single(material); DRW_deferred_shader_remove(material); + GPU_material_free_single(material); } BLI_freelistN(gpumaterial); } diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index f1b36676712..1b76d6a2d70 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -337,6 +337,8 @@ static char attr_prefix_get(CustomDataType type) switch (type) { case CD_TANGENT: return 't'; + case CD_MCOL: + return 'c'; case CD_AUTO_FROM_NAME: return 'a'; case CD_HAIRLENGTH: diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 13238a03688..b92fae4a89b 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -150,6 +150,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .name = "GPU_SHADER_SIMPLE_LIGHTING", .create_info = "gpu_shader_simple_lighting", }, + [GPU_SHADER_3D_IMAGE] = + { + .name = "GPU_SHADER_3D_IMAGE", + .create_info = "gpu_shader_3D_image", + }, [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] = { .name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA", @@ -367,6 +372,16 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, if (sh_cfg == GPU_SHADER_CFG_DEFAULT) { if (stages->create_info != NULL) { *sh_p = GPU_shader_create_from_info_name(stages->create_info); + if (ELEM(shader, + GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR, + GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, + GPU_SHADER_3D_POLYLINE_FLAT_COLOR, + GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR)) { + /* Set a default value for `lineSmooth`. + * Ideally this value should be set by the caller. */ + GPU_shader_bind(*sh_p); + GPU_shader_uniform_1i(*sh_p, "lineSmooth", 1); + } } else { *sh_p = GPU_shader_create_from_arrays_named( diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 76b27dfd52e..ac9f13a3343 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -8,6 +8,7 @@ * shader files. */ +#include <algorithm> #include <iomanip> #include <iostream> #include <sstream> @@ -99,6 +100,10 @@ struct GPUSource { /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */ if (filename.endswith(".h") || filename.endswith(".hh")) { enum_preprocess(); + quote_preprocess(); + } + else { + check_no_quotes(); } if (is_from_material_library()) { @@ -175,6 +180,44 @@ struct GPUSource { } /** + * Some drivers completely forbid quote characters even in unused preprocessor directives. + * We fix the cases where we can't manually patch in `enum_preprocess()`. + * This check ensure none are present in non-patched sources. (see T97545) + */ + void check_no_quotes() + { +#ifdef DEBUG + int64_t pos = -1; + do { + pos = source.find('"', pos + 1); + if (pos == -1) { + break; + } + if (!is_in_comment(source, pos)) { + print_error(source, pos, "Quote characters are forbidden in GLSL files"); + } + } while (true); +#endif + } + + /** + * Some drivers completely forbid string characters even in unused preprocessor directives. + * This fixes the cases we cannot manually patch: Shared headers #includes. (see T97545) + * TODO(fclem): This could be done during the datatoc step. + */ + void quote_preprocess() + { + if (source.find_first_of('"') == -1) { + return; + } + + processed_source = source; + std::replace(processed_source.begin(), processed_source.end(), '"', ' '); + + source = processed_source.c_str(); + } + + /** * Transform C,C++ enum declaration into GLSL compatible defines and constants: * * \code{.cpp} @@ -283,6 +326,7 @@ struct GPUSource { if (last_pos != 0) { output += input.substr(last_pos); } + processed_source = output; source = processed_source.c_str(); }; diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 2974547c858..13f409cfba5 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -199,7 +199,7 @@ void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void BLI_assert(a_idx < format->attr_len); BLI_assert(verts->data != nullptr); verts->flag |= GPU_VERTBUF_DATA_DIRTY; - memcpy(verts->data + a->offset + v_idx * format->stride, data, a->sz); + memcpy(verts->data + a->offset + v_idx * format->stride, data, a->size); } void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) @@ -208,7 +208,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) const GPUVertFormat *format = &verts->format; BLI_assert(a_idx < format->attr_len); const GPUVertAttr *a = &format->attrs[a_idx]; - const uint stride = a->sz; /* tightly packed input data */ + const uint stride = a->size; /* tightly packed input data */ verts->flag |= GPU_VERTBUF_DATA_DIRTY; GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data); } @@ -235,13 +235,13 @@ void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, c if (format->attr_len == 1 && stride == format->stride) { /* we can copy it all at once */ - memcpy(verts->data, data, vertex_len * a->sz); + memcpy(verts->data, data, vertex_len * a->size); } else { /* we must copy it per vertex */ for (uint v = 0; v < vertex_len; v++) { memcpy( - verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->sz); + verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->size); } } } @@ -256,7 +256,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw verts->flag |= GPU_VERTBUF_DATA_DIRTY; verts->flag &= ~GPU_VERTBUF_DATA_UPLOADED; - access->size = a->sz; + access->size = a->size; access->stride = format->stride; access->data = (uchar *)verts->data + a->offset; access->data_init = access->data; diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index a9ac191754b..59ae862aa51 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -49,7 +49,7 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static uint comp_sz(GPUVertCompType type) +static uint comp_size(GPUVertCompType type) { #if TRUST_NO_ONE assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */ @@ -58,12 +58,12 @@ static uint comp_sz(GPUVertCompType type) return sizes[type]; } -static uint attr_sz(const GPUVertAttr *a) +static uint attr_size(const GPUVertAttr *a) { if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -71,7 +71,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -156,7 +156,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ - attr->sz = attr_sz(attr); + attr->size = attr_size(attr); attr->offset = 0; /* offsets & stride are calculated later (during pack) */ attr->fetch_mode = fetch_mode; @@ -294,13 +294,13 @@ uint padding(uint offset, uint alignment) } #if PACK_DEBUG -static void show_pack(uint a_idx, uint sz, uint pad) +static void show_pack(uint a_idx, uint size, uint pad) { const char c = 'A' + a_idx; for (uint i = 0; i < pad; i++) { putchar('-'); } - for (uint i = 0; i < sz; i++) { + for (uint i = 0; i < size; i++) { putchar(c); } } @@ -310,10 +310,10 @@ void VertexFormat_pack(GPUVertFormat *format) { GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; - uint offset = a0->sz; + uint offset = a0->size; #if PACK_DEBUG - show_pack(0, a0->sz, 0); + show_pack(0, a0->size, 0); #endif for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) { @@ -321,10 +321,10 @@ void VertexFormat_pack(GPUVertFormat *format) uint mid_padding = padding(offset, attr_align(a)); offset += mid_padding; a->offset = offset; - offset += a->sz; + offset += a->size; #if PACK_DEBUG - show_pack(a_idx, a->sz, mid_padding); + show_pack(a_idx, a->size, mid_padding); #endif } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index a0efe12f523..c3118ca320c 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -21,6 +21,7 @@ #include "DNA_userdef_types.h" #include "DNA_vec_types.h" +#include "GPU_capabilities.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_matrix.h" @@ -126,19 +127,23 @@ static void gpu_viewport_textures_create(GPUViewport *viewport) if (viewport->color_render_tx[0] == NULL) { viewport->color_render_tx[0] = GPU_texture_create_2d( "dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); - GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[0] = GPU_texture_create_2d( "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); - GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); + if (GPU_clear_viewport_workaround()) { + GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); + GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); + } } if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) { viewport->color_render_tx[1] = GPU_texture_create_2d( "dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL); - GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[1] = GPU_texture_create_2d( "dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); - GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); + if (GPU_clear_viewport_workaround()) { + GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); + GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); + } } /* Can be shared with GPUOffscreen. */ @@ -262,12 +267,15 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo return; } /* The composite framebuffer object needs to be created in the window context. */ - GPU_framebuffer_ensure_config(&viewport->stereo_comp_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), - GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), - }); + GPU_framebuffer_ensure_config( + &viewport->stereo_comp_fb, + { + GPU_ATTACHMENT_NONE, + /* We need the sRGB attachment to be first for GL_FRAMEBUFFER_SRGB to be turned on. + * Note that this is the opposite of what the texture binding is. */ + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), + }); GPUVertFormat *vert_format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 610fd5d980f..17d9b392c69 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -419,6 +419,13 @@ static void detect_workarounds() GCaps.shader_storage_buffer_objects_support = false; } + /* Certain Intel based platforms don't clear the viewport textures. Always clearing leads to + * noticeable performance regressions. */ + if (GPU_type_matches( + GPU_DEVICE_INTEL, static_cast<eGPUOSType>(GPU_OS_MAC | GPU_OS_UNIX), GPU_DRIVER_ANY)) { + GCaps.clear_viewport_workaround = true; + } + /* Metal-related Workarounds. */ /* Minimum Per-Vertex stride is 1 byte for OpenGL. */ @@ -503,6 +510,7 @@ void GLBackend::capabilities_init() glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &GCaps.max_shader_storage_buffer_bindings); + glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &GCaps.max_compute_shader_storage_blocks); } GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 2dde8d6c86b..e5b879f1f15 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -363,7 +363,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format) } /** - * Assume Unorm / Float target. Used with #glReadPixels. + * Assume UNORM/Float target. Used with #glReadPixels. */ inline GLenum channel_len_to_gl(int channel_len) { diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 04f60f10d41..cfcf77fe705 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -39,8 +39,8 @@ static uint16_t vbo_bind(const ShaderInterface *interface, const GPUVertAttr *a = &format->attrs[a_idx]; if (format->deinterleaved) { - offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len; - stride = a->sz; + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * v_len; + stride = a->size; } else { offset = a->offset; diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 5c97eada77d..6091a5c834a 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -191,8 +191,8 @@ struct GlobalData { vec3 N; /** Geometric Normal. */ vec3 Ng; - /** Surface default Tangent. */ - vec3 T; + /** Curve Tangent Space. */ + vec3 curve_T, curve_B, curve_N; /** Barycentric coordinates. */ vec2 barycentric_coords; vec3 barycentric_dists; diff --git a/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl index 9b1e6fe9d23..522f6de169d 100644 --- a/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl @@ -5,18 +5,6 @@ #define S3D_INTERLACE_COLUMN 1 #define S3D_INTERLACE_CHECKERBOARD 2 -/* Composite stereo textures */ - -#ifndef USE_GPU_SHADER_CREATE_INFO -uniform sampler2D imageTexture; -uniform sampler2D overlayTexture; - -uniform int stereoDisplaySettings; - -layout(location = 0) out vec4 imageColor; -layout(location = 1) out vec4 overlayColor; -#endif - #define stereo_display_mode (stereoDisplaySettings & ((1 << 3) - 1)) #define stereo_interlace_mode ((stereoDisplaySettings >> 3) & ((1 << 3) - 1)) #define stereo_interlace_swap bool(stereoDisplaySettings >> 6) diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh index 4b2d59cc159..3cacd0f4b8d 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh @@ -9,8 +9,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_stereo_merge) .vertex_in(0, Type::VEC2, "pos") - .fragment_out(0, Type::VEC4, "imageColor") - .fragment_out(1, Type::VEC4, "overlayColor") + .fragment_out(0, Type::VEC4, "overlayColor") + .fragment_out(1, Type::VEC4, "imageColor") .sampler(0, ImageType::FLOAT_2D, "imageTexture") .sampler(1, ImageType::FLOAT_2D, "overlayTexture") .push_constant(Type::MAT4, "ModelViewProjectionMatrix") diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh index e74e7df1a09..0a4da8d17fe 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_depth_only_info.hh @@ -18,4 +18,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only) GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only_clipped) .additional_info("gpu_shader_3D_depth_only") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh index d23c8985a5d..17d865dbaea 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_flat_color_info.hh @@ -21,4 +21,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color) GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color_clipped) .additional_info("gpu_shader_3D_flat_color") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh new file mode 100644 index 00000000000..94cf58933af --- /dev/null +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "gpu_interface_info.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(gpu_shader_3D_image) + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC2, "texCoord") + .vertex_out(smooth_tex_coord_interp_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .sampler(0, ImageType::FLOAT_2D, "image") + .vertex_source("gpu_shader_3D_image_vert.glsl") + .fragment_source("gpu_shader_image_frag.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh index 1f9c0056f7d..895787e4f1e 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_point_info.hh @@ -41,4 +41,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa) GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa_clipped) .additional_info("gpu_shader_3D_point_uniform_size_uniform_color_aa") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh index a0c21a4ac7c..4cabe110999 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_smooth_color_info.hh @@ -21,4 +21,5 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color) GPU_SHADER_CREATE_INFO(gpu_shader_3D_smooth_color_clipped) .additional_info("gpu_shader_3D_smooth_color") - .additional_info("gpu_clip_planes"); + .additional_info("gpu_clip_planes") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl index 99117400c57..3f42b6d9094 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl @@ -1,4 +1,4 @@ -void node_add_shader(Closure shader1, Closure shader2, out Closure shader) +void node_add_shader(inout Closure shader1, inout Closure shader2, out Closure shader) { shader = closure_add(shader1, shader2); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl index c81880184e3..530907859e9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl @@ -64,6 +64,8 @@ void node_eevee_specular(vec4 diffuse, else { result = closure_eval(diffuse_data, reflection_data); } - result = closure_add(result, closure_eval(emission_data)); - result = closure_add(result, closure_eval(transparency_data)); + Closure emission_cl = closure_eval(emission_data); + Closure transparency_cl = closure_eval(transparency_data); + result = closure_add(result, emission_cl); + result = closure_add(result, transparency_cl); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl index 5e86a4577ee..4c9ff31622f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl @@ -18,7 +18,7 @@ void node_geometry(vec3 orco, true_normal = g_data.Ng; if (g_data.is_strand) { - tangent = g_data.T; + tangent = g_data.curve_T; } else { tangent_orco_z(orco, orco); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl index 7bf8795495a..b24f9ab65f0 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl @@ -40,7 +40,7 @@ void node_bsdf_hair_principled(vec4 color, hair_data.color = color.rgb; hair_data.offset = offset; hair_data.roughness = vec2(0.0); - hair_data.T = g_data.T; + hair_data.T = g_data.curve_B; result = closure_eval(hair_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 8e878b6e14b..61458b05c86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -5,14 +5,14 @@ void node_hair_info(float hair_length, out float intercept, out float out_length, out float thickness, - out vec3 tangent, + out vec3 normal, out float random) { is_strand = float(g_data.is_strand); intercept = g_data.hair_time; thickness = g_data.hair_thickness; out_length = hair_length; - tangent = g_data.T; + normal = g_data.curve_N; /* TODO: could be precomputed per strand instead. */ random = wang_hash_noise(uint(g_data.hair_strand_id)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl index c303d21d7c1..00cfba3ca12 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl @@ -1,4 +1,4 @@ -void node_mix_shader(float fac, Closure shader1, Closure shader2, out Closure shader) +void node_mix_shader(float fac, inout Closure shader1, inout Closure shader2, out Closure shader) { shader = closure_mix(shader1, shader2, fac); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index 033dc05c57d..2e695fa3e14 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -169,6 +169,8 @@ void node_bsdf_principled(vec4 base_color, /* Un-optimized case. */ result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data); } - result = closure_add(result, closure_eval(emission_data)); - result = closure_add(result, closure_eval(transparency_data)); + Closure emission_cl = closure_eval(emission_data); + Closure transparency_cl = closure_eval(transparency_data); + result = closure_add(result, emission_cl); + result = closure_add(result, transparency_cl); } diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc index 6ef8a032a73..5dc70a8bf0f 100644 --- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -35,6 +35,7 @@ static void test_shader_builtin() test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 096089d4c41..0052ce19aa1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -423,7 +423,7 @@ static int startavi(struct anim *anim) anim->cur_position = 0; # if 0 - printf("x:%d y:%d size:%d interl:%d dur:%d\n", + printf("x:%d y:%d size:%d interlace:%d dur:%d\n", anim->x, anim->y, anim->framesize, diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 80fcceb0aa7..cffa61977f7 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -286,8 +286,8 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, } if (max_size > 0) { - /* libjpeg can more quickly decompress while scaling down to 1/2, 1/4, 1/8, - * while libjpeg-turbo can also do 3/8, 5/8, etc. But max is 1/8. */ + /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8, + * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */ float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height); cinfo->scale_denom = 8; cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * (float)cinfo->scale_denom))); @@ -520,9 +520,9 @@ struct ImBuf *imb_thumbnail_jpeg(const char *filepath, if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) && (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) { - /* This is a JPEG in Exif format (SOI + APP1), not JFIF (SOI + APP0). */ + /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */ unsigned int i = JPEG_APP1_MAX; - /* All Exif data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ + /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) && !feof(infile) && i--) ; diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 9948aaac5da..2281d8d85b3 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1394,12 +1394,10 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; - char tokenbuf[EXR_TOT_MAXNAME]; - int len; /* some multilayers have the combined buffer with names A B G R saved */ if (name[1] == 0) { - echan->chan_id = name[0]; + echan->chan_id = BLI_toupper_ascii(name[0]); layname[0] = '\0'; if (ELEM(name[0], 'R', 'G', 'B', 'A')) { @@ -1416,13 +1414,17 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is channel identifier */ - len = imb_exr_split_token(name, end, &token); + size_t len = imb_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; } + + char channelname[EXR_TOT_MAXNAME]; + BLI_strncpy(channelname, token, std::min(len + 1, sizeof(channelname))); + if (len == 1) { - echan->chan_id = token[0]; + echan->chan_id = BLI_toupper_ascii(channelname[0]); } else if (len > 1) { bool ok = false; @@ -1436,36 +1438,35 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa * * Here we do some magic to distinguish such cases. */ - if (ELEM(token[1], 'X', 'Y', 'Z') || ELEM(token[1], 'R', 'G', 'B') || - ELEM(token[1], 'U', 'V', 'A')) { - echan->chan_id = token[1]; + const char chan_id = BLI_toupper_ascii(channelname[1]); + if (ELEM(chan_id, 'X', 'Y', 'Z', 'R', 'G', 'B', 'U', 'V', 'A')) { + echan->chan_id = chan_id; ok = true; } } - else if (BLI_strcaseeq(token, "red")) { + else if (BLI_strcaseeq(channelname, "red")) { echan->chan_id = 'R'; ok = true; } - else if (BLI_strcaseeq(token, "green")) { + else if (BLI_strcaseeq(channelname, "green")) { echan->chan_id = 'G'; ok = true; } - else if (BLI_strcaseeq(token, "blue")) { + else if (BLI_strcaseeq(channelname, "blue")) { echan->chan_id = 'B'; ok = true; } - else if (BLI_strcaseeq(token, "alpha")) { + else if (BLI_strcaseeq(channelname, "alpha")) { echan->chan_id = 'A'; ok = true; } - else if (BLI_strcaseeq(token, "depth")) { + else if (BLI_strcaseeq(channelname, "depth")) { echan->chan_id = 'Z'; ok = true; } if (ok == false) { - BLI_strncpy(tokenbuf, token, std::min(len + 1, EXR_TOT_MAXNAME)); - printf("multilayer read: unknown channel token: %s\n", tokenbuf); + printf("multilayer read: unknown channel token: %s\n", channelname); return 0; } } diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 5abbc84b0ea..8da9eb9ccf7 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -154,7 +154,7 @@ GPUTexture *IMB_touch_gpu_texture( GPUTexture *tex; if (layers > 0) { - tex = GPU_texture_create_2d_array(name, w, h, layers, 1, tex_format, NULL); + tex = GPU_texture_create_2d_array(name, w, h, layers, 9999, tex_format, NULL); } else { tex = GPU_texture_create_2d(name, w, h, 9999, tex_format, NULL); diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 2d2dcfb1f42..8c62484028d 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -622,7 +622,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec if (read_mesh != mesh) { /* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); mesh->flag |= autosmooth; } diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index b1add38bf01..e80bd850825 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -8,7 +8,6 @@ set(INC ../../depsgraph ../../makesdna ../../../../intern/guardedalloc - ../../../../extern/fast_float ) set(INC_SYS @@ -19,11 +18,12 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc - intern/string_utils.cc + intern/path_util.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh - IO_string_utils.hh + IO_path_util.hh + IO_path_util_types.h IO_types.h intern/dupli_parent_finder.hh ) @@ -42,7 +42,6 @@ if(WITH_GTESTS) intern/abstract_hierarchy_iterator_test.cc intern/hierarchy_context_order_test.cc intern/object_identifier_test.cc - intern/string_utils_test.cc ) set(TEST_INC ../../blenloader diff --git a/source/blender/io/common/IO_path_util.hh b/source/blender/io/common/IO_path_util.hh new file mode 100644 index 00000000000..eeb5a9dbcfe --- /dev/null +++ b/source/blender/io/common/IO_path_util.hh @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include "BLI_set.hh" +#include "BLI_string_ref.hh" + +#include "IO_path_util_types.h" + +namespace blender::io { + +/** + * Return a filepath relative to a destination directory, for use with + * exporters. + * + * When PATH_REFERENCE_COPY mode is used, the file path pair (source + * path, destination path) is added to the `copy_set`. + * + * Equivalent of bpy_extras.io_utils.path_reference. + */ +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set = nullptr); + +/** Execute copying files of path_reference. */ +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set); + +} // namespace blender::io diff --git a/source/blender/io/common/IO_path_util_types.h b/source/blender/io/common/IO_path_util_types.h new file mode 100644 index 00000000000..0233f601a81 --- /dev/null +++ b/source/blender/io/common/IO_path_util_types.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +/** Method used to reference paths. Equivalent of bpy_extras.io_utils.path_reference_mode. */ +typedef enum { + /** Use Relative paths with subdirectories only. */ + PATH_REFERENCE_AUTO = 0, + /** Always write absolute paths. */ + PATH_REFERENCE_ABSOLUTE = 1, + /** Write relative paths where possible. */ + PATH_REFERENCE_RELATIVE = 2, + /** Match Absolute/Relative setting with input path. */ + PATH_REFERENCE_MATCH = 3, + /** Filename only. */ + PATH_REFERENCE_STRIP = 4, + /** Copy the file to the destination path. */ + PATH_REFERENCE_COPY = 5, +} ePathReferenceMode; diff --git a/source/blender/io/common/intern/path_util.cc b/source/blender/io/common/intern/path_util.cc new file mode 100644 index 00000000000..902cf552bf0 --- /dev/null +++ b/source/blender/io/common/intern/path_util.cc @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "IO_path_util.hh" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +namespace blender::io { + +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set) +{ + const bool is_relative = BLI_path_is_rel(filepath.c_str()); + char filepath_abs[PATH_MAX]; + BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX); + BLI_path_abs(filepath_abs, base_src.c_str()); + BLI_path_normalize(nullptr, filepath_abs); + + /* Figure out final mode to be used. */ + if (mode == PATH_REFERENCE_MATCH) { + mode = is_relative ? PATH_REFERENCE_RELATIVE : PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_AUTO) { + mode = BLI_path_contains(base_dst.c_str(), filepath_abs) ? PATH_REFERENCE_RELATIVE : + PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_COPY) { + char filepath_cpy[PATH_MAX]; + BLI_path_join( + filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr); + copy_set->add(std::make_pair(filepath_abs, filepath_cpy)); + BLI_strncpy(filepath_abs, filepath_cpy, PATH_MAX); + mode = PATH_REFERENCE_RELATIVE; + } + + /* Now we know the final path mode. */ + if (mode == PATH_REFERENCE_ABSOLUTE) { + return filepath_abs; + } + else if (mode == PATH_REFERENCE_RELATIVE) { + char rel_path[PATH_MAX]; + BLI_strncpy(rel_path, filepath_abs, PATH_MAX); + BLI_path_rel(rel_path, base_dst.c_str()); + /* Can't always find relative path (e.g. between different drives). */ + if (!BLI_path_is_rel(rel_path)) { + return filepath_abs; + } + return rel_path + 2; /* Skip blender's internal "//" prefix. */ + } + else if (mode == PATH_REFERENCE_STRIP) { + return BLI_path_basename(filepath_abs); + } + BLI_assert_msg(false, "Invalid path reference mode"); + return filepath_abs; +} + +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set) +{ + for (const auto © : copy_set) { + const char *src = copy.first.c_str(); + const char *dst = copy.second.c_str(); + if (!BLI_exists(src)) { + fprintf(stderr, "Missing source file '%s', not copying\n", src); + continue; + } + if (0 == BLI_path_cmp_normalized(src, dst)) { + continue; /* Source and dest are the same. */ + } + if (!BLI_make_existing_file(dst)) { + fprintf(stderr, "Can't make directory for '%s', not copying\n", dst); + continue; + } + if (!BLI_copy(src, dst)) { + fprintf(stderr, "Can't copy '%s' to '%s'\n", src, dst); + continue; + } + } +} + +} // namespace blender::io diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 328b4109616..e2562eca69b 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -200,7 +200,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) if (read_mesh != mesh) { /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true); mesh->flag |= autosmooth; } diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index e0fe7ed4992..f7958ef4ec6 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../makesrna ../../nodes ../../windowmanager + ../../../../extern/fast_float ../../../../extern/fmtlib/include ../../../../intern/guardedalloc ) @@ -35,6 +36,7 @@ set(SRC importer/obj_import_mesh.cc importer/obj_import_mtl.cc importer/obj_import_nurbs.cc + importer/obj_import_string_utils.cc importer/obj_importer.cc IO_wavefront_obj.h @@ -50,6 +52,7 @@ set(SRC importer/obj_import_mtl.hh importer/obj_import_nurbs.hh importer/obj_import_objects.hh + importer/obj_import_string_utils.hh importer/obj_importer.hh ) @@ -69,6 +72,7 @@ blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/obj_exporter_tests.cc + tests/obj_import_string_utils_tests.cc tests/obj_importer_tests.cc tests/obj_mtl_parser_tests.cc diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 8b71ec750c0..f7bf678310f 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -9,6 +9,7 @@ #include "BKE_context.h" #include "BLI_path_util.h" #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #ifdef __cplusplus extern "C" { @@ -37,6 +38,8 @@ static const int TOTAL_AXES = 3; struct OBJExportParams { /** Full path to the destination .OBJ file. */ char filepath[FILE_MAX]; + /** Pretend that destination file folder is this, if non-empty. Used only for tests. */ + char file_base_for_tests[FILE_MAX]; /** Full path to current blender file (used for comments in output). */ const char *blen_filepath; @@ -62,6 +65,7 @@ struct OBJExportParams { bool export_materials; bool export_triangulated_mesh; bool export_curves_as_nurbs; + ePathReferenceMode path_mode; /* Grouping options. */ bool export_object_groups; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 194583e71fe..b027df73b1e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -13,6 +13,8 @@ #include "BLI_path_util.h" #include "BLI_task.hh" +#include "IO_path_util.hh" + #include "obj_export_mesh.hh" #include "obj_export_mtl.hh" #include "obj_export_nurbs.hh" @@ -530,7 +532,11 @@ void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material) void MTLWriter::write_texture_map( const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map) + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode path_mode, + Set<std::pair<std::string, std::string>> ©_set) { std::string options; /* Option strings should have their own leading spaces. */ @@ -546,7 +552,11 @@ void MTLWriter::write_texture_map( #define SYNTAX_DISPATCH(eMTLSyntaxElement) \ if (texture_map.key == eMTLSyntaxElement) { \ - fmt_handler_.write<eMTLSyntaxElement>(options, texture_map.value.image_path); \ + std::string path = path_reference( \ + texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set); \ + /* Always emit forward slashes for cross-platform compatibility. */ \ + std::replace(path.begin(), path.end(), '\\', '/'); \ + fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \ return; \ } @@ -561,25 +571,35 @@ void MTLWriter::write_texture_map( BLI_assert(!"This map type was not written to the file."); } -void MTLWriter::write_materials() +void MTLWriter::write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir) { if (mtlmaterials_.size() == 0) { return; } + + char blen_filedir[PATH_MAX]; + BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX); + BLI_path_slash_native(blen_filedir); + BLI_path_normalize(nullptr, blen_filedir); + std::sort(mtlmaterials_.begin(), mtlmaterials_.end(), [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; }); + Set<std::pair<std::string, std::string>> copy_set; for (const MTLMaterial &mtlmat : mtlmaterials_) { fmt_handler_.write<eMTLSyntaxElement::string>("\n"); fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name); write_bsdf_properties(mtlmat); - for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map : - mtlmat.texture_maps.items()) { - if (!texture_map.value.image_path.empty()) { - write_texture_map(mtlmat, texture_map); + for (const auto &tex : mtlmat.texture_maps.items()) { + if (tex.value.image_path.empty()) { + continue; } + write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set); } } + path_reference_copy(copy_set); } Vector<int> MTLWriter::add_materials(const OBJMesh &mesh_to_export) diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 96f7d434338..77da7b44276 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -9,6 +9,7 @@ #include "DNA_meshdata_types.h" #include "BLI_map.hh" +#include "BLI_set.hh" #include "BLI_vector.hh" #include "IO_wavefront_obj.h" @@ -181,7 +182,9 @@ class MTLWriter : NonMovable, NonCopyable { * For consistency of output from run to run (useful for testing), * the materials are sorted by name before writing. */ - void write_materials(); + void write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir); StringRefNull mtl_file_path() const; /** * Add the materials of the given object to #MTLWriter, de-duplicating @@ -203,6 +206,10 @@ class MTLWriter : NonMovable, NonCopyable { * Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image". */ void write_texture_map(const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map); + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> ©_set); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index c48d5a5f7f0..4ed148ec64e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -113,8 +113,7 @@ static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> socket /** * From a texture image shader node, get the image's filepath. - * Returned filepath is stripped of initial "//". If packed image is found, - * only the file "name" is returned. + * If packed image is found, only the file "name" is returned. */ static const char *get_image_filepath(const bNode *tex_node) { @@ -134,9 +133,6 @@ static const char *get_image_filepath(const bNode *tex_node) "directory as the .MTL file.\n", path); } - if (path[0] == '/' && path[1] == '/') { - path += 2; - } return path; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 78b709c884a..b6e636b389d 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -284,7 +284,16 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params); if (mtl_writer) { mtl_writer->write_header(export_params.blen_filepath); - mtl_writer->write_materials(); + char dest_dir[PATH_MAX]; + if (export_params.file_base_for_tests[0] == '\0') { + BLI_split_dir_part(export_params.filepath, dest_dir, PATH_MAX); + } + else { + BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX); + } + BLI_path_slash_native(dest_dir); + BLI_path_normalize(nullptr, dest_dir); + mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir); } write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer); } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index be322f49840..fa89b49b605 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -8,9 +8,8 @@ #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "IO_string_utils.hh" - #include "obj_import_file_reader.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index fc40333c24d..8d560bd2c8c 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -62,7 +62,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, transform_object(obj, import_params); /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ - const short autosmooth = (mesh->flag & ME_AUTOSMOOTH); + const uint16_t autosmooth = (mesh->flag & ME_AUTOSMOOTH); Mesh *dst = static_cast<Mesh *>(obj->data); BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true); dst->flag |= autosmooth; diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index c2ecd8a37de..f39def0a4af 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -13,13 +13,12 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" -#include "IO_string_utils.hh" - #include "NOD_shader.h" /* TODO: move eMTLSyntaxElement out of following file into a more neutral place */ #include "obj_export_io.hh" #include "obj_import_mtl.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index 3a12250e14b..c60306c8375 100644 --- a/source/blender/io/common/intern/string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "IO_string_utils.hh" +#include "obj_import_string_utils.hh" /* Note: we could use C++17 <charconv> from_chars to parse * floats, but even if some compilers claim full support, @@ -11,7 +11,7 @@ #include "fast_float.h" #include <charconv> -namespace blender::io { +namespace blender::io::obj { StringRef read_next_line(StringRef &buffer) { @@ -96,4 +96,4 @@ StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space) return StringRef(res.ptr, str.end()); } -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/io/common/IO_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh index 25f1f01c6ed..532224569ac 100644 --- a/source/blender/io/common/IO_string_utils.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -5,10 +5,13 @@ #include "BLI_string_ref.hh" /* - * Various text parsing utilities commonly used by text-based input formats. + * Various text parsing utilities used by OBJ importer. + * The utilities are not directly usable by other formats, since + * they treat backslash (\) as a whitespace character (OBJ format + * allows backslashes to function as a line-continuation character). */ -namespace blender::io { +namespace blender::io::obj { /** * Fetches next line from an input string buffer. @@ -18,7 +21,7 @@ namespace blender::io { * the input line. * * Note that backslash (\) character is treated as a line - * continuation, similar to OBJ file format or a C preprocessor. + * continuation. */ StringRef read_next_line(StringRef &buffer); @@ -66,4 +69,4 @@ StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space */ StringRef parse_floats(StringRef str, float fallback, float *dst, int count); -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index f74bfc155dd..8c49af90a82 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -11,12 +11,15 @@ #include "BKE_appdir.h" #include "BKE_blender_version.h" +#include "BKE_main.h" #include "BLI_fileops.h" #include "BLI_index_range.hh" #include "BLI_string_utf8.h" #include "BLI_vector.hh" +#include "BLO_readfile.h" + #include "DEG_depsgraph.h" #include "obj_export_file_writer.hh" @@ -259,11 +262,12 @@ class obj_exporter_regression_test : public obj_exporter_test { std::string tempdir = std::string(BKE_tempdir_base()); std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str()); strncpy(params.filepath, out_file_path.c_str(), FILE_MAX - 1); - params.blen_filepath = blendfile.c_str(); + params.blen_filepath = bfile->main->filepath; + std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; + BLI_split_dir_part(golden_file_path.c_str(), params.file_base_for_tests, PATH_MAX); export_frame(depsgraph, params, out_file_path.c_str()); std::string output_str = read_temp_file_in_string(out_file_path); - std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; std::string golden_str = read_temp_file_in_string(golden_file_path); bool are_equal = strings_equal_after_first_lines(output_str, golden_str); if (save_failing_test_output && !are_equal) { @@ -432,19 +436,26 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) _export.params); } -/* Note: texture paths in the resulting mtl file currently are always - * as they are stored in the source .blend file; not relative to where - * the export is done. When that is properly fixed, the expected .mtl - * file should be updated. */ -TEST_F(obj_exporter_regression_test, cubes_with_textures) +TEST_F(obj_exporter_regression_test, cubes_with_textures_strip) { OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_STRIP; compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", "io_tests/obj/cubes_with_textures.obj", "io_tests/obj/cubes_with_textures.mtl", _export.params); } +TEST_F(obj_exporter_regression_test, cubes_with_textures_relative) +{ + OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_RELATIVE; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", + "io_tests/obj/cubes_with_textures_rel.obj", + "io_tests/obj/cubes_with_textures_rel.mtl", + _export.params); +} + TEST_F(obj_exporter_regression_test, suzanne_all_data) { OBJExportParamsDefault _export; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 6a821e0b1bf..ef27a65fb4b 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -11,6 +11,7 @@ struct OBJExportParamsDefault { OBJExportParamsDefault() { params.filepath[0] = '\0'; + params.file_base_for_tests[0] = '\0'; params.blen_filepath = ""; params.export_animation = false; params.start_frame = 0; @@ -26,6 +27,7 @@ struct OBJExportParamsDefault { params.export_uv = true; params.export_normals = true; params.export_materials = true; + params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; params.export_curves_as_nurbs = false; diff --git a/source/blender/io/common/intern/string_utils_test.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc index a78bd7ab8a3..addb1fa473e 100644 --- a/source/blender/io/common/intern/string_utils_test.cc +++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc @@ -1,14 +1,14 @@ /* SPDX-License-Identifier: Apache-2.0 */ -#include "IO_string_utils.hh" +#include "obj_import_string_utils.hh" #include "testing/testing.h" -namespace blender::io { +namespace blender::io::obj { #define EXPECT_STRREF_EQ(str1, str2) EXPECT_STREQ(str1, std::string(str2).c_str()) -TEST(string_utils, read_next_line) +TEST(obj_import_string_utils, read_next_line) { std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; StringRef s = str; @@ -21,7 +21,7 @@ TEST(string_utils, read_next_line) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, drop_whitespace) +TEST(obj_import_string_utils, drop_whitespace) { /* Empty */ EXPECT_STRREF_EQ("", drop_whitespace("")); @@ -39,7 +39,7 @@ TEST(string_utils, drop_whitespace) EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); } -TEST(string_utils, parse_int_valid) +TEST(obj_import_string_utils, parse_int_valid) { std::string str = "1 -10 \t 1234 1234567890 +7 123a"; StringRef s = str; @@ -59,7 +59,7 @@ TEST(string_utils, parse_int_valid) EXPECT_STRREF_EQ("a", s); } -TEST(string_utils, parse_int_invalid) +TEST(obj_import_string_utils, parse_int_invalid) { int val; /* Invalid syntax */ @@ -75,7 +75,7 @@ TEST(string_utils, parse_int_invalid) EXPECT_EQ(val, -4); } -TEST(string_utils, parse_float_valid) +TEST(obj_import_string_utils, parse_float_valid) { std::string str = "1 -10 123.5 -17.125 0.1 1e6 50.0e-1"; StringRef s = str; @@ -97,7 +97,7 @@ TEST(string_utils, parse_float_valid) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, parse_float_invalid) +TEST(obj_import_string_utils, parse_float_invalid) { float val; /* Invalid syntax */ @@ -115,4 +115,4 @@ TEST(string_utils, parse_float_invalid) EXPECT_EQ(val, -4.0f); } -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 1d5f1351de0..f409d1c0442 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -618,6 +618,7 @@ typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_GROW_SHRINK_INVERT = (1 << 1), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 2), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 3), + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT = (1 << 4), } eBrushCurvesSculptFlag; #define MAX_BRUSH_PIXEL_RADIUS 500 diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index e2ad5d38bbc..2388f04cc39 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -110,11 +110,11 @@ typedef struct CurvesGeometry { /** * The total number of control points in all curves. */ - int point_size; + int point_num; /** * The number of curves in the data-block. */ - int curve_size; + int curve_num; /** * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 4e66e2446f0..80f592bb66d 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -64,6 +64,11 @@ typedef struct ImageView { typedef struct ImagePackedFile { struct ImagePackedFile *next, *prev; struct PackedFile *packedfile; + + /* Which view and tile this ImagePackedFile represents. Normal images will use 0 and 1001 + * respectively when creating their ImagePackedFile. Must be provided for each packed image. */ + int view; + int tile_number; /** 1024 = FILE_MAX. */ char filepath[1024]; } ImagePackedFile; diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 97e057361c1..4a57f60be26 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -280,7 +280,7 @@ typedef struct Mesh { /** Various flags used when editing the mesh. */ char editflag; /** Mostly more flags used when editing or displaying the mesh. */ - unsigned short flag; + uint16_t flag; /** * The angle for auto smooth in radians. `M_PI` (180 degrees) causes all edges to be smooth. diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index a8fa1fd4271..275a89ec680 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -652,7 +652,7 @@ typedef struct UserDef_Experimental { char use_override_templates; char enable_eevee_next; char use_sculpt_texture_paint; - char _pad0[1]; + char use_draw_manager_acquire_lock; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 281aeae7a60..f25ff5fbbb8 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -61,6 +61,8 @@ DNA_STRUCT_RENAME_ELEM(Curve, ext1, extrude) DNA_STRUCT_RENAME_ELEM(Curve, ext2, bevel_radius) DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Curve, width, offset) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, curve_size, curve_num) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, point_size, point_num) DNA_STRUCT_RENAME_ELEM(CustomDataExternal, filename, filepath) DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b65e08311fe..b0488bbfa7a 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1976,7 +1976,7 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Extra User", - "Indicates wether an extra user is set or not (mainly for internal/debug usages)"); + "Indicates whether an extra user is set or not (mainly for internal/debug usages)"); RNA_def_property_boolean_funcs(prop, NULL, "rna_ID_extra_user_set"); prop = RNA_def_property(srna, "is_embedded_data", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f7edba24dea..4767ef2c017 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1951,7 +1951,7 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); - RNA_def_property_ui_text(prop, "Add Amount", "Number of curves added by the Add brush"); + RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 2, INT32_MAX); @@ -1975,6 +1975,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT); + RNA_def_property_ui_text(prop, + "Interpolate Point Count", + "Use the number of points from the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 2dc568d0b8a..60530eb8bf9 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -38,7 +38,7 @@ static Curves *rna_curves(PointerRNA *ptr) static int rna_Curves_curve_offset_data_length(PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); - return curves->geometry.curve_size + 1; + return curves->geometry.curve_num + 1; } static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -47,7 +47,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, rna_iterator_array_begin(iter, (void *)curves->geometry.curve_offsets, sizeof(int), - curves->geometry.curve_size + 1, + curves->geometry.curve_num + 1, false, NULL); } @@ -222,7 +222,7 @@ static void rna_def_curves(BlenderRNA *brna) /* Point and Curve RNA API helpers. */ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_num"); RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); @@ -230,7 +230,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "CurvePoint"); RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); RNA_define_verify_sdna(1); @@ -239,7 +239,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); RNA_def_property_update(prop, 0, "rna_Curves_update_data"); RNA_define_verify_sdna(1); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 08986afe15e..05e8d5406b4 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -233,8 +233,8 @@ static const EnumPropertyItem gpencil_envelope_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem modifier_noise_random_mode_items[] = { - {GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Apply random every N steps"}, - {GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "On Keyframes", "Apply random every keyframe"}, + {GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Randomize every number of frames"}, + {GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "Keyframes", "Randomize on keyframes only"}, {0, NULL, 0, NULL, NULL}, }; #endif @@ -929,8 +929,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "step"); RNA_def_property_range(prop, 1, 100); - RNA_def_property_ui_text( - prop, "Step", "Number of frames before recalculate random values again"); + RNA_def_property_ui_text(prop, "Step", "Number of frames interval between randomization steps"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); @@ -967,7 +966,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop = RNA_def_property(srna, "random_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "noise_mode"); RNA_def_property_enum_items(prop, modifier_noise_random_mode_items); - RNA_def_property_ui_text(prop, "Mode", "How the random changes are applied"); + RNA_def_property_ui_text(prop, "Mode", "Where to perform randomization"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); RNA_define_lib_overridable(false); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index bd3b03add95..81708ac8e65 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -737,6 +737,16 @@ static void rna_def_image_packed_files(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "filepath"); RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "view", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "view"); + RNA_def_property_ui_text(prop, "View Index", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "tile_number", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "tile_number"); + RNA_def_property_ui_text(prop, "Tile Number", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_api_image_packed_file(srna); } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 897573f9fd9..7bd2040dab0 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -161,9 +161,8 @@ static void rna_Image_unpack(Image *image, Main *bmain, ReportList *reports, int if (!BKE_image_has_packedfile(image)) { BKE_report(reports, RPT_ERROR, "Image not packed"); } - else if (BKE_image_has_multiple_ibufs(image)) { - BKE_report( - reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + else if (ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + BKE_report(reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return; } else { diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 456f774648a..6dd7fe53774 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -130,7 +130,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_EDGESPLIT, "Edge Split", "Split away joined faces at the edges"}, - {eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""}, + {eModifierType_Nodes, "NODES", ICON_GEOMETRY_NODES, "Geometry Nodes", ""}, {eModifierType_Mask, "MASK", ICON_MOD_MASK, @@ -6991,7 +6991,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Nodes Modifier", ""); RNA_def_struct_sdna(srna, "NodesModifierData"); RNA_def_struct_idprops_func(srna, "rna_NodesModifier_properties"); - RNA_def_struct_ui_icon(srna, ICON_NODETREE); + RNA_def_struct_ui_icon(srna, ICON_GEOMETRY_NODES); RNA_define_lib_overridable(true); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 4cd12acd038..9b9afadbd05 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -12369,7 +12369,7 @@ static void rna_def_nodetree(BlenderRNA *brna) {NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"}, {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, - {NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"}, + {NTREE_GEOMETRY, "GEOMETRY", ICON_GEOMETRY_NODES, "Geometry", "Geometry nodes"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 4a7e16e50fa..0907aa884e6 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3292,7 +3292,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ {FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO, "category_geometry", - ICON_NODETREE, + ICON_GEOMETRY_NODES, "Geometry", "Show meshes, curves, lattice, armatures and metaballs data"}, {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 5ac324b3627..b3d4ae80713 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6424,6 +6424,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1); RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode"); + prop = RNA_def_property(srna, "use_draw_manager_acquire_lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_draw_manager_acquire_lock", 1); + RNA_def_property_ui_text( + prop, "Draw Manager Locking", "Don't lock UI during background rendering"); + prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text(prop, "Extended Asset Browser", diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index a04b29b8815..696d2d0f31d 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -1196,6 +1196,50 @@ static int rna_XrEventData_action_length(PointerRNA *ptr) # endif } +static void rna_XrEventData_user_path_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_user_path_other_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path_other); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_other_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path_other); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrEventData_type_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -2402,6 +2446,19 @@ static void rna_def_xr_eventdata(BlenderRNA *brna) prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL); RNA_def_property_ui_text(prop, "Action", "XR action name"); + prop = RNA_def_property(srna, "user_path", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_get", "rna_XrEventData_user_path_length", NULL); + RNA_def_property_ui_text(prop, "User Path", "User path of the action. E.g. \"/user/hand/left\""); + + prop = RNA_def_property(srna, "user_path_other", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_other_get", "rna_XrEventData_user_path_other_length", NULL); + RNA_def_property_ui_text( + prop, "User Path Other", "Other user path, for bimanual actions. E.g. \"/user/hand/right\""); + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_items(prop, rna_enum_xr_action_types); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a5e5bf36dcd..1aac3c2191d 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -70,7 +70,7 @@ set(SRC intern/MOD_normal_edit.c intern/MOD_ocean.c intern/MOD_particleinstance.c - intern/MOD_particlesystem.c + intern/MOD_particlesystem.cc intern/MOD_remesh.c intern/MOD_screw.c intern/MOD_shapekey.c diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 21041e8e1b2..cdf16d813f3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -985,17 +985,16 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store( if (!component.attribute_domain_supported(domain)) { continue; } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); blender::bke::GeometryComponentFieldContext field_context{component, domain}; - blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_num}; for (const OutputAttributeInfo &output_info : outputs_info) { const CPPType &type = output_info.field.cpp_type(); OutputAttributeToStore store{ component_type, domain, output_info.name, - GMutableSpan{ - type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}}; + GMutableSpan{type, MEM_malloc_arrayN(domain_num, type.size(), __func__), domain_num}}; field_evaluator.add_with_destination(output_info.field, store.data); attributes_to_store.append(store); } @@ -1799,7 +1798,7 @@ ModifierTypeInfo modifierType_Nodes = { eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), - /* icon */ ICON_NODETREE, + /* icon */ ICON_GEOMETRY_NODES, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.cc index 032227307e7..0f75038189a 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -51,11 +51,11 @@ static void freeData(ModifierData *md) ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } psmd->totdmvert = psmd->totdmedge = psmd->totdmface = 0; @@ -81,8 +81,8 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla * code has to be called then to ensure proper remapping of that pointer. See e.g. * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */ - tpsmd->mesh_final = NULL; - tpsmd->mesh_original = NULL; + tpsmd->mesh_final = nullptr; + tpsmd->mesh_original = nullptr; tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0; } @@ -104,7 +104,7 @@ static void deformVerts(ModifierData *md, { Mesh *mesh_src = mesh; ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - ParticleSystem *psys = NULL; + ParticleSystem *psys = nullptr; if (ctx->object->particlesystem.first) { psys = psmd->psys; @@ -117,28 +117,28 @@ static void deformVerts(ModifierData *md, return; } - if (mesh_src == NULL) { + if (mesh_src == nullptr) { mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, NULL, vertexCos, verts_num, false, true); - if (mesh_src == NULL) { + ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true); + if (mesh_src == nullptr) { return; } } /* clear old dm */ - bool had_mesh_final = (psmd->mesh_final != NULL); + bool had_mesh_final = (psmd->mesh_final != nullptr); if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } else if (psmd->flag & eParticleSystemFlag_file_loaded) { /* in file read mesh just wasn't saved in file so no need to reset everything */ psmd->flag &= ~eParticleSystemFlag_file_loaded; - if (psys->particles == NULL) { + if (psys->particles == nullptr) { psys->recalc |= ID_RECALC_PSYS_RESET; } /* TODO(sergey): This is not how particles were working prior to copy on @@ -165,18 +165,18 @@ static void deformVerts(ModifierData *md, /* Get the original mesh from the object, this is what the particles * are attached to so in case of non-deform modifiers we need to remap * them to the final mesh (typically subdivision surfaces). */ - Mesh *mesh_original = NULL; + Mesh *mesh_original = nullptr; if (ctx->object->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(ctx->object); if (em) { /* In edit mode get directly from the edit mesh. */ - psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, mesh); + psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, mesh); } else { /* Otherwise get regular mesh. */ - mesh_original = ctx->object->data; + mesh_original = static_cast<Mesh *>(ctx->object->data); } } else { @@ -193,8 +193,8 @@ static void deformVerts(ModifierData *md, BKE_mesh_tessface_ensure(psmd->mesh_original); } - if (!ELEM(mesh_src, NULL, mesh, psmd->mesh_final)) { - BKE_id_free(NULL, mesh_src); + if (!ELEM(mesh_src, nullptr, mesh, psmd->mesh_final)) { + BKE_id_free(nullptr, mesh_src); } /* Report change in mesh structure. @@ -221,7 +221,7 @@ static void deformVerts(ModifierData *md, if (DEG_is_active(ctx->depsgraph)) { Object *object_orig = DEG_get_original_object(ctx->object); ModifierData *md_orig = BKE_modifiers_findby_name(object_orig, psmd->modifier.name); - BLI_assert(md_orig != NULL); + BLI_assert(md_orig != nullptr); ParticleSystemModifierData *psmd_orig = (ParticleSystemModifierData *)md_orig; psmd_orig->flag = psmd->flag; } @@ -237,16 +237,16 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - const bool do_temp_mesh = (mesh == NULL); + const bool do_temp_mesh = (mesh == nullptr); if (do_temp_mesh) { mesh = BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name); - BM_mesh_bm_to_me(NULL, editData->bm, mesh, &((BMeshToMeshParams){0})); + BM_mesh_bm_to_me(nullptr, editData->bm, mesh, &((BMeshToMeshParams){0})); } deformVerts(md, ob, mesh, vertexCos, verts_num); if (derivedData) { - BKE_id_free(NULL, mesh); + BKE_id_free(nullptr, mesh); } } #endif @@ -258,7 +258,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA ob_ptr; PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - Object *ob = ob_ptr.data; + Object *ob = static_cast<Object *>(ob_ptr.data); ModifierData *md = (ModifierData *)ptr->data; ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; @@ -291,8 +291,8 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) { ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - psmd->mesh_final = NULL; - psmd->mesh_original = NULL; + psmd->mesh_final = nullptr; + psmd->mesh_original = nullptr; /* This is written as part of ob->particlesystem. */ BLO_read_data_address(reader, &psmd->psys); psmd->flag &= ~eParticleSystemFlag_psys_updated; @@ -315,23 +315,23 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* copyData */ copyData, /* deformVerts */ deformVerts, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ NULL, - /* modifyGeometrySet */ NULL, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ nullptr, + /* modifyGeometrySet */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, + /* blendWrite */ nullptr, /* blendRead */ blendRead, }; diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index aef254b1103..c37d6a3f2c1 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -11,6 +11,10 @@ #include "DEG_depsgraph_build.h" +#ifdef __cplusplus +extern "C" { +#endif + struct MDeformVert; struct Mesh; struct ModifierData; @@ -51,3 +55,7 @@ void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, struct Object *object, const char *bonename, const char *description); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 1ad859aa47b..4fbf5192222 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -103,16 +103,16 @@ class GeometryValueLog : public ValueLog { public: struct MeshInfo { - int tot_verts, tot_edges, tot_faces; + int verts_num, edges_num, faces_num; }; struct CurveInfo { - int tot_splines; + int splines_num; }; struct PointCloudInfo { - int tot_points; + int points_num; }; struct InstancesInfo { - int tot_instances; + int instances_num; }; std::optional<MeshInfo> mesh_info; diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index e081e007c81..38e914b9a9f 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -110,7 +110,7 @@ void register_node_tree_type_geo() tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = ICON_NODETREE; + tt->ui_icon = ICON_GEOMETRY_NODES; strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 5b7211e44b4..8f20da66c3b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -57,8 +57,6 @@ Mesh *create_cylinder_or_cone_mesh(float radius_top, GeometryNodeMeshCircleFillType fill_type, ConeAttributeOutputs &attribute_outputs); -Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); - /** * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index ea26eec0c15..b29831ceeb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -217,16 +217,16 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); const VArray<T> &values = evaluator.get_evaluated<T>(0); const VArray<int> &group_indices = evaluator.get_evaluated<int>(1); - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); if (group_indices.is_single()) { T accumulation = T(); @@ -303,9 +303,9 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -317,10 +317,10 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { for (const int i : values.index_range()) { accumulation = values[i] + accumulation; } - return VArray<T>::ForSingle(accumulation, domain_size); + return VArray<T>::ForSingle(accumulation, domain_num); } - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); Map<int, T> accumulations; for (const int i : values.index_range()) { T &value = accumulations.lookup_or_add_default(group_indices[i]); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 45a6aabeb03..18cf005c965 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -112,8 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); OutputAttribute output_attribute = component.attribute_try_get_for_output_only( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index b3fe9d160b3..8ab0eb678e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -72,11 +72,11 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_MESH: { if (geometry_set.has_mesh()) { const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE)); - params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE)); + params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE)); params.set_output("Face Corner Count", - component->attribute_domain_size(ATTR_DOMAIN_CORNER)); + component->attribute_domain_num(ATTR_DOMAIN_CORNER)); } else { params.set_default_remaining_outputs(); @@ -86,8 +86,8 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_CURVE: { if (geometry_set.has_curves()) { const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE)); } else { params.set_default_remaining_outputs(); @@ -98,7 +98,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_pointcloud()) { const PointCloudComponent *component = geometry_set.get_component_for_read<PointCloudComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); } else { params.set_default_remaining_outputs(); @@ -109,8 +109,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>(); - params.set_output("Instance Count", - component->attribute_domain_size(ATTR_DOMAIN_INSTANCE)); + params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1153f18ffd4..c7f65a68d60 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -197,9 +197,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); @@ -275,9 +275,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 558129fb384..00b10cc8a2f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "GEO_mesh_primitive_cuboid.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_bounding_box_cc { @@ -53,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) else { const float3 scale = sub_max - sub_min; const float3 center = sub_min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map"); transform_mesh(*mesh, center, float3(0), float3(1)); sub_geometry.replace_mesh(mesh); sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 09d0f13c50d..31f706c497c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -138,14 +138,14 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) { int span_count = 0; int count = 0; - int total_size = 0; + int total_num = 0; Span<float3> positions_span; if (geometry_set.has_mesh()) { count++; const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT); + total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT); } if (geometry_set.has_pointcloud()) { @@ -155,7 +155,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) geometry_set.get_component_for_read<PointCloudComponent>(); VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - total_size += varray.size(); + total_num += varray.size(); positions_span = varray.get_internal_span(); } @@ -165,7 +165,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); positions_span = curves.evaluated_positions(); - total_size += positions_span.size(); + total_num += positions_span.size(); } if (count == 0) { @@ -178,7 +178,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) return hull_from_bullet(nullptr, positions_span); } - Array<float3> positions(total_size); + Array<float3> positions(total_num); int offset = 0; if (geometry_set.has_mesh()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 95ea978541c..fb8a488ddae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -106,13 +106,13 @@ static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const /* Calculate the direction vectors from each vertex to their previous vertex. */ static Array<float3> calculate_directions(const Span<float3> positions) { - const int size = positions.size(); - Array<float3> directions(size); + const int num = positions.size(); + Array<float3> directions(num); - for (const int i : IndexRange(size - 1)) { + for (const int i : IndexRange(num - 1)) { directions[i] = math::normalize(positions[i + 1] - positions[i]); } - directions[size - 1] = math::normalize(positions[0] - positions[size - 1]); + directions[num - 1] = math::normalize(positions[0] - positions[num - 1]); return directions; } @@ -120,11 +120,11 @@ static Array<float3> calculate_directions(const Span<float3> positions) /* Calculate the axes around which the fillet is built. */ static Array<float3> calculate_axes(const Span<float3> directions) { - const int size = directions.size(); - Array<float3> axes(size); + const int num = directions.size(); + Array<float3> axes(num); - axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0])); - for (const int i : IndexRange(1, size - 1)) { + axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0])); + for (const int i : IndexRange(1, num - 1)) { axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); } @@ -134,11 +134,11 @@ static Array<float3> calculate_axes(const Span<float3> directions) /* Calculate the angle of the arc formed by the fillet. */ static Array<float> calculate_angles(const Span<float3> directions) { - const int size = directions.size(); - Array<float> angles(size); + const int num = directions.size(); + Array<float> angles(num); - angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]); - for (const int i : IndexRange(1, size - 1)) { + angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]); + for (const int i : IndexRange(1, num - 1)) { angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); } @@ -147,18 +147,18 @@ static Array<float> calculate_angles(const Span<float3> directions) /* Calculate the segment count in each filleted arc. */ static Array<int> calculate_counts(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset, const bool cyclic) { - Array<int> counts(size, 1); + Array<int> counts(num, 1); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { counts[i] = fillet_param.counts[spline_offset + i]; } } if (!cyclic) { - counts[0] = counts[size - 1] = 0; + counts[0] = counts[num - 1] = 0; } return counts; @@ -166,17 +166,17 @@ static Array<int> calculate_counts(const FilletParam &fillet_param, /* Calculate the radii for the vertices to be filleted. */ static Array<float> calculate_radii(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset) { - Array<float> radii(size, 0.0f); + Array<float> radii(num, 0.0f); if (fillet_param.limit_radius) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); } } else { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = fillet_param.radii[spline_offset + i]; } } @@ -207,15 +207,15 @@ static FilletData calculate_fillet_data(const Spline &spline, MutableSpan<int> point_counts, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); FilletData fd; fd.directions = calculate_directions(spline.positions()); fd.positions = spline.positions(); fd.axes = calculate_axes(fd.directions); fd.angles = calculate_angles(fd.directions); - fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic()); - fd.radii = calculate_radii(fillet_param, size, spline_offset); + fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic()); + fd.radii = calculate_radii(fillet_param, num, spline_offset); added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); @@ -229,19 +229,19 @@ static void limit_radii(FilletData &fd, const bool cyclic) Span<float> angles(fd.angles); Span<float3> positions(fd.positions); - const int size = radii.size(); - const int fillet_count = cyclic ? size : size - 2; + const int num = radii.size(); + const int fillet_count = cyclic ? num : num - 2; const int start = cyclic ? 0 : 1; - Array<float> max_radii(size, FLT_MAX); + Array<float> max_radii(num, FLT_MAX); if (cyclic) { /* Calculate lengths between adjacent control points. */ - const float len_prev = math::distance(positions[0], positions[size - 1]); + const float len_prev = math::distance(positions[0], positions[num - 1]); const float len_next = math::distance(positions[0], positions[1]); /* Calculate tangent lengths of fillets in control points. */ const float tan_len = radii[0] * tan(angles[0] / 2.0f); - const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f); + const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f); const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); float factor_prev = 1.0f, factor_next = 1.0f; @@ -255,12 +255,12 @@ static void limit_radii(FilletData &fd, const bool cyclic) /* Scale max radii by calculated factors. */ max_radii[0] = radii[0] * std::min(factor_next, factor_prev); max_radii[1] = radii[1] * factor_next; - max_radii[size - 1] = radii[size - 1] * factor_prev; + max_radii[num - 1] = radii[num - 1] * factor_prev; } /* Initialize max_radii to largest possible radii. */ float prev_dist = math::distance(positions[1], positions[0]); - for (const int i : IndexRange(1, size - 2)) { + for (const int i : IndexRange(1, num - 2)) { const float temp_dist = math::distance(positions[i], positions[i + 1]); max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); prev_dist = temp_dist; @@ -282,7 +282,7 @@ static void limit_radii(FilletData &fd, const bool cyclic) } /* Assign the max_radii to the fillet data's radii. */ - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::min(radii[i], max_radii[i]); } } @@ -358,10 +358,10 @@ static void update_bezier_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -385,7 +385,7 @@ static void update_bezier_positions(const FilletData &fd, /* Position the end points of the arc and their handles. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -442,10 +442,10 @@ static void update_poly_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -460,7 +460,7 @@ static void update_poly_positions(const FilletData &fd, /* Position the end points of the arc. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -487,15 +487,15 @@ static SplinePtr fillet_spline(const Spline &spline, const FilletParam &fillet_param, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); const bool cyclic = spline.is_cyclic(); - if (size < 3) { + if (num < 3) { return spline.copy(); } /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ - Array<int> point_counts(size, 1); + Array<int> point_counts(num, 1); int added_count = 0; /* Update point_counts array and added_count. */ @@ -505,7 +505,7 @@ static SplinePtr fillet_spline(const Spline &spline, limit_radii(fd, cyclic); } - const int total_points = added_count + size; + const int total_points = added_count + num; const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points); SplinePtr dst_spline_ptr = spline.copy_only_settings(); (*dst_spline_ptr).resize(total_points); @@ -581,8 +581,8 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add(radius_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 471d6af560f..d9cc8bcf023 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -1,12 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_array.hh" -#include "BLI_index_mask_ops.hh" -#include "BLI_length_parameterize.hh" -#include "BLI_task.hh" -#include "BLI_timeit.hh" +#include "GEO_resample_curves.hh" -#include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "UI_interface.h" @@ -56,495 +51,6 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -/** - * Return true if the attribute should be copied/interpolated to the result curves. - * Don't output attributes that correspond to curve types that have no curves in the result. - */ -static bool interpolate_attribute_to_curves(const AttributeIDRef &attribute_id, - const std::array<int, CURVE_TYPES_NUM> &type_counts) -{ - if (!attribute_id.is_named()) { - return true; - } - if (ELEM(attribute_id.name(), - "handle_type_left", - "handle_type_right", - "handle_left", - "handle_right")) { - return type_counts[CURVE_TYPE_BEZIER] != 0; - } - if (ELEM(attribute_id.name(), "nurbs_weight")) { - return type_counts[CURVE_TYPE_NURBS] != 0; - } - return true; -} - -/** - * Return true if the attribute should be copied to poly curves. - */ -static bool interpolate_attribute_to_poly_curve(const AttributeIDRef &attribute_id) -{ - static const Set<StringRef> no_interpolation{{ - "handle_type_left", - "handle_type_right", - "handle_position_right", - "handle_position_left", - "nurbs_weight", - }}; - return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); -} - -/** - * Retrieve spans from source and result attributes. - */ -static void retrieve_attribute_spans(const Span<AttributeIDRef> ids, - const CurveComponent &src_component, - CurveComponent &dst_component, - Vector<GSpan> &src, - Vector<GMutableSpan> &dst, - Vector<OutputAttribute> &dst_attributes) -{ - for (const int i : ids.index_range()) { - GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); - BLI_assert(src_attribute); - src.append(src_attribute.get_internal_span()); - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - ids[i], ATTR_DOMAIN_POINT, data_type); - dst.append(dst_attribute.as_span()); - dst_attributes.append(std::move(dst_attribute)); - } -} - -struct AttributesForInterpolation : NonCopyable, NonMovable { - Vector<GSpan> src; - Vector<GMutableSpan> dst; - - Vector<OutputAttribute> dst_attributes; - - Vector<GSpan> src_no_interpolation; - Vector<GMutableSpan> dst_no_interpolation; -}; - -/** - * Gather a set of all generic attribute IDs to copy to the result curves. - */ -static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, - CurveComponent &dst_component, - AttributesForInterpolation &result) -{ - const Curves &dst_curves_id = *dst_component.get_for_read(); - const bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id.geometry); - - VectorSet<AttributeIDRef> ids; - VectorSet<AttributeIDRef> ids_no_interpolation; - src_component.attribute_foreach( - [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - return true; - } - if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { - return true; - } - if (interpolate_attribute_to_poly_curve(id)) { - ids.add_new(id); - } - else { - ids_no_interpolation.add_new(id); - } - return true; - }); - - /* Position is handled differently since it has non-generic interpolation for Bezier - * curves and because the evaluated positions are cached for each evaluated point. */ - ids.remove_contained("position"); - - retrieve_attribute_spans( - ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); - - /* Attributes that aren't interpolated like Bezier handles still have to be be copied - * to the result when there are any unselected curves of the corresponding type. */ - retrieve_attribute_spans(ids_no_interpolation, - src_component, - dst_component, - result.src_no_interpolation, - result.dst_no_interpolation, - result.dst_attributes); -} - -/** - * Copy the provided point attribute values between all curves in the #curve_ranges index - * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. - */ -template<typename T> -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> curve_ranges, - const Span<T> src, - const MutableSpan<T> dst) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { - for (const IndexRange range : curve_ranges.slice(range)) { - const IndexRange src_points = src_curves.points_for_curves(range); - const IndexRange dst_points = dst_curves.points_for_curves(range); - /* The arrays might be large, so a threaded copy might make sense here too. */ - dst.slice(dst_points).copy_from(src.slice(src_points)); - } - }); -} -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> unselected_ranges, - const GSpan src, - const GMutableSpan dst) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); - }); -} - -/** - * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. - */ -static void fill_curve_counts(const bke::CurvesGeometry &src_curves, - const Span<IndexRange> curve_ranges, - MutableSpan<int> counts) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { - for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { - for (const int i : curves_range) { - counts[i] = src_curves.points_for_curve(i).size(); - } - } - }); -} - -/** - * Turn an array of sizes into the offset at each index including all previous sizes. - */ -static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets) -{ - int total = 0; - for (const int i : counts_to_offsets.index_range().drop_back(1)) { - const int count = counts_to_offsets[i]; - BLI_assert(count > 0); - counts_to_offsets[i] = total; - total += count; - } - counts_to_offsets.last() = total; -} - -/** - * Create new curves where the selected curves have been resampled with a number of uniform-length - * samples defined by the count field. Interpolate attributes to the result, with an accuracy that - * depends on the curve's resolution parameter. - * - * \warning The values provided by the #count_field must be 1 or greater. - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_uniform_count(const CurveComponent &src_component, - const Field<bool> &selection_field, - const Field<int> &count_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - /* Create the new curves without any points and evaluate the final count directly - * into the offsets array, in order to be accumulated into offsets later. */ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.add_with_destination(count_field, dst_offsets); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - - VArray<bool> curves_cyclic = src_curves.cyclic(); - VArray<int8_t> curve_types = src_curves.curve_types(); - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - src_curves.ensure_evaluated_lengths(); - - /* Sampling arbitrary attributes works by first interpolating them to the curve's standard - * "evaluated points" and then interpolating that result with the uniform samples. This is - * potentially wasteful when down-sampling a curve to many fewer points. There are two possible - * solutions: only sample the necessary points for interpolation, or first sample curve - * parameter/segment indices and evaluate the curve directly. */ - Array<int> sample_indices(dst_curves.points_num()); - Array<float> sample_factors(dst_curves.points_num()); - - /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on - * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a - * time or one curve at a time. */ - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - Vector<std::byte> evaluated_buffer; - - /* Gather uniform samples based on the accumulated lengths of the original curve. */ - for (const int i_curve : sliced_selection) { - const bool cyclic = curves_cyclic[i_curve]; - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::create_uniform_samples( - src_curves.evaluated_lengths_for_curve(i_curve, cyclic), - curves_cyclic[i_curve], - sample_indices.as_mutable_span().slice(dst_points), - sample_factors.as_mutable_span().slice(dst_points)); - } - - /* For every attribute, evaluate attributes from every curve in the range in the original - * curve's "evaluated points", then use linear interpolation to sample to the result. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - - if (curve_types[i_curve] == CURVE_TYPE_POLY) { - length_parameterize::linear_interpolation(src.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - else { - const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); - evaluated_buffer.clear(); - evaluated_buffer.resize(sizeof(T) * evaluated_size); - MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); - src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); - - length_parameterize::linear_interpolation(evaluated.as_span(), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - } - }); - } - - /* Interpolate the evaluated positions to the resampled curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst_positions.slice(dst_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Evaluate each selected curve to its implicit evaluated points. - */ -static Curves *resample_to_evaluated(const CurveComponent &src_component, - const Field<bool> &selection_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - src_curves.ensure_evaluated_offsets(); - threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { - for (const int i : selection.slice(range)) { - dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); - } - }); - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* Create the correct number of uniform-length samples for every selected curve. */ - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - /* Evaluate generic point attributes directly to the result attributes. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - src_curves.interpolate_to_evaluated( - i_curve, src.slice(src_points), dst.slice(dst_points)); - } - }); - } - - /* Copy the evaluated positions to the selected curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Create a resampled curve point count field for both "uniform" options. - * The complexity is handled here in order to make the actual resampling functions simpler. - */ -static Field<int> get_curve_count_field(GeoNodeExecParams params, - const GeometryNodeCurveResampleMode mode) -{ - if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - static fn::CustomMF_SI_SO<int, int> max_one_fn( - "Clamp Above One", - [](int value) { return std::max(1, value); }, - fn::CustomMF_presets::AllSpanOrSingle()); - auto clamp_op = std::make_shared<FieldOperation>( - FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))})); - - return Field<int>(std::move(clamp_op)); - } - - if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( - "Length Input to Count", - [](const float curve_length, const float sample_length) { - /* Find the number of sampled segments by dividing the total length by - * the sample length. Then there is one more sampled point than segment. */ - const int count = int(curve_length / sample_length) + 1; - return std::max(1, count); - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto get_count_op = std::make_shared<FieldOperation>( - FieldOperation(get_count_fn, - {Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), - params.extract_input<Field<float>>("Length")})); - - return Field<int>(std::move(get_count_op)); - } - - BLI_assert_unreachable(); - return {}; -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -555,32 +61,35 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_count(*component, selection, count)); + } + } + }); + break; + } case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - Field<int> count = get_curve_count_field(params, mode); - - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + Field<int> length = params.extract_input<Field<float>>("Length"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_length(*component, selection, length)); + } } - - Curves *result = resample_to_uniform_count( - *geometry_set.get_component_for_read<CurveComponent>(), selection, count); - - geometry_set.replace_curves(result); }); break; } case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_evaluated(*component, selection)); + } } - - Curves *result = resample_to_evaluated( - *geometry_set.get_component_for_read<CurveComponent>(), selection); - - geometry_set.replace_curves(result); }); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index de29735bd2d..64a174df599 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index a548becf24e..ae36248b573 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -75,7 +75,7 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves) case CURVE_TYPE_CATMULL_ROM: { const int resolution = resolutions[i_curve]; for (const int i : IndexRange(points.size()).drop_back(1)) { - lengths[i + 1] = evaluated_lengths[resolution * i - 1]; + lengths[i + 1] = evaluated_lengths[resolution * i]; } break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 6e45411a9af..500804e41f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -107,20 +107,20 @@ static void copy_attributes(const Spline &input_spline, Spline &output_spline, C static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions, const KnotsMode knots_mode) { - const int nurbs_positions_size = nurbs_positions.size(); + const int nurbs_positions_num = nurbs_positions.size(); Vector<float3> handle_positions; if (knots_mode == NURBS_KNOT_MODE_BEZIER) { - for (const int i : IndexRange(nurbs_positions_size)) { + for (const int i : IndexRange(nurbs_positions_num)) { if (i % 3 == 1) { continue; } handle_positions.append(nurbs_positions[i]); } - if (nurbs_positions_size % 3 == 1) { + if (nurbs_positions_num % 3 == 1) { handle_positions.pop_last(); } - else if (nurbs_positions_size % 3 == 2) { - const int last_index = nurbs_positions_size - 1; + else if (nurbs_positions_num % 3 == 2) { + const int last_index = nurbs_positions_num - 1; handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]); } } @@ -134,11 +134,11 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]); handle_positions.append(nurbs_positions[1]); } - const int segments_size = nurbs_positions_size - 1; - const bool ignore_interior_segment = segments_size == 3 && is_periodic == false; + const int segments_num = nurbs_positions_num - 1; + const bool ignore_interior_segment = segments_num == 3 && is_periodic == false; if (ignore_interior_segment == false) { - const float mid_offset = (float)(segments_size - 1) / 2.0f; - for (const int i : IndexRange(1, segments_size - 2)) { + const float mid_offset = (float)(segments_num - 1) / 2.0f; + for (const int i : IndexRange(1, segments_num - 2)) { const int divisor = is_periodic ? 3 : std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f)); @@ -151,7 +151,7 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po } } } - const int last_index = nurbs_positions_size - 1; + const int last_index = nurbs_positions_num - 1; if (is_periodic) { handle_positions.append( nurbs_positions[last_index - 1] + @@ -368,11 +368,11 @@ static void node_geo_exec(GeoNodeExecParams params) const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( *curve_component->get_for_read()); GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; - const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = curve_component->attribute_domain_num(ATTR_DOMAIN_CURVE); Span<SplinePtr> src_splines = curve->splines(); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 371556c04f1..4d8745bf79e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -30,9 +30,9 @@ static Array<int> get_subdivided_offsets(const Spline &spline, const VArray<int> &cuts, const int spline_offset) { - Array<int> offsets(spline.segments_size() + 1); + Array<int> offsets(spline.segments_num() + 1); int offset = 0; - for (const int i : IndexRange(spline.segments_size())) { + for (const int i : IndexRange(spline.segments_num())) { offsets[i] = offset; offset = offset + std::max(cuts[spline_offset + i], 0) + 1; } @@ -46,8 +46,8 @@ static void subdivide_attribute(Span<T> src, const bool is_cyclic, MutableSpan<T> dst) { - const int src_size = src.size(); - threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + const int src_num = src.size(); + threading::parallel_for(IndexRange(src_num - 1), 1024, [&](IndexRange range) { for (const int i : range) { const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src[i]; @@ -60,7 +60,7 @@ static void subdivide_attribute(Span<T> src, }); if (is_cyclic) { - const int i = src_size - 1; + const int i = src_num - 1; const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src.last(); const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; @@ -86,7 +86,7 @@ static void subdivide_attribute(Span<T> src, static void subdivide_bezier_segment(const BezierSpline &src, const int index, const int offset, - const int result_size, + const int result_num, Span<float3> src_positions, Span<float3> src_handles_left, Span<float3> src_handles_right, @@ -106,11 +106,11 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR); - const float factor_delta = 1.0f / result_size; - for (const int cut : IndexRange(result_size)) { + const float factor_delta = 1.0f / result_num; + for (const int cut : IndexRange(result_num)) { const float factor = cut * factor_delta; dst_positions[offset + cut] = attribute_math::mix2( factor, src_positions[index], src_positions[next_index]); @@ -120,10 +120,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE); - const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num; /* Create a Bezier segment to update iteratively for every subdivision * and references to the meaningful values for ease of use. */ @@ -138,8 +138,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, handle_prev = src_handles_right[index]; handle_next = src_handles_left[next_index]; - for (const int cut : IndexRange(result_size - 1)) { - const float parameter = 1.0f / (result_size - cut); + for (const int cut : IndexRange(result_num - 1)) { + const float parameter = 1.0f / (result_num - cut); const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); /* Copy relevant temporary data to the result. */ @@ -154,7 +154,7 @@ static void subdivide_bezier_segment(const BezierSpline &src, } /* Copy the handles for the last segment from the temporary spline. */ - dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_right[offset + result_num - 1] = handle_prev; dst_handles_left[i_segment_last] = handle_next; } } @@ -287,9 +287,9 @@ static SplinePtr subdivide_spline(const Spline &spline, * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); - const int result_size = offsets.last() + int(!spline.is_cyclic()); + const int result_num = offsets.last() + int(!spline.is_cyclic()); SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(result_size); + new_spline->resize(result_num); subdivide_builtin_attributes(spline, offsets, *new_spline); subdivide_dynamic_attributes(spline, offsets, *new_spline); return new_spline; @@ -334,9 +334,9 @@ static void node_geo_exec(GeoNodeExecParams params) const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(cuts_field); evaluator.evaluate(); const VArray<int> &cuts = evaluator.get_evaluated<int>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 894580f2932..b45a0d6c81a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -90,7 +90,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += count; } } @@ -104,7 +104,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += splines[i]->length() / resolution + 1; } } @@ -240,18 +240,18 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, for (const int i : range) { const Spline &spline = *splines[i]; const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size == 0) { + const int num = offsets[i + 1] - offsets[i]; + if (num == 0) { continue; } - const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); + const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), uniform_samples, - data.radii.slice(offset, size)); + data.radii.slice(offset, num)); for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; @@ -260,14 +260,13 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), - uniform_samples, - dst.slice(offset, size)); + spline.sample_with_index_factors( + spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); } if (!data.tangents.is_empty()) { Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size); + MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); for (float3 &vector : sampled_tangents) { vector = math::normalize(vector); @@ -276,7 +275,7 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, if (!data.normals.is_empty()) { Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, size); + MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); for (float3 &vector : sampled_normals) { vector = math::normalize(vector); @@ -298,8 +297,8 @@ static void copy_spline_domain_attributes(const CurveEval &curve, for (const int i : curve.splines().index_range()) { const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], size); + const int num = offsets[i + 1] - offsets[i]; + type.fill_assign_n(curve_attribute[i], dst[offset], num); } return true; @@ -329,13 +328,13 @@ static void node_geo_exec(GeoNodeExecParams params) curve->assert_valid_point_attributes(); const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_size = offsets.last(); - if (total_size == 0) { + const int total_num = offsets.last(); + if (total_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( points, *curve, attribute_outputs); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index df360818313..c993a3d305d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -117,10 +117,10 @@ struct TrimLocation { }; template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size) +static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) { - BLI_assert(start_index + size - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * size); + BLI_assert(start_index + num - 1 <= data.size()); + memmove(data.data(), &data[start_index], sizeof(T) * num); } /* Shift slice to start of span and modifies start and end data. */ @@ -129,17 +129,17 @@ static void linear_trim_data(const TrimLocation &start, const TrimLocation &end, MutableSpan<T> data) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, size); + shift_slice_to_start<T>(data, start.left_index, num); } const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]); + const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); data.first() = start_data; - data[size - 1] = end_data; + data[num - 1] = end_data; } /** @@ -152,12 +152,12 @@ static void linear_trim_to_output_data(const TrimLocation &start, Span<T> src, MutableSpan<T> dst) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - dst.copy_from(src.slice(start.left_index, size)); + dst.copy_from(src.slice(start.left_index, num)); dst.first() = start_data; dst.last() = end_data; } @@ -175,8 +175,8 @@ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lo const int right = left == (spline.size() - 1) ? 0 : left + 1; const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_size = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); + const int segment_eval_num = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); return {left, right, factor}; } @@ -191,7 +191,7 @@ static void trim_poly_spline(Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; linear_trim_data<float3>(start, end, spline.positions()); linear_trim_data<float>(start, end, spline.radii()); @@ -209,7 +209,7 @@ static void trim_poly_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - spline.resize(size); + spline.resize(num); } /** @@ -225,11 +225,11 @@ static PolySpline trim_nurbs_spline(const Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Create poly spline and copy trimmed data to it. */ PolySpline new_spline; - new_spline.resize(size); + new_spline.resize(num); /* Copy generic attribute data. */ spline.attributes.foreach_attribute( @@ -283,7 +283,7 @@ static void trim_bezier_spline(Spline &spline, const Span<int> control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Trim the spline attributes. Done before end.factor recalculation as it needs * the original end.factor value. */ @@ -301,10 +301,10 @@ static void trim_bezier_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - /* Recalculate end.factor if the size is two, because the adjustment in the + /* Recalculate end.factor if the `num` is two, because the adjustment in the * position of the control point of the spline to the left of the new end point will change the * factor between them. */ - if (size == 2) { + if (num == 2) { if (start_lookup.factor == 1.0f) { end.factor = 0.0f; } @@ -328,38 +328,38 @@ static void trim_bezier_spline(Spline &spline, const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( end.left_index, end.right_index, end.factor); - /* If size is two, then the start point right handle needs to change to reflect the end point + /* If `num` is two, then the start point right handle needs to change to reflect the end point * previous handle update. */ - if (size == 2) { + if (num == 2) { start_point.right_handle = end_point.handle_prev; } /* Shift control point position data to start at beginning of array. */ if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size); + shift_slice_to_start(bezier_spline.positions(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); } bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[size - 1] = end_point.position; + bezier_spline.positions()[num - 1] = end_point.position; bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle; + bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle; + bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; /* If there is at least one control point between the endpoints, update the control * point handle to the right of the start point and to the left of the end point. */ - if (size > 2) { + if (num > 2) { bezier_spline.handle_positions_left()[start.right_index - start.left_index] = start_point.handle_next; bezier_spline.handle_positions_right()[end.left_index - start.left_index] = end_point.handle_prev; } - bezier_spline.resize(size); + bezier_spline.resize(num); } static void trim_spline(SplinePtr &spline, @@ -506,9 +506,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); @@ -527,7 +527,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, continue; } - if (spline->evaluated_edges_size() == 0) { + if (spline->evaluated_edges_num() == 0) { continue; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 8a0c900fbde..99edc4d298c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -470,7 +470,7 @@ static void separate_curve_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -493,7 +493,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; fn::FieldEvaluator selection_evaluator{field_context, - src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + src_points.attribute_domain_num(ATTR_DOMAIN_POINT)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -526,8 +526,8 @@ static void separate_instance_selection(GeometrySet &geometry_set, InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0); @@ -1238,7 +1238,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index d9f29d1ef1c..c242cfd5cf3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -398,11 +398,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent { const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER; GeometryComponentFieldContext field_context{component, attribute_domain}; - const int domain_size = component.attribute_domain_size(attribute_domain); + const int domain_num = component.attribute_domain_num(attribute_domain); - Array<float> densities(domain_size, 0.0f); + Array<float> densities(domain_num, 0.0f); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(density_field, densities.as_mutable_span()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 1b26cfe31fe..ee3de995cb1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -351,19 +351,19 @@ static void duplicate_curves(GeometrySet &geometry_set, Array<int> curve_offsets(selection.size() + 1); Array<int> point_offsets(selection.size() + 1); - int dst_curves_size = 0; - int dst_points_size = 0; + int dst_curves_num = 0; + int dst_points_num = 0; for (const int i_curve : selection.index_range()) { const int count = std::max(counts[selection[i_curve]], 0); - curve_offsets[i_curve] = dst_curves_size; - point_offsets[i_curve] = dst_points_size; - dst_curves_size += count; - dst_points_size += count * curves.points_for_curve(selection[i_curve]).size(); + curve_offsets[i_curve] = dst_curves_num; + point_offsets[i_curve] = dst_points_num; + dst_curves_num += count; + dst_points_num += count * curves.points_for_curve(selection[i_curve]).size(); } - curve_offsets.last() = dst_curves_size; - point_offsets.last() = dst_points_size; + curve_offsets.last() = dst_curves_num; + point_offsets.last() = dst_points_num; - Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_curves_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write(); @@ -379,7 +379,7 @@ static void duplicate_curves(GeometrySet &geometry_set, } } }); - all_dst_offsets.last() = dst_points_size; + all_dst_offsets.last() = dst_points_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -807,7 +807,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); - const int dst_size = offsets.last(); + const int dst_num = offsets.last(); Array<int> point_to_curve_map(src_curves.points_num()); threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) { @@ -817,13 +817,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set, } }); - Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write(); for (const int i : new_curves.curves_range()) { new_curve_offsets[i] = i; } - new_curve_offsets.last() = dst_size; + new_curve_offsets.last() = dst_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -940,10 +940,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, { const PointCloudComponent &src_points = *geometry_set.get_component_for_read<PointCloudComponent>(); - const int point_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, point_size}; + FieldEvaluator evaluator{field_context, point_num}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -1026,7 +1026,7 @@ static void duplicate_instances(GeometrySet &geometry_set, *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; - FieldEvaluator evaluator{field_context, src_instances.instances_amount()}; + FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 84acab47661..94fbec66bfe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; - const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 77be98f169e..bf956f3b83d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput { { const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; FieldEvaluator value_evaluator{value_field_context, - component.attribute_domain_size(value_field_domain_)}; + component.attribute_domain_num(value_field_domain_)}; value_evaluator.add(value_field_); value_evaluator.evaluate(); const GVArray &values = value_evaluator.get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 34c3169065c..0484017cf3b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b) static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index d39291a2a7e..8e3a9b6769d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -101,8 +101,7 @@ class IslandCountFieldInput final : public GeometryFieldInput { island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - mesh_component.attribute_domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 61f719ade4e..12582f9e9c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -50,7 +50,7 @@ static void add_instances_from_component( const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate) { const AttributeDomain domain = ATTR_DOMAIN_POINT; - const int domain_size = src_component.attribute_domain_size(domain); + const int domain_num = src_component.attribute_domain_num(domain); VArray<bool> pick_instance; VArray<int> indices; @@ -59,7 +59,7 @@ static void add_instances_from_component( GeometryComponentFieldContext field_context{src_component, domain}; const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ @@ -73,7 +73,7 @@ static void add_instances_from_component( /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ - const int start_len = dst_component.instances_amount(); + const int start_len = dst_component.instances_num(); const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); @@ -119,12 +119,12 @@ static void add_instances_from_component( const bool use_individual_instance = pick_instance[i]; if (use_individual_instance) { if (src_instances != nullptr) { - const int src_instances_amount = src_instances->instances_amount(); + const int src_instances_num = src_instances->instances_num(); const int original_index = indices[i]; /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 * refers to the last element. */ - const int index = mod_i(original_index, std::max(src_instances_amount, 1)); - if (index < src_instances_amount) { + const int index = mod_i(original_index, std::max(src_instances_num, 1)); + if (index < src_instances_num) { /* Get the reference to the source instance. */ const int src_handle = src_instances->instance_reference_handles()[index]; dst_handle = handle_mapping[src_handle]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index e35f152ce49..2126a5cc329 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -40,9 +40,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(std::move(selection_field)); evaluator.add(std::move(position_field)); evaluator.add(std::move(radius_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 0e2803cd035..2067086c298 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -56,8 +56,8 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, int offset = 0; for (const GeometryComponent *component : src_components) { - const int domain_size = component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component->attribute_domain_num(domain); + if (domain_num == 0) { continue; } GVArray read_attribute = component->attribute_get_for_read( @@ -66,9 +66,9 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, GVArray_GSpan src_span{read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; - cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size); + cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num); - offset += domain_size; + offset += domain_num; } } @@ -101,7 +101,7 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_amount(); + tot_instances += src_component->instances_num(); } dst_component.reserve(tot_instances); diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index 1ec97858d4d..1def4089115 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -39,9 +39,9 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p const float merge_distance, const Field<bool> &selection_field) { - const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); @@ -57,10 +57,10 @@ static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponen const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - Array<bool> selection(src_size); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + Array<bool> selection(src_num); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add_with_destination(selection_field, selection.as_mutable_span()); evaluator.evaluate(); @@ -72,9 +72,9 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 636ecb8ab41..0029b547375 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -6,414 +6,9 @@ #include "BKE_material.h" #include "BKE_mesh.h" -#include "node_geometry_util.hh" - -namespace blender::nodes { - -struct CuboidConfig { - float3 size; - int verts_x; - int verts_y; - int verts_z; - int edges_x; - int edges_y; - int edges_z; - int vertex_count; - int poly_count; - int loop_count; - - CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) - : size(size), - verts_x(verts_x), - verts_y(verts_y), - verts_z(verts_z), - edges_x(verts_x - 1), - edges_y(verts_y - 1), - edges_z(verts_z - 1) - { - BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); - this->vertex_count = this->get_vertex_count(); - this->poly_count = this->get_poly_count(); - this->loop_count = this->poly_count * 4; - } - - private: - int get_vertex_count() - { - const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); - return verts_x * verts_y * verts_z - inner_position_count; - } - - int get_poly_count() - { - return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); - } -}; - -static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) -{ - const float z_bottom = -config.size.z / 2.0f; - const float z_delta = config.size.z / config.edges_z; - - const float x_left = -config.size.x / 2.0f; - const float x_delta = config.size.x / config.edges_x; - - const float y_front = -config.size.y / 2.0f; - const float y_delta = config.size.y / config.edges_y; - - int vert_index = 0; - - for (const int z : IndexRange(config.verts_z)) { - if (ELEM(z, 0, config.edges_z)) { - /* Fill bottom and top. */ - const float z_pos = z_bottom + z_delta * z; - for (const int y : IndexRange(config.verts_y)) { - const float y_pos = y_front + y_delta * y; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - } - else { - for (const int y : IndexRange(config.verts_y)) { - if (ELEM(y, 0, config.edges_y)) { - /* Fill y-sides. */ - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - else { - /* Fill x-sides. */ - const float x_pos = x_left; - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - const float x_pos2 = x_left + x_delta * config.edges_x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); - } - } - } - } -} - -/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. - * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for - * anti-clockwise. - */ -static void define_quad(MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops, - const int poly_index, - const int loop_index, - const int vert_1, - const int vert_2, - const int vert_3, - const int vert_4) -{ - MPoly &poly = polys[poly_index]; - poly.loopstart = loop_index; - poly.totloop = 4; - - MLoop &loop_1 = loops[loop_index]; - loop_1.v = vert_1; - MLoop &loop_2 = loops[loop_index + 1]; - loop_2.v = vert_2; - MLoop &loop_3 = loops[loop_index + 2]; - loop_3.v = vert_3; - MLoop &loop_4 = loops[loop_index + 3]; - loop_4.v = vert_4; -} - -static void calculate_polys(const CuboidConfig &config, - MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops) -{ - int loop_index = 0; - int poly_index = 0; - - /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ - const int xy_cross_section_vert_count = config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2); +#include "GEO_mesh_primitive_cuboid.hh" - /* Calculate polys for Bottom faces. */ - int vert_1_start = 0; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - const int vert_1 = vert_1_start + x; - const int vert_2 = vert_1_start + config.verts_x + x; - const int vert_3 = vert_2 + 1; - const int vert_4 = vert_1 + 1; - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - vert_1_start += config.verts_x; - } - - /* Calculate polys for Front faces. */ - vert_1_start = 0; - int vert_2_start = config.verts_x * config.verts_y; - - for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_1_start = vert_2_start; - vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); - } - - /* Calculate polys for Top faces. */ - vert_1_start = config.verts_x * config.verts_y + - (config.verts_z - 2) * (config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2)); - vert_2_start = vert_1_start + config.verts_x; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_2_start += config.verts_x; - vert_1_start += config.verts_x; - } - - /* Calculate polys for Back faces. */ - vert_1_start = config.verts_x * config.edges_y; - vert_2_start = vert_1_start + xy_cross_section_vert_count; - - for (const int z : IndexRange(config.edges_z)) { - if (z == (config.edges_z - 1)) { - vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); - } - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_2_start + x, - vert_2_start + x + 1, - vert_1_start + x + 1); - loop_index += 4; - poly_index++; - } - vert_2_start += xy_cross_section_vert_count; - vert_1_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Left faces. */ - vert_1_start = 0; - vert_2_start = config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1; - int vert_2; - int vert_3; - int vert_4; - - if (z == 0 || y == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_1 += config.verts_x - 2; - vert_4 = vert_1 + 2; - } - - if (y == 0 || z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_2 += config.verts_x - 2; - vert_3 = vert_2 + 2; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Right faces. */ - vert_1_start = config.edges_x; - vert_2_start = vert_1_start + config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1 = vert_1_start; - int vert_2 = vert_2_start; - int vert_3 = vert_2_start + 2; - int vert_4 = vert_1 + config.verts_x; - - if (z == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_4 = vert_1 + 2; - } - - if (z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_3 = vert_2 + 2; - } - - if (y == (config.edges_y - 1)) { - vert_3 = vert_2 + config.verts_x; - vert_4 = vert_1 + config.verts_x; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } -} - -static void calculate_uvs(const CuboidConfig &config, Mesh *mesh) -{ - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttribute_Typed<float2> uv_attribute = - mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); - MutableSpan<float2> uvs = uv_attribute.as_span(); - - int loop_index = 0; - - const float x_delta = 0.25f / static_cast<float>(config.edges_x); - const float y_delta = 0.25f / static_cast<float>(config.edges_y); - const float z_delta = 0.25f / static_cast<float>(config.edges_z); - - /* Calculate bottom face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); - } - } - - /* Calculate front face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); - } - } - - /* Calculate top face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); - } - } - - /* Calculate back face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); - } - } - - /* Calculate left face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); - } - } - - /* Calculate right face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); - } - } - - uv_attribute.save(); -} - -Mesh *create_cuboid_mesh(const float3 size, - const int verts_x, - const int verts_y, - const int verts_z) -{ - const CuboidConfig config(size, verts_x, verts_y, verts_z); - - Mesh *mesh = BKE_mesh_new_nomain( - config.vertex_count, 0, 0, config.loop_count, config.poly_count); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - - calculate_vertices(config, {mesh->mvert, mesh->totvert}); - - calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); - BKE_mesh_calc_edges(mesh, false, false); - - calculate_uvs(config, mesh); - - return mesh; -} - -} // namespace blender::nodes +#include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_primitive_cube_cc { @@ -442,6 +37,16 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } +static Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + Mesh *mesh = geometry::create_cuboid_mesh(size, verts_x, verts_y, verts_z, "uv_map"); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + return mesh; +} + static Mesh *create_cube_mesh(const float3 size, const int verts_x, const int verts_y, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index f6ee3d00dee..ec6acf55dd8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)}; evaluator.add(params.get_input<Field<bool>>("Selection")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 2ed6d555684..6b23b685549 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -66,12 +66,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, return; } GeometryComponentFieldContext field_context{*mesh_component, domain}; - const int domain_size = mesh_component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = mesh_component->attribute_domain_num(domain); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Evaluating directly into the point cloud doesn't work because we are not using the full * "min_array_size" array but compressing the selected elements into the final array with no diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 0f6586617bc..577b001fd06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -38,13 +38,13 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, } GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; - const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index c99b51ffd4c..42cee4c0efe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -168,14 +168,14 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms, Field<float> radius_field = params.get_input<Field<float>>("Radius"); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - r_positions.resize(r_positions.size() + domain_size); - positions.materialize(r_positions.as_mutable_span().take_back(domain_size)); + r_positions.resize(r_positions.size() + domain_num); + positions.materialize(r_positions.as_mutable_span().take_back(domain_num)); - r_radii.resize(r_radii.size() + domain_size); - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + r_radii.resize(r_radii.size() + domain_num); + fn::FieldEvaluator evaluator{field_context, domain_num}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_num)); evaluator.evaluate(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 368954447c9..0c30d50076f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -312,8 +312,8 @@ class RaycastFunction : public fn::MultiFunction { } const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); target_data_ = &target_evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 6eb95859e50..59e203afd08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -19,9 +19,9 @@ static void node_declare(NodeDeclarationBuilder &b) static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances_component.instances_amount(); + const int domain_num = instances_component.instances_num(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 4ca21874f8f..d4716a6b6f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -23,7 +23,7 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index 73e49c7d037..d2082924fa7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -75,12 +75,12 @@ static void set_position_in_component(CurveComponent &component, const Field<float3> &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index a23a6c09551..4c84093bfcb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 1155c97dc38..8b1e5935a61 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -17,15 +17,15 @@ static void set_tilt_in_component(GeometryComponent &component, const Field<float> &tilt_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>( "tilt", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(tilt_field, tilts.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index 0892e068ce2..ec95f9a89f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -20,12 +20,12 @@ static void set_id_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Since adding the ID attribute can change the result of the field evaluation (the random value diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index a0b38209f97..58613dae832 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -17,15 +17,15 @@ static void set_material_index_in_component(GeometryComponent &component, const Field<int> &index_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>( "material_index", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(index_field, indices.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 93024fd81d6..571bead9743 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index d2ff9753897..caf33108716 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -143,12 +143,12 @@ static void set_position_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index 3420e17cc10..b98fbd0a0fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -17,15 +17,15 @@ static void set_smooth_in_component(GeometryComponent &component, const Field<bool> &shade_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>( "shade_smooth", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(shade_field, shades.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index dc7f3b1343a..976857883f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -17,15 +17,15 @@ static void set_cyclic_in_component(GeometryComponent &component, const Field<bool> &cyclic_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>( "cyclic", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(cyclic_field, cyclics.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index f3031ff3678..8b665376c01 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -17,15 +17,15 @@ static void set_resolution_in_component(GeometryComponent &component, const Field<int> &resolution_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>( "resolution", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(resolution_field, resolutions.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index de206be5367..3b348dd0136 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -88,8 +88,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CPPType &type = field.cpp_type(); const CustomDataType data_type = bke::cpp_type_to_custom_data_type(type); @@ -97,10 +97,10 @@ static void try_capture_field_on_geometry(GeometryComponent &component, /* Could avoid allocating a new buffer if: * - We are writing to an attribute that exists already. * - The field does not depend on that attribute (we can't easily check for that yet). */ - void *buffer = MEM_mallocN(type.size() * domain_size, __func__); + void *buffer = MEM_mallocN(type.size() * domain_num, __func__); fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size}); + evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num}); evaluator.evaluate(); component.attribute_try_delete(name); @@ -114,7 +114,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component, else { /* Cannot change type of built-in attribute. */ } - type.destruct_n(buffer, domain_size); + type.destruct_n(buffer, domain_num); MEM_freeN(buffer); } else { diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index f2a859065c6..9eda5bb34ff 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -118,8 +118,8 @@ static void node_geo_exec(GeoNodeExecParams params) } const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); - const int verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - const int edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + const int verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + const int edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); if (verts_num == 0 || edges_num == 0) { return; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 12e306ba480..dca214660c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -365,7 +365,7 @@ static bool component_is_available(const GeometrySet &geometry, if (component.is_empty()) { return false; } - return component.attribute_domain_size(domain) != 0; + return component.attribute_domain_num(domain) != 0; } /** @@ -433,8 +433,8 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { { const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num); source_evaluator_->add(src_field_); source_evaluator_->evaluate(); source_data_ = &source_evaluator_->get_evaluated(0); @@ -578,9 +578,9 @@ class NearestTransferFunction : public fn::MultiFunction { { if (use_mesh_) { const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); - const int domain_size = mesh.attribute_domain_size(domain_); + const int domain_num = mesh.attribute_domain_num(domain_); mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); + mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num); mesh_evaluator_->add(src_field_); mesh_evaluator_->evaluate(); mesh_data_ = &mesh_evaluator_->get_evaluated(0); @@ -588,9 +588,9 @@ class NearestTransferFunction : public fn::MultiFunction { if (use_points_) { const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); - const int domain_size = points.attribute_domain_size(domain_); + const int domain_num = points.attribute_domain_num(domain_); point_context_.emplace(GeometryComponentFieldContext(points, domain_)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size); + point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num); point_evaluator_->add(src_field_); point_evaluator_->evaluate(); point_data_ = &point_evaluator_->get_evaluated(0); @@ -658,9 +658,9 @@ class IndexTransferFunction : public fn::MultiFunction { if (component == nullptr) { return; } - const int domain_size = component->attribute_domain_size(domain_); + const int domain_num = component->attribute_domain_num(domain_); geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); - evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); evaluator_->add(src_field_); evaluator_->evaluate(); src_data_ = &evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index a5ca1cba28f..258c1ac3fba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -19,7 +19,7 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 992470e8279..e47dc22da04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -83,9 +83,9 @@ static void node_geo_exec(GeoNodeExecParams params) GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator{context, domain_size}; + FieldEvaluator evaluator{context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 378bac894e8..9a316190720 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -241,27 +241,27 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful case GEO_COMPONENT_TYPE_MESH: { const MeshComponent &mesh_component = *(const MeshComponent *)component; MeshInfo &info = this->mesh_info.emplace(); - info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE); break; } case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *(const CurveComponent *)component; CurveInfo &info = this->curve_info.emplace(); - info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE); break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; PointCloudInfo &info = this->pointcloud_info.emplace(); - info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT); break; } case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *(const InstancesComponent *)component; InstancesInfo &info = this->instances_info.emplace(); - info.tot_instances = instances_component.instances_amount(); + info.instances_num = instances_component.instances_num(); break; } case GEO_COMPONENT_TYPE_VOLUME: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index cea3084a418..39d8c453e43 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -119,14 +119,14 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); if (default_value == nullptr) { default_value = cpp_type->default_value(); } if (found_socket == nullptr) { - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } if (found_socket->type == SOCK_STRING) { @@ -140,40 +140,40 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, /* If the attribute doesn't exist, use the default value and output an error message * (except when the field is empty, to avoid spamming error messages, and not when * the domain is empty and we don't expect an attribute anyway). */ - if (!name.empty() && component.attribute_domain_size(domain) != 0) { + if (!name.empty() && component.attribute_domain_num(domain) != 0) { this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_INT) { const int value = this->get_input<int>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_RGBA) { const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized( CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } BLI_assert(false); - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc index a2a9aa9ad91..cba944c671c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc @@ -42,10 +42,20 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat, GPUNodeStack *out) { NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage; - /* NOTE: using CD_AUTO_FROM_NAME instead of CD_MCOL or CD_PROP_COLOR as geometry nodes may - * overwrite data which will also change the CustomDataType. This will also make EEVEE and Cycles + /* NOTE: using CD_AUTO_FROM_NAME instead of CD_MCOL or CD_PROP_COLOR for named attributes + * as geometry nodes may overwrite data which will also change the CustomDataType. + * This will also make EEVEE and Cycles * consistent. See T93179. */ - GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_AUTO_FROM_NAME, vertexColor->layer_name); + + GPUNodeLink *vertexColorLink; + + if (vertexColor->layer_name[0]) { + vertexColorLink = GPU_attribute(mat, CD_AUTO_FROM_NAME, vertexColor->layer_name); + } + else { /* Fall back on active render color attribute. */ + vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name); + } + return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); } diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index 76208104a8c..248114f242a 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -49,7 +49,7 @@ static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, sh } } -static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, short thread) +static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread) { TexDelegate *dg = in->data; if (dg) { @@ -59,7 +59,7 @@ static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, sho in->vec[1] = in->vec[2] = in->vec[0]; } } - memcpy(out, in->vec, sz * sizeof(float)); + memcpy(out, in->vec, num * sizeof(float)); } void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread) diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 77333c6dfea..80c48e31510 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -46,6 +46,9 @@ "``3D_FLAT_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ + "``3D_IMAGE``\n" \ + " :Attributes: vec3 pos, vec2 texCoord\n" \ + " :Uniforms: sampler2D image\n" \ "``3D_SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ @@ -68,6 +71,7 @@ static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, + {GPU_SHADER_3D_IMAGE, "3D_IMAGE"}, {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index 3e0698caa50..1e5856a3285 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -35,7 +35,7 @@ static const char *bpyunits_usystem_items[] = { NULL, }; -static const char *bpyunits_ucategorie_items[] = { +static const char *bpyunits_ucategories_items[] = { "NONE", "LENGTH", "AREA", @@ -43,20 +43,26 @@ static const char *bpyunits_ucategorie_items[] = { "MASS", "ROTATION", "TIME", + "TIME_ABSOLUTE", "VELOCITY", "ACCELERATION", "CAMERA", "POWER", + "TEMPERATURE", NULL, }; +BLI_STATIC_ASSERT( + ARRAY_SIZE(bpyunits_ucategories_items) == B_UNIT_TYPE_TOT + 1, + "`bpyunits_ucategories_items` should match `B_UNIT_` enum items in `BKE_units.h`") + /** * These fields are just empty placeholders, actual values get set in initializations functions. * This allows us to avoid many handwriting, and above all, * to keep all systems/categories definition stuff in `BKE_unit.h`. */ static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usystem_items)]; -static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)]; +static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategories_items)]; static PyStructSequence_Desc bpyunits_systems_desc = { "bpy.utils.units.systems", /* name */ @@ -114,7 +120,7 @@ static bool bpyunits_validate(const char *usys_str, const char *ucat_str, int *r return false; } - *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategorie_items); + *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategories_items); if (*r_ucat < 0) { PyErr_Format(PyExc_ValueError, "Unknown unit category specified: %.200s.", ucat_str); return false; @@ -356,7 +362,7 @@ PyObject *BPY_utils_units(void) /* bpy.utils.units.categories */ item = py_structseq_from_strings( - &BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategorie_items); + &BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategories_items); PyModule_AddObject(submodule, "categories", item); /* steals ref */ return submodule; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 5953c0f0f8f..bf876163013 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -330,10 +330,10 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, { int i; int hit_mesh = -1; - float hit_distance = max_ray_distance; - if (hit_distance == 0.0f) { + float hit_distance_squared = max_ray_distance * max_ray_distance; + if (hit_distance_squared == 0.0f) { /* No ray distance set, use maximum. */ - hit_distance = FLT_MAX; + hit_distance_squared = FLT_MAX; } BVHTreeRayHit *hits; @@ -365,16 +365,14 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, } if (hits[i].index != -1) { - float distance; - float hit_world[3]; - /* distance comparison in world space */ + float hit_world[3]; mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); - distance = len_squared_v3v3(hit_world, co); + float distance_squared = len_squared_v3v3(hit_world, co); - if (distance < hit_distance) { + if (distance_squared < hit_distance_squared) { hit_mesh = i; - hit_distance = distance; + hit_distance_squared = distance_squared; } } } diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index f93397eedab..dc9ad2ddb5e 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -464,140 +464,141 @@ static void do_multires_bake(MultiresBakeRender *bkr, const MLoopTri *mlooptri = dm->getLoopTriArray(dm); const int lvl = bkr->lvl; int tot_tri = dm->getNumLoopTri(dm); + if (tot_tri < 1) { + return; + } - if (tot_tri > 0) { - MultiresBakeThread *handles; - MultiresBakeQueue queue; - - MVert *mvert = dm->getVertArray(dm); - MPoly *mpoly = dm->getPolyArray(dm); - MLoop *mloop = dm->getLoopArray(dm); - MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - float *pvtangent = NULL; - - ListBase threads; - int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); - - void *bake_data = NULL; - - Mesh *temp_mesh = BKE_mesh_new_nomain( - dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); - memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); - memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); - memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); - memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); - const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); - - if (require_tangent) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - BKE_mesh_calc_loop_tangent_ex( - dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - true, - NULL, - 0, - vert_normals, - poly_normals, - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), - (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); - } - - pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + MultiresBakeThread *handles; + MultiresBakeQueue queue; + + MVert *mvert = dm->getVertArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + MLoop *mloop = dm->getLoopArray(dm); + MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); + float *pvtangent = NULL; + + ListBase threads; + int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); + + void *bake_data = NULL; + + Mesh *temp_mesh = BKE_mesh_new_nomain( + dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); + memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); + memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); + memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); + memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); + + if (require_tangent) { + if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { + BKE_mesh_calc_loop_tangent_ex( + dm->getVertArray(dm), + dm->getPolyArray(dm), + dm->getNumPolys(dm), + dm->getLoopArray(dm), + dm->getLoopTriArray(dm), + dm->getNumLoopTri(dm), + &dm->loopData, + true, + NULL, + 0, + vert_normals, + poly_normals, + (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ + /* result */ + &dm->loopData, + dm->getNumLoops(dm), + &dm->tangent_mask); } - /* all threads shares the same custom bake data */ - if (initBakeData) { - bake_data = initBakeData(bkr, ibuf); - } + pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + } - if (tot_thread > 1) { - BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); - } + /* all threads shares the same custom bake data */ + if (initBakeData) { + bake_data = initBakeData(bkr, ibuf); + } - handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); - - init_ccgdm_arrays(bkr->hires_dm); - - /* faces queue */ - queue.cur_tri = 0; - queue.tot_tri = tot_tri; - BLI_spin_init(&queue.spin); - - /* fill in threads handles */ - for (i = 0; i < tot_thread; i++) { - MultiresBakeThread *handle = &handles[i]; - - handle->bkr = bkr; - handle->image = ima; - handle->queue = &queue; - - handle->data.mpoly = mpoly; - handle->data.mvert = mvert; - handle->data.vert_normals = vert_normals; - handle->data.mloopuv = mloopuv; - BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); - handle->data.mlooptri = mlooptri; - handle->data.mloop = mloop; - handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ - handle->data.w = ibuf->x; - handle->data.h = ibuf->y; - handle->data.lores_dm = dm; - handle->data.hires_dm = bkr->hires_dm; - handle->data.lvl = lvl; - handle->data.pass_data = passKnownData; - handle->data.thread_data = handle; - handle->data.bake_data = bake_data; - handle->data.ibuf = ibuf; - - handle->height_min = FLT_MAX; - handle->height_max = -FLT_MAX; - - init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - - if (tot_thread > 1) { - BLI_threadpool_insert(&threads, handle); - } - } + if (tot_thread > 1) { + BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); + } + + handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); + + init_ccgdm_arrays(bkr->hires_dm); + + /* faces queue */ + queue.cur_tri = 0; + queue.tot_tri = tot_tri; + BLI_spin_init(&queue.spin); + + /* fill in threads handles */ + for (i = 0; i < tot_thread; i++) { + MultiresBakeThread *handle = &handles[i]; + + handle->bkr = bkr; + handle->image = ima; + handle->queue = &queue; + + handle->data.mpoly = mpoly; + handle->data.mvert = mvert; + handle->data.vert_normals = vert_normals; + handle->data.mloopuv = mloopuv; + BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); + handle->data.mlooptri = mlooptri; + handle->data.mloop = mloop; + handle->data.pvtangent = pvtangent; + handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ + handle->data.w = ibuf->x; + handle->data.h = ibuf->y; + handle->data.lores_dm = dm; + handle->data.hires_dm = bkr->hires_dm; + handle->data.lvl = lvl; + handle->data.pass_data = passKnownData; + handle->data.thread_data = handle; + handle->data.bake_data = bake_data; + handle->data.ibuf = ibuf; + + handle->height_min = FLT_MAX; + handle->height_max = -FLT_MAX; + + init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - /* run threads */ if (tot_thread > 1) { - BLI_threadpool_end(&threads); + BLI_threadpool_insert(&threads, handle); } - else { - do_multires_bake_thread(&handles[0]); - } - - /* construct bake result */ - result->height_min = handles[0].height_min; - result->height_max = handles[0].height_max; + } - for (i = 1; i < tot_thread; i++) { - result->height_min = min_ff(result->height_min, handles[i].height_min); - result->height_max = max_ff(result->height_max, handles[i].height_max); - } + /* run threads */ + if (tot_thread > 1) { + BLI_threadpool_end(&threads); + } + else { + do_multires_bake_thread(&handles[0]); + } - BLI_spin_end(&queue.spin); + /* construct bake result */ + result->height_min = handles[0].height_min; + result->height_max = handles[0].height_max; - /* finalize baking */ - if (freeBakeData) { - freeBakeData(bake_data); - } + for (i = 1; i < tot_thread; i++) { + result->height_min = min_ff(result->height_min, handles[i].height_min); + result->height_max = max_ff(result->height_max, handles[i].height_max); + } - MEM_freeN(handles); + BLI_spin_end(&queue.spin); - BKE_id_free(NULL, temp_mesh); + /* finalize baking */ + if (freeBakeData) { + freeBakeData(bake_data); } + + MEM_freeN(handles); + + BKE_id_free(NULL, temp_mesh); } /* mode = 0: interpolate normals, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 3ee5c85c031..9e9f195c430 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -812,6 +812,10 @@ typedef struct wmXrActionData { char action_set[64]; /** Action name. */ char action[64]; + /** User path. E.g. "/user/hand/left" */ + char user_path[64]; + /** Other user path, for bimanual actions. E.g. "/user/hand/right" */ + char user_path_other[64]; /** Type. */ eXrActionType type; /** State. Set appropriately based on type. */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index a61d73b9f0b..f481f19045d 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -492,7 +492,8 @@ void WM_gizmomap_draw(wmGizmoMap *gzmap, static void gizmo_draw_select_3d_loop(const bContext *C, wmGizmo **visible_gizmos, - const int visible_gizmos_len) + const int visible_gizmos_len, + bool *r_use_select_bias) { /* TODO(campbell): this depends on depth buffer being written to, @@ -528,6 +529,10 @@ static void gizmo_draw_select_3d_loop(const bContext *C, is_depth_skip_prev = is_depth_skip; } + if (gz->select_bias != 0.0) { + *r_use_select_bias = true; + } + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */ gz->type->draw_select(C, gz, select_id << 8); @@ -545,10 +550,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const int visible_gizmos_len, const bContext *C, const int co[2], - const int hotspot, - const bool use_depth_test, - const bool has_3d_select_bias, - int *r_hits) + const int hotspot) { const wmWindowManager *wm = CTX_wm_manager(C); ScrArea *area = CTX_wm_area(C); @@ -562,76 +564,30 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, BLI_rcti_init_pt_radius(&rect, co, hotspot); - /* The selection mode is assigned for the following reasons: - * - * - #GPU_SELECT_ALL: Use it to check if there is anything at the cursor location - * (only ever runs once). - * - #GPU_SELECT_PICK_NEAREST: Use if there are more than 1 item at the cursor location, - * pick the nearest one. - * - #GPU_SELECT_PICK_ALL: Use for the same purpose as #GPU_SELECT_PICK_NEAREST - * when the selection depths need to re-ordered based on a bias. - * */ - const eGPUSelectMode gpu_select_mode = - (use_depth_test ? (has_3d_select_bias ? - /* Using select bias means the depths need to be - * re-calculated based on the bias to pick the best. */ - GPU_SELECT_PICK_ALL : - /* No bias, just pick the closest. */ - GPU_SELECT_PICK_NEAREST) : - /* Fast-path (occlusion queries). */ - GPU_SELECT_ALL); - - /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is - * performed before the viewport is fully initialized (region->draw_buffer = NULL). - * When this is the case we should not use depth testing. */ - GPUViewport *gpu_viewport = WM_draw_region_get_viewport(region); - if (use_depth_test && gpu_viewport == NULL) { - return -1; - } + ED_view3d_draw_setup_view( + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); - if (GPU_select_is_cached()) { - GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); - GPU_select_cache_load_id(); - hits = GPU_select_end(); - } - else { - /* TODO: waiting for the GPU in the middle of the event loop for every - * mouse move is bad for performance, we need to find a solution to not - * use the GPU or draw something once. (see T61474) */ + bool use_select_bias = false; - ED_view3d_draw_setup_view( - wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); + /* TODO: waiting for the GPU in the middle of the event loop for every + * mouse move is bad for performance, we need to find a solution to not + * use the GPU or draw something once. (see T61474) */ + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + /* do the drawing */ + gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias); - /* There is no need to bind to the depth buffer outside this function - * because all future passes the will use the cached depths. */ - GPUFrameBuffer *depth_read_fb = NULL; - if (use_depth_test) { - GPUTexture *depth_tx = GPU_viewport_depth_texture(gpu_viewport); - GPU_framebuffer_ensure_config(&depth_read_fb, - { - GPU_ATTACHMENT_TEXTURE(depth_tx), - GPU_ATTACHMENT_NONE, - }); - GPU_framebuffer_bind(depth_read_fb); - } + hits = GPU_select_end(); - GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); - gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len); - hits = GPU_select_end(); - - if (use_depth_test) { - GPU_framebuffer_restore(); - GPU_framebuffer_free(depth_read_fb); - } - - ED_view3d_draw_setup_view( - wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); + if (hits > 0) { + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias); + GPU_select_end(); } - /* When selection bias is needed, this function will run again with `use_depth_test` enabled. */ - int hit_found = -1; + ED_view3d_draw_setup_view( + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); - if (has_3d_select_bias && use_depth_test && (hits > 1)) { + if (use_select_bias && (hits > 1)) { float co_direction[3]; float co_screen[3] = {co[0], co[1], 0.0f}; ED_view3d_win_to_vector(region, (float[2]){UNPACK2(co)}, co_direction); @@ -643,6 +599,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); GPUSelectResult *buf_iter = buffer; + int hit_found = -1; float dot_best = FLT_MAX; for (int i = 0; i < hits; i++, buf_iter++) { @@ -662,16 +619,11 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, hit_found = buf_iter->id; } } - } - else { - const GPUSelectResult *hit_near = GPU_select_buffer_near(buffer, hits); - if (hit_near) { - hit_found = hit_near->id; - } + return hit_found; } - *r_hits = hits; - return hit_found; + const GPUSelectResult *hit_near = GPU_select_buffer_near(buffer, hits); + return hit_near ? hit_near->id : -1; } /** @@ -694,7 +646,6 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, /* Search for 3D gizmo's that use the 2D callback for checking intersections. */ bool has_3d = false; - bool has_3d_select_bias = false; { for (int select_id = 0; select_id < visible_gizmos_len; select_id++) { wmGizmo *gz = visible_gizmos[select_id]; @@ -710,9 +661,6 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, } else if (gz->type->draw_select != NULL) { has_3d = true; - if (gz->select_bias != 0.0f) { - has_3d_select_bias = true; - } } } } @@ -721,86 +669,39 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, * This way we always use the first hit. */ if (has_3d) { - /* NOTE(@campbellbarton): The selection logic here uses a fast-path that exits early - * where possible. This is important as this runs on cursor-motion in the 3D view-port. - * - * - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos. - * If there are no gizmos or only one - early exit, otherwise. - * - * - Bind the depth buffer and use selection picking logic. - * This is much slower than occlusion queries (since it's reading depths while drawing). - * When there is a single gizmo under the cursor (quite common), early exit, otherwise. - * - * - Perform another pass at a reduced size (see: `hotspot_radii`), - * since the result depths are cached this pass is practically free. - * - * Other notes: - * - * - If any of these passes fail, use the nearest result from the previous pass. - * - * - Drawing is only ever done twice. - */ - - /* Order largest to smallest so the first pass can be used as cache for - * later passes (when `use_depth_test == true`). */ - const int hotspot_radii[] = { - 10 * U.pixelsize, - /* This runs on mouse move, careful doing too many tests! */ - 3 * U.pixelsize, - }; - - /* Narrowing may assign zero to `hit`, allow falling back to the previous test. */ - int hit_prev = -1; + /* The depth buffer is needed for for gizmos to obscure eachother. */ + GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C)); - bool use_depth_test = false; - bool use_depth_cache = false; - - /* Workaround for MS-Windows & NVidia failing to detect any gizmo undo the cursor unless the - * depth test is enabled, see: T97124. - * NOTE(@campbellbarton): Ideally the exact cause of this could be tracked down, - * disable as I don't have a system to test this configuration. */ - if (GPU_type_matches(GPU_DEVICE_NVIDIA | GPU_DEVICE_SOFTWARE, GPU_OS_WIN, GPU_DRIVER_ANY)) { - use_depth_test = true; + /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is + * performed before the viewport is fully initialized (region->draw_buffer = NULL). + * When this is the case we should not use depth testing. */ + if (viewport == NULL) { + return NULL; } + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); + GPUFrameBuffer *depth_read_fb = NULL; + GPU_framebuffer_ensure_config(&depth_read_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + GPU_framebuffer_bind(depth_read_fb); + const int hotspot_radii[] = { + 3 * U.pixelsize, + /* This runs on mouse move, careful doing too many tests! */ + 10 * U.pixelsize, + }; for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) { - - if (use_depth_test && (use_depth_cache == false)) { - GPU_select_cache_begin(); - use_depth_cache = true; - } - - int hit_count; - hit = gizmo_find_intersected_3d_intern(visible_gizmos, - visible_gizmos_len_trim, - C, - co, - hotspot_radii[i], - use_depth_test, - has_3d_select_bias, - &hit_count); - /* Only continue searching when there are multiple options to narrow down. */ - if (hit_count < 2) { + hit = gizmo_find_intersected_3d_intern( + visible_gizmos, visible_gizmos_len_trim, C, co, hotspot_radii[i]); + if (hit != -1) { break; } - - /* Fast path for simple case, one item or nothing. */ - if (use_depth_test == false) { - /* Restart, using depth buffer (slower). */ - use_depth_test = true; - i = -1; - } - hit_prev = hit; - } - /* Narrowing the search area may yield no hits, - * in this case fall back to the previous search. */ - if (hit == -1) { - hit = hit_prev; } - if (use_depth_cache) { - GPU_select_cache_end(); - } + GPU_framebuffer_restore(); + GPU_framebuffer_free(depth_read_fb); if (hit != -1) { const int select_id = hit >> 8; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 02da798495b..d2ade7b0376 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -467,7 +467,7 @@ static bool wm_draw_region_bind(bContext *C, ARegion *region, int view) } if (region->draw_buffer->viewport) { - if (G.is_rendering && C != NULL) { + if (G.is_rendering && C != NULL && U.experimental.use_draw_manager_acquire_lock) { Scene *scene = CTX_data_scene(C); RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine); if (RE_engine_is_opengl(render_engine_type)) { diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 1c3f7ed3e7a..5776184aec0 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -101,6 +101,7 @@ static int wm_operator_call_internal(bContext *C, static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win); +static void wm_operator_free_for_fileselect(wmOperator *file_operator); /* -------------------------------------------------------------------- */ /** \name Event Management @@ -1904,8 +1905,15 @@ void wm_event_free_handler(wmEventHandler *handler) MEM_freeN(handler); } -/* Only set context when area/region is part of screen. */ -static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event) +/** + * Check if the handler's area and/or region are actually part of the screen, and return them if + * so. + */ +static void wm_handler_op_context_get_if_valid(bContext *C, + wmEventHandler_Op *handler, + const wmEvent *event, + ScrArea **r_area, + ARegion **r_region) { wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C); /* It's probably fine to always use #WM_window_get_active_screen() to get the screen. But this @@ -1913,12 +1921,15 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const * possible. */ bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C); + *r_area = NULL; + *r_region = NULL; + if (screen == NULL || handler->op == NULL) { return; } if (handler->context.area == NULL) { - CTX_wm_area_set(C, NULL); + /* Pass */ } else { ScrArea *area = NULL; @@ -1942,7 +1953,7 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const else { ARegion *region; wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL; - CTX_wm_area_set(C, area); + *r_area = area; if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) { region = BKE_area_find_region_xy(area, handler->context.region_type, event->xy); @@ -1965,12 +1976,21 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const /* No warning print here, after full-area and back regions are remade. */ if (region) { - CTX_wm_region_set(C, region); + *r_region = region; } } } } +static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event) +{ + ScrArea *area = NULL; + ARegion *region = NULL; + wm_handler_op_context_get_if_valid(C, handler, event, &area, ®ion); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); +} + void WM_event_remove_handlers(bContext *C, ListBase *handlers) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2017,7 +2037,13 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) } WM_cursor_grab_disable(win, NULL); - WM_operator_free(handler->op); + + if (handler->is_fileselect) { + wm_operator_free_for_fileselect(handler->op); + } + else { + WM_operator_free(handler->op); + } } } else if (handler_base->type == WM_HANDLER_TYPE_UI) { @@ -2454,6 +2480,22 @@ static int wm_handler_operator_call(bContext *C, return WM_HANDLER_BREAK; } +static void wm_operator_free_for_fileselect(wmOperator *file_operator) +{ + LISTBASE_FOREACH (bScreen *, screen, &G_MAIN->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_FILE) { + SpaceFile *sfile = area->spacedata.first; + if (sfile->op == file_operator) { + sfile->op = NULL; + } + } + } + } + + WM_operator_free(file_operator); +} + /** * File-select handlers are only in the window queue, * so it's safe to switch screens or area types. @@ -2507,6 +2549,9 @@ static int wm_handler_fileselect_do(bContext *C, case EVT_FILESELECT_CANCEL: case EVT_FILESELECT_EXTERNAL_CANCEL: { wmWindow *ctx_win = CTX_wm_window(C); + wmEvent *eventstate = ctx_win->eventstate; + /* The root window of the operation as determined in #WM_event_add_fileselect(). */ + wmWindow *root_win = handler->context.win; /* Remove link now, for load file case before removing. */ BLI_remlink(handlers, handler); @@ -2542,21 +2587,17 @@ static int wm_handler_fileselect_do(bContext *C, ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized); if (BLI_listbase_is_single(&file_area->spacedata)) { - BLI_assert(ctx_win != win); + BLI_assert(root_win != win); wm_window_close(C, wm, win); - CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */ + CTX_wm_window_set(C, root_win); /* #wm_window_close() NULLs. */ /* Some operators expect a drawable context (for #EVT_FILESELECT_EXEC). */ - wm_window_make_drawable(wm, ctx_win); + wm_window_make_drawable(wm, root_win); /* Ensure correct cursor position, otherwise, popups may close immediately after * opening (#UI_BLOCK_MOVEMOUSE_QUIT). */ - wm_cursor_position_get( - ctx_win, &ctx_win->eventstate->xy[0], &ctx_win->eventstate->xy[1]); - wm->winactive = ctx_win; /* Reports use this... */ - if (handler->context.win == win) { - handler->context.win = NULL; - } + wm_cursor_position_get(root_win, &eventstate->xy[0], &eventstate->xy[1]); + wm->winactive = root_win; /* Reports use this... */ } else if (file_area->full) { ED_screen_full_prevspace(C, file_area); @@ -2575,7 +2616,13 @@ static int wm_handler_fileselect_do(bContext *C, } } - wm_handler_op_context(C, handler, ctx_win->eventstate); + CTX_wm_window_set(C, root_win); + wm_handler_op_context(C, handler, eventstate); + /* At this point context is supposed to match the root context determined by + * #WM_event_add_fileselect(). */ + BLI_assert(!CTX_wm_area(C) || (CTX_wm_area(C) == handler->context.area)); + BLI_assert(!CTX_wm_region(C) || (CTX_wm_region(C) == handler->context.region)); + ScrArea *handler_area = CTX_wm_area(C); /* Make sure new context area is ready, the operator callback may operate on it. */ if (handler_area) { @@ -2644,7 +2691,7 @@ static int wm_handler_fileselect_do(bContext *C, } if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) { - WM_operator_free(handler->op); + wm_operator_free_for_fileselect(handler->op); } } else { @@ -2659,8 +2706,7 @@ static int wm_handler_fileselect_do(bContext *C, wm->op_undo_depth--; } } - - WM_operator_free(handler->op); + wm_operator_free_for_fileselect(handler->op); } CTX_wm_area_set(C, NULL); @@ -3981,58 +4027,122 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval event.type = EVT_FILESELECT; event.val = eventval; + event.flag = 0; event.customdata = ophandle; /* Only as void pointer type check. */ wm_event_add(win, &event); } } +/** + * From the context window, try to find a window that is appropriate for use as root window of a + * modal File Browser (modal means: there is a #SpaceFile.op to execute). The root window will + * become the parent of the File Browser and provides a context to execute the file operator in, + * even after closing the File Browser. + * + * An appropriate window is either of the following: + * * A parent window that does not yet contain a modal File Browser. This is determined using + * #ED_fileselect_handler_area_find_any_with_op(). + * * A parent window containing a modal File Browser, but in a maximized/fullscreen state. Users + * shouldn't be able to put a temporary screen like the modal File Browser into + * maximized/fullscreen state themselves. So this setup indicates that the File Browser was + * opened using #USER_TEMP_SPACE_DISPLAY_FULLSCREEN. + * + * If no appropriate parent window can be found from the context window, return the first + * registered window (which can be assumed to be a regular window, e.g. no modal File Browser; this + * is asserted). + */ +static wmWindow *wm_event_find_fileselect_root_window_from_context(const bContext *C) +{ + wmWindow *ctx_win = CTX_wm_window(C); + + for (wmWindow *ctx_win_or_parent = ctx_win; ctx_win_or_parent; + ctx_win_or_parent = ctx_win_or_parent->parent) { + ScrArea *file_area = ED_fileselect_handler_area_find_any_with_op(ctx_win_or_parent); + + if (!file_area) { + return ctx_win_or_parent; + } + + if (file_area->full) { + return ctx_win_or_parent; + } + } + + /* Fallback to the first window. */ + const wmWindowManager *wm = CTX_wm_manager(C); + BLI_assert(!ED_fileselect_handler_area_find_any_with_op(wm->windows.first)); + return wm->windows.first; +} + /* Operator is supposed to have a filled "path" property. */ /* Optional property: file-type (XXX enum?) */ void WM_event_add_fileselect(bContext *C, wmOperator *op) { wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - const bool is_temp_screen = WM_window_is_temp_screen(win); + wmWindow *ctx_win = CTX_wm_window(C); + + /* The following vars define the root context. That is essentially the "parent" context of the + * File Browser operation, to be restored for eventually executing the file operation. */ + wmWindow *root_win = wm_event_find_fileselect_root_window_from_context(C); + /* Determined later. */ + ScrArea *root_area = NULL; + ARegion *root_region = NULL; /* Close any popups, like when opening a file browser from the splash. */ - UI_popup_handlers_remove_all(C, &win->modalhandlers); + UI_popup_handlers_remove_all(C, &root_win->modalhandlers); - if (!is_temp_screen) { - /* Only allow 1 file selector open per window. */ - LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &win->modalhandlers) { - if (handler_base->type == WM_HANDLER_TYPE_OP) { - wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; - if (handler->is_fileselect == false) { - continue; - } + /* Setting the context window unsets the context area & screen. Avoid doing that, so operators + * calling the file browser can operate in the context the browser was opened in. */ + if (ctx_win != root_win) { + CTX_wm_window_set(C, root_win); + } - ScrArea *file_area = ED_fileselect_handler_area_find(win, handler->op); + /* The root window may already have a File Browser open. Cancel it if so, only 1 should be open + * per window. The root context of this operation is also used for the new operation. */ + LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &root_win->modalhandlers) { + if (handler_base->type == WM_HANDLER_TYPE_OP) { + wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; + if (handler->is_fileselect == false) { + continue; + } - if (file_area) { - CTX_wm_area_set(C, file_area); - wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL); - } - /* If not found we stop the handler without changing the screen. */ - else { - wm_handler_fileselect_do( - C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); - } + wm_handler_op_context_get_if_valid( + C, handler, ctx_win->eventstate, &root_area, &root_region); + + ScrArea *file_area = ED_fileselect_handler_area_find(root_win, handler->op); + + if (file_area) { + CTX_wm_area_set(C, file_area); + wm_handler_fileselect_do(C, &root_win->modalhandlers, handler, EVT_FILESELECT_CANCEL); + } + /* If not found we stop the handler without changing the screen. */ + else { + wm_handler_fileselect_do( + C, &root_win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL); } } } + BLI_assert(root_win != NULL); + /* When not reusing the root context from a previous file browsing operation, use the current + * area & region, if they are inside the root window. */ + if (!root_area && ctx_win == root_win) { + root_area = CTX_wm_area(C); + root_region = CTX_wm_region(C); + } + wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__); handler->head.type = WM_HANDLER_TYPE_OP; handler->is_fileselect = true; handler->op = op; - handler->context.win = CTX_wm_window(C); - handler->context.area = CTX_wm_area(C); - handler->context.region = CTX_wm_region(C); + handler->context.win = root_win; + handler->context.area = root_area; + handler->context.region = root_region; - BLI_addhead(&win->modalhandlers, handler); + BLI_addhead(&root_win->modalhandlers, handler); /* Check props once before invoking if check is available * ensures initial properties are valid. */ @@ -4041,6 +4151,10 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) } WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN); + + if (ctx_win != root_win) { + CTX_wm_window_set(C, ctx_win); + } } /** \} */ @@ -5727,6 +5841,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wmEvent test_event = *win->eventstate; test_event.type = event_data[data_index].event_type; test_event.val = event_data[data_index].event_value; + test_event.flag = 0; wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 382a37e09e5..c0427f9be9a 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -578,6 +578,9 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, wm_window_swap_buffers(win); + /* Clear double buffer to avoids flickering of new windows on certain drivers. (See T97600) */ + GPU_clear_color(0.55f, 0.55f, 0.55f, 1.0f); + // GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified); } else { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 0a76fd0a25f..2a829e274d9 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1025,6 +1025,10 @@ static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__); strcpy(data->action_set, action_set_name); strcpy(data->action, action->name); + strcpy(data->user_path, action->subaction_paths[subaction_idx]); + if (bimanual) { + strcpy(data->user_path_other, action->subaction_paths[subaction_idx_other]); + } data->type = action->type; switch (action->type) { |