Compare commits
329 Commits
pbr-viewpo
...
surface-de
Author | SHA1 | Date | |
---|---|---|---|
a2ed635a73 | |||
b3aead8fd7 | |||
733b5b8c66 | |||
5a17cb4c08 | |||
e5e44c01f2 | |||
a3e32e2ab5 | |||
e843f42e66 | |||
96f6ec07fb | |||
c38e19ca67 | |||
96d66c7e4d | |||
46821f072d | |||
f870343208 | |||
cf1a7e3944 | |||
cf660b2a02 | |||
6f3957770d | |||
7608f366c7 | |||
8c220c57f9 | |||
a300f80043 | |||
22ce298d73 | |||
3469aa47c1 | |||
097a560bc9 | |||
1b7623fc06 | |||
c546256563 | |||
5c263a9050 | |||
8745cd825a | |||
28622ae81e | |||
d6c7163c06 | |||
0bb57759ec | |||
5e1d438d5e | |||
0721bc0ac4 | |||
7ca0894a17 | |||
751496437b | |||
3014601f3b | |||
b80971ce10 | |||
68f5ce194b | |||
1e9003aea5 | |||
95701b0b04 | |||
![]() |
4d3d2d0299 | ||
403f00e558 | |||
def365e252 | |||
f2b57c3532 | |||
a537e7b426 | |||
411836d97c | |||
57141ea30e | |||
99c5c8befc | |||
9aa8d1bc45 | |||
67b1979c91 | |||
751573ce6f | |||
cb694d6595 | |||
4818b3c97e | |||
1be717d007 | |||
edc10f5529 | |||
af444e913f | |||
272412f9c0 | |||
927a168b07 | |||
bd8cbf5c07 | |||
dd51ec592f | |||
94d8e6fc6c | |||
b97c567c1d | |||
2a78635dea | |||
25c534f20a | |||
af7343ae22 | |||
5eab3b079f | |||
b3c8ee891a | |||
83b1f24140 | |||
b86c6aa6be | |||
f68ef05a56 | |||
aea4ed00d5 | |||
e8641d4474 | |||
2c26a7b71e | |||
53a1b48321 | |||
dd82d70bc5 | |||
8c93178c96 | |||
369872a2c5 | |||
fa6a62fac2 | |||
bd6a9fd734 | |||
5a6534a5bb | |||
884693b42a | |||
b6c0edcb09 | |||
b8710e1468 | |||
40990d52d1 | |||
39be226e93 | |||
a90644ed19 | |||
![]() |
0f8e5f4fb4 | ||
ba7c11aa05 | |||
27de0c40c5 | |||
f6083b7bcd | |||
8f0dc3cef6 | |||
![]() |
3fb11061ba | ||
![]() |
fffb1a4cfb | ||
841c4deed7 | |||
b859fef670 | |||
03a395766a | |||
46739f1e5c | |||
a1a8343281 | |||
d294509dd8 | |||
4dbd61cc64 | |||
1f8762bb8e | |||
0a08d8c892 | |||
0c322c3000 | |||
16e2c0ef3c | |||
60409841a4 | |||
e400f4a53e | |||
2a2eb0c463 | |||
209bc9977c | |||
![]() |
f86eccb1ca | ||
e17b92f535 | |||
4722fc5dd0 | |||
eb9e9f7f1a | |||
8b2905952e | |||
6397319659 | |||
0de157a320 | |||
44b691dc65 | |||
f2690673ba | |||
af0e6b31a5 | |||
9019f8ca95 | |||
cb117f283b | |||
a828818d59 | |||
625db1d86e | |||
85e51b0638 | |||
![]() |
0cd1b5ef85 | ||
![]() |
69470e36d6 | ||
445274fc4f | |||
a284d04093 | |||
4ee08e9533 | |||
46b5cdaa4d | |||
c418ef48cb | |||
d9597ce3ba | |||
a3b61f0639 | |||
e6ad6ff082 | |||
77ba1ed5db | |||
d3919c22b0 | |||
6a2b95e1ca | |||
dc0a9e6535 | |||
b047d79871 | |||
7a4a2ed5f4 | |||
fc9fa07c0e | |||
1b1d6ce131 | |||
7e8bf9dbd6 | |||
cc8132b0c8 | |||
43703fa4bf | |||
447fc7c4ce | |||
4151f12713 | |||
111e2f5aba | |||
b5a58507f2 | |||
69288737ca | |||
478e59a04e | |||
188ecee642 | |||
![]() |
627141082b | ||
e00c3ab13f | |||
7fd2efa507 | |||
dad0c31ceb | |||
8b01a6e0f1 | |||
2dbcb75ed5 | |||
f6ab97c1ae | |||
f1ad2ab85f | |||
8e4066b9d3 | |||
674c3bf894 | |||
cdeaec3b0d | |||
ed957768a0 | |||
7d33d4439f | |||
2a838c71d9 | |||
24d89a1f77 | |||
7dda3cf830 | |||
b1743cda5a | |||
653541ea78 | |||
915c74a33b | |||
89c1f9db37 | |||
9eeca9e7cd | |||
48971da4c8 | |||
4710fa700c | |||
a1aa3a8b75 | |||
80aae2b6fe | |||
e316636fa8 | |||
f0d53ac109 | |||
aef66a6be0 | |||
![]() |
0b9b8ab2dd | ||
![]() |
63b38848a2 | ||
![]() |
1977440770 | ||
2138fdb785 | |||
![]() |
8b8e04cae6 | ||
afc8a4f9e2 | |||
47759b14ab | |||
4d9562a3ae | |||
18be39ff17 | |||
e72caa513a | |||
49a3eaa3dc | |||
e9689e1a20 | |||
4d0f7c320c | |||
682bcb2995 | |||
4f28dac872 | |||
0085001eb0 | |||
a2d78d7a46 | |||
93ace71bd7 | |||
508e2f0d69 | |||
f19f9be1b9 | |||
9912792564 | |||
28639a22bc | |||
535914aa46 | |||
7a98c43f9d | |||
e74e622776 | |||
6f3f27c0cc | |||
c9efcc5e4a | |||
1d01a1a269 | |||
![]() |
f51f215bc3 | ||
37947ed552 | |||
9b5a32cbfb | |||
21350b73df | |||
4c30a9ee42 | |||
65a1fd975c | |||
109be7ed39 | |||
287197c4e3 | |||
c9eca0c6c9 | |||
d872aeaf51 | |||
9331930345 | |||
f8d9a56aa1 | |||
4ef45ba775 | |||
bbd4b96fe9 | |||
a7f53bc512 | |||
f64548daa6 | |||
c8c7414c3f | |||
dd921238d9 | |||
f89fbf580e | |||
818af9c331 | |||
2b1d3318f4 | |||
![]() |
521b981575 | ||
![]() |
c02cce7b75 | ||
f0ac661aa8 | |||
17fb504bcf | |||
4e5d251ccb | |||
4a68ff150f | |||
e27d9facdf | |||
dd6fa94dcc | |||
b6980ade90 | |||
27c559f059 | |||
647255db93 | |||
fc1b35e44c | |||
534f11f71e | |||
f800794b97 | |||
9847ad977a | |||
643c5a24d5 | |||
c5510df268 | |||
c9ffb6fc91 | |||
15f2a51232 | |||
dac5438562 | |||
630c0559f9 | |||
![]() |
4fdf68271c | ||
83ebf501cd | |||
97a8cd6883 | |||
f94a460397 | |||
![]() |
13ee9b8ebe | ||
4e95a9069e | |||
1ee43c5aef | |||
bf1e9bc613 | |||
cf8f6d1dbc | |||
aad46dd175 | |||
6488ce7f33 | |||
60682c37dd | |||
04aa454075 | |||
![]() |
2257e6899a | ||
2e9dd1200f | |||
5050572e89 | |||
b6d35e1fa7 | |||
4e68f48227 | |||
26bf230920 | |||
ce785868a5 | |||
b2974d7ab7 | |||
5aa6a2ec06 | |||
1272ee455e | |||
d3b0977a35 | |||
753edafcb7 | |||
0c13792437 | |||
8a1b38f071 | |||
216dec7eb1 | |||
65c481e145 | |||
194a33ff00 | |||
![]() |
03b8531cea | ||
216a3a3826 | |||
5f0933f07a | |||
f11298692b | |||
7e380ad4c0 | |||
de22e55291 | |||
35f152358b | |||
7c7d23691f | |||
72921a1e43 | |||
44522a5b98 | |||
8125271ddb | |||
3e36cbb3de | |||
a1f137767f | |||
bc71c2bf08 | |||
f523fb43f9 | |||
5c4113a3e4 | |||
a39ab9cfde | |||
cf9a6b416c | |||
42a91f7ad8 | |||
c54381488b | |||
8c761ff838 | |||
f7cf2f659a | |||
064caae7b2 | |||
81c9e0d295 | |||
af411d918e | |||
3e71006448 | |||
d2fe875f8c | |||
10a25b655a | |||
da8f5d6eac | |||
14a55bc059 | |||
cde18cf3b3 | |||
963aa7e270 | |||
80a6e5beb5 | |||
48997d2e40 | |||
3f29259676 | |||
1e1811357d | |||
0b749d57ee | |||
89a3b17853 | |||
c013280eec | |||
1d13950ae5 | |||
![]() |
e0a34e963f | ||
35fde5ff7d | |||
5d2620e9c2 | |||
672e906d49 | |||
![]() |
8905c5c874 | ||
371d3570e0 | |||
5765deecd4 | |||
9d0ac94d52 | |||
b5d527ff6c | |||
132478d4b8 | |||
d5ee031f76 | |||
fd4caafc53 | |||
b51874437d |
@@ -333,7 +333,7 @@ option(WITH_ALEMBIC "Enable Alembic Support" OFF)
|
||||
option(WITH_ALEMBIC_HDF5 "Enable Legacy Alembic Support (not officially supported)" OFF)
|
||||
|
||||
if(APPLE)
|
||||
option(WITH_CODEC_QUICKTIME "Enable Quicktime Support" ON)
|
||||
option(WITH_CODEC_QUICKTIME "Enable Quicktime Support" OFF)
|
||||
endif()
|
||||
|
||||
# 3D format support
|
||||
@@ -343,9 +343,9 @@ option(WITH_OPENCOLLADA "Enable OpenCollada Support (http://www.opencollada.or
|
||||
# Sound output
|
||||
option(WITH_SDL "Enable SDL for sound and joystick support" ${_init_SDL})
|
||||
option(WITH_OPENAL "Enable OpenAL Support (http://www.openal.org)" ON)
|
||||
option(WITH_JACK "Enable Jack Support (http://www.jackaudio.org)" ${_init_JACK})
|
||||
option(WITH_JACK "Enable JACK Support (http://www.jackaudio.org)" ${_init_JACK})
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(WITH_JACK_DYNLOAD "Enable runtime dynamic Jack libraries loading" OFF)
|
||||
option(WITH_JACK_DYNLOAD "Enable runtime dynamic JACK libraries loading" OFF)
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(WITH_SDL_DYNLOAD "Enable runtime dynamic SDL libraries loading" OFF)
|
||||
@@ -508,6 +508,12 @@ mark_as_advanced(WITH_C11)
|
||||
option(WITH_CXX11 "Build with C++11 standard enabled, for development use only!" ${_cxx11_init})
|
||||
mark_as_advanced(WITH_CXX11)
|
||||
|
||||
# Compiler toolchain
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
option(WITH_LINKER_GOLD "Use ld.gold linker which is usually faster than ld.bfd" ON)
|
||||
mark_as_advanced(WITH_LINKER_GOLD)
|
||||
endif()
|
||||
|
||||
# Dependency graph
|
||||
option(WITH_LEGACY_DEPSGRAPH "Build Blender with legacy dependency graph" ON)
|
||||
mark_as_advanced(WITH_LEGACY_DEPSGRAPH)
|
||||
@@ -731,7 +737,7 @@ elseif(WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_AUDASPACE OR WITH_INTERNATIONAL O
|
||||
# Keep enabled
|
||||
else()
|
||||
# New dependency graph needs either Boost or C++11 for function bindings.
|
||||
if(NOT USE_CXX11)
|
||||
if(NOT WITH_CXX11)
|
||||
# Enabled but we don't need it
|
||||
set(WITH_BOOST OFF)
|
||||
endif()
|
||||
|
@@ -713,6 +713,21 @@ if [ "$WITH_ALL" = true -a "$OPENCOLLADA_SKIP" = false ]; then
|
||||
fi
|
||||
|
||||
|
||||
WARNING "****WARNING****"
|
||||
PRINT "If you are experiencing issues building Blender, _*TRY A FRESH, CLEAN BUILD FIRST*_!"
|
||||
PRINT "The same goes for install_deps itself, if you encounter issues, please first erase everything in $SRC and $INST"
|
||||
PRINT "(provided obviously you did not add anything yourself in those dirs!), and run install_deps.sh again!"
|
||||
PRINT "Often, changes in the libs built by this script, or in your distro package, cannot be handled simply, so..."
|
||||
PRINT ""
|
||||
PRINT "You may also try to use the '--build-foo' options to bypass your distribution's packages"
|
||||
PRINT "for some troublesome/buggy libraries..."
|
||||
PRINT ""
|
||||
PRINT ""
|
||||
PRINT "Ran with:"
|
||||
PRINT " install_deps.sh $COMMANDLINE"
|
||||
PRINT ""
|
||||
PRINT ""
|
||||
|
||||
|
||||
# This has to be done here, because user might force some versions...
|
||||
PYTHON_SOURCE=( "https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz" )
|
||||
@@ -785,6 +800,8 @@ However, if you are experiencing linking errors (also when building Blender itse
|
||||
|
||||
Please note that until the transition to C++11-built libraries if completed in your distribution, situation will
|
||||
remain fuzzy and incompatibilities may happen..."
|
||||
PRINT ""
|
||||
PRINT ""
|
||||
CXXFLAGS="$CXXFLAGS -std=c++11"
|
||||
export CXXFLAGS
|
||||
fi
|
||||
@@ -1858,6 +1875,9 @@ compile_OSL() {
|
||||
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
|
||||
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
if [ "$USE_CXX11" = true ]; then
|
||||
cmake_d="$cmake_d -D OSL_BUILD_CPP11=1"
|
||||
fi
|
||||
|
||||
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
|
||||
|
||||
@@ -4023,9 +4043,6 @@ install_OTHER() {
|
||||
fi
|
||||
|
||||
if [ "$_do_compile_llvm" = true ]; then
|
||||
install_packages_DEB libffi-dev
|
||||
# LLVM can't find the debian ffi header dir
|
||||
_FFI_INCLUDE_DIR=`dpkg -L libffi-dev | grep -e ".*/ffi.h" | sed -r 's/(.*)\/ffi.h/\1/'`
|
||||
PRINT ""
|
||||
compile_LLVM
|
||||
have_llvm=true
|
||||
@@ -4044,7 +4061,6 @@ install_OTHER() {
|
||||
|
||||
if [ "$_do_compile_osl" = true ]; then
|
||||
if [ "$have_llvm" = true ]; then
|
||||
install_packages_DEB flex bison libtbb-dev
|
||||
PRINT ""
|
||||
compile_OSL
|
||||
else
|
||||
@@ -4063,7 +4079,6 @@ install_OTHER() {
|
||||
fi
|
||||
|
||||
if [ "$_do_compile_osd" = true ]; then
|
||||
install_packages_DEB flex bison libtbb-dev
|
||||
PRINT ""
|
||||
compile_OSD
|
||||
fi
|
||||
@@ -4080,10 +4095,6 @@ install_OTHER() {
|
||||
fi
|
||||
|
||||
if [ "$_do_compile_collada" = true ]; then
|
||||
install_packages_DEB libpcre3-dev
|
||||
# Find path to libxml shared lib...
|
||||
_XML2_LIB=`dpkg -L libxml2-dev | grep -e ".*/libxml2.so"`
|
||||
# No package
|
||||
PRINT ""
|
||||
compile_OpenCOLLADA
|
||||
fi
|
||||
@@ -4168,16 +4179,6 @@ print_info_ffmpeglink() {
|
||||
}
|
||||
|
||||
print_info() {
|
||||
PRINT ""
|
||||
PRINT ""
|
||||
WARNING "****WARNING****"
|
||||
PRINT "If you are experiencing issues building Blender, _*TRY A FRESH, CLEAN BUILD FIRST*_!"
|
||||
PRINT "The same goes for install_deps itself, if you encounter issues, please first erase everything in $SRC and $INST"
|
||||
PRINT "(provided obviously you did not add anything yourself in those dirs!), and run install_deps.sh again!"
|
||||
PRINT "Often, changes in the libs built by this script, or in your distro package, cannot be handled simply, so..."
|
||||
PRINT ""
|
||||
PRINT "You may also try to use the '--build-foo' options to bypass your distribution's packages"
|
||||
PRINT "for some troublesome/buggy libraries..."
|
||||
PRINT ""
|
||||
PRINT ""
|
||||
PRINT "Ran with:"
|
||||
|
@@ -94,6 +94,7 @@ all_repositories = {
|
||||
r'git://git.blender.org/blender-translations.git': 'blender-translations',
|
||||
r'git://git.blender.org/blender-addons.git': 'blender-addons',
|
||||
r'git://git.blender.org/blender-addons-contrib.git': 'blender-addons-contrib',
|
||||
r'git://git.blender.org/blender-dev-tools.git': 'blender-dev-tools',
|
||||
r'https://svn.blender.org/svnroot/bf-blender/': 'lib svn',
|
||||
}
|
||||
|
||||
@@ -128,6 +129,7 @@ def schedule_force_build(name):
|
||||
forcesched.CodebaseParameter(hide=True, codebase="blender-translations"),
|
||||
forcesched.CodebaseParameter(hide=True, codebase="blender-addons"),
|
||||
forcesched.CodebaseParameter(hide=True, codebase="blender-addons-contrib"),
|
||||
forcesched.CodebaseParameter(hide=True, codebase="blender-dev-tools"),
|
||||
forcesched.CodebaseParameter(hide=True, codebase="lib svn")],
|
||||
properties=[]))
|
||||
|
||||
@@ -143,6 +145,7 @@ def schedule_build(name, hour, minute=0):
|
||||
"blender-translations": {"repository": "", "branch": "master"},
|
||||
"blender-addons": {"repository": "", "branch": "master"},
|
||||
"blender-addons-contrib": {"repository": "", "branch": "master"},
|
||||
"blender-dev-tools": {"repository": "", "branch": "master"},
|
||||
"lib svn": {"repository": "", "branch": "trunk"}},
|
||||
branch=current_branch,
|
||||
builderNames=[name],
|
||||
@@ -264,7 +267,8 @@ def generic_builder(id, libdir='', branch='', rsync=False):
|
||||
|
||||
for submodule in ('blender-translations',
|
||||
'blender-addons',
|
||||
'blender-addons-contrib'):
|
||||
'blender-addons-contrib',
|
||||
'blender-dev-tools'):
|
||||
f.addStep(git_submodule_step(submodule))
|
||||
|
||||
f.addStep(git_step(branch))
|
||||
@@ -299,7 +303,8 @@ add_builder(c, 'linux_glibc219_i686_cmake', '', generic_builder, hour=3)
|
||||
add_builder(c, 'linux_glibc219_x86_64_cmake', '', generic_builder, hour=4)
|
||||
add_builder(c, 'win32_cmake_vc2013', 'windows_vc12', generic_builder, hour=3)
|
||||
add_builder(c, 'win64_cmake_vc2013', 'win64_vc12', generic_builder, hour=4)
|
||||
add_builder(c, 'win64_cmake_vc2015', 'win64_vc14', generic_builder, hour=5)
|
||||
add_builder(c, 'win32_cmake_vc2015', 'windows_vc14', generic_builder, hour=5)
|
||||
add_builder(c, 'win64_cmake_vc2015', 'win64_vc14', generic_builder, hour=6)
|
||||
|
||||
# STATUS TARGETS
|
||||
#
|
||||
|
@@ -183,10 +183,8 @@ if 'cmake' in builder:
|
||||
print('Condifuration FAILED!')
|
||||
sys.exit(retcode)
|
||||
|
||||
if 'win32' in builder:
|
||||
command = ['msbuild', 'INSTALL.vcxproj', '/Property:PlatformToolset=v120_xp', '/p:Configuration=Release']
|
||||
elif 'win64' in builder:
|
||||
command = ['msbuild', 'INSTALL.vcxproj', '/p:Configuration=Release']
|
||||
if 'win32' in builder or 'win64' in builder:
|
||||
command = ['cmake', '--build', '.', '--target', target_name, '--config', 'Release']
|
||||
else:
|
||||
command = target_chroot_prefix + ['make', '-s', '-j2', target_name]
|
||||
|
||||
|
@@ -1,15 +1,15 @@
|
||||
# - Find Jack library
|
||||
# Find the native Jack includes and library
|
||||
# - Find JACK library
|
||||
# Find the native JACK includes and library
|
||||
# This module defines
|
||||
# JACK_INCLUDE_DIRS, where to find jack.h, Set when
|
||||
# JACK_INCLUDE_DIR is found.
|
||||
# JACK_LIBRARIES, libraries to link against to use Jack.
|
||||
# JACK_ROOT_DIR, The base directory to search for Jack.
|
||||
# JACK_LIBRARIES, libraries to link against to use JACK.
|
||||
# JACK_ROOT_DIR, The base directory to search for JACK.
|
||||
# This can also be an environment variable.
|
||||
# JACK_FOUND, If false, do not try to use Jack.
|
||||
# JACK_FOUND, If false, do not try to use JACK.
|
||||
#
|
||||
# also defined, but not for general use are
|
||||
# JACK_LIBRARY, where to find the Jack library.
|
||||
# JACK_LIBRARY, where to find the JACK library.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2011 Blender Foundation.
|
||||
|
@@ -518,7 +518,8 @@ function(setup_liblinks
|
||||
target_link_libraries(${target}
|
||||
${BLENDER_GL_LIBRARIES})
|
||||
|
||||
target_link_libraries(${target} ${PLATFORM_LINKLIBS} ${CMAKE_DL_LIBS})
|
||||
#target_link_libraries(${target} ${PLATFORM_LINKLIBS} ${CMAKE_DL_LIBS})
|
||||
target_link_libraries(${target} ${PLATFORM_LINKLIBS})
|
||||
endfunction()
|
||||
|
||||
|
||||
|
@@ -384,17 +384,18 @@ add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set(PLATFORM_CFLAGS "-pipe -fPIC -funsigned-char -fno-strict-aliasing")
|
||||
|
||||
# use ld.gold linker if available, could make optional
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version
|
||||
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
if("${LD_VERSION}" MATCHES "GNU gold")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold")
|
||||
else()
|
||||
message(STATUS "GNU gold linker isn't available, using the default system linker.")
|
||||
if(WITH_LINKER_GOLD)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version
|
||||
ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
|
||||
if("${LD_VERSION}" MATCHES "GNU gold")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold")
|
||||
else()
|
||||
message(STATUS "GNU gold linker isn't available, using the default system linker.")
|
||||
endif()
|
||||
unset(LD_VERSION)
|
||||
endif()
|
||||
unset(LD_VERSION)
|
||||
|
||||
# CLang is the same as GCC for now.
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
|
@@ -129,8 +129,10 @@ if(NOT DEFINED LIBDIR)
|
||||
message(STATUS "32 bit compiler detected.")
|
||||
set(LIBDIR_BASE "windows")
|
||||
endif()
|
||||
|
||||
if(MSVC_VERSION EQUAL 1900)
|
||||
if(MSVC_VERSION EQUAL 1910)
|
||||
message(STATUS "Visual Studio 2017 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc14)
|
||||
elseif(MSVC_VERSION EQUAL 1900)
|
||||
message(STATUS "Visual Studio 2015 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc14)
|
||||
else()
|
||||
|
@@ -187,7 +187,7 @@ The next table describes the information in the file-header.
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="http://en.wikipedia.org/wiki/Endianness">Endianness</a> addresses the way values are ordered in a sequence of bytes(see the <a href="#example-endianess">example</a> below):
|
||||
<a href="https://en.wikipedia.org/wiki/Endianness">Endianness</a> addresses the way values are ordered in a sequence of bytes(see the <a href="#example-endianess">example</a> below):
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
|
@@ -699,7 +699,7 @@ LAYOUT_FILE =
|
||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||
# the reference definitions. This must be a list of .bib files. The .bib
|
||||
# extension is automatically appended if omitted. This requires the bibtex tool
|
||||
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# For LaTeX the style of the bibliography can be controlled using
|
||||
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
|
||||
# search path. See also \cite for info how to create references.
|
||||
@@ -1145,7 +1145,7 @@ HTML_EXTRA_FILES =
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a colorwheel, see
|
||||
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
|
||||
# purple, and 360 is red again.
|
||||
# Minimum value: 0, maximum value: 359, default value: 220.
|
||||
@@ -1752,7 +1752,7 @@ LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# The default value is: plain.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
|
@@ -24,8 +24,8 @@ Then, call ``bpy.app.translations.register(__name__, your_dict)`` in your ``regi
|
||||
The ``Manage UI translations`` add-on has several functions to help you collect strings to translate, and
|
||||
generate the needed python code (the translation dictionary), as well as optional intermediary po files
|
||||
if you want some... See
|
||||
`How to Translate Blender <http://wiki.blender.org/index.php/Dev:Doc/Process/Translate_Blender>`_ and
|
||||
`Using i18n in Blender Code <http://wiki.blender.org/index.php/Dev:Source/Interface/Internationalization>`_
|
||||
`How to Translate Blender <https://wiki.blender.org/index.php/Dev:Doc/Process/Translate_Blender>`_ and
|
||||
`Using i18n in Blender Code <https://wiki.blender.org/index.php/Dev:Source/Interface/Internationalization>`_
|
||||
for more info.
|
||||
|
||||
Module References
|
||||
|
@@ -49,7 +49,7 @@ vec2d[:] = vec3d[:2]
|
||||
|
||||
|
||||
# Vectors support 'swizzle' operations
|
||||
# See http://en.wikipedia.org/wiki/Swizzling_(computer_graphics)
|
||||
# See https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)
|
||||
vec.xyz = vec.zyx
|
||||
vec.xy = vec4d.zw
|
||||
vec.xyz = vec4d.wzz
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ The features exposed closely follow the C API,
|
||||
giving python access to the functions used by blenders own mesh editing tools.
|
||||
|
||||
For an overview of BMesh data types and how they reference each other see:
|
||||
`BMesh Design Document <http://wiki.blender.org/index.php/Dev:2.6/Source/Modeling/BMesh/Design>`_ .
|
||||
`BMesh Design Document <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`_ .
|
||||
|
||||
|
||||
.. note::
|
||||
@@ -31,13 +31,12 @@ For an overview of BMesh data types and how they reference each other see:
|
||||
**Disk** and **Radial** data is not exposed by the python api since this is for internal use only.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
TODO items are...
|
||||
.. warning:: TODO items are...
|
||||
|
||||
* add access to BMesh **walkers**
|
||||
* add custom-data manipulation functions add/remove/rename.
|
||||
|
||||
|
||||
Example Script
|
||||
--------------
|
||||
|
||||
|
@@ -18,7 +18,7 @@ amongst our own scripts and make it easier to use python scripts from other proj
|
||||
|
||||
Using our style guide for your own scripts makes it easier if you eventually want to contribute them to blender.
|
||||
|
||||
This style guide is known as pep8 and can be found `here <http://www.python.org/dev/peps/pep-0008>`_
|
||||
This style guide is known as pep8 and can be found `here <https://www.python.org/dev/peps/pep-0008/>`_
|
||||
|
||||
A brief listing of pep8 criteria.
|
||||
|
||||
@@ -316,7 +316,7 @@ use to join a list of strings (the list may be temporary). In the following exam
|
||||
|
||||
|
||||
Join is fastest on many strings,
|
||||
`string formatting <http://docs.python.org/py3k/library/string.html#string-formatting>`__
|
||||
`string formatting <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`__
|
||||
is quite fast too (better for converting data types). String arithmetic is slowest.
|
||||
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
|
||||
*******
|
||||
Gotchas
|
||||
*******
|
||||
@@ -38,7 +39,6 @@ but some operators are more picky about when they run.
|
||||
In most cases you can figure out what context an operator needs
|
||||
simply be seeing how it's used in Blender and thinking about what it does.
|
||||
|
||||
|
||||
Unfortunately if you're still stuck - the only way to **really** know
|
||||
whats going on is to read the source code for the poll function and see what its checking.
|
||||
|
||||
@@ -82,7 +82,6 @@ it should be reported to the bug tracker.
|
||||
Stale Data
|
||||
==========
|
||||
|
||||
|
||||
No updates after setting values
|
||||
-------------------------------
|
||||
|
||||
@@ -174,8 +173,8 @@ In this situation you can...
|
||||
|
||||
.. _info_gotcha_mesh_faces:
|
||||
|
||||
NGons and Tessellation Faces
|
||||
============================
|
||||
N-Gons and Tessellation Faces
|
||||
=============================
|
||||
|
||||
Since 2.63 NGons are supported, this adds some complexity
|
||||
since in some cases you need to access triangles/quads still (some exporters for example).
|
||||
@@ -509,7 +508,7 @@ Unicode Problems
|
||||
Python supports many different encodings so there is nothing stopping you from
|
||||
writing a script in ``latin1`` or ``iso-8859-15``.
|
||||
|
||||
See `pep-0263 <http://www.python.org/dev/peps/pep-0263/>`_
|
||||
See `pep-0263 <https://www.python.org/dev/peps/pep-0263/>`_
|
||||
|
||||
However this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding.
|
||||
|
||||
@@ -657,7 +656,7 @@ Here are some general hints to avoid running into these problems.
|
||||
.. note::
|
||||
|
||||
To find the line of your script that crashes you can use the ``faulthandler`` module.
|
||||
See `faulthandler docs <http://docs.python.org/dev/library/faulthandler.html>`_.
|
||||
See the `faulthandler docs <https://docs.python.org/dev/library/faulthandler.html>`_.
|
||||
|
||||
While the crash may be in Blenders C/C++ code,
|
||||
this can help a lot to track down the area of the script that causes the crash.
|
||||
|
@@ -43,8 +43,7 @@ scene manipulation, automation, defining your own toolset and customization.
|
||||
|
||||
On startup Blender scans the ``scripts/startup/`` directory for Python modules and imports them.
|
||||
The exact location of this directory depends on your installation.
|
||||
`See the directory layout docs
|
||||
<https://www.blender.org/manual/getting_started/installing_blender/directorylayout.html>`__
|
||||
See the :ref:`directory layout docs <blender_manual:getting-started_installing-config-directories>`.
|
||||
|
||||
|
||||
Script Loading
|
||||
@@ -92,7 +91,7 @@ variable which Blender uses to read metadata such as name, author, category and
|
||||
|
||||
The User Preferences add-on listing uses **bl_info** to display information about each add-on.
|
||||
|
||||
`See Add-ons <http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons>`__
|
||||
`See Add-ons <https://wiki.blender.org/index.php/Dev:Py/Scripts/Guidelines/Addons>`__
|
||||
for details on the ``bl_info`` dictionary.
|
||||
|
||||
|
||||
|
@@ -51,8 +51,7 @@ A quick list of helpful things to know before starting:
|
||||
| ``scripts/startup/bl_operators`` for operators.
|
||||
|
||||
Exact location depends on platform, see:
|
||||
`Configuration and Data Paths
|
||||
<https://www.blender.org/manual/getting_started/installing_blender/directorylayout.html>`__.
|
||||
:ref:`Configuration and Data Paths <blender_manual:getting-started_installing-config-directories>`.
|
||||
|
||||
|
||||
Running Scripts
|
||||
|
@@ -27,7 +27,7 @@ There are 3 main uses for the terminal, these are:
|
||||
|
||||
.. note::
|
||||
|
||||
For Linux and OSX users this means starting the terminal first, then running Blender from within it.
|
||||
For Linux and macOS users this means starting the terminal first, then running Blender from within it.
|
||||
On Windows the terminal can be enabled from the help menu.
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ Advantages include:
|
||||
This is marked advanced because to run Blender as a Python module requires a special build option.
|
||||
|
||||
For instructions on building see
|
||||
`Building Blender as a Python module <http://wiki.blender.org/index.php/User:Ideasman42/BlenderAsPyModule>`_
|
||||
`Building Blender as a Python module <https://wiki.blender.org/index.php/User:Ideasman42/BlenderAsPyModule>`_
|
||||
|
||||
|
||||
Python Safety (Build Option)
|
||||
|
@@ -121,14 +121,8 @@ Add the following script to the text editor in Blender.
|
||||
obj.location.x += 1.0
|
||||
|
||||
|
||||
.. image:: run_script.png
|
||||
:width: 924px
|
||||
:align: center
|
||||
:height: 574px
|
||||
:alt: Run Script button
|
||||
|
||||
Click the Run Script button, all objects in the active scene are moved by 1.0 Blender unit.
|
||||
Next we will make this script into an add-on.
|
||||
Click the :ref:`Run Script button <blender_manual:editors-text-run-script>`,
|
||||
all objects in the active scene are moved by 1.0 Blender unit.
|
||||
|
||||
|
||||
Write the Add-on (Simple)
|
||||
@@ -238,7 +232,7 @@ if you want it to be enabled on restart, press *Save as Default*.
|
||||
print(addon_utils.paths())
|
||||
|
||||
More is written on this topic here:
|
||||
`Directory Layout <https://www.blender.org/manual/getting_started/installing_blender/directorylayout.html>`_
|
||||
:ref:`Directory Layout <blender_manual:getting-started_installing-config-directories>`.
|
||||
|
||||
|
||||
Your Second Add-on
|
||||
@@ -636,6 +630,6 @@ Here are some sites you might like to check on after completing this tutorial.
|
||||
*Great info for those who are still learning Python.*
|
||||
- `Blender Development (Wiki) <https://wiki.blender.org/index.php/Dev:Contents>`_ -
|
||||
*Blender Development, general information and helpful links.*
|
||||
- `Blender Artists (Coding Section) <http://blenderartists.org/forum/forumdisplay.php?47-Coding>`_ -
|
||||
- `Blender Artists (Coding Section) <https://blenderartists.org/forum/forumdisplay.php?47-Coding>`_ -
|
||||
*forum where people ask Python development questions*
|
||||
|
||||
|
@@ -1632,6 +1632,13 @@ def write_sphinx_conf_py(basepath):
|
||||
file = open(filepath, "w", encoding="utf-8")
|
||||
fw = file.write
|
||||
|
||||
fw("import sys, os\n")
|
||||
fw("\n")
|
||||
fw("extensions = ['sphinx.ext.intersphinx']\n")
|
||||
fw("\n")
|
||||
fw("intersphinx_mapping = {'blender_manual': ('https://www.blender.org/manual/', None)}\n")
|
||||
fw("\n")
|
||||
|
||||
fw("project = 'Blender'\n")
|
||||
# fw("master_doc = 'index'\n")
|
||||
fw("copyright = u'Blender Foundation'\n")
|
||||
@@ -1648,6 +1655,7 @@ def write_sphinx_conf_py(basepath):
|
||||
|
||||
# not helpful since the source is generated, adds to upload size.
|
||||
fw("html_copy_source = False\n")
|
||||
fw("html_split_index = True\n")
|
||||
fw("\n")
|
||||
|
||||
# needed for latex, pdf gen
|
||||
|
@@ -142,8 +142,11 @@ def main():
|
||||
zip_name = "blender_python_reference_%s" % blenver_zip # We can't use 'release' postfix here...
|
||||
zip_path = os.path.join(args.mirror_dir, zip_name)
|
||||
with zipfile.ZipFile(zip_path, 'w') as zf:
|
||||
for de in os.scandir(api_dir):
|
||||
zf.write(de.path, arcname=os.path.join(zip_name, de.name))
|
||||
for dirname, _, filenames in os.walk(api_dir):
|
||||
for filename in filenames:
|
||||
filepath = os.path.join(dirname, filename)
|
||||
zip_filepath = os.path.join(zip_name, os.path.relpath(filepath, api_dir))
|
||||
zf.write(filepath, arcname=zip_filepath)
|
||||
os.rename(zip_path, os.path.join(api_dir, "%s.zip" % zip_name))
|
||||
|
||||
# VII) Create symlinks and html redirects.
|
||||
|
5
extern/ceres/CMakeLists.txt
vendored
5
extern/ceres/CMakeLists.txt
vendored
@@ -73,10 +73,12 @@ set(SRC
|
||||
internal/ceres/file.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/is_close.cc
|
||||
internal/ceres/iterative_schur_complement_solver.cc
|
||||
internal/ceres/lapack.cc
|
||||
internal/ceres/levenberg_marquardt_strategy.cc
|
||||
@@ -116,6 +118,7 @@ set(SRC
|
||||
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/wall_time.cc
|
||||
@@ -204,6 +207,7 @@ set(SRC
|
||||
internal/ceres/householder_vector.h
|
||||
internal/ceres/implicit_schur_complement.h
|
||||
internal/ceres/integral_types.h
|
||||
internal/ceres/is_close.h
|
||||
internal/ceres/iterative_schur_complement_solver.h
|
||||
internal/ceres/lapack.h
|
||||
internal/ceres/levenberg_marquardt_strategy.h
|
||||
@@ -248,6 +252,7 @@ set(SRC
|
||||
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/wall_time.h
|
||||
|
1035
extern/ceres/ChangeLog
vendored
1035
extern/ceres/ChangeLog
vendored
File diff suppressed because it is too large
Load Diff
21
extern/ceres/bundle.sh
vendored
21
extern/ceres/bundle.sh
vendored
@@ -173,26 +173,5 @@ if(WITH_OPENMP)
|
||||
)
|
||||
endif()
|
||||
|
||||
TEST_UNORDERED_MAP_SUPPORT()
|
||||
if(HAVE_STD_UNORDERED_MAP_HEADER)
|
||||
if(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE)
|
||||
add_definitions(-DCERES_STD_UNORDERED_MAP)
|
||||
else()
|
||||
if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE)
|
||||
add_definitions(-DCERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE)
|
||||
else()
|
||||
add_definitions(-DCERES_NO_UNORDERED_MAP)
|
||||
message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE)
|
||||
add_definitions(-DCERES_TR1_UNORDERED_MAP)
|
||||
else()
|
||||
add_definitions(-DCERES_NO_UNORDERED_MAP)
|
||||
message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}")
|
||||
EOF
|
||||
|
5
extern/ceres/files.txt
vendored
5
extern/ceres/files.txt
vendored
@@ -149,6 +149,7 @@ internal/ceres/generated/schur_eliminator_4_4_d.cc
|
||||
internal/ceres/generated/schur_eliminator_d_d_d.cc
|
||||
internal/ceres/generate_eliminator_specialization.py
|
||||
internal/ceres/generate_partitioned_matrix_view_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
|
||||
@@ -160,6 +161,8 @@ internal/ceres/householder_vector.h
|
||||
internal/ceres/implicit_schur_complement.cc
|
||||
internal/ceres/implicit_schur_complement.h
|
||||
internal/ceres/integral_types.h
|
||||
internal/ceres/is_close.cc
|
||||
internal/ceres/is_close.h
|
||||
internal/ceres/iterative_schur_complement_solver.cc
|
||||
internal/ceres/iterative_schur_complement_solver.h
|
||||
internal/ceres/lapack.cc
|
||||
@@ -243,6 +246,8 @@ 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
|
||||
|
@@ -130,7 +130,8 @@ class CostFunctionToFunctor {
|
||||
const int num_parameter_blocks =
|
||||
(N0 > 0) + (N1 > 0) + (N2 > 0) + (N3 > 0) + (N4 > 0) +
|
||||
(N5 > 0) + (N6 > 0) + (N7 > 0) + (N8 > 0) + (N9 > 0);
|
||||
CHECK_EQ(parameter_block_sizes.size(), num_parameter_blocks);
|
||||
CHECK_EQ(static_cast<int>(parameter_block_sizes.size()),
|
||||
num_parameter_blocks);
|
||||
|
||||
CHECK_EQ(N0, parameter_block_sizes[0]);
|
||||
if (parameter_block_sizes.size() > 1) CHECK_EQ(N1, parameter_block_sizes[1]); // NOLINT
|
||||
|
56
extern/ceres/include/ceres/covariance.h
vendored
56
extern/ceres/include/ceres/covariance.h
vendored
@@ -357,6 +357,28 @@ class CERES_EXPORT Covariance {
|
||||
const double*> >& covariance_blocks,
|
||||
Problem* problem);
|
||||
|
||||
// Compute a part of the covariance matrix.
|
||||
//
|
||||
// The vector parameter_blocks contains the parameter blocks that
|
||||
// are used for computing the covariance matrix. From this vector
|
||||
// all covariance pairs are generated. This allows the covariance
|
||||
// estimation algorithm to only compute and store these blocks.
|
||||
//
|
||||
// parameter_blocks cannot contain duplicates. Bad things will
|
||||
// happen if they do.
|
||||
//
|
||||
// Note that the list of covariance_blocks is only used to determine
|
||||
// what parts of the covariance matrix are computed. The full
|
||||
// Jacobian is used to do the computation, i.e. they do not have an
|
||||
// impact on what part of the Jacobian is used for computation.
|
||||
//
|
||||
// The return value indicates the success or failure of the
|
||||
// covariance computation. Please see the documentation for
|
||||
// Covariance::Options for more on the conditions under which this
|
||||
// function returns false.
|
||||
bool Compute(const std::vector<const double*>& parameter_blocks,
|
||||
Problem* problem);
|
||||
|
||||
// Return the block of the cross-covariance matrix corresponding to
|
||||
// parameter_block1 and parameter_block2.
|
||||
//
|
||||
@@ -394,6 +416,40 @@ class CERES_EXPORT Covariance {
|
||||
const double* parameter_block2,
|
||||
double* covariance_block) const;
|
||||
|
||||
// Return the covariance matrix corresponding to all parameter_blocks.
|
||||
//
|
||||
// Compute must be called before calling GetCovarianceMatrix and all
|
||||
// parameter_blocks must have been present in the vector
|
||||
// parameter_blocks when Compute was called. Otherwise
|
||||
// GetCovarianceMatrix returns false.
|
||||
//
|
||||
// covariance_matrix must point to a memory location that can store
|
||||
// the size of the covariance matrix. The covariance matrix will be
|
||||
// a square matrix whose row and column count is equal to the sum of
|
||||
// the sizes of the individual parameter blocks. The covariance
|
||||
// matrix will be a row-major matrix.
|
||||
bool GetCovarianceMatrix(const std::vector<const double *> ¶meter_blocks,
|
||||
double *covariance_matrix);
|
||||
|
||||
// Return the covariance matrix corresponding to parameter_blocks
|
||||
// in the tangent space if a local parameterization is associated
|
||||
// with one of the parameter blocks else returns the covariance
|
||||
// matrix in the ambient space.
|
||||
//
|
||||
// Compute must be called before calling GetCovarianceMatrix and all
|
||||
// parameter_blocks must have been present in the vector
|
||||
// parameters_blocks when Compute was called. Otherwise
|
||||
// GetCovarianceMatrix returns false.
|
||||
//
|
||||
// covariance_matrix must point to a memory location that can store
|
||||
// the size of the covariance matrix. The covariance matrix will be
|
||||
// a square matrix whose row and column count is equal to the sum of
|
||||
// the sizes of the tangent spaces of the individual parameter
|
||||
// blocks. The covariance matrix will be a row-major matrix.
|
||||
bool GetCovarianceMatrixInTangentSpace(
|
||||
const std::vector<const double*>& parameter_blocks,
|
||||
double* covariance_matrix);
|
||||
|
||||
private:
|
||||
internal::scoped_ptr<internal::CovarianceImpl> impl_;
|
||||
};
|
||||
|
@@ -85,22 +85,6 @@ class DynamicNumericDiffCostFunction : public CostFunction {
|
||||
options_(options) {
|
||||
}
|
||||
|
||||
// Deprecated. New users should avoid using this constructor. Instead, use the
|
||||
// constructor with NumericDiffOptions.
|
||||
DynamicNumericDiffCostFunction(
|
||||
const CostFunctor* functor,
|
||||
Ownership ownership,
|
||||
double relative_step_size)
|
||||
: functor_(functor),
|
||||
ownership_(ownership),
|
||||
options_() {
|
||||
LOG(WARNING) << "This constructor is deprecated and will be removed in "
|
||||
"a future version. Please use the NumericDiffOptions "
|
||||
"constructor instead.";
|
||||
|
||||
options_.relative_step_size = relative_step_size;
|
||||
}
|
||||
|
||||
virtual ~DynamicNumericDiffCostFunction() {
|
||||
if (ownership_ != TAKE_OWNERSHIP) {
|
||||
functor_.release();
|
||||
@@ -138,19 +122,19 @@ class DynamicNumericDiffCostFunction : public CostFunction {
|
||||
std::vector<double> parameters_copy(parameters_size);
|
||||
std::vector<double*> parameters_references_copy(block_sizes.size());
|
||||
parameters_references_copy[0] = ¶meters_copy[0];
|
||||
for (int block = 1; block < block_sizes.size(); ++block) {
|
||||
for (size_t block = 1; block < block_sizes.size(); ++block) {
|
||||
parameters_references_copy[block] = parameters_references_copy[block - 1]
|
||||
+ block_sizes[block - 1];
|
||||
}
|
||||
|
||||
// Copy the parameters into the local temp space.
|
||||
for (int block = 0; block < block_sizes.size(); ++block) {
|
||||
for (size_t block = 0; block < block_sizes.size(); ++block) {
|
||||
memcpy(parameters_references_copy[block],
|
||||
parameters[block],
|
||||
block_sizes[block] * sizeof(*parameters[block]));
|
||||
}
|
||||
|
||||
for (int block = 0; block < block_sizes.size(); ++block) {
|
||||
for (size_t block = 0; block < block_sizes.size(); ++block) {
|
||||
if (jacobians[block] != NULL &&
|
||||
!NumericDiff<CostFunctor, method, DYNAMIC,
|
||||
DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC,
|
||||
|
237
extern/ceres/include/ceres/gradient_checker.h
vendored
237
extern/ceres/include/ceres/gradient_checker.h
vendored
@@ -27,194 +27,121 @@
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// Copyright 2007 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Author: wjr@google.com (William Rucklidge)
|
||||
//
|
||||
// This file contains a class that exercises a cost function, to make sure
|
||||
// that it is computing reasonable derivatives. It compares the Jacobians
|
||||
// computed by the cost function with those obtained by finite
|
||||
// differences.
|
||||
// Authors: wjr@google.com (William Rucklidge),
|
||||
// keir@google.com (Keir Mierle),
|
||||
// dgossow@google.com (David Gossow)
|
||||
|
||||
#ifndef CERES_PUBLIC_GRADIENT_CHECKER_H_
|
||||
#define CERES_PUBLIC_GRADIENT_CHECKER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "ceres/cost_function.h"
|
||||
#include "ceres/dynamic_numeric_diff_cost_function.h"
|
||||
#include "ceres/internal/eigen.h"
|
||||
#include "ceres/internal/fixed_array.h"
|
||||
#include "ceres/internal/macros.h"
|
||||
#include "ceres/internal/scoped_ptr.h"
|
||||
#include "ceres/numeric_diff_cost_function.h"
|
||||
#include "ceres/local_parameterization.h"
|
||||
#include "glog/logging.h"
|
||||
|
||||
namespace ceres {
|
||||
|
||||
// An object that exercises a cost function, to compare the answers that it
|
||||
// gives with derivatives estimated using finite differencing.
|
||||
// GradientChecker compares the Jacobians returned by a cost function against
|
||||
// derivatives estimated using finite differencing.
|
||||
//
|
||||
// The only likely usage of this is for testing.
|
||||
// The condition enforced is that
|
||||
//
|
||||
// (J_actual(i, j) - J_numeric(i, j))
|
||||
// ------------------------------------ < relative_precision
|
||||
// max(J_actual(i, j), J_numeric(i, j))
|
||||
//
|
||||
// where J_actual(i, j) is the jacobian as computed by the supplied cost
|
||||
// function (by the user) multiplied by the local parameterization Jacobian
|
||||
// and J_numeric is the jacobian as computed by finite differences, multiplied
|
||||
// by the local parameterization Jacobian as well.
|
||||
//
|
||||
// How to use: Fill in an array of pointers to parameter blocks for your
|
||||
// CostFunction, and then call Probe(). Check that the return value is
|
||||
// 'true'. See prober_test.cc for an example.
|
||||
//
|
||||
// This is templated similarly to NumericDiffCostFunction, as it internally
|
||||
// uses that.
|
||||
template <typename CostFunctionToProbe,
|
||||
int M = 0, int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0>
|
||||
// CostFunction, and then call Probe(). Check that the return value is 'true'.
|
||||
class GradientChecker {
|
||||
public:
|
||||
// Here we stash some results from the probe, for later
|
||||
// inspection.
|
||||
struct GradientCheckResults {
|
||||
// Computed cost.
|
||||
Vector cost;
|
||||
// This will not take ownership of the cost function or local
|
||||
// parameterizations.
|
||||
//
|
||||
// function: The cost function to probe.
|
||||
// local_parameterization: A vector of local parameterizations for each
|
||||
// parameter. May be NULL or contain NULL pointers to indicate that the
|
||||
// respective parameter does not have a local parameterization.
|
||||
// options: Options to use for numerical differentiation.
|
||||
GradientChecker(
|
||||
const CostFunction* function,
|
||||
const std::vector<const LocalParameterization*>* local_parameterizations,
|
||||
const NumericDiffOptions& options);
|
||||
|
||||
// The sizes of these matrices are dictated by the cost function's
|
||||
// parameter and residual block sizes. Each vector's length will
|
||||
// term->parameter_block_sizes().size(), and each matrix is the
|
||||
// Jacobian of the residual with respect to the corresponding parameter
|
||||
// block.
|
||||
// Contains results from a call to Probe for later inspection.
|
||||
struct ProbeResults {
|
||||
// The return value of the cost function.
|
||||
bool return_value;
|
||||
|
||||
// Computed residual vector.
|
||||
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.
|
||||
|
||||
// Derivatives as computed by the cost function.
|
||||
std::vector<Matrix> term_jacobians;
|
||||
std::vector<Matrix> jacobians;
|
||||
|
||||
// Derivatives as computed by finite differencing.
|
||||
std::vector<Matrix> finite_difference_jacobians;
|
||||
// Derivatives as computed by the cost function in local space.
|
||||
std::vector<Matrix> local_jacobians;
|
||||
|
||||
// Infinity-norm of term_jacobians - finite_difference_jacobians.
|
||||
double error_jacobians;
|
||||
// Derivatives as computed by nuerical differentiation in local space.
|
||||
std::vector<Matrix> numeric_jacobians;
|
||||
|
||||
// Derivatives as computed by nuerical differentiation in local space.
|
||||
std::vector<Matrix> local_numeric_jacobians;
|
||||
|
||||
// Contains the maximum relative error found in the local Jacobians.
|
||||
double maximum_relative_error;
|
||||
|
||||
// If an error was detected, this will contain a detailed description of
|
||||
// that error.
|
||||
std::string error_log;
|
||||
};
|
||||
|
||||
// Checks the Jacobian computed by a cost function.
|
||||
// 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.
|
||||
//
|
||||
// probe_point: The parameter values at which to probe.
|
||||
// error_tolerance: A threshold for the infinity-norm difference
|
||||
// between the Jacobians. If the Jacobians differ by more than
|
||||
// this amount, then the probe fails.
|
||||
//
|
||||
// term: The cost function to test. Not retained after this call returns.
|
||||
//
|
||||
// results: On return, the two Jacobians (and other information)
|
||||
// will be stored here. May be NULL.
|
||||
// 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.
|
||||
//
|
||||
// Returns true if no problems are detected and the difference between the
|
||||
// Jacobians is less than error_tolerance.
|
||||
static bool Probe(double const* const* probe_point,
|
||||
double error_tolerance,
|
||||
CostFunctionToProbe *term,
|
||||
GradientCheckResults* results) {
|
||||
CHECK_NOTNULL(probe_point);
|
||||
CHECK_NOTNULL(term);
|
||||
LOG(INFO) << "-------------------- Starting Probe() --------------------";
|
||||
|
||||
// We need a GradientCheckeresults, whether or not they supplied one.
|
||||
internal::scoped_ptr<GradientCheckResults> owned_results;
|
||||
if (results == NULL) {
|
||||
owned_results.reset(new GradientCheckResults);
|
||||
results = owned_results.get();
|
||||
}
|
||||
|
||||
// Do a consistency check between the term and the template parameters.
|
||||
CHECK_EQ(M, term->num_residuals());
|
||||
const int num_residuals = M;
|
||||
const std::vector<int32>& block_sizes = term->parameter_block_sizes();
|
||||
const int num_blocks = block_sizes.size();
|
||||
|
||||
CHECK_LE(num_blocks, 5) << "Unable to test functions that take more "
|
||||
<< "than 5 parameter blocks";
|
||||
if (N0) {
|
||||
CHECK_EQ(N0, block_sizes[0]);
|
||||
CHECK_GE(num_blocks, 1);
|
||||
} else {
|
||||
CHECK_LT(num_blocks, 1);
|
||||
}
|
||||
if (N1) {
|
||||
CHECK_EQ(N1, block_sizes[1]);
|
||||
CHECK_GE(num_blocks, 2);
|
||||
} else {
|
||||
CHECK_LT(num_blocks, 2);
|
||||
}
|
||||
if (N2) {
|
||||
CHECK_EQ(N2, block_sizes[2]);
|
||||
CHECK_GE(num_blocks, 3);
|
||||
} else {
|
||||
CHECK_LT(num_blocks, 3);
|
||||
}
|
||||
if (N3) {
|
||||
CHECK_EQ(N3, block_sizes[3]);
|
||||
CHECK_GE(num_blocks, 4);
|
||||
} else {
|
||||
CHECK_LT(num_blocks, 4);
|
||||
}
|
||||
if (N4) {
|
||||
CHECK_EQ(N4, block_sizes[4]);
|
||||
CHECK_GE(num_blocks, 5);
|
||||
} else {
|
||||
CHECK_LT(num_blocks, 5);
|
||||
}
|
||||
|
||||
results->term_jacobians.clear();
|
||||
results->term_jacobians.resize(num_blocks);
|
||||
results->finite_difference_jacobians.clear();
|
||||
results->finite_difference_jacobians.resize(num_blocks);
|
||||
|
||||
internal::FixedArray<double*> term_jacobian_pointers(num_blocks);
|
||||
internal::FixedArray<double*>
|
||||
finite_difference_jacobian_pointers(num_blocks);
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
results->term_jacobians[i].resize(num_residuals, block_sizes[i]);
|
||||
term_jacobian_pointers[i] = results->term_jacobians[i].data();
|
||||
results->finite_difference_jacobians[i].resize(
|
||||
num_residuals, block_sizes[i]);
|
||||
finite_difference_jacobian_pointers[i] =
|
||||
results->finite_difference_jacobians[i].data();
|
||||
}
|
||||
results->cost.resize(num_residuals, 1);
|
||||
|
||||
CHECK(term->Evaluate(probe_point, results->cost.data(),
|
||||
term_jacobian_pointers.get()));
|
||||
NumericDiffCostFunction<CostFunctionToProbe, CENTRAL, M, N0, N1, N2, N3, N4>
|
||||
numeric_term(term, DO_NOT_TAKE_OWNERSHIP);
|
||||
CHECK(numeric_term.Evaluate(probe_point, results->cost.data(),
|
||||
finite_difference_jacobian_pointers.get()));
|
||||
|
||||
results->error_jacobians = 0;
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
Matrix jacobian_difference = results->term_jacobians[i] -
|
||||
results->finite_difference_jacobians[i];
|
||||
results->error_jacobians =
|
||||
std::max(results->error_jacobians,
|
||||
jacobian_difference.lpNorm<Eigen::Infinity>());
|
||||
}
|
||||
|
||||
LOG(INFO) << "========== term-computed derivatives ==========";
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
LOG(INFO) << "term_computed block " << i;
|
||||
LOG(INFO) << "\n" << results->term_jacobians[i];
|
||||
}
|
||||
|
||||
LOG(INFO) << "========== finite-difference derivatives ==========";
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
LOG(INFO) << "finite_difference block " << i;
|
||||
LOG(INFO) << "\n" << results->finite_difference_jacobians[i];
|
||||
}
|
||||
|
||||
LOG(INFO) << "========== difference ==========";
|
||||
for (int i = 0; i < num_blocks; i++) {
|
||||
LOG(INFO) << "difference block " << i;
|
||||
LOG(INFO) << (results->term_jacobians[i] -
|
||||
results->finite_difference_jacobians[i]);
|
||||
}
|
||||
|
||||
LOG(INFO) << "||difference|| = " << results->error_jacobians;
|
||||
|
||||
return results->error_jacobians < error_tolerance;
|
||||
}
|
||||
bool Probe(double const* const* parameters,
|
||||
double relative_precision,
|
||||
ProbeResults* results) const;
|
||||
|
||||
private:
|
||||
CERES_DISALLOW_IMPLICIT_CONSTRUCTORS(GradientChecker);
|
||||
|
||||
std::vector<const LocalParameterization*> local_parameterizations_;
|
||||
const CostFunction* function_;
|
||||
internal::scoped_ptr<CostFunction> finite_diff_cost_function_;
|
||||
};
|
||||
|
||||
} // namespace ceres
|
||||
|
22
extern/ceres/include/ceres/internal/port.h
vendored
22
extern/ceres/include/ceres/internal/port.h
vendored
@@ -33,9 +33,8 @@
|
||||
|
||||
// This file needs to compile as c code.
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstddef>
|
||||
#include "ceres/internal/config.h"
|
||||
|
||||
#if defined(CERES_TR1_MEMORY_HEADER)
|
||||
#include <tr1/memory>
|
||||
#else
|
||||
@@ -50,6 +49,25 @@ using std::tr1::shared_ptr;
|
||||
using std::shared_ptr;
|
||||
#endif
|
||||
|
||||
// We allocate some Eigen objects on the stack and other places they
|
||||
// might not be aligned to 16-byte boundaries. If we have C++11, we
|
||||
// can specify their alignment anyway, and thus can safely enable
|
||||
// vectorization on those matrices; in C++99, we are out of luck. Figure out
|
||||
// what case we're in and write macros that do the right thing.
|
||||
#ifdef CERES_USE_CXX11
|
||||
namespace port_constants {
|
||||
static constexpr size_t kMaxAlignBytes =
|
||||
// Work around a GCC 4.8 bug
|
||||
// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56019) where
|
||||
// std::max_align_t is misplaced.
|
||||
#if defined (__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
alignof(::max_align_t);
|
||||
#else
|
||||
alignof(std::max_align_t);
|
||||
#endif
|
||||
} // namespace port_constants
|
||||
#endif
|
||||
|
||||
} // namespace ceres
|
||||
|
||||
#endif // __cplusplus
|
||||
|
@@ -69,7 +69,7 @@ struct CERES_EXPORT IterationSummary {
|
||||
// Step was numerically valid, i.e., all values are finite and the
|
||||
// step reduces the value of the linearized model.
|
||||
//
|
||||
// Note: step_is_valid is false when iteration = 0.
|
||||
// Note: step_is_valid is always true when iteration = 0.
|
||||
bool step_is_valid;
|
||||
|
||||
// Step did not reduce the value of the objective function
|
||||
@@ -77,7 +77,7 @@ struct CERES_EXPORT IterationSummary {
|
||||
// acceptance criterion used by the non-monotonic trust region
|
||||
// algorithm.
|
||||
//
|
||||
// Note: step_is_nonmonotonic is false when iteration = 0;
|
||||
// Note: step_is_nonmonotonic is always false when iteration = 0;
|
||||
bool step_is_nonmonotonic;
|
||||
|
||||
// Whether or not the minimizer accepted this step or not. If the
|
||||
@@ -89,7 +89,7 @@ struct CERES_EXPORT IterationSummary {
|
||||
// relative decrease is not sufficient, the algorithm may accept the
|
||||
// step and the step is declared successful.
|
||||
//
|
||||
// Note: step_is_successful is false when iteration = 0.
|
||||
// Note: step_is_successful is always true when iteration = 0.
|
||||
bool step_is_successful;
|
||||
|
||||
// Value of the objective function.
|
||||
|
106
extern/ceres/include/ceres/jet.h
vendored
106
extern/ceres/include/ceres/jet.h
vendored
@@ -164,6 +164,7 @@
|
||||
|
||||
#include "Eigen/Core"
|
||||
#include "ceres/fpclassify.h"
|
||||
#include "ceres/internal/port.h"
|
||||
|
||||
namespace ceres {
|
||||
|
||||
@@ -227,21 +228,23 @@ struct Jet {
|
||||
T a;
|
||||
|
||||
// The infinitesimal part.
|
||||
//
|
||||
// Note the Eigen::DontAlign bit is needed here because this object
|
||||
// gets allocated on the stack and as part of other arrays and
|
||||
// structs. Forcing the right alignment there is the source of much
|
||||
// pain and suffering. Even if that works, passing Jets around to
|
||||
// functions by value has problems because the C++ ABI does not
|
||||
// guarantee alignment for function arguments.
|
||||
//
|
||||
// Setting the DontAlign bit prevents Eigen from using SSE for the
|
||||
// various operations on Jets. This is a small performance penalty
|
||||
// since the AutoDiff code will still expose much of the code as
|
||||
// statically sized loops to the compiler. But given the subtle
|
||||
// issues that arise due to alignment, especially when dealing with
|
||||
// multiple platforms, it seems to be a trade off worth making.
|
||||
|
||||
// We allocate Jets on the stack and other places they
|
||||
// might not be aligned to 16-byte boundaries. If we have C++11, we
|
||||
// can specify their alignment anyway, and thus can safely enable
|
||||
// vectorization on those matrices; in C++99, we are out of luck. Figure out
|
||||
// what case we're in and do the right thing.
|
||||
#ifndef CERES_USE_CXX11
|
||||
// fall back to safe version:
|
||||
Eigen::Matrix<T, N, 1, Eigen::DontAlign> v;
|
||||
#else
|
||||
static constexpr bool kShouldAlignMatrix =
|
||||
16 <= ::ceres::port_constants::kMaxAlignBytes;
|
||||
static constexpr int kAlignHint = kShouldAlignMatrix ?
|
||||
Eigen::AutoAlign : Eigen::DontAlign;
|
||||
static constexpr size_t kAlignment = kShouldAlignMatrix ? 16 : 1;
|
||||
alignas(kAlignment) Eigen::Matrix<T, N, 1, kAlignHint> v;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Unary +
|
||||
@@ -388,6 +391,8 @@ inline double atan (double x) { return std::atan(x); }
|
||||
inline double sinh (double x) { return std::sinh(x); }
|
||||
inline double cosh (double x) { return std::cosh(x); }
|
||||
inline double tanh (double x) { return std::tanh(x); }
|
||||
inline double floor (double x) { return std::floor(x); }
|
||||
inline double ceil (double x) { return std::ceil(x); }
|
||||
inline double pow (double x, double y) { return std::pow(x, y); }
|
||||
inline double atan2(double y, double x) { return std::atan2(y, x); }
|
||||
|
||||
@@ -482,10 +487,51 @@ Jet<T, N> tanh(const Jet<T, N>& f) {
|
||||
return Jet<T, N>(tanh_a, tmp * f.v);
|
||||
}
|
||||
|
||||
// The floor function should be used with extreme care as this operation will
|
||||
// result in a zero derivative which provides no information to the solver.
|
||||
//
|
||||
// floor(a + h) ~= floor(a) + 0
|
||||
template <typename T, int N> inline
|
||||
Jet<T, N> floor(const Jet<T, N>& f) {
|
||||
return Jet<T, N>(floor(f.a));
|
||||
}
|
||||
|
||||
// The ceil function should be used with extreme care as this operation will
|
||||
// result in a zero derivative which provides no information to the solver.
|
||||
//
|
||||
// ceil(a + h) ~= ceil(a) + 0
|
||||
template <typename T, int N> inline
|
||||
Jet<T, N> ceil(const Jet<T, N>& f) {
|
||||
return Jet<T, N>(ceil(f.a));
|
||||
}
|
||||
|
||||
// Bessel functions of the first kind with integer order equal to 0, 1, n.
|
||||
inline double BesselJ0(double x) { return j0(x); }
|
||||
inline double BesselJ1(double x) { return j1(x); }
|
||||
inline double BesselJn(int n, double x) { return jn(n, x); }
|
||||
//
|
||||
// Microsoft has deprecated the j[0,1,n]() POSIX Bessel functions in favour of
|
||||
// _j[0,1,n](). Where available on MSVC, use _j[0,1,n]() to avoid deprecated
|
||||
// function errors in client code (the specific warning is suppressed when
|
||||
// Ceres itself is built).
|
||||
inline double BesselJ0(double x) {
|
||||
#if defined(_MSC_VER) && defined(_j0)
|
||||
return _j0(x);
|
||||
#else
|
||||
return j0(x);
|
||||
#endif
|
||||
}
|
||||
inline double BesselJ1(double x) {
|
||||
#if defined(_MSC_VER) && defined(_j1)
|
||||
return _j1(x);
|
||||
#else
|
||||
return j1(x);
|
||||
#endif
|
||||
}
|
||||
inline double BesselJn(int n, double x) {
|
||||
#if defined(_MSC_VER) && defined(_jn)
|
||||
return _jn(n, x);
|
||||
#else
|
||||
return jn(n, x);
|
||||
#endif
|
||||
}
|
||||
|
||||
// For the formulae of the derivatives of the Bessel functions see the book:
|
||||
// Olver, Lozier, Boisvert, Clark, NIST Handbook of Mathematical Functions,
|
||||
@@ -743,7 +789,15 @@ template<typename T, int N> inline Jet<T, N> ei_pow (const Jet<T, N>& x,
|
||||
// strange compile errors.
|
||||
template <typename T, int N>
|
||||
inline std::ostream &operator<<(std::ostream &s, const Jet<T, N>& z) {
|
||||
return s << "[" << z.a << " ; " << z.v.transpose() << "]";
|
||||
s << "[" << z.a << " ; ";
|
||||
for (int i = 0; i < N; ++i) {
|
||||
s << z.v[i];
|
||||
if (i != N - 1) {
|
||||
s << ", ";
|
||||
}
|
||||
}
|
||||
s << "]";
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace ceres
|
||||
@@ -757,6 +811,7 @@ 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;
|
||||
|
||||
static typename ceres::Jet<T, N> dummy_precision() {
|
||||
return ceres::Jet<T, N>(1e-12);
|
||||
@@ -777,6 +832,21 @@ struct NumTraits<ceres::Jet<T, N> > {
|
||||
HasFloatingPoint = 1,
|
||||
RequireInitialization = 1
|
||||
};
|
||||
|
||||
template<bool Vectorized>
|
||||
struct Div {
|
||||
enum {
|
||||
#if defined(EIGEN_VECTORIZE_AVX)
|
||||
AVX = true,
|
||||
#else
|
||||
AVX = false,
|
||||
#endif
|
||||
|
||||
// Assuming that for Jets, division is as expensive as
|
||||
// multiplication.
|
||||
Cost = 3
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Eigen
|
||||
|
@@ -211,6 +211,28 @@ class CERES_EXPORT QuaternionParameterization : public LocalParameterization {
|
||||
virtual int LocalSize() const { return 3; }
|
||||
};
|
||||
|
||||
// Implements the quaternion local parameterization for Eigen's representation
|
||||
// of the quaternion. Eigen uses a different internal memory layout for the
|
||||
// elements of the quaternion than what is commonly used. Specifically, Eigen
|
||||
// stores the elements in memory as [x, y, z, w] where the real part is last
|
||||
// whereas it is typically stored first. Note, when creating an Eigen quaternion
|
||||
// through the constructor the elements are accepted in w, x, y, z order. Since
|
||||
// Ceres operates on parameter blocks which are raw double pointers this
|
||||
// difference is important and requires a different parameterization.
|
||||
//
|
||||
// Plus(x, delta) = [sin(|delta|) delta / |delta|, cos(|delta|)] * x
|
||||
// with * being the quaternion multiplication operator.
|
||||
class EigenQuaternionParameterization : public ceres::LocalParameterization {
|
||||
public:
|
||||
virtual ~EigenQuaternionParameterization() {}
|
||||
virtual bool Plus(const double* x,
|
||||
const double* delta,
|
||||
double* x_plus_delta) const;
|
||||
virtual bool ComputeJacobian(const double* x,
|
||||
double* jacobian) const;
|
||||
virtual int GlobalSize() const { return 4; }
|
||||
virtual int LocalSize() const { return 3; }
|
||||
};
|
||||
|
||||
// This provides a parameterization for homogeneous vectors which are commonly
|
||||
// used in Structure for Motion problems. One example where they are used is
|
||||
|
@@ -206,29 +206,6 @@ class NumericDiffCostFunction
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated. New users should avoid using this constructor. Instead, use the
|
||||
// constructor with NumericDiffOptions.
|
||||
NumericDiffCostFunction(CostFunctor* functor,
|
||||
Ownership ownership,
|
||||
int num_residuals,
|
||||
const double relative_step_size)
|
||||
:functor_(functor),
|
||||
ownership_(ownership),
|
||||
options_() {
|
||||
LOG(WARNING) << "This constructor is deprecated and will be removed in "
|
||||
"a future version. Please use the NumericDiffOptions "
|
||||
"constructor instead.";
|
||||
|
||||
if (kNumResiduals == DYNAMIC) {
|
||||
SizedCostFunction<kNumResiduals,
|
||||
N0, N1, N2, N3, N4,
|
||||
N5, N6, N7, N8, N9>
|
||||
::set_num_residuals(num_residuals);
|
||||
}
|
||||
|
||||
options_.relative_step_size = relative_step_size;
|
||||
}
|
||||
|
||||
~NumericDiffCostFunction() {
|
||||
if (ownership_ != TAKE_OWNERSHIP) {
|
||||
functor_.release();
|
||||
|
7
extern/ceres/include/ceres/problem.h
vendored
7
extern/ceres/include/ceres/problem.h
vendored
@@ -309,6 +309,9 @@ 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.
|
||||
bool IsParameterBlockConstant(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
|
||||
@@ -461,6 +464,10 @@ class CERES_EXPORT Problem {
|
||||
// parameter block has a local parameterization, then it contributes
|
||||
// "LocalSize" 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.
|
||||
bool Evaluate(const EvaluateOptions& options,
|
||||
double* cost,
|
||||
std::vector<double>* residuals,
|
||||
|
3
extern/ceres/include/ceres/rotation.h
vendored
3
extern/ceres/include/ceres/rotation.h
vendored
@@ -48,7 +48,6 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include "glog/logging.h"
|
||||
|
||||
namespace ceres {
|
||||
|
||||
@@ -418,7 +417,6 @@ template <typename T>
|
||||
inline void EulerAnglesToRotationMatrix(const T* euler,
|
||||
const int row_stride_parameter,
|
||||
T* R) {
|
||||
CHECK_EQ(row_stride_parameter, 3);
|
||||
EulerAnglesToRotationMatrix(euler, RowMajorAdapter3x3(R));
|
||||
}
|
||||
|
||||
@@ -496,7 +494,6 @@ void QuaternionToRotation(const T q[4],
|
||||
QuaternionToScaledRotation(q, R);
|
||||
|
||||
T normalizer = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
|
||||
CHECK_NE(normalizer, T(0));
|
||||
normalizer = T(1) / normalizer;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
|
31
extern/ceres/include/ceres/solver.h
vendored
31
extern/ceres/include/ceres/solver.h
vendored
@@ -134,7 +134,7 @@ class CERES_EXPORT Solver {
|
||||
trust_region_problem_dump_format_type = TEXTFILE;
|
||||
check_gradients = false;
|
||||
gradient_check_relative_precision = 1e-8;
|
||||
numeric_derivative_relative_step_size = 1e-6;
|
||||
gradient_check_numeric_derivative_relative_step_size = 1e-6;
|
||||
update_state_every_iteration = false;
|
||||
}
|
||||
|
||||
@@ -701,12 +701,22 @@ class CERES_EXPORT Solver {
|
||||
// this number, then the jacobian for that cost term is dumped.
|
||||
double gradient_check_relative_precision;
|
||||
|
||||
// Relative shift used for taking numeric derivatives. For finite
|
||||
// differencing, each dimension is evaluated at slightly shifted
|
||||
// values; for the case of central difference, this is what gets
|
||||
// evaluated:
|
||||
// WARNING: This option only applies to the to the numeric
|
||||
// differentiation used for checking the user provided derivatives
|
||||
// when when Solver::Options::check_gradients is true. If you are
|
||||
// using NumericDiffCostFunction and are interested in changing
|
||||
// the step size for numeric differentiation in your cost
|
||||
// function, please have a look at
|
||||
// include/ceres/numeric_diff_options.h.
|
||||
//
|
||||
// delta = numeric_derivative_relative_step_size;
|
||||
// Relative shift used for taking numeric derivatives when
|
||||
// Solver::Options::check_gradients is true.
|
||||
//
|
||||
// For finite differencing, each dimension is evaluated at
|
||||
// slightly shifted values; for the case of central difference,
|
||||
// this is what gets evaluated:
|
||||
//
|
||||
// delta = gradient_check_numeric_derivative_relative_step_size;
|
||||
// f_initial = f(x)
|
||||
// f_forward = f((1 + delta) * x)
|
||||
// f_backward = f((1 - delta) * x)
|
||||
@@ -723,7 +733,7 @@ class CERES_EXPORT Solver {
|
||||
// theory a good choice is sqrt(eps) * x, which for doubles means
|
||||
// about 1e-8 * x. However, I have found this number too
|
||||
// optimistic. This number should be exposed for users to change.
|
||||
double numeric_derivative_relative_step_size;
|
||||
double gradient_check_numeric_derivative_relative_step_size;
|
||||
|
||||
// If true, the user's parameter blocks are updated at the end of
|
||||
// every Minimizer iteration, otherwise they are updated when the
|
||||
@@ -801,6 +811,13 @@ class CERES_EXPORT Solver {
|
||||
// Number of times inner iterations were performed.
|
||||
int num_inner_iteration_steps;
|
||||
|
||||
// Total number of iterations inside the line search algorithm
|
||||
// across all invocations. We call these iterations "steps" to
|
||||
// distinguish them from the outer iterations of the line search
|
||||
// and trust region minimizer algorithms which call the line
|
||||
// search algorithm as a subroutine.
|
||||
int num_line_search_steps;
|
||||
|
||||
// All times reported below are wall times.
|
||||
|
||||
// When the user calls Solve, before the actual optimization
|
||||
|
2
extern/ceres/include/ceres/version.h
vendored
2
extern/ceres/include/ceres/version.h
vendored
@@ -32,7 +32,7 @@
|
||||
#define CERES_PUBLIC_VERSION_H_
|
||||
|
||||
#define CERES_VERSION_MAJOR 1
|
||||
#define CERES_VERSION_MINOR 11
|
||||
#define CERES_VERSION_MINOR 12
|
||||
#define CERES_VERSION_REVISION 0
|
||||
|
||||
// Classic CPP stringifcation; the extra level of indirection allows the
|
||||
|
@@ -46,6 +46,7 @@ namespace internal {
|
||||
using std::make_pair;
|
||||
using std::pair;
|
||||
using std::vector;
|
||||
using std::adjacent_find;
|
||||
|
||||
void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors(
|
||||
const Program* program, CompressedRowSparseMatrix* jacobian) {
|
||||
@@ -140,12 +141,21 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const {
|
||||
|
||||
// Sort the parameters by their position in the state vector.
|
||||
sort(parameter_indices.begin(), parameter_indices.end());
|
||||
CHECK(unique(parameter_indices.begin(), parameter_indices.end()) ==
|
||||
parameter_indices.end())
|
||||
<< "Ceres internal error: "
|
||||
<< "Duplicate parameter blocks detected in a cost function. "
|
||||
<< "This should never happen. Please report this to "
|
||||
<< "the Ceres developers.";
|
||||
if (adjacent_find(parameter_indices.begin(), parameter_indices.end()) !=
|
||||
parameter_indices.end()) {
|
||||
std::string parameter_block_description;
|
||||
for (int j = 0; j < num_parameter_blocks; ++j) {
|
||||
ParameterBlock* parameter_block = residual_block->parameter_blocks()[j];
|
||||
parameter_block_description +=
|
||||
parameter_block->ToString() + "\n";
|
||||
}
|
||||
LOG(FATAL) << "Ceres internal error: "
|
||||
<< "Duplicate parameter blocks detected in a cost function. "
|
||||
<< "This should never happen. Please report this to "
|
||||
<< "the Ceres developers.\n"
|
||||
<< "Residual Block: " << residual_block->ToString() << "\n"
|
||||
<< "Parameter Blocks: " << parameter_block_description;
|
||||
}
|
||||
|
||||
// Update the row indices.
|
||||
const int num_residuals = residual_block->NumResiduals();
|
||||
|
23
extern/ceres/internal/ceres/covariance.cc
vendored
23
extern/ceres/internal/ceres/covariance.cc
vendored
@@ -38,6 +38,7 @@
|
||||
|
||||
namespace ceres {
|
||||
|
||||
using std::make_pair;
|
||||
using std::pair;
|
||||
using std::vector;
|
||||
|
||||
@@ -54,6 +55,12 @@ bool Covariance::Compute(
|
||||
return impl_->Compute(covariance_blocks, problem->problem_impl_.get());
|
||||
}
|
||||
|
||||
bool Covariance::Compute(
|
||||
const vector<const double*>& parameter_blocks,
|
||||
Problem* problem) {
|
||||
return impl_->Compute(parameter_blocks, problem->problem_impl_.get());
|
||||
}
|
||||
|
||||
bool Covariance::GetCovarianceBlock(const double* parameter_block1,
|
||||
const double* parameter_block2,
|
||||
double* covariance_block) const {
|
||||
@@ -73,4 +80,20 @@ bool Covariance::GetCovarianceBlockInTangentSpace(
|
||||
covariance_block);
|
||||
}
|
||||
|
||||
bool Covariance::GetCovarianceMatrix(
|
||||
const vector<const double*>& parameter_blocks,
|
||||
double* covariance_matrix) {
|
||||
return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks,
|
||||
true, // ambient
|
||||
covariance_matrix);
|
||||
}
|
||||
|
||||
bool Covariance::GetCovarianceMatrixInTangentSpace(
|
||||
const std::vector<const double *>& parameter_blocks,
|
||||
double *covariance_matrix) {
|
||||
return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks,
|
||||
false, // tangent
|
||||
covariance_matrix);
|
||||
}
|
||||
|
||||
} // namespace ceres
|
||||
|
172
extern/ceres/internal/ceres/covariance_impl.cc
vendored
172
extern/ceres/internal/ceres/covariance_impl.cc
vendored
@@ -36,6 +36,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -43,6 +45,7 @@
|
||||
#include "Eigen/SparseQR"
|
||||
#include "Eigen/SVD"
|
||||
|
||||
#include "ceres/collections_port.h"
|
||||
#include "ceres/compressed_col_sparse_matrix_utils.h"
|
||||
#include "ceres/compressed_row_sparse_matrix.h"
|
||||
#include "ceres/covariance.h"
|
||||
@@ -51,6 +54,7 @@
|
||||
#include "ceres/map_util.h"
|
||||
#include "ceres/parameter_block.h"
|
||||
#include "ceres/problem_impl.h"
|
||||
#include "ceres/residual_block.h"
|
||||
#include "ceres/suitesparse.h"
|
||||
#include "ceres/wall_time.h"
|
||||
#include "glog/logging.h"
|
||||
@@ -61,6 +65,7 @@ namespace internal {
|
||||
using std::make_pair;
|
||||
using std::map;
|
||||
using std::pair;
|
||||
using std::sort;
|
||||
using std::swap;
|
||||
using std::vector;
|
||||
|
||||
@@ -86,8 +91,38 @@ CovarianceImpl::CovarianceImpl(const Covariance::Options& options)
|
||||
CovarianceImpl::~CovarianceImpl() {
|
||||
}
|
||||
|
||||
template <typename T> void CheckForDuplicates(vector<T> blocks) {
|
||||
sort(blocks.begin(), blocks.end());
|
||||
typename vector<T>::iterator it =
|
||||
std::adjacent_find(blocks.begin(), blocks.end());
|
||||
if (it != blocks.end()) {
|
||||
// In case there are duplicates, we search for their location.
|
||||
map<T, vector<int> > blocks_map;
|
||||
for (int i = 0; i < blocks.size(); ++i) {
|
||||
blocks_map[blocks[i]].push_back(i);
|
||||
}
|
||||
|
||||
std::ostringstream duplicates;
|
||||
while (it != blocks.end()) {
|
||||
duplicates << "(";
|
||||
for (int i = 0; i < blocks_map[*it].size() - 1; ++i) {
|
||||
duplicates << blocks_map[*it][i] << ", ";
|
||||
}
|
||||
duplicates << blocks_map[*it].back() << ")";
|
||||
it = std::adjacent_find(it + 1, blocks.end());
|
||||
if (it < blocks.end()) {
|
||||
duplicates << " and ";
|
||||
}
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Covariance::Compute called with duplicate blocks at "
|
||||
<< "indices " << duplicates.str();
|
||||
}
|
||||
}
|
||||
|
||||
bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks,
|
||||
ProblemImpl* problem) {
|
||||
CheckForDuplicates<pair<const double*, const double*> >(covariance_blocks);
|
||||
problem_ = problem;
|
||||
parameter_block_to_row_index_.clear();
|
||||
covariance_matrix_.reset(NULL);
|
||||
@@ -97,6 +132,20 @@ bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks,
|
||||
return is_valid_;
|
||||
}
|
||||
|
||||
bool CovarianceImpl::Compute(const vector<const double*>& parameter_blocks,
|
||||
ProblemImpl* problem) {
|
||||
CheckForDuplicates<const double*>(parameter_blocks);
|
||||
CovarianceBlocks covariance_blocks;
|
||||
for (int i = 0; i < parameter_blocks.size(); ++i) {
|
||||
for (int j = i; j < parameter_blocks.size(); ++j) {
|
||||
covariance_blocks.push_back(make_pair(parameter_blocks[i],
|
||||
parameter_blocks[j]));
|
||||
}
|
||||
}
|
||||
|
||||
return Compute(covariance_blocks, problem);
|
||||
}
|
||||
|
||||
bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace(
|
||||
const double* original_parameter_block1,
|
||||
const double* original_parameter_block2,
|
||||
@@ -120,9 +169,17 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace(
|
||||
ParameterBlock* block2 =
|
||||
FindOrDie(parameter_map,
|
||||
const_cast<double*>(original_parameter_block2));
|
||||
|
||||
const int block1_size = block1->Size();
|
||||
const int block2_size = block2->Size();
|
||||
MatrixRef(covariance_block, block1_size, block2_size).setZero();
|
||||
const int block1_local_size = block1->LocalSize();
|
||||
const int block2_local_size = block2->LocalSize();
|
||||
if (!lift_covariance_to_ambient_space) {
|
||||
MatrixRef(covariance_block, block1_local_size, block2_local_size)
|
||||
.setZero();
|
||||
} else {
|
||||
MatrixRef(covariance_block, block1_size, block2_size).setZero();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -240,6 +297,94 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CovarianceImpl::GetCovarianceMatrixInTangentOrAmbientSpace(
|
||||
const vector<const double*>& parameters,
|
||||
bool lift_covariance_to_ambient_space,
|
||||
double* covariance_matrix) const {
|
||||
CHECK(is_computed_)
|
||||
<< "Covariance::GetCovarianceMatrix called before Covariance::Compute";
|
||||
CHECK(is_valid_)
|
||||
<< "Covariance::GetCovarianceMatrix called when Covariance::Compute "
|
||||
<< "returned false.";
|
||||
|
||||
const ProblemImpl::ParameterMap& parameter_map = problem_->parameter_map();
|
||||
// For OpenMP compatibility we need to define these vectors in advance
|
||||
const int num_parameters = parameters.size();
|
||||
vector<int> parameter_sizes;
|
||||
vector<int> cum_parameter_size;
|
||||
parameter_sizes.reserve(num_parameters);
|
||||
cum_parameter_size.resize(num_parameters + 1);
|
||||
cum_parameter_size[0] = 0;
|
||||
for (int i = 0; i < num_parameters; ++i) {
|
||||
ParameterBlock* block =
|
||||
FindOrDie(parameter_map, const_cast<double*>(parameters[i]));
|
||||
if (lift_covariance_to_ambient_space) {
|
||||
parameter_sizes.push_back(block->Size());
|
||||
} else {
|
||||
parameter_sizes.push_back(block->LocalSize());
|
||||
}
|
||||
}
|
||||
std::partial_sum(parameter_sizes.begin(), parameter_sizes.end(),
|
||||
cum_parameter_size.begin() + 1);
|
||||
const int max_covariance_block_size =
|
||||
*std::max_element(parameter_sizes.begin(), parameter_sizes.end());
|
||||
const int covariance_size = cum_parameter_size.back();
|
||||
|
||||
// Assemble the blocks in the covariance matrix.
|
||||
MatrixRef covariance(covariance_matrix, covariance_size, covariance_size);
|
||||
const int num_threads = options_.num_threads;
|
||||
scoped_array<double> workspace(
|
||||
new double[num_threads * max_covariance_block_size *
|
||||
max_covariance_block_size]);
|
||||
|
||||
bool success = true;
|
||||
|
||||
// The collapse() directive is only supported in OpenMP 3.0 and higher. OpenMP
|
||||
// 3.0 was released in May 2008 (hence the version number).
|
||||
#if _OPENMP >= 200805
|
||||
# pragma omp parallel for num_threads(num_threads) schedule(dynamic) collapse(2)
|
||||
#else
|
||||
# pragma omp parallel for num_threads(num_threads) schedule(dynamic)
|
||||
#endif
|
||||
for (int i = 0; i < num_parameters; ++i) {
|
||||
for (int j = 0; j < num_parameters; ++j) {
|
||||
// The second loop can't start from j = i for compatibility with OpenMP
|
||||
// collapse command. The conditional serves as a workaround
|
||||
if (j >= i) {
|
||||
int covariance_row_idx = cum_parameter_size[i];
|
||||
int covariance_col_idx = cum_parameter_size[j];
|
||||
int size_i = parameter_sizes[i];
|
||||
int size_j = parameter_sizes[j];
|
||||
#ifdef CERES_USE_OPENMP
|
||||
int thread_id = omp_get_thread_num();
|
||||
#else
|
||||
int thread_id = 0;
|
||||
#endif
|
||||
double* covariance_block =
|
||||
workspace.get() +
|
||||
thread_id * max_covariance_block_size * max_covariance_block_size;
|
||||
if (!GetCovarianceBlockInTangentOrAmbientSpace(
|
||||
parameters[i], parameters[j], lift_covariance_to_ambient_space,
|
||||
covariance_block)) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
covariance.block(covariance_row_idx, covariance_col_idx,
|
||||
size_i, size_j) =
|
||||
MatrixRef(covariance_block, size_i, size_j);
|
||||
|
||||
if (i != j) {
|
||||
covariance.block(covariance_col_idx, covariance_row_idx,
|
||||
size_j, size_i) =
|
||||
MatrixRef(covariance_block, size_i, size_j).transpose();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Determine the sparsity pattern of the covariance matrix based on
|
||||
// the block pairs requested by the user.
|
||||
bool CovarianceImpl::ComputeCovarianceSparsity(
|
||||
@@ -252,18 +397,28 @@ bool CovarianceImpl::ComputeCovarianceSparsity(
|
||||
vector<double*> all_parameter_blocks;
|
||||
problem->GetParameterBlocks(&all_parameter_blocks);
|
||||
const ProblemImpl::ParameterMap& parameter_map = problem->parameter_map();
|
||||
HashSet<ParameterBlock*> parameter_blocks_in_use;
|
||||
vector<ResidualBlock*> residual_blocks;
|
||||
problem->GetResidualBlocks(&residual_blocks);
|
||||
|
||||
for (int i = 0; i < residual_blocks.size(); ++i) {
|
||||
ResidualBlock* residual_block = residual_blocks[i];
|
||||
parameter_blocks_in_use.insert(residual_block->parameter_blocks(),
|
||||
residual_block->parameter_blocks() +
|
||||
residual_block->NumParameterBlocks());
|
||||
}
|
||||
|
||||
constant_parameter_blocks_.clear();
|
||||
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];
|
||||
|
||||
ParameterBlock* block = FindOrDie(parameter_map, parameter_block);
|
||||
if (block->IsConstant()) {
|
||||
constant_parameter_blocks_.insert(parameter_block);
|
||||
} else {
|
||||
if (!block->IsConstant() && (parameter_blocks_in_use.count(block) > 0)) {
|
||||
active_parameter_blocks.push_back(parameter_block);
|
||||
} else {
|
||||
constant_parameter_blocks_.insert(parameter_block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,8 +541,8 @@ bool CovarianceImpl::ComputeCovarianceValues() {
|
||||
switch (options_.algorithm_type) {
|
||||
case DENSE_SVD:
|
||||
return ComputeCovarianceValuesUsingDenseSVD();
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
case SUITE_SPARSE_QR:
|
||||
#ifndef CERES_NO_SUITESPARSE
|
||||
return ComputeCovarianceValuesUsingSuiteSparseQR();
|
||||
#else
|
||||
LOG(ERROR) << "SuiteSparse is required to use the "
|
||||
@@ -624,7 +779,10 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() {
|
||||
if (automatic_truncation) {
|
||||
break;
|
||||
} else {
|
||||
LOG(ERROR) << "Cholesky factorization of J'J is not reliable. "
|
||||
LOG(ERROR) << "Error: Covariance matrix is near rank deficient "
|
||||
<< "and the user did not specify a non-zero"
|
||||
<< "Covariance::Options::null_space_rank "
|
||||
<< "to enable the computation of a Pseudo-Inverse. "
|
||||
<< "Reciprocal condition number: "
|
||||
<< singular_value_ratio * singular_value_ratio << " "
|
||||
<< "min_reciprocal_condition_number: "
|
||||
|
@@ -55,12 +55,21 @@ class CovarianceImpl {
|
||||
const double*> >& covariance_blocks,
|
||||
ProblemImpl* problem);
|
||||
|
||||
bool Compute(
|
||||
const std::vector<const double*>& parameter_blocks,
|
||||
ProblemImpl* problem);
|
||||
|
||||
bool GetCovarianceBlockInTangentOrAmbientSpace(
|
||||
const double* parameter_block1,
|
||||
const double* parameter_block2,
|
||||
bool lift_covariance_to_ambient_space,
|
||||
double* covariance_block) const;
|
||||
|
||||
bool GetCovarianceMatrixInTangentOrAmbientSpace(
|
||||
const std::vector<const double*>& parameters,
|
||||
bool lift_covariance_to_ambient_space,
|
||||
double *covariance_matrix) const;
|
||||
|
||||
bool ComputeCovarianceSparsity(
|
||||
const std::vector<std::pair<const double*,
|
||||
const double*> >& covariance_blocks,
|
||||
|
276
extern/ceres/internal/ceres/gradient_checker.cc
vendored
Normal file
276
extern/ceres/internal/ceres/gradient_checker.cc
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2016 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.
|
||||
//
|
||||
// Authors: wjr@google.com (William Rucklidge),
|
||||
// keir@google.com (Keir Mierle),
|
||||
// dgossow@google.com (David Gossow)
|
||||
|
||||
#include "ceres/gradient_checker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ceres/is_close.h"
|
||||
#include "ceres/stringprintf.h"
|
||||
#include "ceres/types.h"
|
||||
|
||||
namespace ceres {
|
||||
|
||||
using internal::IsClose;
|
||||
using internal::StringAppendF;
|
||||
using internal::StringPrintf;
|
||||
using std::string;
|
||||
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) {
|
||||
CHECK_NOTNULL(residuals);
|
||||
CHECK_NOTNULL(jacobians);
|
||||
CHECK_NOTNULL(local_jacobians);
|
||||
|
||||
const vector<int32>& block_sizes = function->parameter_block_sizes();
|
||||
const int num_parameter_blocks = block_sizes.size();
|
||||
|
||||
// Allocate Jacobian matrices in local 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();
|
||||
}
|
||||
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.
|
||||
jacobians->resize(num_parameter_blocks);
|
||||
vector<double*> jacobian_data(num_parameter_blocks);
|
||||
for (int i = 0; i < num_parameter_blocks; ++i) {
|
||||
jacobians->at(i).resize(function->num_residuals(), block_sizes.at(i));
|
||||
jacobians->at(i).setZero();
|
||||
jacobian_data.at(i) = jacobians->at(i).data();
|
||||
}
|
||||
|
||||
// Compute residuals & jacobians.
|
||||
CHECK_NE(0, function->num_residuals());
|
||||
residuals->resize(function->num_residuals());
|
||||
residuals->setZero();
|
||||
if (!function->Evaluate(parameters, residuals->data(),
|
||||
jacobian_data.data())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert Jacobians from global to local space.
|
||||
for (size_t i = 0; i < local_jacobians->size(); ++i) {
|
||||
if (local_parameterizations.at(i) == NULL) {
|
||||
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) = jacobians->at(i) * global_J_local;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
GradientChecker::GradientChecker(
|
||||
const CostFunction* function,
|
||||
const vector<const LocalParameterization*>* local_parameterizations,
|
||||
const NumericDiffOptions& options) :
|
||||
function_(function) {
|
||||
CHECK_NOTNULL(function);
|
||||
if (local_parameterizations != NULL) {
|
||||
local_parameterizations_ = *local_parameterizations;
|
||||
} else {
|
||||
local_parameterizations_.resize(function->parameter_block_sizes().size(),
|
||||
NULL);
|
||||
}
|
||||
DynamicNumericDiffCostFunction<CostFunction, CENTRAL>*
|
||||
finite_diff_cost_function =
|
||||
new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>(
|
||||
function, DO_NOT_TAKE_OWNERSHIP, options);
|
||||
finite_diff_cost_function_.reset(finite_diff_cost_function);
|
||||
|
||||
const vector<int32>& parameter_block_sizes =
|
||||
function->parameter_block_sizes();
|
||||
const int num_parameter_blocks = parameter_block_sizes.size();
|
||||
for (int i = 0; i < num_parameter_blocks; ++i) {
|
||||
finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]);
|
||||
}
|
||||
finite_diff_cost_function->SetNumResiduals(function->num_residuals());
|
||||
}
|
||||
|
||||
bool GradientChecker::Probe(double const* const * parameters,
|
||||
double relative_precision,
|
||||
ProbeResults* results_param) const {
|
||||
int num_residuals = function_->num_residuals();
|
||||
|
||||
// Make sure that we have a place to store results, no matter if the user has
|
||||
// provided an output argument.
|
||||
ProbeResults* results;
|
||||
ProbeResults results_local;
|
||||
if (results_param != NULL) {
|
||||
results = results_param;
|
||||
results->residuals.resize(0);
|
||||
results->jacobians.clear();
|
||||
results->numeric_jacobians.clear();
|
||||
results->local_jacobians.clear();
|
||||
results->local_numeric_jacobians.clear();
|
||||
results->error_log.clear();
|
||||
} else {
|
||||
results = &results_local;
|
||||
}
|
||||
results->maximum_relative_error = 0.0;
|
||||
results->return_value = true;
|
||||
|
||||
// Evaluate the derivative using the user supplied code.
|
||||
vector<Matrix>& jacobians = results->jacobians;
|
||||
vector<Matrix>& local_jacobians = results->local_jacobians;
|
||||
if (!EvaluateCostFunction(function_, parameters, local_parameterizations_,
|
||||
&results->residuals, &jacobians, &local_jacobians)) {
|
||||
results->error_log = "Function evaluation with Jacobians failed.";
|
||||
results->return_value = false;
|
||||
}
|
||||
|
||||
// Evaluate the derivative using numeric derivatives.
|
||||
vector<Matrix>& numeric_jacobians = results->numeric_jacobians;
|
||||
vector<Matrix>& local_numeric_jacobians = results->local_numeric_jacobians;
|
||||
Vector finite_diff_residuals;
|
||||
if (!EvaluateCostFunction(finite_diff_cost_function_.get(), parameters,
|
||||
local_parameterizations_, &finite_diff_residuals,
|
||||
&numeric_jacobians, &local_numeric_jacobians)) {
|
||||
results->error_log += "\nFunction evaluation with numerical "
|
||||
"differentiation failed.";
|
||||
results->return_value = false;
|
||||
}
|
||||
|
||||
if (!results->return_value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_residuals; ++i) {
|
||||
if (!IsClose(
|
||||
results->residuals[i],
|
||||
finite_diff_residuals[i],
|
||||
relative_precision,
|
||||
NULL,
|
||||
NULL)) {
|
||||
results->error_log = "Function evaluation with and without Jacobians "
|
||||
"resulted in different residuals.";
|
||||
LOG(INFO) << results->residuals.transpose();
|
||||
LOG(INFO) << finite_diff_residuals.transpose();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// See if any elements have relative error larger than the threshold.
|
||||
int num_bad_jacobian_components = 0;
|
||||
double& worst_relative_error = results->maximum_relative_error;
|
||||
worst_relative_error = 0;
|
||||
|
||||
// Accumulate the error message for all the jacobians, since it won't get
|
||||
// output if there are no bad jacobian components.
|
||||
string error_log;
|
||||
for (int k = 0; k < function_->parameter_block_sizes().size(); k++) {
|
||||
StringAppendF(&error_log,
|
||||
"========== "
|
||||
"Jacobian for " "block %d: (%ld by %ld)) "
|
||||
"==========\n",
|
||||
k,
|
||||
static_cast<long>(local_jacobians[k].rows()),
|
||||
static_cast<long>(local_jacobians[k].cols()));
|
||||
// The funny spacing creates appropriately aligned column headers.
|
||||
error_log +=
|
||||
" block row col user dx/dy num diff dx/dy "
|
||||
"abs error relative error parameter residual\n";
|
||||
|
||||
for (int i = 0; i < local_jacobians[k].rows(); i++) {
|
||||
for (int j = 0; j < local_jacobians[k].cols(); j++) {
|
||||
double term_jacobian = local_jacobians[k](i, j);
|
||||
double finite_jacobian = local_numeric_jacobians[k](i, j);
|
||||
double relative_error, absolute_error;
|
||||
bool bad_jacobian_entry =
|
||||
!IsClose(term_jacobian,
|
||||
finite_jacobian,
|
||||
relative_precision,
|
||||
&relative_error,
|
||||
&absolute_error);
|
||||
worst_relative_error = std::max(worst_relative_error, relative_error);
|
||||
|
||||
StringAppendF(&error_log,
|
||||
"%6d %4d %4d %17g %17g %17g %17g %17g %17g",
|
||||
k, i, j,
|
||||
term_jacobian, finite_jacobian,
|
||||
absolute_error, relative_error,
|
||||
parameters[k][j],
|
||||
results->residuals[i]);
|
||||
|
||||
if (bad_jacobian_entry) {
|
||||
num_bad_jacobian_components++;
|
||||
StringAppendF(
|
||||
&error_log,
|
||||
" ------ (%d,%d,%d) Relative error worse than %g",
|
||||
k, i, j, relative_precision);
|
||||
}
|
||||
error_log += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since there were some bad errors, dump comprehensive debug info.
|
||||
if (num_bad_jacobian_components) {
|
||||
string header = StringPrintf("\nDetected %d bad Jacobian component(s). "
|
||||
"Worst relative error was %g.\n",
|
||||
num_bad_jacobian_components,
|
||||
worst_relative_error);
|
||||
results->error_log = header + "\n" + error_log;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ceres
|
@@ -26,7 +26,8 @@
|
||||
// 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)
|
||||
// Authors: keir@google.com (Keir Mierle),
|
||||
// dgossow@google.com (David Gossow)
|
||||
|
||||
#include "ceres/gradient_checking_cost_function.h"
|
||||
|
||||
@@ -36,7 +37,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ceres/cost_function.h"
|
||||
#include "ceres/gradient_checker.h"
|
||||
#include "ceres/internal/eigen.h"
|
||||
#include "ceres/internal/scoped_ptr.h"
|
||||
#include "ceres/parameter_block.h"
|
||||
@@ -59,55 +60,25 @@ using std::vector;
|
||||
|
||||
namespace {
|
||||
|
||||
// True if x and y have an absolute relative difference less than
|
||||
// relative_precision and false otherwise. Stores the relative and absolute
|
||||
// difference in relative/absolute_error if non-NULL.
|
||||
bool IsClose(double x, double y, double relative_precision,
|
||||
double *relative_error,
|
||||
double *absolute_error) {
|
||||
double local_absolute_error;
|
||||
double local_relative_error;
|
||||
if (!absolute_error) {
|
||||
absolute_error = &local_absolute_error;
|
||||
}
|
||||
if (!relative_error) {
|
||||
relative_error = &local_relative_error;
|
||||
}
|
||||
*absolute_error = abs(x - y);
|
||||
*relative_error = *absolute_error / max(abs(x), abs(y));
|
||||
if (x == 0 || y == 0) {
|
||||
// If x or y is exactly zero, then relative difference doesn't have any
|
||||
// meaning. Take the absolute difference instead.
|
||||
*relative_error = *absolute_error;
|
||||
}
|
||||
return abs(*relative_error) < abs(relative_precision);
|
||||
}
|
||||
|
||||
class GradientCheckingCostFunction : public CostFunction {
|
||||
public:
|
||||
GradientCheckingCostFunction(const CostFunction* function,
|
||||
const NumericDiffOptions& options,
|
||||
double relative_precision,
|
||||
const string& extra_info)
|
||||
GradientCheckingCostFunction(
|
||||
const CostFunction* function,
|
||||
const std::vector<const LocalParameterization*>* local_parameterizations,
|
||||
const NumericDiffOptions& options,
|
||||
double relative_precision,
|
||||
const string& extra_info,
|
||||
GradientCheckingIterationCallback* callback)
|
||||
: function_(function),
|
||||
gradient_checker_(function, local_parameterizations, options),
|
||||
relative_precision_(relative_precision),
|
||||
extra_info_(extra_info) {
|
||||
DynamicNumericDiffCostFunction<CostFunction, CENTRAL>*
|
||||
finite_diff_cost_function =
|
||||
new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>(
|
||||
function,
|
||||
DO_NOT_TAKE_OWNERSHIP,
|
||||
options);
|
||||
|
||||
extra_info_(extra_info),
|
||||
callback_(callback) {
|
||||
CHECK_NOTNULL(callback_);
|
||||
const vector<int32>& parameter_block_sizes =
|
||||
function->parameter_block_sizes();
|
||||
for (int i = 0; i < parameter_block_sizes.size(); ++i) {
|
||||
finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]);
|
||||
}
|
||||
*mutable_parameter_block_sizes() = parameter_block_sizes;
|
||||
set_num_residuals(function->num_residuals());
|
||||
finite_diff_cost_function->SetNumResiduals(num_residuals());
|
||||
finite_diff_cost_function_.reset(finite_diff_cost_function);
|
||||
}
|
||||
|
||||
virtual ~GradientCheckingCostFunction() { }
|
||||
@@ -120,133 +91,92 @@ class GradientCheckingCostFunction : public CostFunction {
|
||||
return function_->Evaluate(parameters, residuals, NULL);
|
||||
}
|
||||
|
||||
int num_residuals = function_->num_residuals();
|
||||
GradientChecker::ProbeResults results;
|
||||
bool okay = gradient_checker_.Probe(parameters,
|
||||
relative_precision_,
|
||||
&results);
|
||||
|
||||
// Make space for the jacobians of the two methods.
|
||||
const vector<int32>& block_sizes = function_->parameter_block_sizes();
|
||||
vector<Matrix> term_jacobians(block_sizes.size());
|
||||
vector<Matrix> finite_difference_jacobians(block_sizes.size());
|
||||
vector<double*> term_jacobian_pointers(block_sizes.size());
|
||||
vector<double*> finite_difference_jacobian_pointers(block_sizes.size());
|
||||
for (int i = 0; i < block_sizes.size(); i++) {
|
||||
term_jacobians[i].resize(num_residuals, block_sizes[i]);
|
||||
term_jacobian_pointers[i] = term_jacobians[i].data();
|
||||
finite_difference_jacobians[i].resize(num_residuals, block_sizes[i]);
|
||||
finite_difference_jacobian_pointers[i] =
|
||||
finite_difference_jacobians[i].data();
|
||||
}
|
||||
|
||||
// Evaluate the derivative using the user supplied code.
|
||||
if (!function_->Evaluate(parameters,
|
||||
residuals,
|
||||
&term_jacobian_pointers[0])) {
|
||||
LOG(WARNING) << "Function evaluation failed.";
|
||||
// If the cost function returned false, there's nothing we can say about
|
||||
// the gradients.
|
||||
if (results.return_value == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Evaluate the derivative using numeric derivatives.
|
||||
finite_diff_cost_function_->Evaluate(
|
||||
parameters,
|
||||
residuals,
|
||||
&finite_difference_jacobian_pointers[0]);
|
||||
// Copy the residuals.
|
||||
const int num_residuals = function_->num_residuals();
|
||||
MatrixRef(residuals, num_residuals, 1) = results.residuals;
|
||||
|
||||
// See if any elements have relative error larger than the threshold.
|
||||
int num_bad_jacobian_components = 0;
|
||||
double worst_relative_error = 0;
|
||||
|
||||
// Accumulate the error message for all the jacobians, since it won't get
|
||||
// output if there are no bad jacobian components.
|
||||
string m;
|
||||
// Copy the original jacobian blocks into the jacobians array.
|
||||
const vector<int32>& block_sizes = function_->parameter_block_sizes();
|
||||
for (int k = 0; k < block_sizes.size(); k++) {
|
||||
// Copy the original jacobian blocks into the jacobians array.
|
||||
if (jacobians[k] != NULL) {
|
||||
MatrixRef(jacobians[k],
|
||||
term_jacobians[k].rows(),
|
||||
term_jacobians[k].cols()) = term_jacobians[k];
|
||||
}
|
||||
|
||||
StringAppendF(&m,
|
||||
"========== "
|
||||
"Jacobian for " "block %d: (%ld by %ld)) "
|
||||
"==========\n",
|
||||
k,
|
||||
static_cast<long>(term_jacobians[k].rows()),
|
||||
static_cast<long>(term_jacobians[k].cols()));
|
||||
// The funny spacing creates appropriately aligned column headers.
|
||||
m += " block row col user dx/dy num diff dx/dy "
|
||||
"abs error relative error parameter residual\n";
|
||||
|
||||
for (int i = 0; i < term_jacobians[k].rows(); i++) {
|
||||
for (int j = 0; j < term_jacobians[k].cols(); j++) {
|
||||
double term_jacobian = term_jacobians[k](i, j);
|
||||
double finite_jacobian = finite_difference_jacobians[k](i, j);
|
||||
double relative_error, absolute_error;
|
||||
bool bad_jacobian_entry =
|
||||
!IsClose(term_jacobian,
|
||||
finite_jacobian,
|
||||
relative_precision_,
|
||||
&relative_error,
|
||||
&absolute_error);
|
||||
worst_relative_error = max(worst_relative_error, relative_error);
|
||||
|
||||
StringAppendF(&m, "%6d %4d %4d %17g %17g %17g %17g %17g %17g",
|
||||
k, i, j,
|
||||
term_jacobian, finite_jacobian,
|
||||
absolute_error, relative_error,
|
||||
parameters[k][j],
|
||||
residuals[i]);
|
||||
|
||||
if (bad_jacobian_entry) {
|
||||
num_bad_jacobian_components++;
|
||||
StringAppendF(
|
||||
&m, " ------ (%d,%d,%d) Relative error worse than %g",
|
||||
k, i, j, relative_precision_);
|
||||
}
|
||||
m += "\n";
|
||||
}
|
||||
results.jacobians[k].rows(),
|
||||
results.jacobians[k].cols()) = results.jacobians[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Since there were some bad errors, dump comprehensive debug info.
|
||||
if (num_bad_jacobian_components) {
|
||||
string header = StringPrintf("Detected %d bad jacobian component(s). "
|
||||
"Worst relative error was %g.\n",
|
||||
num_bad_jacobian_components,
|
||||
worst_relative_error);
|
||||
if (!extra_info_.empty()) {
|
||||
header += "Extra info for this residual: " + extra_info_ + "\n";
|
||||
}
|
||||
LOG(WARNING) << "\n" << header << m;
|
||||
if (!okay) {
|
||||
std::string error_log = "Gradient Error detected!\nExtra info for "
|
||||
"this residual: " + extra_info_ + "\n" + results.error_log;
|
||||
callback_->SetGradientErrorDetected(error_log);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const CostFunction* function_;
|
||||
internal::scoped_ptr<CostFunction> finite_diff_cost_function_;
|
||||
GradientChecker gradient_checker_;
|
||||
double relative_precision_;
|
||||
string extra_info_;
|
||||
GradientCheckingIterationCallback* callback_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CostFunction *CreateGradientCheckingCostFunction(
|
||||
const CostFunction *cost_function,
|
||||
GradientCheckingIterationCallback::GradientCheckingIterationCallback()
|
||||
: gradient_error_detected_(false) {
|
||||
}
|
||||
|
||||
CallbackReturnType GradientCheckingIterationCallback::operator()(
|
||||
const IterationSummary& summary) {
|
||||
if (gradient_error_detected_) {
|
||||
LOG(ERROR)<< "Gradient error detected. Terminating solver.";
|
||||
return SOLVER_ABORT;
|
||||
}
|
||||
return SOLVER_CONTINUE;
|
||||
}
|
||||
void GradientCheckingIterationCallback::SetGradientErrorDetected(
|
||||
std::string& error_log) {
|
||||
mutex_.Lock();
|
||||
gradient_error_detected_ = true;
|
||||
error_log_ += "\n" + error_log;
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
CostFunction* CreateGradientCheckingCostFunction(
|
||||
const CostFunction* cost_function,
|
||||
const std::vector<const LocalParameterization*>* local_parameterizations,
|
||||
double relative_step_size,
|
||||
double relative_precision,
|
||||
const string& extra_info) {
|
||||
const std::string& extra_info,
|
||||
GradientCheckingIterationCallback* callback) {
|
||||
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);
|
||||
relative_precision, extra_info,
|
||||
callback);
|
||||
}
|
||||
|
||||
ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
|
||||
double relative_step_size,
|
||||
double relative_precision) {
|
||||
ProblemImpl* CreateGradientCheckingProblemImpl(
|
||||
ProblemImpl* problem_impl,
|
||||
double relative_step_size,
|
||||
double relative_precision,
|
||||
GradientCheckingIterationCallback* callback) {
|
||||
CHECK_NOTNULL(callback);
|
||||
// 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
|
||||
@@ -260,6 +190,9 @@ ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
|
||||
gradient_checking_problem_options.local_parameterization_ownership =
|
||||
DO_NOT_TAKE_OWNERSHIP;
|
||||
|
||||
NumericDiffOptions numeric_diff_options;
|
||||
numeric_diff_options.relative_step_size = relative_step_size;
|
||||
|
||||
ProblemImpl* gradient_checking_problem_impl = new ProblemImpl(
|
||||
gradient_checking_problem_options);
|
||||
|
||||
@@ -294,19 +227,26 @@ ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
|
||||
string extra_info = StringPrintf(
|
||||
"Residual block id %d; depends on parameters [", i);
|
||||
vector<double*> parameter_blocks;
|
||||
vector<const LocalParameterization*> local_parameterizations;
|
||||
parameter_blocks.reserve(residual_block->NumParameterBlocks());
|
||||
local_parameterizations.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()));
|
||||
}
|
||||
|
||||
// Wrap the original CostFunction in a GradientCheckingCostFunction.
|
||||
CostFunction* gradient_checking_cost_function =
|
||||
CreateGradientCheckingCostFunction(residual_block->cost_function(),
|
||||
relative_step_size,
|
||||
relative_precision,
|
||||
extra_info);
|
||||
new GradientCheckingCostFunction(residual_block->cost_function(),
|
||||
&local_parameterizations,
|
||||
numeric_diff_options,
|
||||
relative_precision,
|
||||
extra_info,
|
||||
callback);
|
||||
|
||||
// The const_cast is necessary because
|
||||
// ProblemImpl::AddResidualBlock can potentially take ownership of
|
||||
|
@@ -26,7 +26,8 @@
|
||||
// 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)
|
||||
// Authors: keir@google.com (Keir Mierle),
|
||||
// dgossow@google.com (David Gossow)
|
||||
|
||||
#ifndef CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_
|
||||
#define CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_
|
||||
@@ -34,50 +35,76 @@
|
||||
#include <string>
|
||||
|
||||
#include "ceres/cost_function.h"
|
||||
#include "ceres/iteration_callback.h"
|
||||
#include "ceres/local_parameterization.h"
|
||||
#include "ceres/mutex.h"
|
||||
|
||||
namespace ceres {
|
||||
namespace internal {
|
||||
|
||||
class ProblemImpl;
|
||||
|
||||
// Creates a CostFunction that checks the jacobians that cost_function computes
|
||||
// with finite differences. Bad results are logged; required precision is
|
||||
// controlled by relative_precision and the numeric differentiation step size is
|
||||
// controlled with relative_step_size. See solver.h for a better explanation of
|
||||
// relative_step_size. Caller owns result.
|
||||
//
|
||||
// The condition enforced is that
|
||||
//
|
||||
// (J_actual(i, j) - J_numeric(i, j))
|
||||
// ------------------------------------ < relative_precision
|
||||
// max(J_actual(i, j), J_numeric(i, j))
|
||||
//
|
||||
// where J_actual(i, j) is the jacobian as computed by the supplied cost
|
||||
// function (by the user) and J_numeric is the jacobian as computed by finite
|
||||
// differences.
|
||||
//
|
||||
// Note: This is quite inefficient and is intended only for debugging.
|
||||
// Callback that collects information about gradient checking errors, and
|
||||
// will abort the solve as soon as an error occurs.
|
||||
class GradientCheckingIterationCallback : public IterationCallback {
|
||||
public:
|
||||
GradientCheckingIterationCallback();
|
||||
|
||||
// Will return SOLVER_CONTINUE until a gradient error has been detected,
|
||||
// then return SOLVER_ABORT.
|
||||
virtual CallbackReturnType operator()(const IterationSummary& summary);
|
||||
|
||||
// Notify this that a gradient error has occurred (thread safe).
|
||||
void SetGradientErrorDetected(std::string& error_log);
|
||||
|
||||
// Retrieve error status (not thread safe).
|
||||
bool gradient_error_detected() const { return gradient_error_detected_; }
|
||||
const std::string& error_log() const { return error_log_; }
|
||||
private:
|
||||
bool gradient_error_detected_;
|
||||
std::string error_log_;
|
||||
// Mutex protecting member variables.
|
||||
ceres::internal::Mutex mutex_;
|
||||
};
|
||||
|
||||
// Creates a CostFunction that checks the Jacobians that cost_function computes
|
||||
// with finite differences. This API is only intended for unit tests that intend
|
||||
// to check the functionality of the GradientCheckingCostFunction
|
||||
// implementation directly.
|
||||
CostFunction* CreateGradientCheckingCostFunction(
|
||||
const CostFunction* cost_function,
|
||||
const std::vector<const LocalParameterization*>* local_parameterizations,
|
||||
double relative_step_size,
|
||||
double relative_precision,
|
||||
const std::string& extra_info);
|
||||
const std::string& extra_info,
|
||||
GradientCheckingIterationCallback* callback);
|
||||
|
||||
// Create a new ProblemImpl object from the input problem_impl, where
|
||||
// each CostFunctions in problem_impl are wrapped inside a
|
||||
// GradientCheckingCostFunctions. This gives us a ProblemImpl object
|
||||
// which checks its derivatives against estimates from numeric
|
||||
// differentiation everytime a ResidualBlock is evaluated.
|
||||
// Create a new ProblemImpl object from the input problem_impl, where all
|
||||
// cost functions are wrapped so that each time their Evaluate method is called,
|
||||
// an additional check is performed that compares the Jacobians computed by
|
||||
// the original cost function with alternative Jacobians computed using
|
||||
// numerical differentiation. If local parameterizations are given for any
|
||||
// parameters, the Jacobians will be compared in the local space instead of the
|
||||
// ambient space. For details on the gradient checking procedure, see the
|
||||
// documentation of the GradientChecker class. If an error is detected in any
|
||||
// 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
|
||||
// the numeric differentiation and the relative tolerance between the
|
||||
// jacobian computed by the CostFunctions in problem_impl and
|
||||
// jacobians obtained by numerically differentiating them. For more
|
||||
// details see the documentation for
|
||||
// CreateGradientCheckingCostFunction above.
|
||||
ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl,
|
||||
double relative_step_size,
|
||||
double relative_precision);
|
||||
// jacobians obtained by numerically differentiating them. See the
|
||||
// documentation of 'numeric_derivative_relative_step_size' in solver.h for a
|
||||
// better explanation.
|
||||
ProblemImpl* CreateGradientCheckingProblemImpl(
|
||||
ProblemImpl* problem_impl,
|
||||
double relative_step_size,
|
||||
double relative_precision,
|
||||
GradientCheckingIterationCallback* callback);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ceres
|
||||
|
@@ -84,6 +84,12 @@ Solver::Options GradientProblemSolverOptionsToSolverOptions(
|
||||
|
||||
} // namespace
|
||||
|
||||
bool GradientProblemSolver::Options::IsValid(std::string* error) const {
|
||||
const Solver::Options solver_options =
|
||||
GradientProblemSolverOptionsToSolverOptions(*this);
|
||||
return solver_options.IsValid(error);
|
||||
}
|
||||
|
||||
GradientProblemSolver::~GradientProblemSolver() {
|
||||
}
|
||||
|
||||
@@ -99,8 +105,6 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
|
||||
using internal::SetSummaryFinalCost;
|
||||
|
||||
double start_time = WallTimeInSeconds();
|
||||
Solver::Options solver_options =
|
||||
GradientProblemSolverOptionsToSolverOptions(options);
|
||||
|
||||
*CHECK_NOTNULL(summary) = Summary();
|
||||
summary->num_parameters = problem.NumParameters();
|
||||
@@ -112,14 +116,16 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options,
|
||||
summary->nonlinear_conjugate_gradient_type = options.nonlinear_conjugate_gradient_type; // NOLINT
|
||||
|
||||
// Check validity
|
||||
if (!solver_options.IsValid(&summary->message)) {
|
||||
if (!options.IsValid(&summary->message)) {
|
||||
LOG(ERROR) << "Terminating: " << summary->message;
|
||||
return;
|
||||
}
|
||||
|
||||
// Assuming that the parameter blocks in the program have been
|
||||
Minimizer::Options minimizer_options;
|
||||
minimizer_options = Minimizer::Options(solver_options);
|
||||
// TODO(sameeragarwal): This is a bit convoluted, we should be able
|
||||
// to convert to minimizer options directly, but this will do for
|
||||
// now.
|
||||
Minimizer::Options minimizer_options =
|
||||
Minimizer::Options(GradientProblemSolverOptionsToSolverOptions(options));
|
||||
minimizer_options.evaluator.reset(new GradientProblemEvaluator(problem));
|
||||
|
||||
scoped_ptr<IterationCallback> logging_callback;
|
||||
|
59
extern/ceres/internal/ceres/is_close.cc
vendored
Normal file
59
extern/ceres/internal/ceres/is_close.cc
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2016 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.
|
||||
//
|
||||
// Authors: keir@google.com (Keir Mierle), dgossow@google.com (David Gossow)
|
||||
|
||||
#include "ceres/is_close.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace ceres {
|
||||
namespace internal {
|
||||
bool IsClose(double x, double y, double relative_precision,
|
||||
double *relative_error,
|
||||
double *absolute_error) {
|
||||
double local_absolute_error;
|
||||
double local_relative_error;
|
||||
if (!absolute_error) {
|
||||
absolute_error = &local_absolute_error;
|
||||
}
|
||||
if (!relative_error) {
|
||||
relative_error = &local_relative_error;
|
||||
}
|
||||
*absolute_error = std::fabs(x - y);
|
||||
*relative_error = *absolute_error / std::max(std::fabs(x), std::fabs(y));
|
||||
if (x == 0 || y == 0) {
|
||||
// If x or y is exactly zero, then relative difference doesn't have any
|
||||
// meaning. Take the absolute difference instead.
|
||||
*relative_error = *absolute_error;
|
||||
}
|
||||
return *relative_error < std::fabs(relative_precision);
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace ceres
|
51
extern/ceres/internal/ceres/is_close.h
vendored
Normal file
51
extern/ceres/internal/ceres/is_close.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2016 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.
|
||||
//
|
||||
// Authors: keir@google.com (Keir Mierle), dgossow@google.com (David Gossow)
|
||||
//
|
||||
// Utility routine for comparing two values.
|
||||
|
||||
#ifndef CERES_INTERNAL_IS_CLOSE_H_
|
||||
#define CERES_INTERNAL_IS_CLOSE_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.
|
||||
bool IsClose(double x,
|
||||
double y,
|
||||
double relative_precision,
|
||||
double *relative_error,
|
||||
double *absolute_error);
|
||||
} // namespace internal
|
||||
} // namespace ceres
|
||||
|
||||
#endif // CERES_INTERNAL_IS_CLOSE_H_
|
@@ -191,6 +191,7 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
|
||||
options.line_search_sufficient_curvature_decrease;
|
||||
line_search_options.max_step_expansion =
|
||||
options.max_line_search_step_expansion;
|
||||
line_search_options.is_silent = options.is_silent;
|
||||
line_search_options.function = &line_search_function;
|
||||
|
||||
scoped_ptr<LineSearch>
|
||||
@@ -341,10 +342,12 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
|
||||
"as the step was valid when it was selected by the line search.";
|
||||
LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
|
||||
break;
|
||||
} else if (!Evaluate(evaluator,
|
||||
x_plus_delta,
|
||||
¤t_state,
|
||||
&summary->message)) {
|
||||
}
|
||||
|
||||
if (!Evaluate(evaluator,
|
||||
x_plus_delta,
|
||||
¤t_state,
|
||||
&summary->message)) {
|
||||
summary->termination_type = FAILURE;
|
||||
summary->message =
|
||||
"Step failed to evaluate. This should not happen as the step was "
|
||||
@@ -352,15 +355,17 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
|
||||
summary->message;
|
||||
LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
|
||||
break;
|
||||
} else {
|
||||
x = x_plus_delta;
|
||||
}
|
||||
|
||||
// Compute the norm of the step in the ambient space.
|
||||
iteration_summary.step_norm = (x_plus_delta - x).norm();
|
||||
x = x_plus_delta;
|
||||
|
||||
iteration_summary.gradient_max_norm = current_state.gradient_max_norm;
|
||||
iteration_summary.gradient_norm = sqrt(current_state.gradient_squared_norm);
|
||||
iteration_summary.cost_change = previous_state.cost - current_state.cost;
|
||||
iteration_summary.cost = current_state.cost + summary->fixed_cost;
|
||||
iteration_summary.step_norm = delta.norm();
|
||||
|
||||
iteration_summary.step_is_valid = true;
|
||||
iteration_summary.step_is_successful = true;
|
||||
iteration_summary.step_size = current_state.step_size;
|
||||
@@ -376,6 +381,13 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options,
|
||||
WallTimeInSeconds() - start_time
|
||||
+ summary->preprocessor_time_in_seconds;
|
||||
|
||||
// Iterations inside the line search algorithm are considered
|
||||
// 'steps' in the broader context, to distinguish these inner
|
||||
// iterations from from the outer iterations of the line search
|
||||
// minimizer. The number of line search steps is the total number
|
||||
// of inner line search iterations (or steps) across the entire
|
||||
// minimization.
|
||||
summary->num_line_search_steps += line_search_summary.num_iterations;
|
||||
summary->line_search_cost_evaluation_time_in_seconds +=
|
||||
line_search_summary.cost_evaluation_time_in_seconds;
|
||||
summary->line_search_gradient_evaluation_time_in_seconds +=
|
||||
|
@@ -30,6 +30,8 @@
|
||||
|
||||
#include "ceres/local_parameterization.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "Eigen/Geometry"
|
||||
#include "ceres/householder_vector.h"
|
||||
#include "ceres/internal/eigen.h"
|
||||
#include "ceres/internal/fixed_array.h"
|
||||
@@ -87,28 +89,17 @@ bool IdentityParameterization::MultiplyByJacobian(const double* x,
|
||||
}
|
||||
|
||||
SubsetParameterization::SubsetParameterization(
|
||||
int size,
|
||||
const vector<int>& constant_parameters)
|
||||
: local_size_(size - constant_parameters.size()),
|
||||
constancy_mask_(size, 0) {
|
||||
CHECK_GT(constant_parameters.size(), 0)
|
||||
<< "The set of constant parameters should contain at least "
|
||||
<< "one element. If you do not wish to hold any parameters "
|
||||
<< "constant, then do not use a SubsetParameterization";
|
||||
|
||||
int size, const vector<int>& constant_parameters)
|
||||
: local_size_(size - constant_parameters.size()), constancy_mask_(size, 0) {
|
||||
vector<int> constant = constant_parameters;
|
||||
sort(constant.begin(), constant.end());
|
||||
CHECK(unique(constant.begin(), constant.end()) == constant.end())
|
||||
std::sort(constant.begin(), constant.end());
|
||||
CHECK_GE(constant.front(), 0)
|
||||
<< "Indices indicating constant parameter must be greater than 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";
|
||||
CHECK_LT(constant_parameters.size(), size)
|
||||
<< "Number of parameters held constant should be less "
|
||||
<< "than the size of the parameter block. If you wish "
|
||||
<< "to hold the entire parameter block constant, then a "
|
||||
<< "efficient way is to directly mark it as constant "
|
||||
<< "instead of using a LocalParameterization to do so.";
|
||||
CHECK_GE(*min_element(constant.begin(), constant.end()), 0);
|
||||
CHECK_LT(*max_element(constant.begin(), constant.end()), size);
|
||||
|
||||
for (int i = 0; i < constant_parameters.size(); ++i) {
|
||||
constancy_mask_[constant_parameters[i]] = 1;
|
||||
}
|
||||
@@ -129,6 +120,10 @@ bool SubsetParameterization::Plus(const double* x,
|
||||
|
||||
bool SubsetParameterization::ComputeJacobian(const double* x,
|
||||
double* jacobian) const {
|
||||
if (local_size_ == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MatrixRef m(jacobian, constancy_mask_.size(), local_size_);
|
||||
m.setZero();
|
||||
for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) {
|
||||
@@ -143,6 +138,10 @@ bool SubsetParameterization::MultiplyByJacobian(const double* x,
|
||||
const int num_rows,
|
||||
const double* global_matrix,
|
||||
double* local_matrix) const {
|
||||
if (local_size_ == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int row = 0; row < num_rows; ++row) {
|
||||
for (int col = 0, j = 0; col < constancy_mask_.size(); ++col) {
|
||||
if (!constancy_mask_[col]) {
|
||||
@@ -184,6 +183,39 @@ bool QuaternionParameterization::ComputeJacobian(const double* x,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EigenQuaternionParameterization::Plus(const double* x_ptr,
|
||||
const double* delta,
|
||||
double* x_plus_delta_ptr) const {
|
||||
Eigen::Map<Eigen::Quaterniond> x_plus_delta(x_plus_delta_ptr);
|
||||
Eigen::Map<const Eigen::Quaterniond> x(x_ptr);
|
||||
|
||||
const double norm_delta =
|
||||
sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
|
||||
if (norm_delta > 0.0) {
|
||||
const double sin_delta_by_delta = sin(norm_delta) / norm_delta;
|
||||
|
||||
// Note, in the constructor w is first.
|
||||
Eigen::Quaterniond delta_q(cos(norm_delta),
|
||||
sin_delta_by_delta * delta[0],
|
||||
sin_delta_by_delta * delta[1],
|
||||
sin_delta_by_delta * delta[2]);
|
||||
x_plus_delta = delta_q * x;
|
||||
} else {
|
||||
x_plus_delta = x;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EigenQuaternionParameterization::ComputeJacobian(const double* x,
|
||||
double* jacobian) const {
|
||||
jacobian[0] = x[3]; jacobian[1] = x[2]; jacobian[2] = -x[1]; // NOLINT
|
||||
jacobian[3] = -x[2]; jacobian[4] = x[3]; jacobian[5] = x[0]; // NOLINT
|
||||
jacobian[6] = x[1]; jacobian[7] = -x[0]; jacobian[8] = x[3]; // NOLINT
|
||||
jacobian[9] = -x[0]; jacobian[10] = -x[1]; jacobian[11] = -x[2]; // NOLINT
|
||||
return true;
|
||||
}
|
||||
|
||||
HomogeneousVectorParameterization::HomogeneousVectorParameterization(int size)
|
||||
: size_(size) {
|
||||
CHECK_GT(size_, 1) << "The size of the homogeneous vector needs to be "
|
||||
@@ -332,9 +364,9 @@ bool ProductParameterization::ComputeJacobian(const double* x,
|
||||
if (!param->ComputeJacobian(x + x_cursor, buffer.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jacobian.block(x_cursor, delta_cursor, global_size, local_size)
|
||||
= MatrixRef(buffer.get(), global_size, local_size);
|
||||
|
||||
delta_cursor += local_size;
|
||||
x_cursor += global_size;
|
||||
}
|
||||
|
2
extern/ceres/internal/ceres/map_util.h
vendored
2
extern/ceres/internal/ceres/map_util.h
vendored
@@ -67,7 +67,7 @@ FindOrDie(const Collection& collection,
|
||||
// If the key is present in the map then the value associated with that
|
||||
// key is returned, otherwise the value passed as a default is returned.
|
||||
template <class Collection>
|
||||
const typename Collection::value_type::second_type&
|
||||
const typename Collection::value_type::second_type
|
||||
FindWithDefault(const Collection& collection,
|
||||
const typename Collection::value_type::first_type& key,
|
||||
const typename Collection::value_type::second_type& value) {
|
||||
|
37
extern/ceres/internal/ceres/parameter_block.h
vendored
37
extern/ceres/internal/ceres/parameter_block.h
vendored
@@ -161,25 +161,34 @@ class ParameterBlock {
|
||||
// does not take ownership of the parameterization.
|
||||
void SetParameterization(LocalParameterization* new_parameterization) {
|
||||
CHECK(new_parameterization != NULL) << "NULL parameterization invalid.";
|
||||
// Nothing to do if the new parameterization is the same as the
|
||||
// old parameterization.
|
||||
if (new_parameterization == local_parameterization_) {
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(local_parameterization_ == NULL)
|
||||
<< "Can't re-set the local parameterization; it leads to "
|
||||
<< "ambiguous ownership. Current local parameterization is: "
|
||||
<< local_parameterization_;
|
||||
|
||||
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?";
|
||||
if (new_parameterization != local_parameterization_) {
|
||||
CHECK(local_parameterization_ == NULL)
|
||||
<< "Can't re-set the local parameterization; it leads to "
|
||||
<< "ambiguous ownership.";
|
||||
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: "
|
||||
<< ConstVectorRef(state_, Size()).transpose();
|
||||
} else {
|
||||
// Ignore the case that the parameterizations match.
|
||||
}
|
||||
|
||||
CHECK_GT(new_parameterization->LocalSize(), 0)
|
||||
<< "Invalid parameterization. Parameterizations must have a positive "
|
||||
<< "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: "
|
||||
<< ConstVectorRef(state_, Size()).transpose();
|
||||
}
|
||||
|
||||
void SetUpperBound(int index, double upper_bound) {
|
||||
|
4
extern/ceres/internal/ceres/problem.cc
vendored
4
extern/ceres/internal/ceres/problem.cc
vendored
@@ -174,6 +174,10 @@ void Problem::SetParameterBlockVariable(double* values) {
|
||||
problem_impl_->SetParameterBlockVariable(values);
|
||||
}
|
||||
|
||||
bool Problem::IsParameterBlockConstant(double* values) const {
|
||||
return problem_impl_->IsParameterBlockConstant(values);
|
||||
}
|
||||
|
||||
void Problem::SetParameterization(
|
||||
double* values,
|
||||
LocalParameterization* local_parameterization) {
|
||||
|
19
extern/ceres/internal/ceres/problem_impl.cc
vendored
19
extern/ceres/internal/ceres/problem_impl.cc
vendored
@@ -249,10 +249,11 @@ ResidualBlock* ProblemImpl::AddResidualBlock(
|
||||
// Check for duplicate parameter blocks.
|
||||
vector<double*> sorted_parameter_blocks(parameter_blocks);
|
||||
sort(sorted_parameter_blocks.begin(), sorted_parameter_blocks.end());
|
||||
vector<double*>::const_iterator duplicate_items =
|
||||
unique(sorted_parameter_blocks.begin(),
|
||||
sorted_parameter_blocks.end());
|
||||
if (duplicate_items != sorted_parameter_blocks.end()) {
|
||||
const bool has_duplicate_items =
|
||||
(std::adjacent_find(sorted_parameter_blocks.begin(),
|
||||
sorted_parameter_blocks.end())
|
||||
!= sorted_parameter_blocks.end());
|
||||
if (has_duplicate_items) {
|
||||
string blocks;
|
||||
for (int i = 0; i < parameter_blocks.size(); ++i) {
|
||||
blocks += StringPrintf(" %p ", parameter_blocks[i]);
|
||||
@@ -572,6 +573,16 @@ void ProblemImpl::SetParameterBlockConstant(double* values) {
|
||||
parameter_block->SetConstant();
|
||||
}
|
||||
|
||||
bool ProblemImpl::IsParameterBlockConstant(double* values) const {
|
||||
const ParameterBlock* parameter_block =
|
||||
FindWithDefault(parameter_block_map_, values, NULL);
|
||||
CHECK(parameter_block != NULL)
|
||||
<< "Parameter block not found: " << values << ". You must add the "
|
||||
<< "parameter block to the problem before it can be queried.";
|
||||
|
||||
return parameter_block->IsConstant();
|
||||
}
|
||||
|
||||
void ProblemImpl::SetParameterBlockVariable(double* values) {
|
||||
ParameterBlock* parameter_block =
|
||||
FindWithDefault(parameter_block_map_, values, NULL);
|
||||
|
2
extern/ceres/internal/ceres/problem_impl.h
vendored
2
extern/ceres/internal/ceres/problem_impl.h
vendored
@@ -128,6 +128,8 @@ class ProblemImpl {
|
||||
|
||||
void SetParameterBlockConstant(double* values);
|
||||
void SetParameterBlockVariable(double* values);
|
||||
bool IsParameterBlockConstant(double* values) const;
|
||||
|
||||
void SetParameterization(double* values,
|
||||
LocalParameterization* local_parameterization);
|
||||
const LocalParameterization* GetParameterization(double* values) const;
|
||||
|
@@ -142,6 +142,11 @@ void OrderingForSparseNormalCholeskyUsingSuiteSparse(
|
||||
ordering);
|
||||
}
|
||||
|
||||
VLOG(2) << "Block ordering stats: "
|
||||
<< " flops: " << ss.mutable_cc()->fl
|
||||
<< " lnz : " << ss.mutable_cc()->lnz
|
||||
<< " anz : " << ss.mutable_cc()->anz;
|
||||
|
||||
ss.Free(block_jacobian_transpose);
|
||||
#endif // CERES_NO_SUITESPARSE
|
||||
}
|
||||
|
2
extern/ceres/internal/ceres/residual_block.h
vendored
2
extern/ceres/internal/ceres/residual_block.h
vendored
@@ -127,7 +127,7 @@ class ResidualBlock {
|
||||
int index() const { return index_; }
|
||||
void set_index(int index) { index_ = index; }
|
||||
|
||||
std::string ToString() {
|
||||
std::string ToString() const {
|
||||
return StringPrintf("{residual block; index=%d}", index_);
|
||||
}
|
||||
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "ceres/block_random_access_dense_matrix.h"
|
||||
@@ -563,6 +564,12 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingEigen(
|
||||
// worse than the one computed using the block version of the
|
||||
// algorithm.
|
||||
simplicial_ldlt_->analyzePattern(eigen_lhs);
|
||||
if (VLOG_IS_ON(2)) {
|
||||
std::stringstream ss;
|
||||
simplicial_ldlt_->dumpMemory(ss);
|
||||
VLOG(2) << "Symbolic Analysis\n"
|
||||
<< ss.str();
|
||||
}
|
||||
event_logger.AddEvent("Analysis");
|
||||
if (simplicial_ldlt_->info() != Eigen::Success) {
|
||||
summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
|
||||
|
61
extern/ceres/internal/ceres/solver.cc
vendored
61
extern/ceres/internal/ceres/solver.cc
vendored
@@ -94,7 +94,7 @@ bool CommonOptionsAreValid(const Solver::Options& options, string* error) {
|
||||
OPTION_GT(num_linear_solver_threads, 0);
|
||||
if (options.check_gradients) {
|
||||
OPTION_GT(gradient_check_relative_precision, 0.0);
|
||||
OPTION_GT(numeric_derivative_relative_step_size, 0.0);
|
||||
OPTION_GT(gradient_check_numeric_derivative_relative_step_size, 0.0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -351,6 +351,7 @@ void PreSolveSummarize(const Solver::Options& options,
|
||||
summary->dense_linear_algebra_library_type = options.dense_linear_algebra_library_type; // NOLINT
|
||||
summary->dogleg_type = options.dogleg_type;
|
||||
summary->inner_iteration_time_in_seconds = 0.0;
|
||||
summary->num_line_search_steps = 0;
|
||||
summary->line_search_cost_evaluation_time_in_seconds = 0.0;
|
||||
summary->line_search_gradient_evaluation_time_in_seconds = 0.0;
|
||||
summary->line_search_polynomial_minimization_time_in_seconds = 0.0;
|
||||
@@ -495,21 +496,28 @@ void Solver::Solve(const Solver::Options& options,
|
||||
// values provided by the user.
|
||||
program->SetParameterBlockStatePtrsToUserStatePtrs();
|
||||
|
||||
// If gradient_checking is enabled, wrap all cost functions in a
|
||||
// gradient checker and install a callback that terminates if any gradient
|
||||
// error is detected.
|
||||
scoped_ptr<internal::ProblemImpl> gradient_checking_problem;
|
||||
internal::GradientCheckingIterationCallback gradient_checking_callback;
|
||||
Solver::Options modified_options = options;
|
||||
if (options.check_gradients) {
|
||||
modified_options.callbacks.push_back(&gradient_checking_callback);
|
||||
gradient_checking_problem.reset(
|
||||
CreateGradientCheckingProblemImpl(
|
||||
problem_impl,
|
||||
options.numeric_derivative_relative_step_size,
|
||||
options.gradient_check_relative_precision));
|
||||
options.gradient_check_numeric_derivative_relative_step_size,
|
||||
options.gradient_check_relative_precision,
|
||||
&gradient_checking_callback));
|
||||
problem_impl = gradient_checking_problem.get();
|
||||
program = problem_impl->mutable_program();
|
||||
}
|
||||
|
||||
scoped_ptr<Preprocessor> preprocessor(
|
||||
Preprocessor::Create(options.minimizer_type));
|
||||
Preprocessor::Create(modified_options.minimizer_type));
|
||||
PreprocessedProblem pp;
|
||||
const bool status = preprocessor->Preprocess(options, problem_impl, &pp);
|
||||
const bool status = preprocessor->Preprocess(modified_options, problem_impl, &pp);
|
||||
summary->fixed_cost = pp.fixed_cost;
|
||||
summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time;
|
||||
|
||||
@@ -534,6 +542,13 @@ void Solver::Solve(const Solver::Options& options,
|
||||
summary->postprocessor_time_in_seconds =
|
||||
WallTimeInSeconds() - postprocessor_start_time;
|
||||
|
||||
// If the gradient checker reported an error, we want to report FAILURE
|
||||
// instead of USER_FAILURE and provide the error log.
|
||||
if (gradient_checking_callback.gradient_error_detected()) {
|
||||
summary->termination_type = FAILURE;
|
||||
summary->message = gradient_checking_callback.error_log();
|
||||
}
|
||||
|
||||
summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
|
||||
}
|
||||
|
||||
@@ -556,6 +571,7 @@ Solver::Summary::Summary()
|
||||
num_successful_steps(-1),
|
||||
num_unsuccessful_steps(-1),
|
||||
num_inner_iteration_steps(-1),
|
||||
num_line_search_steps(-1),
|
||||
preprocessor_time_in_seconds(-1.0),
|
||||
minimizer_time_in_seconds(-1.0),
|
||||
postprocessor_time_in_seconds(-1.0),
|
||||
@@ -696,16 +712,14 @@ string Solver::Summary::FullReport() const {
|
||||
num_linear_solver_threads_given,
|
||||
num_linear_solver_threads_used);
|
||||
|
||||
if (IsSchurType(linear_solver_type_used)) {
|
||||
string given;
|
||||
StringifyOrdering(linear_solver_ordering_given, &given);
|
||||
string used;
|
||||
StringifyOrdering(linear_solver_ordering_used, &used);
|
||||
StringAppendF(&report,
|
||||
"Linear solver ordering %22s %24s\n",
|
||||
given.c_str(),
|
||||
used.c_str());
|
||||
}
|
||||
string given;
|
||||
StringifyOrdering(linear_solver_ordering_given, &given);
|
||||
string used;
|
||||
StringifyOrdering(linear_solver_ordering_used, &used);
|
||||
StringAppendF(&report,
|
||||
"Linear solver ordering %22s %24s\n",
|
||||
given.c_str(),
|
||||
used.c_str());
|
||||
|
||||
if (inner_iterations_given) {
|
||||
StringAppendF(&report,
|
||||
@@ -784,9 +798,14 @@ string Solver::Summary::FullReport() const {
|
||||
num_inner_iteration_steps);
|
||||
}
|
||||
|
||||
const bool print_line_search_timing_information =
|
||||
minimizer_type == LINE_SEARCH ||
|
||||
(minimizer_type == TRUST_REGION && is_constrained);
|
||||
const bool line_search_used =
|
||||
(minimizer_type == LINE_SEARCH ||
|
||||
(minimizer_type == TRUST_REGION && is_constrained));
|
||||
|
||||
if (line_search_used) {
|
||||
StringAppendF(&report, "Line search steps % 14d\n",
|
||||
num_line_search_steps);
|
||||
}
|
||||
|
||||
StringAppendF(&report, "\nTime (in seconds):\n");
|
||||
StringAppendF(&report, "Preprocessor %25.4f\n",
|
||||
@@ -794,13 +813,13 @@ string Solver::Summary::FullReport() const {
|
||||
|
||||
StringAppendF(&report, "\n Residual evaluation %23.4f\n",
|
||||
residual_evaluation_time_in_seconds);
|
||||
if (print_line_search_timing_information) {
|
||||
if (line_search_used) {
|
||||
StringAppendF(&report, " Line search cost evaluation %10.4f\n",
|
||||
line_search_cost_evaluation_time_in_seconds);
|
||||
}
|
||||
StringAppendF(&report, " Jacobian evaluation %23.4f\n",
|
||||
jacobian_evaluation_time_in_seconds);
|
||||
if (print_line_search_timing_information) {
|
||||
if (line_search_used) {
|
||||
StringAppendF(&report, " Line search gradient evaluation %6.4f\n",
|
||||
line_search_gradient_evaluation_time_in_seconds);
|
||||
}
|
||||
@@ -815,7 +834,7 @@ string Solver::Summary::FullReport() const {
|
||||
inner_iteration_time_in_seconds);
|
||||
}
|
||||
|
||||
if (print_line_search_timing_information) {
|
||||
if (line_search_used) {
|
||||
StringAppendF(&report, " Line search polynomial minimization %.4f\n",
|
||||
line_search_polynomial_minimization_time_in_seconds);
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
#include "ceres/compressed_row_sparse_matrix.h"
|
||||
#include "ceres/cxsparse.h"
|
||||
@@ -71,6 +72,12 @@ LinearSolver::Summary SimplicialLDLTSolve(
|
||||
|
||||
if (do_symbolic_analysis) {
|
||||
solver->analyzePattern(lhs);
|
||||
if (VLOG_IS_ON(2)) {
|
||||
std::stringstream ss;
|
||||
solver->dumpMemory(ss);
|
||||
VLOG(2) << "Symbolic Analysis\n"
|
||||
<< ss.str();
|
||||
}
|
||||
event_logger->AddEvent("Analyze");
|
||||
if (solver->info() != Eigen::Success) {
|
||||
summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
|
||||
|
41
extern/ceres/internal/ceres/stringprintf.cc
vendored
41
extern/ceres/internal/ceres/stringprintf.cc
vendored
@@ -43,14 +43,27 @@ namespace internal {
|
||||
|
||||
using std::string;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
enum { IS_COMPILER_MSVC = 1 };
|
||||
#if _MSC_VER < 1800
|
||||
#define va_copy(d, s) ((d) = (s))
|
||||
#endif
|
||||
// va_copy() was defined in the C99 standard. However, it did not appear in the
|
||||
// C++ standard until C++11. This means that if Ceres is being compiled with a
|
||||
// strict pre-C++11 standard (e.g. -std=c++03), va_copy() will NOT be defined,
|
||||
// as we are using the C++ compiler (it would however be defined if we were
|
||||
// using the C compiler). Note however that both GCC & Clang will in fact
|
||||
// define va_copy() when compiling for C++ if the C++ standard is not explicitly
|
||||
// specified (i.e. no -std=c++<XX> arg), even though it should not strictly be
|
||||
// defined unless -std=c++11 (or greater) was passed.
|
||||
#if !defined(va_copy)
|
||||
#if defined (__GNUC__)
|
||||
// On GCC/Clang, if va_copy() is not defined (C++ standard < C++11 explicitly
|
||||
// specified), use the internal __va_copy() version, which should be present
|
||||
// in even very old GCC versions.
|
||||
#define va_copy(d, s) __va_copy(d, s)
|
||||
#else
|
||||
enum { IS_COMPILER_MSVC = 0 };
|
||||
#endif
|
||||
// Some older versions of MSVC do not have va_copy(), in which case define it.
|
||||
// Although this is required for older MSVC versions, it should also work for
|
||||
// other non-GCC/Clang compilers which also do not defined va_copy().
|
||||
#define va_copy(d, s) ((d) = (s))
|
||||
#endif // defined (__GNUC__)
|
||||
#endif // !defined(va_copy)
|
||||
|
||||
void StringAppendV(string* dst, const char* format, va_list ap) {
|
||||
// First try with a small fixed size buffer
|
||||
@@ -71,13 +84,13 @@ void StringAppendV(string* dst, const char* format, va_list ap) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_COMPILER_MSVC) {
|
||||
// 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);
|
||||
va_end(backup_ap);
|
||||
}
|
||||
#if defined (_MSC_VER)
|
||||
// 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);
|
||||
va_end(backup_ap);
|
||||
#endif
|
||||
|
||||
if (result < 0) {
|
||||
// Just an error.
|
||||
|
1341
extern/ceres/internal/ceres/trust_region_minimizer.cc
vendored
1341
extern/ceres/internal/ceres/trust_region_minimizer.cc
vendored
File diff suppressed because it is too large
Load Diff
123
extern/ceres/internal/ceres/trust_region_minimizer.h
vendored
123
extern/ceres/internal/ceres/trust_region_minimizer.h
vendored
@@ -1,5 +1,5 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// http://ceres-solver.org/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -31,35 +31,136 @@
|
||||
#ifndef CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
|
||||
#define CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
|
||||
|
||||
#include "ceres/internal/eigen.h"
|
||||
#include "ceres/internal/scoped_ptr.h"
|
||||
#include "ceres/minimizer.h"
|
||||
#include "ceres/solver.h"
|
||||
#include "ceres/sparse_matrix.h"
|
||||
#include "ceres/trust_region_step_evaluator.h"
|
||||
#include "ceres/trust_region_strategy.h"
|
||||
#include "ceres/types.h"
|
||||
|
||||
namespace ceres {
|
||||
namespace internal {
|
||||
|
||||
// Generic trust region minimization algorithm. The heavy lifting is
|
||||
// done by a TrustRegionStrategy object passed in as part of options.
|
||||
// Generic trust region minimization algorithm.
|
||||
//
|
||||
// For example usage, see SolverImpl::Minimize.
|
||||
class TrustRegionMinimizer : public Minimizer {
|
||||
public:
|
||||
~TrustRegionMinimizer() {}
|
||||
~TrustRegionMinimizer();
|
||||
|
||||
// This method is not thread safe.
|
||||
virtual void Minimize(const Minimizer::Options& options,
|
||||
double* parameters,
|
||||
Solver::Summary* summary);
|
||||
Solver::Summary* solver_summary);
|
||||
|
||||
private:
|
||||
void Init(const Minimizer::Options& options);
|
||||
void EstimateScale(const SparseMatrix& jacobian, double* scale) const;
|
||||
bool MaybeDumpLinearLeastSquaresProblem(const int iteration,
|
||||
const SparseMatrix* jacobian,
|
||||
const double* residuals,
|
||||
const double* step) const;
|
||||
void Init(const Minimizer::Options& options,
|
||||
double* parameters,
|
||||
Solver::Summary* solver_summary);
|
||||
bool IterationZero();
|
||||
bool FinalizeIterationAndCheckIfMinimizerCanContinue();
|
||||
bool ComputeTrustRegionStep();
|
||||
|
||||
bool EvaluateGradientAndJacobian();
|
||||
void ComputeCandidatePointAndEvaluateCost();
|
||||
|
||||
void DoLineSearch(const Vector& x,
|
||||
const Vector& gradient,
|
||||
const double cost,
|
||||
Vector* delta);
|
||||
void DoInnerIterationsIfNeeded();
|
||||
|
||||
bool ParameterToleranceReached();
|
||||
bool FunctionToleranceReached();
|
||||
bool GradientToleranceReached();
|
||||
bool MaxSolverTimeReached();
|
||||
bool MaxSolverIterationsReached();
|
||||
bool MinTrustRegionRadiusReached();
|
||||
|
||||
bool IsStepSuccessful();
|
||||
void HandleUnsuccessfulStep();
|
||||
bool HandleSuccessfulStep();
|
||||
bool HandleInvalidStep();
|
||||
|
||||
Minimizer::Options options_;
|
||||
|
||||
// These pointers are shortcuts to objects passed to the
|
||||
// TrustRegionMinimizer. The TrustRegionMinimizer does not own them.
|
||||
double* parameters_;
|
||||
Solver::Summary* solver_summary_;
|
||||
Evaluator* evaluator_;
|
||||
SparseMatrix* jacobian_;
|
||||
TrustRegionStrategy* strategy_;
|
||||
|
||||
scoped_ptr<TrustRegionStepEvaluator> step_evaluator_;
|
||||
|
||||
bool is_not_silent_;
|
||||
bool inner_iterations_are_enabled_;
|
||||
bool inner_iterations_were_useful_;
|
||||
|
||||
// Summary of the current iteration.
|
||||
IterationSummary iteration_summary_;
|
||||
|
||||
// Dimensionality of the problem in the ambient space.
|
||||
int num_parameters_;
|
||||
// Dimensionality of the problem in the tangent space. This is the
|
||||
// number of columns in the Jacobian.
|
||||
int num_effective_parameters_;
|
||||
// Length of the residual vector, also the number of rows in the Jacobian.
|
||||
int num_residuals_;
|
||||
|
||||
// Current point.
|
||||
Vector x_;
|
||||
// Residuals at x_;
|
||||
Vector residuals_;
|
||||
// Gradient at x_.
|
||||
Vector gradient_;
|
||||
// Solution computed by the inner iterations.
|
||||
Vector inner_iteration_x_;
|
||||
// model_residuals = J * trust_region_step
|
||||
Vector model_residuals_;
|
||||
Vector negative_gradient_;
|
||||
// projected_gradient_step = Plus(x, -gradient), an intermediate
|
||||
// quantity used to compute the projected gradient norm.
|
||||
Vector projected_gradient_step_;
|
||||
// The step computed by the trust region strategy. If Jacobi scaling
|
||||
// is enabled, this is a vector in the scaled space.
|
||||
Vector trust_region_step_;
|
||||
// The current proposal for how far the trust region algorithm
|
||||
// thinks we should move. In the most basic case, it is just the
|
||||
// trust_region_step_ with the Jacobi scaling undone. If bounds
|
||||
// constraints are present, then it is the result of the projected
|
||||
// line search.
|
||||
Vector delta_;
|
||||
// candidate_x = Plus(x, delta)
|
||||
Vector candidate_x_;
|
||||
// Scaling vector to scale the columns of the Jacobian.
|
||||
Vector jacobian_scaling_;
|
||||
|
||||
// Euclidean norm of x_.
|
||||
double x_norm_;
|
||||
// Cost at x_.
|
||||
double x_cost_;
|
||||
// Minimum cost encountered up till now.
|
||||
double minimum_cost_;
|
||||
// How much did the trust region strategy reduce the cost of the
|
||||
// linearized Gauss-Newton model.
|
||||
double model_cost_change_;
|
||||
// Cost at candidate_x_.
|
||||
double candidate_cost_;
|
||||
|
||||
// Time at which the minimizer was started.
|
||||
double start_time_in_secs_;
|
||||
// Time at which the current iteration was started.
|
||||
double iteration_start_time_in_secs_;
|
||||
// Number of consecutive steps where the minimizer loop computed a
|
||||
// numerically invalid step.
|
||||
int num_consecutive_invalid_steps_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ceres
|
||||
|
||||
#endif // CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_
|
||||
|
107
extern/ceres/internal/ceres/trust_region_step_evaluator.cc
vendored
Normal file
107
extern/ceres/internal/ceres/trust_region_step_evaluator.cc
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2016 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 <algorithm>
|
||||
#include "ceres/trust_region_step_evaluator.h"
|
||||
#include "glog/logging.h"
|
||||
|
||||
namespace ceres {
|
||||
namespace internal {
|
||||
|
||||
TrustRegionStepEvaluator::TrustRegionStepEvaluator(
|
||||
const double initial_cost,
|
||||
const int max_consecutive_nonmonotonic_steps)
|
||||
: max_consecutive_nonmonotonic_steps_(max_consecutive_nonmonotonic_steps),
|
||||
minimum_cost_(initial_cost),
|
||||
current_cost_(initial_cost),
|
||||
reference_cost_(initial_cost),
|
||||
candidate_cost_(initial_cost),
|
||||
accumulated_reference_model_cost_change_(0.0),
|
||||
accumulated_candidate_model_cost_change_(0.0),
|
||||
num_consecutive_nonmonotonic_steps_(0){
|
||||
}
|
||||
|
||||
double TrustRegionStepEvaluator::StepQuality(
|
||||
const double cost,
|
||||
const double model_cost_change) const {
|
||||
const double relative_decrease = (current_cost_ - cost) / model_cost_change;
|
||||
const double historical_relative_decrease =
|
||||
(reference_cost_ - cost) /
|
||||
(accumulated_reference_model_cost_change_ + model_cost_change);
|
||||
return std::max(relative_decrease, historical_relative_decrease);
|
||||
}
|
||||
|
||||
void TrustRegionStepEvaluator::StepAccepted(
|
||||
const double cost,
|
||||
const double model_cost_change) {
|
||||
// Algorithm 10.1.2 from Trust Region Methods by Conn, Gould &
|
||||
// Toint.
|
||||
//
|
||||
// Step 3a
|
||||
current_cost_ = cost;
|
||||
accumulated_candidate_model_cost_change_ += model_cost_change;
|
||||
accumulated_reference_model_cost_change_ += model_cost_change;
|
||||
|
||||
// Step 3b.
|
||||
if (current_cost_ < minimum_cost_) {
|
||||
minimum_cost_ = current_cost_;
|
||||
num_consecutive_nonmonotonic_steps_ = 0;
|
||||
candidate_cost_ = current_cost_;
|
||||
accumulated_candidate_model_cost_change_ = 0.0;
|
||||
} else {
|
||||
// Step 3c.
|
||||
++num_consecutive_nonmonotonic_steps_;
|
||||
if (current_cost_ > candidate_cost_) {
|
||||
candidate_cost_ = current_cost_;
|
||||
accumulated_candidate_model_cost_change_ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3d.
|
||||
//
|
||||
// At this point we have made too many non-monotonic steps and
|
||||
// we are going to reset the value of the reference iterate so
|
||||
// as to force the algorithm to descend.
|
||||
//
|
||||
// Note: In the original algorithm by Toint, this step was only
|
||||
// executed if the step was non-monotonic, but that would not handle
|
||||
// the case of max_consecutive_nonmonotonic_steps = 0. The small
|
||||
// modification of doing this always handles that corner case
|
||||
// correctly.
|
||||
if (num_consecutive_nonmonotonic_steps_ ==
|
||||
max_consecutive_nonmonotonic_steps_) {
|
||||
reference_cost_ = candidate_cost_;
|
||||
accumulated_reference_model_cost_change_ =
|
||||
accumulated_candidate_model_cost_change_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ceres
|
122
extern/ceres/internal/ceres/trust_region_step_evaluator.h
vendored
Normal file
122
extern/ceres/internal/ceres/trust_region_step_evaluator.h
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Ceres Solver - A fast non-linear least squares minimizer
|
||||
// Copyright 2016 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_TRUST_REGION_STEP_EVALUATOR_H_
|
||||
#define CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_
|
||||
|
||||
namespace ceres {
|
||||
namespace internal {
|
||||
|
||||
// The job of the TrustRegionStepEvaluator is to evaluate the quality
|
||||
// of a step, i.e., how the cost of a step compares with the reduction
|
||||
// in the objective of the trust region problem.
|
||||
//
|
||||
// Classic trust region methods are descent methods, in that they only
|
||||
// accept a point if it strictly reduces the value of the objective
|
||||
// function. They do this by measuring the quality of a step as
|
||||
//
|
||||
// cost_change / model_cost_change.
|
||||
//
|
||||
// Relaxing the monotonic descent requirement allows the algorithm to
|
||||
// be more efficient in the long term at the cost of some local
|
||||
// increase in the value of the objective function.
|
||||
//
|
||||
// This is because allowing for non-decreasing objective function
|
||||
// values in a principled manner allows the algorithm to "jump over
|
||||
// boulders" as the method is not restricted to move into narrow
|
||||
// valleys while preserving its convergence properties.
|
||||
//
|
||||
// The parameter max_consecutive_nonmonotonic_steps controls the
|
||||
// window size used by the step selection algorithm to accept
|
||||
// non-monotonic steps. Setting this parameter to zero, recovers the
|
||||
// classic montonic descent algorithm.
|
||||
//
|
||||
// Based on algorithm 10.1.2 (page 357) of "Trust Region
|
||||
// Methods" by Conn Gould & Toint, or equations 33-40 of
|
||||
// "Non-monotone trust-region algorithms for nonlinear
|
||||
// optimization subject to convex constraints" by Phil Toint,
|
||||
// Mathematical Programming, 77, 1997.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// TrustRegionStepEvaluator* step_evaluator = ...
|
||||
//
|
||||
// cost = ... // Compute the non-linear objective function value.
|
||||
// model_cost_change = ... // Change in the value of the trust region objective.
|
||||
// if (step_evaluator->StepQuality(cost, model_cost_change) > threshold) {
|
||||
// x = x + delta;
|
||||
// step_evaluator->StepAccepted(cost, model_cost_change);
|
||||
// }
|
||||
class TrustRegionStepEvaluator {
|
||||
public:
|
||||
// initial_cost is as the name implies the cost of the starting
|
||||
// state of the trust region minimizer.
|
||||
//
|
||||
// max_consecutive_nonmonotonic_steps controls the window size used
|
||||
// by the step selection algorithm to accept non-monotonic
|
||||
// steps. Setting this parameter to zero, recovers the classic
|
||||
// montonic descent algorithm.
|
||||
TrustRegionStepEvaluator(double initial_cost,
|
||||
int max_consecutive_nonmonotonic_steps);
|
||||
|
||||
// Return the quality of the step given its cost and the decrease in
|
||||
// the cost of the model. model_cost_change has to be positive.
|
||||
double StepQuality(double cost, double model_cost_change) const;
|
||||
|
||||
// Inform the step evaluator that a step with the given cost and
|
||||
// model_cost_change has been accepted by the trust region
|
||||
// minimizer.
|
||||
void StepAccepted(double cost, double model_cost_change);
|
||||
|
||||
private:
|
||||
const int max_consecutive_nonmonotonic_steps_;
|
||||
// The minimum cost encountered up till now.
|
||||
double minimum_cost_;
|
||||
// The current cost of the trust region minimizer as informed by the
|
||||
// last call to StepAccepted.
|
||||
double current_cost_;
|
||||
double reference_cost_;
|
||||
double candidate_cost_;
|
||||
// Accumulated model cost since the last time the reference model
|
||||
// cost was updated, i.e., when a step with cost less than the
|
||||
// current known minimum cost is accepted.
|
||||
double accumulated_reference_model_cost_change_;
|
||||
// Accumulated model cost since the last time the candidate model
|
||||
// cost was updated, i.e., a non-monotonic step was taken with a
|
||||
// cost that was greater than the current candidate cost.
|
||||
double accumulated_candidate_model_cost_change_;
|
||||
// Number of steps taken since the last time minimum_cost was updated.
|
||||
int num_consecutive_nonmonotonic_steps_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ceres
|
||||
|
||||
#endif // CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_
|
@@ -86,20 +86,20 @@ class TrustRegionStrategy {
|
||||
struct PerSolveOptions {
|
||||
PerSolveOptions()
|
||||
: eta(0),
|
||||
dump_filename_base(""),
|
||||
dump_format_type(TEXTFILE) {
|
||||
}
|
||||
|
||||
// Forcing sequence for inexact solves.
|
||||
double eta;
|
||||
|
||||
DumpFormatType dump_format_type;
|
||||
|
||||
// If non-empty and dump_format_type is not CONSOLE, the trust
|
||||
// regions strategy will write the linear system to file(s) with
|
||||
// name starting with dump_filename_base. If dump_format_type is
|
||||
// CONSOLE then dump_filename_base will be ignored and the linear
|
||||
// system will be written to the standard error.
|
||||
std::string dump_filename_base;
|
||||
DumpFormatType dump_format_type;
|
||||
};
|
||||
|
||||
struct Summary {
|
||||
|
3
extern/clew/include/clew.h
vendored
3
extern/clew/include/clew.h
vendored
@@ -369,7 +369,8 @@ typedef unsigned int cl_GLenum;
|
||||
#endif
|
||||
|
||||
/* Define basic vector types */
|
||||
#if defined( __VEC__ )
|
||||
/* WOrkaround for ppc64el platform: conflicts with bool from C++. */
|
||||
#if defined( __VEC__ ) && !(defined(__PPC64__) && defined(__LITTLE_ENDIAN__))
|
||||
#include <altivec.h> /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */
|
||||
typedef vector unsigned char __cl_uchar16;
|
||||
typedef vector signed char __cl_char16;
|
||||
|
3
extern/glog/src/raw_logging.cc
vendored
3
extern/glog/src/raw_logging.cc
vendored
@@ -59,7 +59,8 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)
|
||||
// Hurd does not have SYS_write.
|
||||
#if (defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)) && !defined(__GNU__)
|
||||
# define safe_write(fd, s, len) syscall(SYS_write, fd, s, len)
|
||||
#else
|
||||
// Not so safe, but what can you do?
|
||||
|
2
extern/glog/src/stacktrace_powerpc-inl.h
vendored
2
extern/glog/src/stacktrace_powerpc-inl.h
vendored
@@ -111,7 +111,7 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
||||
result[n++] = *(sp+2);
|
||||
#elif defined(_CALL_SYSV)
|
||||
result[n++] = *(sp+1);
|
||||
#elif defined(__APPLE__) || (defined(__linux) && defined(__PPC64__))
|
||||
#elif defined(__APPLE__) || ((defined(__linux) || defined(__linux__)) && defined(__PPC64__))
|
||||
// This check is in case the compiler doesn't define _CALL_AIX/etc.
|
||||
result[n++] = *(sp+2);
|
||||
#elif defined(__linux)
|
||||
|
6
extern/rangetree/CMakeLists.txt
vendored
6
extern/rangetree/CMakeLists.txt
vendored
@@ -21,10 +21,10 @@ set(INC
|
||||
)
|
||||
|
||||
set(SRC
|
||||
range_tree.hh
|
||||
range_tree_c_api.h
|
||||
range_tree.h
|
||||
intern/generic_alloc_impl.h
|
||||
|
||||
range_tree_c_api.cc
|
||||
intern/range_tree.c
|
||||
)
|
||||
|
||||
blender_add_lib(extern_rangetree "${SRC}" "${INC}" "")
|
||||
|
6
extern/rangetree/README.blender
vendored
6
extern/rangetree/README.blender
vendored
@@ -1,5 +1,5 @@
|
||||
Project: RangeTree
|
||||
URL: https://github.com/nicholasbishop/RangeTree
|
||||
License: GPLv2+
|
||||
Upstream version: c4ecf6bb7dfd
|
||||
URL: https://github.com/ideasman42/rangetree-c
|
||||
License: Apache 2.0
|
||||
Upstream version: 40ebed8aa209
|
||||
Local modifications: None
|
||||
|
13
extern/rangetree/README.org
vendored
13
extern/rangetree/README.org
vendored
@@ -1,13 +0,0 @@
|
||||
* Overview
|
||||
Basic class for storing non-overlapping scalar ranges. Underlying
|
||||
representation is a C++ STL set for fast lookups.
|
||||
|
||||
* License
|
||||
GPL version 2 or later (see COPYING)
|
||||
|
||||
* Author Note
|
||||
This implementation is intended for storing free unique IDs in a new
|
||||
undo system for BMesh in Blender, but could be useful elsewhere.
|
||||
|
||||
* Website
|
||||
https://github.com/nicholasbishop/RangeTree
|
215
extern/rangetree/intern/generic_alloc_impl.h
vendored
Normal file
215
extern/rangetree/intern/generic_alloc_impl.h
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Blender Foundation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "Apache License")
|
||||
* with the following modification; you may not use this file except in
|
||||
* compliance with the Apache License and the following modification to it:
|
||||
* Section 6. Trademarks. is deleted and replaced with:
|
||||
*
|
||||
* 6. Trademarks. This License does not grant permission to use the trade
|
||||
* names, trademarks, service marks, or product names of the Licensor
|
||||
* and its affiliates, except as required to comply with Section 4(c) of
|
||||
* the License and to reproduce the content of the NOTICE file.
|
||||
*
|
||||
* You may obtain a copy of the Apache License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Apache License with the above modification is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the Apache License for the specific
|
||||
* language governing permissions and limitations under the Apache License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple Memory Chunking Allocator
|
||||
* ================================
|
||||
*
|
||||
* Defines need to be set:
|
||||
* - #TPOOL_IMPL_PREFIX: Prefix to use for the API.
|
||||
* - #TPOOL_ALLOC_TYPE: Struct type this pool handles.
|
||||
* - #TPOOL_STRUCT: Name for pool struct name.
|
||||
* - #TPOOL_CHUNK_SIZE: Chunk size (optional), use 64kb when not defined.
|
||||
*
|
||||
* \note #TPOOL_ALLOC_TYPE must be at least ``sizeof(void *)``.
|
||||
*
|
||||
* Defines the API, uses #TPOOL_IMPL_PREFIX to prefix each function.
|
||||
*
|
||||
* - *_pool_create()
|
||||
* - *_pool_destroy()
|
||||
* - *_pool_clear()
|
||||
*
|
||||
* - *_pool_elem_alloc()
|
||||
* - *_pool_elem_calloc()
|
||||
* - *_pool_elem_free()
|
||||
*/
|
||||
|
||||
/* check we're not building directly */
|
||||
#if !defined(TPOOL_IMPL_PREFIX) || \
|
||||
!defined(TPOOL_ALLOC_TYPE) || \
|
||||
!defined(TPOOL_STRUCT)
|
||||
# error "This file can't be compiled directly, include in another source file"
|
||||
#endif
|
||||
|
||||
#define _CONCAT_AUX(MACRO_ARG1, MACRO_ARG2) MACRO_ARG1 ## MACRO_ARG2
|
||||
#define _CONCAT(MACRO_ARG1, MACRO_ARG2) _CONCAT_AUX(MACRO_ARG1, MACRO_ARG2)
|
||||
#define _TPOOL_PREFIX(id) _CONCAT(TPOOL_IMPL_PREFIX, _##id)
|
||||
|
||||
/* local identifiers */
|
||||
#define pool_create _TPOOL_PREFIX(pool_create)
|
||||
#define pool_destroy _TPOOL_PREFIX(pool_destroy)
|
||||
#define pool_clear _TPOOL_PREFIX(pool_clear)
|
||||
|
||||
#define pool_elem_alloc _TPOOL_PREFIX(pool_elem_alloc)
|
||||
#define pool_elem_calloc _TPOOL_PREFIX(pool_elem_calloc)
|
||||
#define pool_elem_free _TPOOL_PREFIX(pool_elem_free)
|
||||
|
||||
/* private identifiers (only for this file, undefine after) */
|
||||
#define pool_alloc_chunk _TPOOL_PREFIX(pool_alloc_chunk)
|
||||
#define TPoolChunk _TPOOL_PREFIX(TPoolChunk)
|
||||
#define TPoolChunkElemFree _TPOOL_PREFIX(TPoolChunkElemFree)
|
||||
|
||||
#ifndef TPOOL_CHUNK_SIZE
|
||||
#define TPOOL_CHUNK_SIZE (1 << 16) /* 64kb */
|
||||
#define _TPOOL_CHUNK_SIZE_UNDEF
|
||||
#endif
|
||||
|
||||
#ifndef UNLIKELY
|
||||
# ifdef __GNUC__
|
||||
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# else
|
||||
# define UNLIKELY(x) (x)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define MAYBE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
# define MAYBE_UNUSED
|
||||
#endif
|
||||
|
||||
|
||||
struct TPoolChunk {
|
||||
struct TPoolChunk *prev;
|
||||
unsigned int size;
|
||||
unsigned int bufsize;
|
||||
TPOOL_ALLOC_TYPE buf[0];
|
||||
};
|
||||
|
||||
struct TPoolChunkElemFree {
|
||||
struct TPoolChunkElemFree *next;
|
||||
};
|
||||
|
||||
struct TPOOL_STRUCT {
|
||||
/* Always keep at least one chunk (never NULL) */
|
||||
struct TPoolChunk *chunk;
|
||||
/* when NULL, allocate a new chunk */
|
||||
struct TPoolChunkElemFree *free;
|
||||
};
|
||||
|
||||
/**
|
||||
* Number of elems to include per #TPoolChunk when no reserved size is passed,
|
||||
* or we allocate past the reserved number.
|
||||
*
|
||||
* \note Optimize number for 64kb allocs.
|
||||
*/
|
||||
#define _TPOOL_CHUNK_DEFAULT_NUM \
|
||||
(((1 << 16) - sizeof(struct TPoolChunk)) / sizeof(TPOOL_ALLOC_TYPE))
|
||||
|
||||
|
||||
/** \name Internal Memory Management
|
||||
* \{ */
|
||||
|
||||
static struct TPoolChunk *pool_alloc_chunk(
|
||||
unsigned int tot_elems, struct TPoolChunk *chunk_prev)
|
||||
{
|
||||
struct TPoolChunk *chunk = malloc(
|
||||
sizeof(struct TPoolChunk) + (sizeof(TPOOL_ALLOC_TYPE) * tot_elems));
|
||||
chunk->prev = chunk_prev;
|
||||
chunk->bufsize = tot_elems;
|
||||
chunk->size = 0;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static TPOOL_ALLOC_TYPE *pool_elem_alloc(struct TPOOL_STRUCT *pool)
|
||||
{
|
||||
TPOOL_ALLOC_TYPE *elem;
|
||||
|
||||
if (pool->free) {
|
||||
elem = (TPOOL_ALLOC_TYPE *)pool->free;
|
||||
pool->free = pool->free->next;
|
||||
}
|
||||
else {
|
||||
struct TPoolChunk *chunk = pool->chunk;
|
||||
if (UNLIKELY(chunk->size == chunk->bufsize)) {
|
||||
chunk = pool->chunk = pool_alloc_chunk(_TPOOL_CHUNK_DEFAULT_NUM, chunk);
|
||||
}
|
||||
elem = &chunk->buf[chunk->size++];
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
MAYBE_UNUSED
|
||||
static TPOOL_ALLOC_TYPE *pool_elem_calloc(struct TPOOL_STRUCT *pool)
|
||||
{
|
||||
TPOOL_ALLOC_TYPE *elem = pool_elem_alloc(pool);
|
||||
memset(elem, 0, sizeof(*elem));
|
||||
return elem;
|
||||
}
|
||||
|
||||
static void pool_elem_free(struct TPOOL_STRUCT *pool, TPOOL_ALLOC_TYPE *elem)
|
||||
{
|
||||
struct TPoolChunkElemFree *elem_free = (struct TPoolChunkElemFree *)elem;
|
||||
elem_free->next = pool->free;
|
||||
pool->free = elem_free;
|
||||
}
|
||||
|
||||
static void pool_create(struct TPOOL_STRUCT *pool, unsigned int tot_reserve)
|
||||
{
|
||||
pool->chunk = pool_alloc_chunk((tot_reserve > 1) ? tot_reserve : _TPOOL_CHUNK_DEFAULT_NUM, NULL);
|
||||
pool->free = NULL;
|
||||
}
|
||||
|
||||
MAYBE_UNUSED
|
||||
static void pool_clear(struct TPOOL_STRUCT *pool)
|
||||
{
|
||||
/* Remove all except the last chunk */
|
||||
while (pool->chunk->prev) {
|
||||
struct TPoolChunk *chunk_prev = pool->chunk->prev;
|
||||
free(pool->chunk);
|
||||
pool->chunk = chunk_prev;
|
||||
}
|
||||
pool->chunk->size = 0;
|
||||
pool->free = NULL;
|
||||
}
|
||||
|
||||
static void pool_destroy(struct TPOOL_STRUCT *pool)
|
||||
{
|
||||
struct TPoolChunk *chunk = pool->chunk;
|
||||
do {
|
||||
struct TPoolChunk *chunk_prev;
|
||||
chunk_prev = chunk->prev;
|
||||
free(chunk);
|
||||
chunk = chunk_prev;
|
||||
} while (chunk);
|
||||
|
||||
pool->chunk = NULL;
|
||||
pool->free = NULL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef _TPOOL_CHUNK_DEFAULT_NUM
|
||||
#undef _CONCAT_AUX
|
||||
#undef _CONCAT
|
||||
#undef _TPOOL_PREFIX
|
||||
|
||||
#undef TPoolChunk
|
||||
#undef TPoolChunkElemFree
|
||||
|
||||
#ifdef _TPOOL_CHUNK_SIZE_UNDEF
|
||||
# undef TPOOL_CHUNK_SIZE
|
||||
# undef _TPOOL_CHUNK_SIZE_UNDEF
|
||||
#endif
|
873
extern/rangetree/intern/range_tree.c
vendored
Normal file
873
extern/rangetree/intern/range_tree.c
vendored
Normal file
@@ -0,0 +1,873 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Campbell Barton.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "Apache License")
|
||||
* with the following modification; you may not use this file except in
|
||||
* compliance with the Apache License and the following modification to it:
|
||||
* Section 6. Trademarks. is deleted and replaced with:
|
||||
*
|
||||
* 6. Trademarks. This License does not grant permission to use the trade
|
||||
* names, trademarks, service marks, or product names of the Licensor
|
||||
* and its affiliates, except as required to comply with Section 4(c) of
|
||||
* the License and to reproduce the content of the NOTICE file.
|
||||
*
|
||||
* You may obtain a copy of the Apache License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Apache License with the above modification is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the Apache License for the specific
|
||||
* language governing permissions and limitations under the Apache License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "range_tree.h"
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
/* Use binary-tree for lookups, else fallback to full search */
|
||||
#define USE_BTREE
|
||||
/* Use memory pool for nodes, else do individual allocations */
|
||||
#define USE_TPOOL
|
||||
|
||||
/* Node representing a range in the RangeTreeUInt. */
|
||||
typedef struct Node {
|
||||
struct Node *next, *prev;
|
||||
|
||||
/* range (inclusive) */
|
||||
uint min, max;
|
||||
|
||||
#ifdef USE_BTREE
|
||||
/* Left leaning red-black tree, for reference implementation see:
|
||||
* https://gitlab.com/ideasman42/btree-mini-py */
|
||||
struct Node *left, *right;
|
||||
/* RED/BLACK */
|
||||
bool color;
|
||||
#endif
|
||||
} Node;
|
||||
|
||||
#ifdef USE_TPOOL
|
||||
/* rt_pool_* pool allocator */
|
||||
#define TPOOL_IMPL_PREFIX rt_node
|
||||
#define TPOOL_ALLOC_TYPE Node
|
||||
#define TPOOL_STRUCT ElemPool_Node
|
||||
#include "generic_alloc_impl.h"
|
||||
#undef TPOOL_IMPL_PREFIX
|
||||
#undef TPOOL_ALLOC_TYPE
|
||||
#undef TPOOL_STRUCT
|
||||
#endif /* USE_TPOOL */
|
||||
|
||||
typedef struct LinkedList {
|
||||
Node *first, *last;
|
||||
} LinkedList;
|
||||
|
||||
typedef struct RangeTreeUInt {
|
||||
uint range[2];
|
||||
LinkedList list;
|
||||
#ifdef USE_BTREE
|
||||
Node *root;
|
||||
#endif
|
||||
#ifdef USE_TPOOL
|
||||
struct ElemPool_Node epool;
|
||||
#endif
|
||||
} RangeTreeUInt;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* List API */
|
||||
|
||||
static void list_push_front(LinkedList *list, Node *node)
|
||||
{
|
||||
if (list->first != NULL) {
|
||||
node->next = list->first;
|
||||
node->next->prev = node;
|
||||
node->prev = NULL;
|
||||
}
|
||||
else {
|
||||
list->last = node;
|
||||
}
|
||||
list->first = node;
|
||||
}
|
||||
|
||||
static void list_push_back(LinkedList *list, Node *node)
|
||||
{
|
||||
if (list->first != NULL) {
|
||||
node->prev = list->last;
|
||||
node->prev->next = node;
|
||||
node->next = NULL;
|
||||
}
|
||||
else {
|
||||
list->first = node;
|
||||
}
|
||||
list->last = node;
|
||||
}
|
||||
|
||||
static void list_push_after(LinkedList *list, Node *node_prev, Node *node_new)
|
||||
{
|
||||
/* node_new before node_next */
|
||||
|
||||
/* empty list */
|
||||
if (list->first == NULL) {
|
||||
list->first = node_new;
|
||||
list->last = node_new;
|
||||
return;
|
||||
}
|
||||
|
||||
/* insert at head of list */
|
||||
if (node_prev == NULL) {
|
||||
node_new->prev = NULL;
|
||||
node_new->next = list->first;
|
||||
node_new->next->prev = node_new;
|
||||
list->first = node_new;
|
||||
return;
|
||||
}
|
||||
|
||||
/* at end of list */
|
||||
if (list->last == node_prev) {
|
||||
list->last = node_new;
|
||||
}
|
||||
|
||||
node_new->next = node_prev->next;
|
||||
node_new->prev = node_prev;
|
||||
node_prev->next = node_new;
|
||||
if (node_new->next) {
|
||||
node_new->next->prev = node_new;
|
||||
}
|
||||
}
|
||||
|
||||
static void list_push_before(LinkedList *list, Node *node_next, Node *node_new)
|
||||
{
|
||||
/* node_new before node_next */
|
||||
|
||||
/* empty list */
|
||||
if (list->first == NULL) {
|
||||
list->first = node_new;
|
||||
list->last = node_new;
|
||||
return;
|
||||
}
|
||||
|
||||
/* insert at end of list */
|
||||
if (node_next == NULL) {
|
||||
node_new->prev = list->last;
|
||||
node_new->next = NULL;
|
||||
list->last->next = node_new;
|
||||
list->last = node_new;
|
||||
return;
|
||||
}
|
||||
|
||||
/* at beginning of list */
|
||||
if (list->first == node_next) {
|
||||
list->first = node_new;
|
||||
}
|
||||
|
||||
node_new->next = node_next;
|
||||
node_new->prev = node_next->prev;
|
||||
node_next->prev = node_new;
|
||||
if (node_new->prev) {
|
||||
node_new->prev->next = node_new;
|
||||
}
|
||||
}
|
||||
|
||||
static void list_remove(LinkedList *list, Node *node)
|
||||
{
|
||||
if (node->next != NULL) {
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
if (node->prev != NULL) {
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
|
||||
if (list->last == node) {
|
||||
list->last = node->prev;
|
||||
}
|
||||
if (list->first == node) {
|
||||
list->first = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void list_clear(LinkedList *list)
|
||||
{
|
||||
list->first = NULL;
|
||||
list->last = NULL;
|
||||
}
|
||||
|
||||
/* end list API */
|
||||
|
||||
|
||||
/* forward declarations */
|
||||
static void rt_node_free(RangeTreeUInt *rt, Node *node);
|
||||
|
||||
|
||||
#ifdef USE_BTREE
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool rb_is_balanced_root(const Node *root);
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Internal BTree API
|
||||
*
|
||||
* Left-leaning red-black tree.
|
||||
*/
|
||||
|
||||
/* use minimum, could use max too since nodes never overlap */
|
||||
#define KEY(n) ((n)->min)
|
||||
|
||||
enum {
|
||||
RED = 0,
|
||||
BLACK = 1,
|
||||
};
|
||||
|
||||
|
||||
static bool is_red(const Node *node)
|
||||
{
|
||||
return (node && (node->color == RED));
|
||||
}
|
||||
|
||||
static int key_cmp(uint key1, uint key2)
|
||||
{
|
||||
return (key1 == key2) ? 0 : ((key1 < key2) ? -1 : 1);
|
||||
}
|
||||
|
||||
/* removed from the tree */
|
||||
static void rb_node_invalidate(Node *node)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
node->color = false;
|
||||
#else
|
||||
(void)node;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rb_flip_color(Node *node)
|
||||
{
|
||||
node->color ^= 1;
|
||||
node->left->color ^= 1;
|
||||
node->right->color ^= 1;
|
||||
}
|
||||
|
||||
static Node *rb_rotate_left(Node *left)
|
||||
{
|
||||
/* Make a right-leaning 3-node lean to the left. */
|
||||
Node *right = left->right;
|
||||
left->right = right->left;
|
||||
right->left = left;
|
||||
right->color = left->color;
|
||||
left->color = RED;
|
||||
return right;
|
||||
}
|
||||
|
||||
static Node *rb_rotate_right(Node *right)
|
||||
{
|
||||
/* Make a left-leaning 3-node lean to the right. */
|
||||
Node *left = right->left;
|
||||
right->left = left->right;
|
||||
left->right = right;
|
||||
left->color = right->color;
|
||||
right->color = RED;
|
||||
return left;
|
||||
}
|
||||
|
||||
/* Fixup colors when insert happened */
|
||||
static Node *rb_fixup_insert(Node *node)
|
||||
{
|
||||
if (is_red(node->right) && !is_red(node->left)) {
|
||||
node = rb_rotate_left(node);
|
||||
}
|
||||
if (is_red(node->left) && is_red(node->left->left)) {
|
||||
node = rb_rotate_right(node);
|
||||
}
|
||||
|
||||
if (is_red(node->left) && is_red(node->right)) {
|
||||
rb_flip_color(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static Node *rb_insert_recursive(Node *node, Node *node_to_insert)
|
||||
{
|
||||
if (node == NULL) {
|
||||
return node_to_insert;
|
||||
}
|
||||
|
||||
const int cmp = key_cmp(KEY(node_to_insert), KEY(node));
|
||||
if (cmp == 0) {
|
||||
/* caller ensures no collisions */
|
||||
assert(0);
|
||||
}
|
||||
else if (cmp == -1) {
|
||||
node->left = rb_insert_recursive(node->left, node_to_insert);
|
||||
}
|
||||
else {
|
||||
node->right = rb_insert_recursive(node->right, node_to_insert);
|
||||
}
|
||||
|
||||
return rb_fixup_insert(node);
|
||||
}
|
||||
|
||||
static Node *rb_insert_root(Node *root, Node *node_to_insert)
|
||||
{
|
||||
root = rb_insert_recursive(root, node_to_insert);
|
||||
root->color = BLACK;
|
||||
return root;
|
||||
}
|
||||
|
||||
static Node *rb_move_red_to_left(Node *node)
|
||||
{
|
||||
/* Assuming that h is red and both h->left and h->left->left
|
||||
* are black, make h->left or one of its children red.
|
||||
*/
|
||||
rb_flip_color(node);
|
||||
if (node->right && is_red(node->right->left)) {
|
||||
node->right = rb_rotate_right(node->right);
|
||||
node = rb_rotate_left(node);
|
||||
rb_flip_color(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static Node *rb_move_red_to_right(Node *node)
|
||||
{
|
||||
/* Assuming that h is red and both h->right and h->right->left
|
||||
* are black, make h->right or one of its children red.
|
||||
*/
|
||||
rb_flip_color(node);
|
||||
if (node->left && is_red(node->left->left)) {
|
||||
node = rb_rotate_right(node);
|
||||
rb_flip_color(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Fixup colors when remove happened */
|
||||
static Node *rb_fixup_remove(Node *node)
|
||||
{
|
||||
if (is_red(node->right)) {
|
||||
node = rb_rotate_left(node);
|
||||
}
|
||||
if (is_red(node->left) && is_red(node->left->left)) {
|
||||
node = rb_rotate_right(node);
|
||||
}
|
||||
if (is_red(node->left) && is_red(node->right)) {
|
||||
rb_flip_color(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static Node *rb_pop_min_recursive(Node *node, Node **r_node_pop)
|
||||
{
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (node->left == NULL) {
|
||||
rb_node_invalidate(node);
|
||||
*r_node_pop = node;
|
||||
return NULL;
|
||||
}
|
||||
if ((!is_red(node->left)) && (!is_red(node->left->left))) {
|
||||
node = rb_move_red_to_left(node);
|
||||
}
|
||||
node->left = rb_pop_min_recursive(node->left, r_node_pop);
|
||||
return rb_fixup_remove(node);
|
||||
}
|
||||
|
||||
static Node *rb_remove_recursive(Node *node, const Node *node_to_remove)
|
||||
{
|
||||
if (node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (key_cmp(KEY(node_to_remove), KEY(node)) == -1) {
|
||||
if (node->left != NULL) {
|
||||
if ((!is_red(node->left)) && (!is_red(node->left->left))) {
|
||||
node = rb_move_red_to_left(node);
|
||||
}
|
||||
}
|
||||
node->left = rb_remove_recursive(node->left, node_to_remove);
|
||||
}
|
||||
else {
|
||||
if (is_red(node->left)) {
|
||||
node = rb_rotate_right(node);
|
||||
}
|
||||
if ((node == node_to_remove) && (node->right == NULL)) {
|
||||
rb_node_invalidate(node);
|
||||
return NULL;
|
||||
}
|
||||
assert(node->right != NULL);
|
||||
if ((!is_red(node->right)) && (!is_red(node->right->left))) {
|
||||
node = rb_move_red_to_right(node);
|
||||
}
|
||||
|
||||
if (node == node_to_remove) {
|
||||
/* minor improvement over original method:
|
||||
* no need to double lookup min */
|
||||
Node *node_free; /* will always be set */
|
||||
node->right = rb_pop_min_recursive(node->right, &node_free);
|
||||
|
||||
node_free->left = node->left;
|
||||
node_free->right = node->right;
|
||||
node_free->color = node->color;
|
||||
|
||||
rb_node_invalidate(node);
|
||||
node = node_free;
|
||||
}
|
||||
else {
|
||||
node->right = rb_remove_recursive(node->right, node_to_remove);
|
||||
}
|
||||
}
|
||||
return rb_fixup_remove(node);
|
||||
}
|
||||
|
||||
static Node *rb_btree_remove(Node *root, const Node *node_to_remove)
|
||||
{
|
||||
root = rb_remove_recursive(root, node_to_remove);
|
||||
if (root != NULL) {
|
||||
root->color = BLACK;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the node closest to and including 'key',
|
||||
* excluding anything below.
|
||||
*/
|
||||
static Node *rb_get_or_upper_recursive(Node *n, const uint key)
|
||||
{
|
||||
if (n == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
const int cmp_upper = key_cmp(KEY(n), key);
|
||||
if (cmp_upper == 0) {
|
||||
return n; // exact match
|
||||
}
|
||||
else if (cmp_upper == 1) {
|
||||
assert(KEY(n) >= key);
|
||||
Node *n_test = rb_get_or_upper_recursive(n->left, key);
|
||||
return n_test ? n_test : n;
|
||||
}
|
||||
else { // cmp_upper == -1
|
||||
return rb_get_or_upper_recursive(n->right, key);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the node closest to and including 'key',
|
||||
* excluding anything above.
|
||||
*/
|
||||
static Node *rb_get_or_lower_recursive(Node *n, const uint key)
|
||||
{
|
||||
if (n == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
const int cmp_lower = key_cmp(KEY(n), key);
|
||||
if (cmp_lower == 0) {
|
||||
return n; // exact match
|
||||
}
|
||||
else if (cmp_lower == -1) {
|
||||
assert(KEY(n) <= key);
|
||||
Node *n_test = rb_get_or_lower_recursive(n->right, key);
|
||||
return n_test ? n_test : n;
|
||||
}
|
||||
else { // cmp_lower == 1
|
||||
return rb_get_or_lower_recursive(n->left, key);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
static bool rb_is_balanced_recursive(const Node *node, int black)
|
||||
{
|
||||
// Does every path from the root to a leaf have the given number
|
||||
// of black links?
|
||||
if (node == NULL) {
|
||||
return black == 0;
|
||||
}
|
||||
if (!is_red(node)) {
|
||||
black--;
|
||||
}
|
||||
return rb_is_balanced_recursive(node->left, black) &&
|
||||
rb_is_balanced_recursive(node->right, black);
|
||||
}
|
||||
|
||||
static bool rb_is_balanced_root(const Node *root)
|
||||
{
|
||||
// Do all paths from root to leaf have same number of black edges?
|
||||
int black = 0; // number of black links on path from root to min
|
||||
const Node *node = root;
|
||||
while (node != NULL) {
|
||||
if (!is_red(node)) {
|
||||
black++;
|
||||
}
|
||||
node = node->left;
|
||||
}
|
||||
return rb_is_balanced_recursive(root, black);
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
/* End BTree API */
|
||||
#endif // USE_BTREE
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Internal RangeTreeUInt API */
|
||||
|
||||
#ifdef _WIN32
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
static inline Node *rt_node_alloc(RangeTreeUInt *rt)
|
||||
{
|
||||
#ifdef USE_TPOOL
|
||||
return rt_node_pool_elem_alloc(&rt->epool);
|
||||
#else
|
||||
(void)rt;
|
||||
return malloc(sizeof(Node));
|
||||
#endif
|
||||
}
|
||||
|
||||
static Node *rt_node_new(RangeTreeUInt *rt, uint min, uint max)
|
||||
{
|
||||
Node *node = rt_node_alloc(rt);
|
||||
|
||||
assert(min <= max);
|
||||
node->prev = NULL;
|
||||
node->next = NULL;
|
||||
node->min = min;
|
||||
node->max = max;
|
||||
#ifdef USE_BTREE
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
#endif
|
||||
return node;
|
||||
}
|
||||
|
||||
static void rt_node_free(RangeTreeUInt *rt, Node *node)
|
||||
{
|
||||
#ifdef USE_TPOOL
|
||||
rt_node_pool_elem_free(&rt->epool, node);
|
||||
#else
|
||||
(void)rt;
|
||||
free(node);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_BTREE
|
||||
static void rt_btree_insert(RangeTreeUInt *rt, Node *node)
|
||||
{
|
||||
node->color = RED;
|
||||
node->left = NULL;
|
||||
node->right = NULL;
|
||||
rt->root = rb_insert_root(rt->root, node);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void rt_node_add_back(RangeTreeUInt *rt, Node *node)
|
||||
{
|
||||
list_push_back(&rt->list, node);
|
||||
#ifdef USE_BTREE
|
||||
rt_btree_insert(rt, node);
|
||||
#endif
|
||||
}
|
||||
static void rt_node_add_front(RangeTreeUInt *rt, Node *node)
|
||||
{
|
||||
list_push_front(&rt->list, node);
|
||||
#ifdef USE_BTREE
|
||||
rt_btree_insert(rt, node);
|
||||
#endif
|
||||
}
|
||||
static void rt_node_add_before(RangeTreeUInt *rt, Node *node_next, Node *node)
|
||||
{
|
||||
list_push_before(&rt->list, node_next, node);
|
||||
#ifdef USE_BTREE
|
||||
rt_btree_insert(rt, node);
|
||||
#endif
|
||||
}
|
||||
static void rt_node_add_after(RangeTreeUInt *rt, Node *node_prev, Node *node)
|
||||
{
|
||||
list_push_after(&rt->list, node_prev, node);
|
||||
#ifdef USE_BTREE
|
||||
rt_btree_insert(rt, node);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rt_node_remove(RangeTreeUInt *rt, Node *node)
|
||||
{
|
||||
list_remove(&rt->list, node);
|
||||
#ifdef USE_BTREE
|
||||
rt->root = rb_btree_remove(rt->root, node);
|
||||
#endif
|
||||
rt_node_free(rt, node);
|
||||
}
|
||||
|
||||
static Node *rt_find_node_from_value(RangeTreeUInt *rt, const uint value)
|
||||
{
|
||||
#ifdef USE_BTREE
|
||||
Node *node = rb_get_or_lower_recursive(rt->root, value);
|
||||
if (node != NULL) {
|
||||
if ((value >= node->min) && (value <= node->max)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
#else
|
||||
for (Node *node = rt->list.first; node; node = node->next) {
|
||||
if ((value >= node->min) && (value <= node->max)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
#endif // USE_BTREE
|
||||
}
|
||||
|
||||
static void rt_find_node_pair_around_value(RangeTreeUInt *rt, const uint value,
|
||||
Node **r_node_prev, Node **r_node_next)
|
||||
{
|
||||
if (value < rt->list.first->min) {
|
||||
*r_node_prev = NULL;
|
||||
*r_node_next = rt->list.first;
|
||||
return;
|
||||
}
|
||||
else if (value > rt->list.last->max) {
|
||||
*r_node_prev = rt->list.last;
|
||||
*r_node_next = NULL;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
#ifdef USE_BTREE
|
||||
Node *node_next = rb_get_or_upper_recursive(rt->root, value);
|
||||
if (node_next != NULL) {
|
||||
Node *node_prev = node_next->prev;
|
||||
if ((node_prev->max < value) && (value < node_next->min)) {
|
||||
*r_node_prev = node_prev;
|
||||
*r_node_next = node_next;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
Node *node_prev = rt->list.first;
|
||||
Node *node_next;
|
||||
while ((node_next = node_prev->next)) {
|
||||
if ((node_prev->max < value) && (value < node_next->min)) {
|
||||
*r_node_prev = node_prev;
|
||||
*r_node_next = node_next;
|
||||
return;
|
||||
}
|
||||
node_prev = node_next;
|
||||
}
|
||||
#endif // USE_BTREE
|
||||
}
|
||||
*r_node_prev = NULL;
|
||||
*r_node_next = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Public API */
|
||||
|
||||
static RangeTreeUInt *rt_create_empty(uint min, uint max)
|
||||
{
|
||||
RangeTreeUInt *rt = malloc(sizeof(*rt));
|
||||
rt->range[0] = min;
|
||||
rt->range[1] = max;
|
||||
|
||||
list_clear(&rt->list);
|
||||
|
||||
#ifdef USE_BTREE
|
||||
rt->root = NULL;
|
||||
#endif
|
||||
#ifdef USE_TPOOL
|
||||
rt_node_pool_create(&rt->epool, 512);
|
||||
#endif
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
RangeTreeUInt *range_tree_uint_alloc(uint min, uint max)
|
||||
{
|
||||
RangeTreeUInt *rt = rt_create_empty(min, max);
|
||||
|
||||
Node *node = rt_node_new(rt, min, max);
|
||||
rt_node_add_front(rt, node);
|
||||
return rt;
|
||||
}
|
||||
|
||||
void range_tree_uint_free(RangeTreeUInt *rt)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#ifdef USE_BTREE
|
||||
assert(rb_is_balanced_root(rt->root));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_TPOOL
|
||||
|
||||
rt_node_pool_destroy(&rt->epool);
|
||||
#else
|
||||
for (Node *node = rt->list.first, *node_next; node; node = node_next) {
|
||||
node_next = node->next;
|
||||
rt_node_free(rt, node);
|
||||
}
|
||||
#endif
|
||||
|
||||
free(rt);
|
||||
}
|
||||
|
||||
#ifdef USE_BTREE
|
||||
static Node *rt_copy_recursive(RangeTreeUInt *rt_dst, const Node *node_src)
|
||||
{
|
||||
if (node_src == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node *node_dst = rt_node_alloc(rt_dst);
|
||||
|
||||
*node_dst = *node_src;
|
||||
node_dst->left = rt_copy_recursive(rt_dst, node_dst->left);
|
||||
list_push_back(&rt_dst->list, node_dst);
|
||||
node_dst->right = rt_copy_recursive(rt_dst, node_dst->right);
|
||||
|
||||
return node_dst;
|
||||
}
|
||||
#endif // USE_BTREE
|
||||
|
||||
RangeTreeUInt *range_tree_uint_copy(const RangeTreeUInt *rt_src)
|
||||
{
|
||||
RangeTreeUInt *rt_dst = rt_create_empty(rt_src->range[0], rt_src->range[1]);
|
||||
#ifdef USE_BTREE
|
||||
rt_dst->root = rt_copy_recursive(rt_dst, rt_src->root);
|
||||
#else
|
||||
for (Node *node_src = rt_src->list.first; node_src; node_src = node_src->next) {
|
||||
Node *node_dst = rt_node_alloc(rt_dst);
|
||||
*node_dst = *node_src;
|
||||
list_push_back(&rt_dst->list, node_dst);
|
||||
}
|
||||
#endif
|
||||
return rt_dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the tree has the value (not taken).
|
||||
*/
|
||||
bool range_tree_uint_has(RangeTreeUInt *rt, const uint value)
|
||||
{
|
||||
assert(value >= rt->range[0] && value <= rt->range[1]);
|
||||
Node *node = rt_find_node_from_value(rt, value);
|
||||
return (node != NULL);
|
||||
}
|
||||
|
||||
static void range_tree_uint_take_impl(RangeTreeUInt *rt, const uint value, Node *node)
|
||||
{
|
||||
assert(node == rt_find_node_from_value(rt, value));
|
||||
if (node->min == value) {
|
||||
if (node->max != value) {
|
||||
node->min += 1;
|
||||
}
|
||||
else {
|
||||
assert(node->min == node->max);
|
||||
rt_node_remove(rt, node);
|
||||
}
|
||||
}
|
||||
else if (node->max == value) {
|
||||
node->max -= 1;
|
||||
}
|
||||
else {
|
||||
Node *node_next = rt_node_new(rt, value + 1, node->max);
|
||||
node->max = value - 1;
|
||||
rt_node_add_after(rt, node, node_next);
|
||||
}
|
||||
}
|
||||
|
||||
void range_tree_uint_take(RangeTreeUInt *rt, const uint value)
|
||||
{
|
||||
Node *node = rt_find_node_from_value(rt, value);
|
||||
assert(node != NULL);
|
||||
range_tree_uint_take_impl(rt, value, node);
|
||||
}
|
||||
|
||||
bool range_tree_uint_retake(RangeTreeUInt *rt, const uint value)
|
||||
{
|
||||
Node *node = rt_find_node_from_value(rt, value);
|
||||
if (node != NULL) {
|
||||
range_tree_uint_take_impl(rt, value, node);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint range_tree_uint_take_any(RangeTreeUInt *rt)
|
||||
{
|
||||
Node *node = rt->list.first;
|
||||
uint value = node->min;
|
||||
if (value == node->max) {
|
||||
rt_node_remove(rt, node);
|
||||
}
|
||||
else {
|
||||
node->min += 1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void range_tree_uint_release(RangeTreeUInt *rt, const uint value)
|
||||
{
|
||||
bool touch_prev, touch_next;
|
||||
Node *node_prev, *node_next;
|
||||
|
||||
if (rt->list.first != NULL) {
|
||||
rt_find_node_pair_around_value(rt, value, &node_prev, &node_next);
|
||||
/* the value must have been already taken */
|
||||
assert(node_prev || node_next);
|
||||
|
||||
/* Cases:
|
||||
* 1) fill the gap between prev & next (two spans into one span).
|
||||
* 2) touching prev, (grow node_prev->max up one).
|
||||
* 3) touching next, (grow node_next->min down one).
|
||||
* 4) touching neither, add a new segment. */
|
||||
touch_prev = (node_prev != NULL && node_prev->max + 1 == value);
|
||||
touch_next = (node_next != NULL && node_next->min - 1 == value);
|
||||
}
|
||||
else {
|
||||
// we could handle this case (4) inline,
|
||||
// since its not a common case - use regular logic.
|
||||
node_prev = node_next = NULL;
|
||||
touch_prev = false;
|
||||
touch_next = false;
|
||||
}
|
||||
|
||||
if (touch_prev && touch_next) { // 1)
|
||||
node_prev->max = node_next->max;
|
||||
rt_node_remove(rt, node_next);
|
||||
}
|
||||
else if (touch_prev) { // 2)
|
||||
assert(node_prev->max + 1 == value);
|
||||
node_prev->max = value;
|
||||
}
|
||||
else if (touch_next) { // 3)
|
||||
assert(node_next->min - 1 == value);
|
||||
node_next->min = value;
|
||||
}
|
||||
else { // 4)
|
||||
Node *node_new = rt_node_new(rt, value, value);
|
||||
if (node_prev != NULL) {
|
||||
rt_node_add_after(rt, node_prev, node_new);
|
||||
}
|
||||
else if (node_next != NULL) {
|
||||
rt_node_add_before(rt, node_next, node_new);
|
||||
}
|
||||
else {
|
||||
assert(rt->list.first == NULL);
|
||||
rt_node_add_back(rt, node_new);
|
||||
}
|
||||
}
|
||||
}
|
48
extern/rangetree/range_tree.h
vendored
Normal file
48
extern/rangetree/range_tree.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Campbell Barton.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "Apache License")
|
||||
* with the following modification; you may not use this file except in
|
||||
* compliance with the Apache License and the following modification to it:
|
||||
* Section 6. Trademarks. is deleted and replaced with:
|
||||
*
|
||||
* 6. Trademarks. This License does not grant permission to use the trade
|
||||
* names, trademarks, service marks, or product names of the Licensor
|
||||
* and its affiliates, except as required to comply with Section 4(c) of
|
||||
* the License and to reproduce the content of the NOTICE file.
|
||||
*
|
||||
* You may obtain a copy of the Apache License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Apache License with the above modification is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the Apache License for the specific
|
||||
* language governing permissions and limitations under the Apache License.
|
||||
*/
|
||||
|
||||
#ifndef __RANGE_TREE_H__
|
||||
#define __RANGE_TREE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct RangeTreeUInt RangeTreeUInt;
|
||||
|
||||
struct RangeTreeUInt *range_tree_uint_alloc(unsigned int min, unsigned int max);
|
||||
void range_tree_uint_free(struct RangeTreeUInt *rt);
|
||||
struct RangeTreeUInt *range_tree_uint_copy(const struct RangeTreeUInt *rt_src);
|
||||
|
||||
bool range_tree_uint_has(struct RangeTreeUInt *rt, const unsigned int value);
|
||||
void range_tree_uint_take(struct RangeTreeUInt *rt, const unsigned int value);
|
||||
bool range_tree_uint_retake(struct RangeTreeUInt *rt, const unsigned int value);
|
||||
unsigned int range_tree_uint_take_any(struct RangeTreeUInt *rt);
|
||||
void range_tree_uint_release(struct RangeTreeUInt *rt, const unsigned int value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RANGE_TREE_H__ */
|
251
extern/rangetree/range_tree.hh
vendored
251
extern/rangetree/range_tree.hh
vendored
@@ -1,251 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
#ifndef RANGE_TREE_DEBUG_PRINT_FUNCTION
|
||||
# define RANGE_TREE_DEBUG_PRINT_FUNCTION 0
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
struct RangeTree {
|
||||
struct Range {
|
||||
Range(T min_, T max_)
|
||||
: min(min_), max(max_), single(min_ == max_) {
|
||||
assert(min_ <= max_);
|
||||
}
|
||||
|
||||
Range(T t)
|
||||
: min(t), max(t), single(true)
|
||||
{}
|
||||
|
||||
Range& operator=(const Range& v) {
|
||||
*this = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const Range& v) const {
|
||||
return max < v.min;
|
||||
}
|
||||
|
||||
const T min;
|
||||
const T max;
|
||||
const bool single;
|
||||
};
|
||||
|
||||
typedef std::set<Range> Tree;
|
||||
typedef typename Tree::iterator TreeIter;
|
||||
typedef typename Tree::reverse_iterator TreeIterReverse;
|
||||
typedef typename Tree::const_iterator TreeIterConst;
|
||||
|
||||
/* Initialize with a single range from 'min' to 'max', inclusive. */
|
||||
RangeTree(T min, T max) {
|
||||
tree.insert(Range(min, max));
|
||||
}
|
||||
|
||||
/* Initialize with a single range from 0 to 'max', inclusive. */
|
||||
RangeTree(T max) {
|
||||
tree.insert(Range(0, max));
|
||||
}
|
||||
|
||||
RangeTree(const RangeTree<T>& src) {
|
||||
tree = src.tree;
|
||||
}
|
||||
|
||||
/* Remove 't' from the associated range in the tree. Precondition:
|
||||
a range including 't' must exist in the tree. */
|
||||
void take(T t) {
|
||||
#if RANGE_TREE_DEBUG_PRINT_FUNCTION
|
||||
std::cout << __func__ << "(" << t << ")\n";
|
||||
#endif
|
||||
|
||||
/* Find the range that includes 't' and its neighbors */
|
||||
TreeIter iter = tree.find(Range(t));
|
||||
assert(iter != tree.end());
|
||||
Range cur = *iter;
|
||||
|
||||
/* Remove the original range (note that this does not
|
||||
invalidate the prev/next iterators) */
|
||||
tree.erase(iter);
|
||||
|
||||
/* Construct two new ranges that together cover the original
|
||||
range, except for 't' */
|
||||
if (t > cur.min)
|
||||
tree.insert(Range(cur.min, t - 1));
|
||||
if (t + 1 <= cur.max)
|
||||
tree.insert(Range(t + 1, cur.max));
|
||||
}
|
||||
|
||||
/* clone of 'take' that checks if the item exists */
|
||||
bool retake(T t) {
|
||||
#if RANGE_TREE_DEBUG_PRINT_FUNCTION
|
||||
std::cout << __func__ << "(" << t << ")\n";
|
||||
#endif
|
||||
|
||||
TreeIter iter = tree.find(Range(t));
|
||||
if (iter == tree.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Range cur = *iter;
|
||||
tree.erase(iter);
|
||||
if (t > cur.min)
|
||||
tree.insert(Range(cur.min, t - 1));
|
||||
if (t + 1 <= cur.max)
|
||||
tree.insert(Range(t + 1, cur.max));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Take the first element out of the first range in the
|
||||
tree. Precondition: tree must not be empty. */
|
||||
T take_any() {
|
||||
#if RANGE_TREE_DEBUG_PRINT_FUNCTION
|
||||
std::cout << __func__ << "()\n";
|
||||
#endif
|
||||
|
||||
/* Find the first element */
|
||||
TreeIter iter = tree.begin();
|
||||
assert(iter != tree.end());
|
||||
T first = iter->min;
|
||||
|
||||
/* Take the first element */
|
||||
take(first);
|
||||
return first;
|
||||
}
|
||||
|
||||
/* Return 't' to the tree, either expanding/merging existing
|
||||
ranges or adding a range to cover it. Precondition: 't' cannot
|
||||
be in an existing range. */
|
||||
void release(T t) {
|
||||
#if RANGE_TREE_DEBUG_PRINT_FUNCTION
|
||||
std::cout << __func__ << "(" << t << ")\n";
|
||||
#endif
|
||||
|
||||
/* TODO: these cases should be simplified/unified */
|
||||
|
||||
TreeIter right = tree.upper_bound(t);
|
||||
if (right != tree.end()) {
|
||||
TreeIter left = right;
|
||||
if (left != tree.begin())
|
||||
--left;
|
||||
|
||||
if (left == right) {
|
||||
/* 't' lies before any existing ranges */
|
||||
if (t + 1 == left->min) {
|
||||
/* 't' lies directly before the first range,
|
||||
resize and replace that range */
|
||||
const Range r(t, left->max);
|
||||
tree.erase(left);
|
||||
tree.insert(r);
|
||||
}
|
||||
else {
|
||||
/* There's a gap between 't' and the first range,
|
||||
add a new range */
|
||||
tree.insert(Range(t));
|
||||
}
|
||||
}
|
||||
else if ((left->max + 1 == t) &&
|
||||
(t + 1 == right->min)) {
|
||||
/* 't' fills a hole. Remove left and right, and insert a
|
||||
new range that covers both. */
|
||||
const Range r(left->min, right->max);
|
||||
tree.erase(left);
|
||||
tree.erase(right);
|
||||
tree.insert(r);
|
||||
}
|
||||
else if (left->max + 1 == t) {
|
||||
/* 't' lies directly after 'left' range, resize and
|
||||
replace that range */
|
||||
const Range r(left->min, t);
|
||||
tree.erase(left);
|
||||
tree.insert(r);
|
||||
}
|
||||
else if (t + 1 == right->min) {
|
||||
/* 't' lies directly before 'right' range, resize and
|
||||
replace that range */
|
||||
const Range r(t, right->max);
|
||||
tree.erase(right);
|
||||
tree.insert(r);
|
||||
}
|
||||
else {
|
||||
/* There's a gap between 't' and both adjacent ranges,
|
||||
add a new range */
|
||||
tree.insert(Range(t));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 't' lies after any existing ranges */
|
||||
right = tree.end();
|
||||
right--;
|
||||
if (right->max + 1 == t) {
|
||||
/* 't' lies directly after last range, resize and
|
||||
replace that range */
|
||||
const Range r(right->min, t);
|
||||
tree.erase(right);
|
||||
tree.insert(r);
|
||||
}
|
||||
else {
|
||||
/* There's a gap between the last range and 't', add a
|
||||
new range */
|
||||
tree.insert(Range(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has(T t) const {
|
||||
TreeIterConst iter = tree.find(Range(t));
|
||||
return (iter != tree.end()) && (t <= iter->max);
|
||||
}
|
||||
|
||||
bool has_range(T min, T max) const {
|
||||
TreeIterConst iter = tree.find(Range(min, max));
|
||||
return (iter != tree.end()) && (min == iter->min && max == iter->max);
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return tree.empty();
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return tree.size();
|
||||
}
|
||||
|
||||
void print() const {
|
||||
std::cout << "RangeTree:\n";
|
||||
for (TreeIterConst iter = tree.begin(); iter != tree.end(); ++iter) {
|
||||
const Range& r = *iter;
|
||||
if (r.single)
|
||||
std::cout << " [" << r.min << "]\n";
|
||||
else
|
||||
std::cout << " [" << r.min << ", " << r.max << "]\n";
|
||||
}
|
||||
if (empty())
|
||||
std::cout << " <empty>";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
unsigned int allocation_lower_bound() const {
|
||||
return tree.size() * sizeof(Range);
|
||||
}
|
||||
|
||||
private:
|
||||
Tree tree;
|
||||
};
|
92
extern/rangetree/range_tree_c_api.cc
vendored
92
extern/rangetree/range_tree_c_api.cc
vendored
@@ -1,92 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#include "range_tree.hh"
|
||||
|
||||
/* Give RangeTreeUInt a real type rather than the opaque struct type
|
||||
defined for external use. */
|
||||
#define RANGE_TREE_C_API_INTERNAL
|
||||
typedef RangeTree<unsigned> RangeTreeUInt;
|
||||
|
||||
#include "range_tree_c_api.h"
|
||||
|
||||
RangeTreeUInt *range_tree_uint_alloc(unsigned min, unsigned max)
|
||||
{
|
||||
return new RangeTreeUInt(min, max);
|
||||
}
|
||||
|
||||
RangeTreeUInt *range_tree_uint_copy(RangeTreeUInt *src)
|
||||
{
|
||||
return new RangeTreeUInt(*src);
|
||||
}
|
||||
|
||||
void range_tree_uint_free(RangeTreeUInt *rt)
|
||||
{
|
||||
delete rt;
|
||||
}
|
||||
|
||||
void range_tree_uint_take(RangeTreeUInt *rt, unsigned v)
|
||||
{
|
||||
rt->take(v);
|
||||
}
|
||||
|
||||
bool range_tree_uint_retake(RangeTreeUInt *rt, unsigned v)
|
||||
{
|
||||
return rt->retake(v);
|
||||
}
|
||||
|
||||
unsigned range_tree_uint_take_any(RangeTreeUInt *rt)
|
||||
{
|
||||
return rt->take_any();
|
||||
}
|
||||
|
||||
void range_tree_uint_release(RangeTreeUInt *rt, unsigned v)
|
||||
{
|
||||
rt->release(v);
|
||||
}
|
||||
|
||||
bool range_tree_uint_has(const RangeTreeUInt *rt, unsigned v)
|
||||
{
|
||||
return rt->has(v);
|
||||
}
|
||||
|
||||
bool range_tree_uint_has_range(
|
||||
const RangeTreeUInt *rt,
|
||||
unsigned vmin,
|
||||
unsigned vmax)
|
||||
{
|
||||
return rt->has_range(vmin, vmax);
|
||||
}
|
||||
|
||||
bool range_tree_uint_empty(const RangeTreeUInt *rt)
|
||||
{
|
||||
return rt->empty();
|
||||
}
|
||||
|
||||
unsigned range_tree_uint_size(const RangeTreeUInt *rt)
|
||||
{
|
||||
return rt->size();
|
||||
}
|
||||
|
||||
void range_tree_uint_print(const RangeTreeUInt *rt)
|
||||
{
|
||||
rt->print();
|
||||
}
|
||||
|
||||
unsigned int range_tree_uint_allocation_lower_bound(const RangeTreeUInt *rt)
|
||||
{
|
||||
return rt->allocation_lower_bound();
|
||||
}
|
62
extern/rangetree/range_tree_c_api.h
vendored
62
extern/rangetree/range_tree_c_api.h
vendored
@@ -1,62 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __RANGE_TREE_C_API_H__
|
||||
#define __RANGE_TREE_C_API_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Simple C-accessible wrapper for RangeTree<unsigned> */
|
||||
|
||||
#ifndef RANGE_TREE_C_API_INTERNAL
|
||||
typedef struct RangeTreeUInt RangeTreeUInt;
|
||||
#endif
|
||||
|
||||
RangeTreeUInt *range_tree_uint_alloc(unsigned min, unsigned max);
|
||||
|
||||
RangeTreeUInt *range_tree_uint_copy(RangeTreeUInt *src);
|
||||
|
||||
void range_tree_uint_free(RangeTreeUInt *rt);
|
||||
|
||||
void range_tree_uint_take(RangeTreeUInt *rt, unsigned v);
|
||||
|
||||
bool range_tree_uint_retake(RangeTreeUInt *rt, unsigned v);
|
||||
|
||||
unsigned range_tree_uint_take_any(RangeTreeUInt *rt);
|
||||
|
||||
void range_tree_uint_release(RangeTreeUInt *rt, unsigned v);
|
||||
|
||||
bool range_tree_uint_has(const RangeTreeUInt *rt, unsigned v);
|
||||
|
||||
bool range_tree_uint_has_range(
|
||||
const RangeTreeUInt *rt,
|
||||
unsigned vmin, unsigned vmax);
|
||||
|
||||
bool range_tree_uint_empty(const RangeTreeUInt *rt);
|
||||
|
||||
unsigned range_tree_uint_size(const RangeTreeUInt *rt);
|
||||
|
||||
void range_tree_uint_print(const RangeTreeUInt *rt);
|
||||
|
||||
unsigned int range_tree_uint_allocation_lower_bound(const RangeTreeUInt *rt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RANGE_TREE_C_API_H__ */
|
@@ -77,32 +77,40 @@
|
||||
/* Function prototypes. */
|
||||
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x);
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new);
|
||||
#endif
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new);
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x);
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x);
|
||||
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b);
|
||||
ATOMIC_INLINE uint8_t atomic_fetch_and_and_uint8(uint8_t *p, uint8_t b);
|
||||
|
||||
ATOMIC_INLINE size_t atomic_add_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_sub_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x);
|
||||
ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new);
|
||||
|
||||
ATOMIC_INLINE unsigned atomic_add_u(unsigned *p, unsigned x);
|
||||
ATOMIC_INLINE unsigned atomic_sub_u(unsigned *p, unsigned x);
|
||||
ATOMIC_INLINE unsigned atomic_add_and_fetch_u(unsigned *p, unsigned x);
|
||||
ATOMIC_INLINE unsigned atomic_sub_and_fetch_u(unsigned *p, unsigned x);
|
||||
ATOMIC_INLINE unsigned atomic_fetch_and_add_u(unsigned *p, unsigned x);
|
||||
ATOMIC_INLINE unsigned atomic_fetch_and_sub_u(unsigned *p, unsigned x);
|
||||
ATOMIC_INLINE unsigned atomic_cas_u(unsigned *v, unsigned old, unsigned _new);
|
||||
|
||||
/* WARNING! Float 'atomics' are really faked ones, those are actually closer to some kind of spinlock-sync'ed operation,
|
||||
* which means they are only efficient if collisions are highly unlikely (i.e. if probability of two threads
|
||||
* working on the same pointer at the same time is very low). */
|
||||
ATOMIC_INLINE float atomic_add_fl(float *p, const float x);
|
||||
ATOMIC_INLINE float atomic_add_and_fetch_fl(float *p, const float x);
|
||||
|
||||
/******************************************************************************/
|
||||
/* Include system-dependent implementations. */
|
||||
|
@@ -56,25 +56,47 @@
|
||||
|
||||
/******************************************************************************/
|
||||
/* size_t operations. */
|
||||
ATOMIC_INLINE size_t atomic_add_z(size_t *p, size_t x)
|
||||
ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x)
|
||||
{
|
||||
assert(sizeof(size_t) == LG_SIZEOF_PTR);
|
||||
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_add_uint64((uint64_t *)p, (uint64_t)x);
|
||||
return (size_t)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_add_uint32((uint32_t *)p, (uint32_t)x);
|
||||
return (size_t)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_sub_z(size_t *p, size_t x)
|
||||
ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x)
|
||||
{
|
||||
assert(sizeof(size_t) == LG_SIZEOF_PTR);
|
||||
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
return (size_t)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
return (size_t)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_add_z(size_t *p, size_t x)
|
||||
{
|
||||
assert(sizeof(size_t) == LG_SIZEOF_PTR);
|
||||
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE size_t atomic_fetch_and_sub_z(size_t *p, size_t x)
|
||||
{
|
||||
assert(sizeof(size_t) == LG_SIZEOF_PTR);
|
||||
|
||||
#if (LG_SIZEOF_PTR == 8)
|
||||
return (size_t)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_PTR == 4)
|
||||
return (size_t)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -91,25 +113,47 @@ ATOMIC_INLINE size_t atomic_cas_z(size_t *v, size_t old, size_t _new)
|
||||
|
||||
/******************************************************************************/
|
||||
/* unsigned operations. */
|
||||
ATOMIC_INLINE unsigned atomic_add_u(unsigned *p, unsigned x)
|
||||
ATOMIC_INLINE unsigned atomic_add_and_fetch_u(unsigned *p, unsigned x)
|
||||
{
|
||||
assert(sizeof(unsigned) == LG_SIZEOF_INT);
|
||||
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned)atomic_add_uint64((uint64_t *)p, (uint64_t)x);
|
||||
return (unsigned)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned)atomic_add_uint32((uint32_t *)p, (uint32_t)x);
|
||||
return (unsigned)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned atomic_sub_u(unsigned *p, unsigned x)
|
||||
ATOMIC_INLINE unsigned atomic_sub_and_fetch_u(unsigned *p, unsigned x)
|
||||
{
|
||||
assert(sizeof(unsigned) == LG_SIZEOF_INT);
|
||||
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned)atomic_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
return (unsigned)atomic_add_and_fetch_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned)atomic_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
return (unsigned)atomic_add_and_fetch_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned atomic_fetch_and_add_u(unsigned *p, unsigned x)
|
||||
{
|
||||
assert(sizeof(unsigned) == LG_SIZEOF_INT);
|
||||
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)x);
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)x);
|
||||
#endif
|
||||
}
|
||||
|
||||
ATOMIC_INLINE unsigned atomic_fetch_and_sub_u(unsigned *p, unsigned x)
|
||||
{
|
||||
assert(sizeof(unsigned) == LG_SIZEOF_INT);
|
||||
|
||||
#if (LG_SIZEOF_INT == 8)
|
||||
return (unsigned)atomic_fetch_and_add_uint64((uint64_t *)p, (uint64_t)-((int64_t)x));
|
||||
#elif (LG_SIZEOF_INT == 4)
|
||||
return (unsigned)atomic_fetch_and_add_uint32((uint32_t *)p, (uint32_t)-((int32_t)x));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -127,7 +171,7 @@ ATOMIC_INLINE unsigned atomic_cas_u(unsigned *v, unsigned old, unsigned _new)
|
||||
/******************************************************************************/
|
||||
/* float operations. */
|
||||
|
||||
ATOMIC_INLINE float atomic_add_fl(float *p, const float x)
|
||||
ATOMIC_INLINE float atomic_add_and_fetch_fl(float *p, const float x)
|
||||
{
|
||||
assert(sizeof(float) == sizeof(uint32_t));
|
||||
|
||||
|
@@ -43,12 +43,12 @@
|
||||
/******************************************************************************/
|
||||
/* 64-bit operations. */
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x;
|
||||
}
|
||||
@@ -57,16 +57,26 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne
|
||||
{
|
||||
return InterlockedCompareExchange64((int64_t *)v, _new, old);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/* 32-bit operations. */
|
||||
ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd(p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x)
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedExchangeAdd(p, -((int32_t)x)) - x;
|
||||
}
|
||||
@@ -81,6 +91,16 @@ ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
|
||||
return InterlockedExchangeAdd(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedOr((long *)p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return InterlockedAnd((long *)p, x);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* 8-bit operations. */
|
||||
|
||||
|
@@ -58,22 +58,32 @@
|
||||
/* 64-bit operations. */
|
||||
#if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8)
|
||||
# if (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8))
|
||||
ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_add_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_sub_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_fetch_and_add(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return __sync_fetch_and_sub(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
|
||||
{
|
||||
return __sync_val_compare_and_swap(v, old, _new);
|
||||
}
|
||||
# elif (defined(__amd64__) || defined(__x86_64__))
|
||||
ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
asm volatile (
|
||||
"lock; xaddq %0, %1;"
|
||||
@@ -83,7 +93,7 @@ ATOMIC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x)
|
||||
return x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
|
||||
ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
x = (uint64_t)(-(int64_t)x);
|
||||
asm volatile (
|
||||
@@ -94,6 +104,16 @@ ATOMIC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x)
|
||||
return x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return atomic_fetch_and_add_uint64(p, x) + x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x)
|
||||
{
|
||||
return atomic_fetch_and_sub_uint64(p, x) - x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new)
|
||||
{
|
||||
uint64_t ret;
|
||||
@@ -112,12 +132,12 @@ ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _ne
|
||||
/******************************************************************************/
|
||||
/* 32-bit operations. */
|
||||
#if (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4))
|
||||
ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_add_and_fetch(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x)
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_sub_and_fetch(p, x);
|
||||
}
|
||||
@@ -127,7 +147,7 @@ ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _ne
|
||||
return __sync_val_compare_and_swap(v, old, _new);
|
||||
}
|
||||
#elif (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
|
||||
ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
|
||||
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
uint32_t ret = x;
|
||||
asm volatile (
|
||||
@@ -138,7 +158,7 @@ ATOMIC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x)
|
||||
return ret+x;
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x)
|
||||
ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
ret = (uint32_t)(-(int32_t)x);
|
||||
asm volatile (
|
||||
@@ -169,6 +189,16 @@ ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x)
|
||||
return __sync_fetch_and_add(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_fetch_and_or(p, x);
|
||||
}
|
||||
|
||||
ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x)
|
||||
{
|
||||
return __sync_fetch_and_and(p, x);
|
||||
}
|
||||
|
||||
#else
|
||||
# error "Missing implementation for 32-bit atomic operations"
|
||||
#endif
|
||||
|
@@ -2698,7 +2698,7 @@ Device_set_doppler_factor(Device *self, PyObject *args, void* nothing)
|
||||
|
||||
PyDoc_STRVAR(M_aud_Device_distance_model_doc,
|
||||
"The distance model of the device.\n\n"
|
||||
".. seealso:: http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.htm#_Toc199835864");
|
||||
".. seealso:: `OpenAL documentation <https://www.openal.org/documentation>`");
|
||||
|
||||
static PyObject *
|
||||
Device_get_distance_model(Device *self, void* nothing)
|
||||
|
@@ -155,18 +155,18 @@ AUD_Device* AUD_init(const char* device, AUD_DeviceSpecs specs, int buffersize,
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_JACK
|
||||
else if(dname == "Jack")
|
||||
else if(dname == "JACK")
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
struct stat st;
|
||||
if (stat("/Library/Frameworks/Jackmp.framework", &st) != 0) {
|
||||
printf("Warning: Jack Framework not installed\n");
|
||||
printf("Warning: JACK Framework not installed\n");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!AUD_jack_supported()) {
|
||||
printf("Warning: Jack cllient not installed\n");
|
||||
printf("Warning: JACK cllient not installed\n");
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
|
@@ -61,7 +61,7 @@ typedef struct
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes audio rutines (FFMPEG/Jack if it is enabled).
|
||||
* Initializes audio routines (FFMPEG/JACK if it is enabled).
|
||||
*/
|
||||
extern void AUD_initOnce(void);
|
||||
|
||||
|
@@ -95,6 +95,21 @@ void AUD_Mixer::mix(sample_t* buffer, int start, int length, float volume)
|
||||
out[i + start] += buffer[i] * volume;
|
||||
}
|
||||
|
||||
void AUD_Mixer::mix(sample_t* buffer, int start, int length, float volume_to, float volume_from)
|
||||
{
|
||||
sample_t* out = m_buffer.getBuffer();
|
||||
|
||||
length = (std::min(m_length, length + start) - start);
|
||||
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
float volume = volume_from * (1.0f - i / float(length)) + volume_to * (i / float(length));
|
||||
|
||||
for(int c = 0; c < m_specs.channels; c++)
|
||||
out[(i + start) * m_specs.channels + c] += buffer[i * m_specs.channels + c] * volume;
|
||||
}
|
||||
}
|
||||
|
||||
void AUD_Mixer::read(data_t* buffer, float volume)
|
||||
{
|
||||
sample_t* out = m_buffer.getBuffer();
|
||||
|
@@ -95,6 +95,8 @@ public:
|
||||
*/
|
||||
void mix(sample_t* buffer, int start, int length, float volume);
|
||||
|
||||
void mix(sample_t* buffer, int start, int length, float volume_to, float volume_from);
|
||||
|
||||
/**
|
||||
* Writes the mixing buffer into an output buffer.
|
||||
* \param buffer The target buffer for superposing.
|
||||
|
@@ -89,7 +89,7 @@ bool AUD_SoftwareDevice::AUD_SoftwareHandle::pause(bool keep)
|
||||
}
|
||||
|
||||
AUD_SoftwareDevice::AUD_SoftwareHandle::AUD_SoftwareHandle(AUD_SoftwareDevice* device, boost::shared_ptr<AUD_IReader> reader, boost::shared_ptr<AUD_PitchReader> pitch, boost::shared_ptr<AUD_ResampleReader> resampler, boost::shared_ptr<AUD_ChannelMapperReader> mapper, bool keep) :
|
||||
m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(1.0f), m_loopcount(0),
|
||||
m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(1.0f), m_old_volume(1.0f), m_loopcount(0),
|
||||
m_relative(true), m_volume_max(1.0f), m_volume_min(0), m_distance_max(std::numeric_limits<float>::max()),
|
||||
m_distance_reference(1.0f), m_attenuation(1.0f), m_cone_angle_outer(M_PI), m_cone_angle_inner(M_PI), m_cone_volume_outer(0),
|
||||
m_flags(AUD_RENDER_CONE), m_stop(NULL), m_stop_data(NULL), m_status(AUD_STATUS_PLAYING), m_device(device)
|
||||
@@ -100,6 +100,8 @@ void AUD_SoftwareDevice::AUD_SoftwareHandle::update()
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
m_old_volume = m_volume;
|
||||
|
||||
AUD_Vector3 SL;
|
||||
if(m_relative)
|
||||
SL = -m_location;
|
||||
@@ -404,7 +406,7 @@ bool AUD_SoftwareDevice::AUD_SoftwareHandle::setVolume(float volume)
|
||||
|
||||
if(volume == 0)
|
||||
{
|
||||
m_volume = volume;
|
||||
m_old_volume = m_volume = volume;
|
||||
m_flags |= AUD_RENDER_VOLUME;
|
||||
}
|
||||
else
|
||||
@@ -772,7 +774,7 @@ void AUD_SoftwareDevice::mix(data_t* buffer, int length)
|
||||
// in case of looping
|
||||
while(pos + len < length && sound->m_loopcount && eos)
|
||||
{
|
||||
m_mixer->mix(buf, pos, len, sound->m_volume);
|
||||
m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume);
|
||||
|
||||
pos += len;
|
||||
|
||||
@@ -789,7 +791,7 @@ void AUD_SoftwareDevice::mix(data_t* buffer, int length)
|
||||
break;
|
||||
}
|
||||
|
||||
m_mixer->mix(buf, pos, len, sound->m_volume);
|
||||
m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume);
|
||||
|
||||
// in case the end of the sound is reached
|
||||
if(eos && !sound->m_loopcount)
|
||||
|
@@ -84,6 +84,7 @@ protected:
|
||||
|
||||
/// The calculated final volume of the source.
|
||||
float m_volume;
|
||||
float m_old_volume;
|
||||
|
||||
/// The loop count of the source.
|
||||
int m_loopcount;
|
||||
|
@@ -41,7 +41,7 @@
|
||||
typedef void (*AUD_syncFunction)(void*, int, float);
|
||||
|
||||
/**
|
||||
* This device plays back through Jack.
|
||||
* This device plays back through JACK.
|
||||
*/
|
||||
class AUD_JackDevice : public AUD_SoftwareDevice
|
||||
{
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
static int jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data);
|
||||
|
||||
/**
|
||||
* Next Jack Transport state (-1 if not expected to change).
|
||||
* Next JACK Transport state (-1 if not expected to change).
|
||||
*/
|
||||
jack_transport_state_t m_nextState;
|
||||
|
||||
@@ -150,7 +150,7 @@ protected:
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a Jack client for audio output.
|
||||
* Creates a JACK client for audio output.
|
||||
* \param name The client name.
|
||||
* \param specs The wanted audio specification, where only the channel count
|
||||
* is important.
|
||||
@@ -160,7 +160,7 @@ public:
|
||||
AUD_JackDevice(std::string name, AUD_DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
/**
|
||||
* Closes the Jack client.
|
||||
* Closes the JACK client.
|
||||
*/
|
||||
virtual ~AUD_JackDevice();
|
||||
|
||||
|
@@ -42,7 +42,7 @@
|
||||
# define JACK_SYM extern
|
||||
#endif
|
||||
|
||||
/* All loadable Jack sumbols, prototypes from original jack.h */
|
||||
/* All loadable JACK sumbols, prototypes from original jack.h */
|
||||
|
||||
JACK_SYM jack_transport_state_t (*AUD_jack_transport_query) (
|
||||
const jack_client_t *client,
|
||||
|
@@ -337,7 +337,7 @@ static void options_parse(int argc, const char **argv)
|
||||
|
||||
/* device names */
|
||||
string device_names = "";
|
||||
string devicename = "cpu";
|
||||
string devicename = "CPU";
|
||||
bool list = false;
|
||||
|
||||
vector<DeviceType>& types = Device::available_types();
|
||||
|
@@ -210,17 +210,6 @@ static void xml_read_camera(XMLReadState& state, pugi::xml_node node)
|
||||
|
||||
/* Shader */
|
||||
|
||||
static string xml_socket_name(const char *name)
|
||||
{
|
||||
string sname = name;
|
||||
size_t i;
|
||||
|
||||
while((i = sname.find(" ")) != string::npos)
|
||||
sname.replace(i, 1, "");
|
||||
|
||||
return sname;
|
||||
}
|
||||
|
||||
static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml_node graph_node)
|
||||
{
|
||||
xml_read_node(state, shader, graph_node);
|
||||
@@ -255,7 +244,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
|
||||
ShaderNode *fromnode = (ShaderNode*)graph_reader.node_map[from_node_name];
|
||||
|
||||
foreach(ShaderOutput *out, fromnode->outputs)
|
||||
if(string_iequals(xml_socket_name(out->name().c_str()), from_socket_name.c_str()))
|
||||
if(string_iequals(out->socket_type.name.string(), from_socket_name.string()))
|
||||
output = out;
|
||||
|
||||
if(!output)
|
||||
@@ -268,7 +257,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
|
||||
ShaderNode *tonode = (ShaderNode*)graph_reader.node_map[to_node_name];
|
||||
|
||||
foreach(ShaderInput *in, tonode->inputs)
|
||||
if(string_iequals(xml_socket_name(in->name().c_str()), to_socket_name.c_str()))
|
||||
if(string_iequals(in->socket_type.name.string(), to_socket_name.string()))
|
||||
input = in;
|
||||
|
||||
if(!input)
|
||||
@@ -406,7 +395,7 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
|
||||
int shader = 0;
|
||||
bool smooth = state.smooth;
|
||||
|
||||
/* read vertices and polygons, RIB style */
|
||||
/* read vertices and polygons */
|
||||
vector<float3> P;
|
||||
vector<float> UV;
|
||||
vector<int> verts, nverts;
|
||||
@@ -532,8 +521,12 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node)
|
||||
sdparams.objecttoworld = state.tfm;
|
||||
}
|
||||
|
||||
/* temporary for test compatibility */
|
||||
mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
|
||||
/* we don't yet support arbitrary attributes, for now add vertex
|
||||
* coordinates as generated coordinates if requested */
|
||||
if (mesh->need_attribute(state.scene, ATTR_STD_GENERATED)) {
|
||||
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
||||
memcpy(attr->data_float3(), mesh->verts.data(), sizeof(float3)*mesh->verts.size());
|
||||
}
|
||||
}
|
||||
|
||||
/* Light */
|
||||
|
@@ -21,17 +21,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* returns a list of devices for selection, array is empty identifier
|
||||
* terminated and must not be freed */
|
||||
|
||||
typedef struct CCLDeviceInfo {
|
||||
char identifier[128];
|
||||
char name[512];
|
||||
int value;
|
||||
} CCLDeviceInfo;
|
||||
|
||||
CCLDeviceInfo *CCL_compute_device_list(int device_type);
|
||||
|
||||
/* create python module _cycles used by addon */
|
||||
|
||||
void *CCL_python_module_init(void);
|
||||
|
@@ -21,7 +21,8 @@ from bpy.props import (BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
PointerProperty)
|
||||
PointerProperty,
|
||||
StringProperty)
|
||||
|
||||
# enums
|
||||
|
||||
@@ -29,7 +30,7 @@ import _cycles
|
||||
|
||||
enum_devices = (
|
||||
('CPU', "CPU", "Use CPU for rendering"),
|
||||
('GPU', "GPU Compute", "Use GPU compute device for rendering, configured in user preferences"),
|
||||
('GPU', "GPU Compute", "Use GPU compute device for rendering, configured in the system tab in the user preferences"),
|
||||
)
|
||||
|
||||
if _cycles.with_network:
|
||||
@@ -122,6 +123,22 @@ enum_volume_interpolation = (
|
||||
('CUBIC', "Cubic", "Smoothed high quality interpolation, but slower")
|
||||
)
|
||||
|
||||
enum_device_type = (
|
||||
('CPU', "CPU", "CPU", 0),
|
||||
('CUDA', "CUDA", "CUDA", 1),
|
||||
('OPENCL', "OpenCL", "OpenCL", 2)
|
||||
)
|
||||
|
||||
enum_texture_limit = (
|
||||
('OFF', "No Limit", "No texture size limit", 0),
|
||||
('128', "128", "Limit texture size to 128 pixels", 1),
|
||||
('256', "256", "Limit texture size to 256 pixels", 2),
|
||||
('512', "512", "Limit texture size to 512 pixels", 3),
|
||||
('1024', "1024", "Limit texture size to 1024 pixels", 4),
|
||||
('2048', "2048", "Limit texture size to 2048 pixels", 5),
|
||||
('4096', "4096", "Limit texture size to 4096 pixels", 6),
|
||||
('8192', "8192", "Limit texture size to 8192 pixels", 7),
|
||||
)
|
||||
|
||||
class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
@classmethod
|
||||
@@ -266,6 +283,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
description="Sample all lights (for indirect samples), rather than randomly picking one",
|
||||
default=True,
|
||||
)
|
||||
cls.light_sampling_threshold = FloatProperty(
|
||||
name="Light Sampling Threshold",
|
||||
description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). "
|
||||
"Zero disables the test and never ignores lights",
|
||||
min=0.0, max=1.0,
|
||||
default=0.05,
|
||||
)
|
||||
|
||||
cls.caustics_reflective = BoolProperty(
|
||||
name="Reflective Caustics",
|
||||
@@ -552,6 +576,19 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
min=0.0, max=5.0
|
||||
)
|
||||
|
||||
cls.use_distance_cull = BoolProperty(
|
||||
name="Use Distance Cull",
|
||||
description="Allow objects to be culled based on the distance from camera",
|
||||
default=False,
|
||||
)
|
||||
|
||||
cls.distance_cull_margin = FloatProperty(
|
||||
name="Cull Distance",
|
||||
description="Cull objects which are further away from camera than this distance",
|
||||
default=50,
|
||||
min=0.0
|
||||
)
|
||||
|
||||
cls.motion_blur_position = EnumProperty(
|
||||
name="Motion Blur Position",
|
||||
default='CENTER',
|
||||
@@ -581,6 +618,20 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||
min=0.0, max=1.0,
|
||||
)
|
||||
|
||||
cls.texture_limit = EnumProperty(
|
||||
name="Viewport Texture Limit",
|
||||
default='OFF',
|
||||
description="Limit texture size used by viewport rendering",
|
||||
items=enum_texture_limit
|
||||
)
|
||||
|
||||
cls.texture_limit_render = EnumProperty(
|
||||
name="Render Texture Limit",
|
||||
default='OFF',
|
||||
description="Limit texture size used by final rendering",
|
||||
items=enum_texture_limit
|
||||
)
|
||||
|
||||
# Various fine-tuning debug flags
|
||||
|
||||
def devices_update_callback(self, context):
|
||||
@@ -1002,6 +1053,12 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
|
||||
default=False,
|
||||
)
|
||||
|
||||
cls.use_distance_cull = BoolProperty(
|
||||
name="Use Distance Cull",
|
||||
description="Allow this object and its duplicators to be culled by distance from camera",
|
||||
default=False,
|
||||
)
|
||||
|
||||
cls.use_adaptive_subdivision = BoolProperty(
|
||||
name="Use Adaptive Subdivision",
|
||||
description="Use adaptive render time subdivision",
|
||||
@@ -1123,6 +1180,107 @@ class CyclesCurveSettings(bpy.types.PropertyGroup):
|
||||
del bpy.types.ParticleSettings.cycles
|
||||
|
||||
|
||||
class CyclesDeviceSettings(bpy.types.PropertyGroup):
|
||||
@classmethod
|
||||
def register(cls):
|
||||
cls.id = StringProperty(name="ID")
|
||||
cls.name = StringProperty(name="Name")
|
||||
cls.use = BoolProperty(name="Use", default=True)
|
||||
cls.type = EnumProperty(name="Type", items=enum_device_type, default='CUDA')
|
||||
|
||||
|
||||
class CyclesPreferences(bpy.types.AddonPreferences):
|
||||
bl_idname = __package__
|
||||
|
||||
def get_device_types(self, context):
|
||||
import _cycles
|
||||
has_cuda, has_opencl = _cycles.get_device_types()
|
||||
list = [('NONE', "None", "Don't use compute device", 0)]
|
||||
if has_cuda:
|
||||
list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1))
|
||||
if has_opencl:
|
||||
list.append(('OPENCL', "OpenCL", "Use OpenCL for GPU acceleration", 2))
|
||||
return list
|
||||
|
||||
compute_device_type = EnumProperty(
|
||||
name="Compute Device Type",
|
||||
description="Device to use for computation (rendering with Cycles)",
|
||||
items=get_device_types,
|
||||
)
|
||||
|
||||
devices = bpy.props.CollectionProperty(type=CyclesDeviceSettings)
|
||||
|
||||
def get_devices(self):
|
||||
import _cycles
|
||||
# Layout of the device tuples: (Name, Type, Persistent ID)
|
||||
device_list = _cycles.available_devices()
|
||||
|
||||
cuda_devices = []
|
||||
opencl_devices = []
|
||||
for device in device_list:
|
||||
if not device[1] in {'CUDA', 'OPENCL'}:
|
||||
continue
|
||||
|
||||
entry = None
|
||||
# Try to find existing Device entry
|
||||
for dev in self.devices:
|
||||
if dev.id == device[2] and dev.type == device[1]:
|
||||
entry = dev
|
||||
break
|
||||
# Create new entry if no existing one was found
|
||||
if not entry:
|
||||
entry = self.devices.add()
|
||||
entry.id = device[2]
|
||||
entry.name = device[0]
|
||||
entry.type = device[1]
|
||||
|
||||
# Sort entries into lists
|
||||
if entry.type == 'CUDA':
|
||||
cuda_devices.append(entry)
|
||||
elif entry.type == 'OPENCL':
|
||||
opencl_devices.append(entry)
|
||||
return cuda_devices, opencl_devices
|
||||
|
||||
|
||||
def get_num_gpu_devices(self):
|
||||
import _cycles
|
||||
device_list = _cycles.available_devices()
|
||||
num = 0
|
||||
for device in device_list:
|
||||
if device[1] != self.compute_device_type:
|
||||
continue
|
||||
for dev in self.devices:
|
||||
if dev.use and dev.id == device[2]:
|
||||
num += 1
|
||||
return num
|
||||
|
||||
|
||||
def has_active_device(self):
|
||||
return self.get_num_gpu_devices() > 0
|
||||
|
||||
|
||||
def draw_impl(self, layout, context):
|
||||
layout.label(text="Cycles Compute Device:")
|
||||
layout.row().prop(self, "compute_device_type", expand=True)
|
||||
|
||||
cuda_devices, opencl_devices = self.get_devices()
|
||||
row = layout.row()
|
||||
|
||||
if self.compute_device_type == 'CUDA' and cuda_devices:
|
||||
col = row.column(align=True)
|
||||
for device in cuda_devices:
|
||||
col.prop(device, "use", text=device.name, toggle=True)
|
||||
|
||||
if self.compute_device_type == 'OPENCL' and opencl_devices:
|
||||
col = row.column(align=True)
|
||||
for device in opencl_devices:
|
||||
col.prop(device, "use", text=device.name, toggle=True)
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_impl(self.layout, context)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(CyclesRenderSettings)
|
||||
bpy.utils.register_class(CyclesCameraSettings)
|
||||
@@ -1134,6 +1292,8 @@ def register():
|
||||
bpy.utils.register_class(CyclesObjectSettings)
|
||||
bpy.utils.register_class(CyclesCurveRenderSettings)
|
||||
bpy.utils.register_class(CyclesCurveSettings)
|
||||
bpy.utils.register_class(CyclesDeviceSettings)
|
||||
bpy.utils.register_class(CyclesPreferences)
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -1147,3 +1307,5 @@ def unregister():
|
||||
bpy.utils.unregister_class(CyclesVisibilitySettings)
|
||||
bpy.utils.unregister_class(CyclesCurveRenderSettings)
|
||||
bpy.utils.unregister_class(CyclesCurveSettings)
|
||||
bpy.utils.unregister_class(CyclesDeviceSettings)
|
||||
bpy.utils.unregister_class(CyclesPreferences)
|
||||
|
@@ -53,25 +53,26 @@ class CyclesButtonsPanel:
|
||||
return rd.engine in cls.COMPAT_ENGINES
|
||||
|
||||
|
||||
def get_device_type(context):
|
||||
return context.user_preferences.addons[__package__].preferences.compute_device_type
|
||||
|
||||
|
||||
def use_cpu(context):
|
||||
cscene = context.scene.cycles
|
||||
device_type = context.user_preferences.system.compute_device_type
|
||||
|
||||
return (device_type == 'NONE' or cscene.device == 'CPU')
|
||||
return (get_device_type(context) == 'NONE' or cscene.device == 'CPU')
|
||||
|
||||
|
||||
def use_opencl(context):
|
||||
cscene = context.scene.cycles
|
||||
device_type = context.user_preferences.system.compute_device_type
|
||||
|
||||
return (device_type == 'OPENCL' and cscene.device == 'GPU')
|
||||
return (get_device_type(context) == 'OPENCL' and cscene.device == 'GPU')
|
||||
|
||||
|
||||
def use_cuda(context):
|
||||
cscene = context.scene.cycles
|
||||
device_type = context.user_preferences.system.compute_device_type
|
||||
|
||||
return (device_type == 'CUDA' and cscene.device == 'GPU')
|
||||
return (get_device_type(context) == 'CUDA' and cscene.device == 'GPU')
|
||||
|
||||
|
||||
def use_branched_path(context):
|
||||
@@ -85,6 +86,14 @@ def use_sample_all_lights(context):
|
||||
|
||||
return cscene.sample_all_lights_direct or cscene.sample_all_lights_indirect
|
||||
|
||||
def show_device_selection(context):
|
||||
type = get_device_type(context)
|
||||
if type == 'NETWORK':
|
||||
return True
|
||||
if not type in {'CUDA', 'OPENCL'}:
|
||||
return False
|
||||
return context.user_preferences.addons[__package__].preferences.has_active_device()
|
||||
|
||||
|
||||
def draw_samples_info(layout, context):
|
||||
cscene = context.scene.cycles
|
||||
@@ -141,7 +150,6 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
|
||||
|
||||
scene = context.scene
|
||||
cscene = scene.cycles
|
||||
device_type = context.user_preferences.system.compute_device_type
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.menu("CYCLES_MT_sampling_presets", text=bpy.types.CYCLES_MT_sampling_presets.bl_label)
|
||||
@@ -150,7 +158,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
|
||||
|
||||
row = layout.row()
|
||||
sub = row.row()
|
||||
sub.active = device_type != 'OPENCL' or use_cpu(context)
|
||||
sub.active = get_device_type(context) != 'OPENCL' or use_cpu(context)
|
||||
sub.prop(cscene, "progressive", text="")
|
||||
row.prop(cscene, "use_square_samples")
|
||||
|
||||
@@ -166,6 +174,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
|
||||
|
||||
sub.prop(cscene, "sample_clamp_direct")
|
||||
sub.prop(cscene, "sample_clamp_indirect")
|
||||
sub.prop(cscene, "light_sampling_threshold")
|
||||
|
||||
if cscene.progressive == 'PATH' or use_branched_path(context) is False:
|
||||
col = split.column()
|
||||
@@ -760,6 +769,8 @@ class CyclesObject_PT_cycles_settings(CyclesButtonsPanel, Panel):
|
||||
row = col.row()
|
||||
row.active = scene.render.use_simplify and cscene.use_camera_cull
|
||||
row.prop(cob, "use_camera_cull")
|
||||
row.active = scene.render.use_simplify and cscene.use_distance_cull
|
||||
row.prop(cob, "use_distance_cull")
|
||||
|
||||
|
||||
class CYCLES_OT_use_shading_nodes(Operator):
|
||||
@@ -1576,24 +1587,40 @@ class CyclesScene_PT_simplify(CyclesButtonsPanel, Panel):
|
||||
cscene = scene.cycles
|
||||
|
||||
layout.active = rd.use_simplify
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Subdivision")
|
||||
row = col.row(align=True)
|
||||
row.prop(rd, "simplify_subdivision", text="Viewport")
|
||||
row.prop(rd, "simplify_subdivision_render", text="Render")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Child Particles")
|
||||
row = col.row(align=True)
|
||||
row.prop(rd, "simplify_child_particles", text="Viewport")
|
||||
row.prop(rd, "simplify_child_particles_render", text="Render")
|
||||
|
||||
col = layout.column(align=True)
|
||||
split = col.split()
|
||||
sub = split.column()
|
||||
sub.label(text="Texture Limit Viewport")
|
||||
sub.prop(cscene, "texture_limit", text="")
|
||||
sub = split.column()
|
||||
sub.label(text="Texture Limit Render")
|
||||
sub.prop(cscene, "texture_limit_render", text="")
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Viewport:")
|
||||
col.prop(rd, "simplify_subdivision", text="Subdivision")
|
||||
col.prop(rd, "simplify_child_particles", text="Child Particles")
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Render:")
|
||||
col.prop(rd, "simplify_subdivision_render", text="Subdivision")
|
||||
col.prop(rd, "simplify_child_particles_render", text="Child Particles")
|
||||
|
||||
col = layout.column()
|
||||
col.prop(cscene, "use_camera_cull")
|
||||
subsub = col.column()
|
||||
subsub.active = cscene.use_camera_cull
|
||||
subsub.prop(cscene, "camera_cull_margin")
|
||||
row = col.row()
|
||||
row.active = cscene.use_camera_cull
|
||||
row.prop(cscene, "camera_cull_margin")
|
||||
|
||||
col = split.column()
|
||||
col.prop(cscene, "use_distance_cull")
|
||||
row = col.row()
|
||||
row.active = cscene.use_distance_cull
|
||||
row.prop(cscene, "distance_cull_margin", text="Distance")
|
||||
|
||||
def draw_device(self, context):
|
||||
scene = context.scene
|
||||
@@ -1605,9 +1632,11 @@ def draw_device(self, context):
|
||||
|
||||
layout.prop(cscene, "feature_set")
|
||||
|
||||
device_type = context.user_preferences.system.compute_device_type
|
||||
if device_type in {'CUDA', 'OPENCL', 'NETWORK'}:
|
||||
layout.prop(cscene, "device")
|
||||
split = layout.split(percentage=1/3)
|
||||
split.label("Device:")
|
||||
row = split.row()
|
||||
row.active = show_device_selection(context)
|
||||
row.prop(cscene, "device", text="")
|
||||
|
||||
if engine.with_osl() and use_cpu(context):
|
||||
layout.prop(cscene, "shading_system")
|
||||
|
@@ -172,6 +172,24 @@ def custom_bake_remap(scene):
|
||||
|
||||
@persistent
|
||||
def do_versions(self):
|
||||
if bpy.context.user_preferences.version <= (2, 78, 1):
|
||||
prop = bpy.context.user_preferences.addons[__package__].preferences
|
||||
system = bpy.context.user_preferences.system
|
||||
if not prop.is_property_set("compute_device_type"):
|
||||
# Device might not currently be available so this can fail
|
||||
try:
|
||||
if system.legacy_compute_device_type == 1:
|
||||
prop.compute_device_type = 'OPENCL'
|
||||
elif system.legacy_compute_device_type == 2:
|
||||
prop.compute_device_type = 'CUDA'
|
||||
else:
|
||||
prop.compute_device_type = 'NONE'
|
||||
except:
|
||||
pass
|
||||
|
||||
# Init device list for UI
|
||||
prop.get_devices()
|
||||
|
||||
# We don't modify startup file because it assumes to
|
||||
# have all the default values only.
|
||||
if not bpy.data.is_saved:
|
||||
@@ -278,3 +296,9 @@ def do_versions(self):
|
||||
cscene.pixel_filter_type = cscene.filter_type
|
||||
if cscene.filter_type == 'BLACKMAN_HARRIS':
|
||||
cscene.filter_type = 'GAUSSIAN'
|
||||
|
||||
if bpy.data.version <= (2, 78, 2):
|
||||
for scene in bpy.data.scenes:
|
||||
cscene = scene.cycles
|
||||
if not cscene.is_property_set("light_sampling_threshold"):
|
||||
cscene.light_sampling_threshold = 0.0
|
||||
|
@@ -847,7 +847,7 @@ static void sync_mesh_fluid_motion(BL::Object& b_ob, Scene *scene, Mesh *mesh)
|
||||
|
||||
/* Only export previous and next frame, we don't have any in between data. */
|
||||
float motion_times[2] = {-1.0f, 1.0f};
|
||||
for (int step = 0; step < 2; step++) {
|
||||
for(int step = 0; step < 2; step++) {
|
||||
float relative_time = motion_times[step] * scene->motion_shutter_time() * 0.5f;
|
||||
float3 *mP = attr_mP->data_float3() + step*mesh->verts.size();
|
||||
|
||||
@@ -1081,7 +1081,7 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
||||
|
||||
/* fluid motion is exported immediate with mesh, skip here */
|
||||
BL::DomainFluidSettings b_fluid_domain = object_fluid_domain_find(b_ob);
|
||||
if (b_fluid_domain)
|
||||
if(b_fluid_domain)
|
||||
return;
|
||||
|
||||
if(ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
|
||||
|
@@ -88,6 +88,143 @@ static uint object_ray_visibility(BL::Object& b_ob)
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* Culling */
|
||||
|
||||
class BlenderObjectCulling
|
||||
{
|
||||
public:
|
||||
BlenderObjectCulling(Scene *scene, BL::Scene& b_scene)
|
||||
: use_scene_camera_cull(false),
|
||||
use_camera_cull(false),
|
||||
camera_cull_margin(0.0f),
|
||||
use_scene_distance_cull(false),
|
||||
use_distance_cull(false),
|
||||
distance_cull_margin(0.0f)
|
||||
{
|
||||
if(b_scene.render().use_simplify()) {
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
|
||||
use_scene_camera_cull = scene->camera->type != CAMERA_PANORAMA &&
|
||||
!b_scene.render().use_multiview() &&
|
||||
get_boolean(cscene, "use_camera_cull");
|
||||
use_scene_distance_cull = scene->camera->type != CAMERA_PANORAMA &&
|
||||
!b_scene.render().use_multiview() &&
|
||||
get_boolean(cscene, "use_distance_cull");
|
||||
|
||||
camera_cull_margin = get_float(cscene, "camera_cull_margin");
|
||||
distance_cull_margin = get_float(cscene, "distance_cull_margin");
|
||||
|
||||
if (distance_cull_margin == 0.0f) {
|
||||
use_scene_distance_cull = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_object(Scene *scene, BL::Object& b_ob)
|
||||
{
|
||||
if(!use_scene_camera_cull && !use_scene_distance_cull) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
||||
|
||||
use_camera_cull = use_scene_camera_cull && get_boolean(cobject, "use_camera_cull");
|
||||
use_distance_cull = use_scene_distance_cull && get_boolean(cobject, "use_distance_cull");
|
||||
|
||||
if(use_camera_cull || use_distance_cull) {
|
||||
/* Need to have proper projection matrix. */
|
||||
scene->camera->update();
|
||||
}
|
||||
}
|
||||
|
||||
bool test(Scene *scene, BL::Object& b_ob, Transform& tfm)
|
||||
{
|
||||
if(!use_camera_cull && !use_distance_cull) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Compute world space bounding box corners. */
|
||||
float3 bb[8];
|
||||
BL::Array<float, 24> boundbox = b_ob.bound_box();
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
float3 p = make_float3(boundbox[3 * i + 0],
|
||||
boundbox[3 * i + 1],
|
||||
boundbox[3 * i + 2]);
|
||||
bb[i] = transform_point(&tfm, p);
|
||||
}
|
||||
|
||||
bool camera_culled = use_camera_cull && test_camera(scene, bb);
|
||||
bool distance_culled = use_distance_cull && test_distance(scene, bb);
|
||||
|
||||
return ((camera_culled && distance_culled) ||
|
||||
(camera_culled && !use_distance_cull) ||
|
||||
(distance_culled && !use_camera_cull));
|
||||
}
|
||||
|
||||
private:
|
||||
/* TODO(sergey): Not really optimal, consider approaches based on k-DOP in order
|
||||
* to reduce number of objects which are wrongly considered visible.
|
||||
*/
|
||||
bool test_camera(Scene *scene, float3 bb[8])
|
||||
{
|
||||
Camera *cam = scene->camera;
|
||||
Transform& worldtondc = cam->worldtondc;
|
||||
float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
|
||||
bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
bool all_behind = true;
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
float3 p = bb[i];
|
||||
float4 b = make_float4(p.x, p.y, p.z, 1.0f);
|
||||
float4 c = make_float4(dot(worldtondc.x, b),
|
||||
dot(worldtondc.y, b),
|
||||
dot(worldtondc.z, b),
|
||||
dot(worldtondc.w, b));
|
||||
p = float4_to_float3(c / c.w);
|
||||
if(c.z < 0.0f) {
|
||||
p.x = 1.0f - p.x;
|
||||
p.y = 1.0f - p.y;
|
||||
}
|
||||
if(c.z >= -camera_cull_margin) {
|
||||
all_behind = false;
|
||||
}
|
||||
bb_min = min(bb_min, p);
|
||||
bb_max = max(bb_max, p);
|
||||
}
|
||||
if(all_behind) {
|
||||
return true;
|
||||
}
|
||||
return (bb_min.x >= 1.0f + camera_cull_margin ||
|
||||
bb_min.y >= 1.0f + camera_cull_margin ||
|
||||
bb_max.x <= -camera_cull_margin ||
|
||||
bb_max.y <= -camera_cull_margin);
|
||||
}
|
||||
|
||||
bool test_distance(Scene *scene, float3 bb[8])
|
||||
{
|
||||
float3 camera_position = transform_get_column(&scene->camera->matrix, 3);
|
||||
float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
|
||||
bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
|
||||
/* Find min & max points for x & y & z on bounding box */
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
float3 p = bb[i];
|
||||
bb_min = min(bb_min, p);
|
||||
bb_max = max(bb_max, p);
|
||||
}
|
||||
|
||||
float3 closest_point = max(min(bb_max,camera_position),bb_min);
|
||||
return (len_squared(camera_position - closest_point) >
|
||||
distance_cull_margin * distance_cull_margin);
|
||||
}
|
||||
|
||||
bool use_scene_camera_cull;
|
||||
bool use_camera_cull;
|
||||
float camera_cull_margin;
|
||||
bool use_scene_distance_cull;
|
||||
bool use_distance_cull;
|
||||
float distance_cull_margin;
|
||||
};
|
||||
|
||||
/* Light */
|
||||
|
||||
void BlenderSync::sync_light(BL::Object& b_parent,
|
||||
@@ -153,6 +290,7 @@ void BlenderSync::sync_light(BL::Object& b_parent,
|
||||
/* location and (inverted!) direction */
|
||||
light->co = transform_get_column(&tfm, 3);
|
||||
light->dir = -transform_get_column(&tfm, 2);
|
||||
light->tfm = tfm;
|
||||
|
||||
/* shader */
|
||||
vector<Shader*> used_shaders;
|
||||
@@ -234,55 +372,6 @@ void BlenderSync::sync_background_light(bool use_portal)
|
||||
|
||||
/* Object */
|
||||
|
||||
/* TODO(sergey): Not really optimal, consider approaches based on k-DOP in order
|
||||
* to reduce number of objects which are wrongly considered visible.
|
||||
*/
|
||||
static bool object_boundbox_clip(Scene *scene,
|
||||
BL::Object& b_ob,
|
||||
Transform& tfm,
|
||||
float margin)
|
||||
{
|
||||
Camera *cam = scene->camera;
|
||||
Transform& worldtondc = cam->worldtondc;
|
||||
BL::Array<float, 24> boundbox = b_ob.bound_box();
|
||||
float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
|
||||
bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
bool all_behind = true;
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
float3 p = make_float3(boundbox[3 * i + 0],
|
||||
boundbox[3 * i + 1],
|
||||
boundbox[3 * i + 2]);
|
||||
p = transform_point(&tfm, p);
|
||||
|
||||
float4 b = make_float4(p.x, p.y, p.z, 1.0f);
|
||||
float4 c = make_float4(dot(worldtondc.x, b),
|
||||
dot(worldtondc.y, b),
|
||||
dot(worldtondc.z, b),
|
||||
dot(worldtondc.w, b));
|
||||
p = float4_to_float3(c / c.w);
|
||||
if(c.z < 0.0f) {
|
||||
p.x = 1.0f - p.x;
|
||||
p.y = 1.0f - p.y;
|
||||
}
|
||||
if(c.z >= -margin) {
|
||||
all_behind = false;
|
||||
}
|
||||
bb_min = min(bb_min, p);
|
||||
bb_max = max(bb_max, p);
|
||||
}
|
||||
if(!all_behind) {
|
||||
if(bb_min.x >= 1.0f + margin ||
|
||||
bb_min.y >= 1.0f + margin ||
|
||||
bb_max.x <= -margin ||
|
||||
bb_max.y <= -margin)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Object *BlenderSync::sync_object(BL::Object& b_parent,
|
||||
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
|
||||
BL::DupliObject& b_dupli_ob,
|
||||
@@ -290,8 +379,7 @@ Object *BlenderSync::sync_object(BL::Object& b_parent,
|
||||
uint layer_flag,
|
||||
float motion_time,
|
||||
bool hide_tris,
|
||||
bool use_camera_cull,
|
||||
float camera_cull_margin,
|
||||
BlenderObjectCulling& culling,
|
||||
bool *use_portal)
|
||||
{
|
||||
BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
|
||||
@@ -307,11 +395,12 @@ Object *BlenderSync::sync_object(BL::Object& b_parent,
|
||||
}
|
||||
|
||||
/* only interested in object that we can create meshes from */
|
||||
if(!object_is_mesh(b_ob))
|
||||
if(!object_is_mesh(b_ob)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Perform camera space culling. */
|
||||
if(use_camera_cull && object_boundbox_clip(scene, b_ob, tfm, camera_cull_margin)) {
|
||||
/* Perform object culling. */
|
||||
if(culling.test(scene, b_ob, tfm)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -547,17 +636,8 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
|
||||
mesh_motion_synced.clear();
|
||||
}
|
||||
|
||||
bool allow_camera_cull = false;
|
||||
float camera_cull_margin = 0.0f;
|
||||
if(b_scene.render().use_simplify()) {
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
allow_camera_cull = scene->camera->type != CAMERA_PANORAMA &&
|
||||
!b_scene.render().use_multiview() &&
|
||||
get_boolean(cscene, "use_camera_cull");
|
||||
if(allow_camera_cull) {
|
||||
camera_cull_margin = get_float(cscene, "camera_cull_margin");
|
||||
}
|
||||
}
|
||||
/* initialize culling */
|
||||
BlenderObjectCulling culling(scene, b_scene);
|
||||
|
||||
/* object loop */
|
||||
BL::Scene::object_bases_iterator b_base;
|
||||
@@ -589,12 +669,9 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
|
||||
if(!hide) {
|
||||
progress.set_sync_status("Synchronizing object", b_ob.name());
|
||||
|
||||
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
||||
bool use_camera_cull = allow_camera_cull && get_boolean(cobject, "use_camera_cull");
|
||||
if(use_camera_cull) {
|
||||
/* Need to have proper projection matrix. */
|
||||
scene->camera->update();
|
||||
}
|
||||
/* load per-object culling data */
|
||||
culling.init_object(scene, b_ob);
|
||||
|
||||
if(b_ob.is_duplicator() && !object_render_hide_duplis(b_ob)) {
|
||||
/* dupli objects */
|
||||
b_ob.dupli_list_create(b_scene, dupli_settings);
|
||||
@@ -621,8 +698,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
|
||||
ob_layer,
|
||||
motion_time,
|
||||
hide_tris,
|
||||
use_camera_cull,
|
||||
camera_cull_margin,
|
||||
culling,
|
||||
&use_portal);
|
||||
|
||||
/* sync possible particle data, note particle_id
|
||||
@@ -651,8 +727,7 @@ void BlenderSync::sync_objects(BL::SpaceView3D& b_v3d, float motion_time)
|
||||
ob_layer,
|
||||
motion_time,
|
||||
hide_tris,
|
||||
use_camera_cull,
|
||||
camera_cull_margin,
|
||||
culling,
|
||||
&use_portal);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user