WIP: Functions: new local allocator for better memory reuse and performance #104630
|
@ -61,17 +61,17 @@ ContinuationIndentWidth: 4
|
||||||
# This tries to match Blender's style as much as possible. One
|
# This tries to match Blender's style as much as possible. One
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BraceWrapping: {
|
BraceWrapping: {
|
||||||
AfterClass: 'false'
|
AfterClass: 'false',
|
||||||
AfterControlStatement: 'false'
|
AfterControlStatement: 'false',
|
||||||
AfterEnum : 'false'
|
AfterEnum : 'false',
|
||||||
AfterFunction : 'true'
|
AfterFunction : 'true',
|
||||||
AfterNamespace : 'false'
|
AfterNamespace : 'false',
|
||||||
AfterStruct : 'false'
|
AfterStruct : 'false',
|
||||||
AfterUnion : 'false'
|
AfterUnion : 'false',
|
||||||
BeforeCatch : 'true'
|
BeforeCatch : 'true',
|
||||||
BeforeElse : 'true'
|
BeforeElse : 'true',
|
||||||
IndentBraces : 'false'
|
IndentBraces : 'false',
|
||||||
AfterObjCDeclaration: 'true'
|
AfterObjCDeclaration: 'true',
|
||||||
}
|
}
|
||||||
|
|
||||||
# For switch statements, indent the cases.
|
# For switch statements, indent the cases.
|
||||||
|
|
|
@ -501,12 +501,14 @@ endif()
|
||||||
if(NOT APPLE)
|
if(NOT APPLE)
|
||||||
option(WITH_CYCLES_DEVICE_ONEAPI "Enable Cycles oneAPI compute support" OFF)
|
option(WITH_CYCLES_DEVICE_ONEAPI "Enable Cycles oneAPI compute support" OFF)
|
||||||
option(WITH_CYCLES_ONEAPI_BINARIES "Enable Ahead-Of-Time compilation for Cycles oneAPI device" OFF)
|
option(WITH_CYCLES_ONEAPI_BINARIES "Enable Ahead-Of-Time compilation for Cycles oneAPI device" OFF)
|
||||||
|
option(WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION "Switch target of oneAPI implementation from SYCL devices to Host Task (single thread on CPU). This option is only for debugging purposes." OFF)
|
||||||
|
|
||||||
# https://www.intel.com/content/www/us/en/develop/documentation/oneapi-dpcpp-cpp-compiler-dev-guide-and-reference/top/compilation/ahead-of-time-compilation.html
|
# https://www.intel.com/content/www/us/en/develop/documentation/oneapi-dpcpp-cpp-compiler-dev-guide-and-reference/top/compilation/ahead-of-time-compilation.html
|
||||||
# acm-g10 is the target for the first Intel Arc Alchemist GPUs.
|
# acm-g10 is the target for the first Intel Arc Alchemist GPUs.
|
||||||
set(CYCLES_ONEAPI_SPIR64_GEN_DEVICES "acm-g10" CACHE STRING "oneAPI Intel GPU architectures to build binaries for")
|
set(CYCLES_ONEAPI_SPIR64_GEN_DEVICES "acm-g10" CACHE STRING "oneAPI Intel GPU architectures to build binaries for")
|
||||||
set(CYCLES_ONEAPI_SYCL_TARGETS spir64 spir64_gen CACHE STRING "oneAPI targets to build AOT binaries for")
|
set(CYCLES_ONEAPI_SYCL_TARGETS spir64 spir64_gen CACHE STRING "oneAPI targets to build AOT binaries for")
|
||||||
|
|
||||||
|
mark_as_advanced(WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)
|
||||||
mark_as_advanced(CYCLES_ONEAPI_SPIR64_GEN_DEVICES)
|
mark_as_advanced(CYCLES_ONEAPI_SPIR64_GEN_DEVICES)
|
||||||
mark_as_advanced(CYCLES_ONEAPI_SYCL_TARGETS)
|
mark_as_advanced(CYCLES_ONEAPI_SYCL_TARGETS)
|
||||||
endif()
|
endif()
|
||||||
|
@ -830,27 +832,17 @@ endif()
|
||||||
# enable boost for cycles, audaspace or i18n
|
# enable boost for cycles, audaspace or i18n
|
||||||
# otherwise if the user disabled
|
# otherwise if the user disabled
|
||||||
|
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_CYCLES OFF)
|
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_INTERNATIONAL OFF)
|
set_and_warn_dependency(WITH_BOOST WITH_INTERNATIONAL OFF)
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_OPENVDB OFF)
|
set_and_warn_dependency(WITH_BOOST WITH_OPENVDB OFF)
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_OPENCOLORIO OFF)
|
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_QUADRIFLOW OFF)
|
set_and_warn_dependency(WITH_BOOST WITH_QUADRIFLOW OFF)
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_USD OFF)
|
set_and_warn_dependency(WITH_BOOST WITH_USD OFF)
|
||||||
set_and_warn_dependency(WITH_BOOST WITH_ALEMBIC OFF)
|
|
||||||
if(WITH_CYCLES)
|
if(WITH_CYCLES)
|
||||||
|
set_and_warn_dependency(WITH_BOOST WITH_CYCLES_OSL OFF)
|
||||||
set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF)
|
set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF)
|
||||||
endif()
|
endif()
|
||||||
set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF)
|
|
||||||
|
|
||||||
if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR
|
|
||||||
WITH_OPENVDB OR WITH_OPENCOLORIO OR WITH_USD OR WITH_ALEMBIC))
|
|
||||||
message(STATUS "No dependencies need 'WITH_BOOST' forcing WITH_BOOST=OFF")
|
|
||||||
set(WITH_BOOST OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_and_warn_dependency(WITH_TBB WITH_CYCLES OFF)
|
set_and_warn_dependency(WITH_TBB WITH_CYCLES OFF)
|
||||||
set_and_warn_dependency(WITH_TBB WITH_USD OFF)
|
set_and_warn_dependency(WITH_TBB WITH_USD OFF)
|
||||||
set_and_warn_dependency(WITH_TBB WITH_OPENIMAGEDENOISE OFF)
|
|
||||||
set_and_warn_dependency(WITH_TBB WITH_OPENVDB OFF)
|
set_and_warn_dependency(WITH_TBB WITH_OPENVDB OFF)
|
||||||
set_and_warn_dependency(WITH_TBB WITH_MOD_FLUID OFF)
|
set_and_warn_dependency(WITH_TBB WITH_MOD_FLUID OFF)
|
||||||
|
|
||||||
|
@ -859,14 +851,10 @@ set_and_warn_dependency(WITH_OPENVDB WITH_NANOVDB OFF)
|
||||||
|
|
||||||
# OpenVDB and OpenColorIO uses 'half' type from OpenEXR
|
# OpenVDB and OpenColorIO uses 'half' type from OpenEXR
|
||||||
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_OPENVDB OFF)
|
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_OPENVDB OFF)
|
||||||
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_OPENCOLORIO OFF)
|
|
||||||
|
|
||||||
# Haru needs `TIFFFaxBlackCodes` & `TIFFFaxWhiteCodes` symbols from TIFF.
|
# Haru needs `TIFFFaxBlackCodes` & `TIFFFaxWhiteCodes` symbols from TIFF.
|
||||||
set_and_warn_dependency(WITH_IMAGE_TIFF WITH_HARU OFF)
|
set_and_warn_dependency(WITH_IMAGE_TIFF WITH_HARU OFF)
|
||||||
|
|
||||||
# USD needs OpenSubDiv, since that is used by the Cycles Hydra render delegate.
|
|
||||||
set_and_warn_dependency(WITH_OPENSUBDIV WITH_USD OFF)
|
|
||||||
|
|
||||||
# auto enable openimageio for cycles
|
# auto enable openimageio for cycles
|
||||||
if(WITH_CYCLES)
|
if(WITH_CYCLES)
|
||||||
set(WITH_OPENIMAGEIO ON)
|
set(WITH_OPENIMAGEIO ON)
|
||||||
|
@ -880,17 +868,6 @@ else()
|
||||||
set(WITH_CYCLES_OSL OFF)
|
set(WITH_CYCLES_OSL OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# auto enable openimageio linking dependencies
|
|
||||||
if(WITH_OPENIMAGEIO)
|
|
||||||
set(WITH_IMAGE_OPENEXR ON)
|
|
||||||
set(WITH_IMAGE_TIFF ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# auto enable alembic linking dependencies
|
|
||||||
if(WITH_ALEMBIC)
|
|
||||||
set(WITH_IMAGE_OPENEXR ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# don't store paths to libs for portable distribution
|
# don't store paths to libs for portable distribution
|
||||||
if(WITH_INSTALL_PORTABLE)
|
if(WITH_INSTALL_PORTABLE)
|
||||||
set(CMAKE_SKIP_BUILD_RPATH TRUE)
|
set(CMAKE_SKIP_BUILD_RPATH TRUE)
|
||||||
|
@ -1093,14 +1070,6 @@ if(WITH_CYCLES)
|
||||||
"Configure OIIO or disable WITH_CYCLES"
|
"Configure OIIO or disable WITH_CYCLES"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
if(NOT WITH_BOOST)
|
|
||||||
message(
|
|
||||||
FATAL_ERROR
|
|
||||||
"Cycles requires WITH_BOOST, the library may not have been found. "
|
|
||||||
"Configure BOOST or disable WITH_CYCLES"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_CYCLES_OSL)
|
if(WITH_CYCLES_OSL)
|
||||||
if(NOT WITH_LLVM)
|
if(NOT WITH_LLVM)
|
||||||
message(
|
message(
|
||||||
|
@ -2007,24 +1976,6 @@ if(0)
|
||||||
print_all_vars()
|
print_all_vars()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(LIBDIR_STALE)
|
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
# Only search for the path if it's found on the system.
|
|
||||||
if(EXISTS "../lib/linux_centos7_x86_64")
|
|
||||||
set(LIBDIR_STALE "/lib/linux_centos7_x86_64/")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(LIBDIR_STALE)
|
|
||||||
print_cached_vars_containing_value(
|
|
||||||
"${LIBDIR_STALE}"
|
|
||||||
"\nWARNING: found cached references to old library paths!\n"
|
|
||||||
"\nIt is *strongly* recommended to reference updated library paths!\n"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
unset(LIBDIR_STALE)
|
|
||||||
|
|
||||||
# Should be the last step of configuration.
|
# Should be the last step of configuration.
|
||||||
if(POSTCONFIGURE_SCRIPT)
|
if(POSTCONFIGURE_SCRIPT)
|
||||||
include(${POSTCONFIGURE_SCRIPT})
|
include(${POSTCONFIGURE_SCRIPT})
|
||||||
|
|
|
@ -1209,43 +1209,6 @@ function(print_all_vars)
|
||||||
endforeach()
|
endforeach()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Print a list of all cached variables with values containing `contents`.
|
|
||||||
function(print_cached_vars_containing_value
|
|
||||||
contents
|
|
||||||
msg_header
|
|
||||||
msg_footer
|
|
||||||
)
|
|
||||||
set(_list_info)
|
|
||||||
set(_found)
|
|
||||||
get_cmake_property(_vars VARIABLES)
|
|
||||||
foreach(_var ${_vars})
|
|
||||||
if(DEFINED CACHE{${_var}})
|
|
||||||
# Skip "_" prefixed variables, these are used for internal book-keeping,
|
|
||||||
# not under user control.
|
|
||||||
string(FIND "${_var}" "_" _found)
|
|
||||||
if(NOT (_found EQUAL 0))
|
|
||||||
string(FIND "${${_var}}" "${contents}" _found)
|
|
||||||
if(NOT (_found EQUAL -1))
|
|
||||||
if(_found)
|
|
||||||
list(APPEND _list_info "${_var}=${${_var}}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
unset(_var)
|
|
||||||
unset(_vars)
|
|
||||||
unset(_found)
|
|
||||||
if(_list_info)
|
|
||||||
message(${msg_header})
|
|
||||||
foreach(_var ${_list_info})
|
|
||||||
message(" * ${_var}")
|
|
||||||
endforeach()
|
|
||||||
message(${msg_footer})
|
|
||||||
endif()
|
|
||||||
unset(_list_info)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
macro(openmp_delayload
|
macro(openmp_delayload
|
||||||
projectname
|
projectname
|
||||||
)
|
)
|
||||||
|
|
|
@ -86,16 +86,14 @@ endif()
|
||||||
|
|
||||||
if(WITH_USD)
|
if(WITH_USD)
|
||||||
find_package(USD REQUIRED)
|
find_package(USD REQUIRED)
|
||||||
add_bundled_libraries(usd/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(usd/lib)
|
||||||
|
|
||||||
if(WITH_MATERIALX)
|
if(WITH_MATERIALX)
|
||||||
find_package(MaterialX)
|
find_package(MaterialX)
|
||||||
set_and_warn_library_found("MaterialX" MaterialX_FOUND WITH_MATERIALX)
|
set_and_warn_library_found("MaterialX" MaterialX_FOUND WITH_MATERIALX)
|
||||||
if(WITH_MATERIALX)
|
|
||||||
add_bundled_libraries(materialx/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(materialx/lib)
|
||||||
|
|
||||||
if(WITH_VULKAN_BACKEND)
|
if(WITH_VULKAN_BACKEND)
|
||||||
find_package(MoltenVK REQUIRED)
|
find_package(MoltenVK REQUIRED)
|
||||||
|
@ -117,8 +115,8 @@ endif()
|
||||||
|
|
||||||
if(WITH_OPENSUBDIV)
|
if(WITH_OPENSUBDIV)
|
||||||
find_package(OpenSubdiv)
|
find_package(OpenSubdiv)
|
||||||
add_bundled_libraries(opensubdiv/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(opensubdiv/lib)
|
||||||
|
|
||||||
if(WITH_CODEC_SNDFILE)
|
if(WITH_CODEC_SNDFILE)
|
||||||
find_package(SndFile)
|
find_package(SndFile)
|
||||||
|
@ -156,9 +154,9 @@ list(APPEND FREETYPE_LIBRARIES
|
||||||
|
|
||||||
if(WITH_IMAGE_OPENEXR)
|
if(WITH_IMAGE_OPENEXR)
|
||||||
find_package(OpenEXR)
|
find_package(OpenEXR)
|
||||||
add_bundled_libraries(openexr/lib)
|
|
||||||
add_bundled_libraries(imath/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openexr/lib)
|
||||||
|
add_bundled_libraries(imath/lib)
|
||||||
|
|
||||||
if(WITH_CODEC_FFMPEG)
|
if(WITH_CODEC_FFMPEG)
|
||||||
set(FFMPEG_ROOT_DIR ${LIBDIR}/ffmpeg)
|
set(FFMPEG_ROOT_DIR ${LIBDIR}/ffmpeg)
|
||||||
|
@ -270,12 +268,11 @@ if(WITH_BOOST)
|
||||||
set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
|
set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
|
||||||
set(BOOST_DEFINITIONS)
|
set(BOOST_DEFINITIONS)
|
||||||
|
|
||||||
add_bundled_libraries(boost/lib)
|
|
||||||
|
|
||||||
mark_as_advanced(Boost_LIBRARIES)
|
mark_as_advanced(Boost_LIBRARIES)
|
||||||
mark_as_advanced(Boost_INCLUDE_DIRS)
|
mark_as_advanced(Boost_INCLUDE_DIRS)
|
||||||
unset(_boost_FIND_COMPONENTS)
|
unset(_boost_FIND_COMPONENTS)
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(boost/lib)
|
||||||
|
|
||||||
if(WITH_INTERNATIONAL OR WITH_CODEC_FFMPEG)
|
if(WITH_INTERNATIONAL OR WITH_CODEC_FFMPEG)
|
||||||
string(APPEND PLATFORM_LINKFLAGS " -liconv") # boost_locale and ffmpeg needs it !
|
string(APPEND PLATFORM_LINKFLAGS " -liconv") # boost_locale and ffmpeg needs it !
|
||||||
|
@ -297,13 +294,13 @@ if(WITH_OPENIMAGEIO)
|
||||||
)
|
)
|
||||||
set(OPENIMAGEIO_DEFINITIONS "-DOIIO_STATIC_BUILD")
|
set(OPENIMAGEIO_DEFINITIONS "-DOIIO_STATIC_BUILD")
|
||||||
set(OPENIMAGEIO_IDIFF "${LIBDIR}/openimageio/bin/idiff")
|
set(OPENIMAGEIO_IDIFF "${LIBDIR}/openimageio/bin/idiff")
|
||||||
add_bundled_libraries(openimageio/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openimageio/lib)
|
||||||
|
|
||||||
if(WITH_OPENCOLORIO)
|
if(WITH_OPENCOLORIO)
|
||||||
find_package(OpenColorIO 2.0.0 REQUIRED)
|
find_package(OpenColorIO 2.0.0 REQUIRED)
|
||||||
add_bundled_libraries(opencolorio/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(opencolorio/lib)
|
||||||
|
|
||||||
if(WITH_OPENVDB)
|
if(WITH_OPENVDB)
|
||||||
find_package(OpenVDB)
|
find_package(OpenVDB)
|
||||||
|
@ -314,8 +311,8 @@ if(WITH_OPENVDB)
|
||||||
unset(BLOSC_LIBRARIES CACHE)
|
unset(BLOSC_LIBRARIES CACHE)
|
||||||
endif()
|
endif()
|
||||||
set(OPENVDB_DEFINITIONS)
|
set(OPENVDB_DEFINITIONS)
|
||||||
add_bundled_libraries(openvdb/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openvdb/lib)
|
||||||
|
|
||||||
if(WITH_NANOVDB)
|
if(WITH_NANOVDB)
|
||||||
find_package(NanoVDB)
|
find_package(NanoVDB)
|
||||||
|
@ -363,8 +360,8 @@ endif()
|
||||||
|
|
||||||
if(WITH_TBB)
|
if(WITH_TBB)
|
||||||
find_package(TBB REQUIRED)
|
find_package(TBB REQUIRED)
|
||||||
add_bundled_libraries(tbb/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(tbb/lib)
|
||||||
|
|
||||||
if(WITH_POTRACE)
|
if(WITH_POTRACE)
|
||||||
find_package(Potrace REQUIRED)
|
find_package(Potrace REQUIRED)
|
||||||
|
@ -382,9 +379,9 @@ if(WITH_OPENMP)
|
||||||
set(OpenMP_LIBRARY_DIR "${LIBDIR}/openmp/lib/")
|
set(OpenMP_LIBRARY_DIR "${LIBDIR}/openmp/lib/")
|
||||||
set(OpenMP_LINKER_FLAGS "-L'${OpenMP_LIBRARY_DIR}' -lomp")
|
set(OpenMP_LINKER_FLAGS "-L'${OpenMP_LIBRARY_DIR}' -lomp")
|
||||||
set(OpenMP_LIBRARY "${OpenMP_LIBRARY_DIR}/libomp.dylib")
|
set(OpenMP_LIBRARY "${OpenMP_LIBRARY_DIR}/libomp.dylib")
|
||||||
add_bundled_libraries(openmp/lib)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openmp/lib)
|
||||||
|
|
||||||
if(WITH_XR_OPENXR)
|
if(WITH_XR_OPENXR)
|
||||||
find_package(XR_OpenXR_SDK REQUIRED)
|
find_package(XR_OpenXR_SDK REQUIRED)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
# Auto update existing CMake caches for new libraries
|
# Auto update existing CMake caches for new libraries
|
||||||
|
|
||||||
|
# Clear cached variables whose name matches `pattern`.
|
||||||
function(unset_cache_variables pattern)
|
function(unset_cache_variables pattern)
|
||||||
get_cmake_property(_cache_variables CACHE_VARIABLES)
|
get_cmake_property(_cache_variables CACHE_VARIABLES)
|
||||||
foreach(_cache_variable ${_cache_variables})
|
foreach(_cache_variable ${_cache_variables})
|
||||||
|
@ -12,6 +13,30 @@ function(unset_cache_variables pattern)
|
||||||
endforeach()
|
endforeach()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Clear cached variables with values containing `contents`.
|
||||||
|
function(unset_cached_varables_containting contents msg)
|
||||||
|
get_cmake_property(_cache_variables CACHE_VARIABLES)
|
||||||
|
set(_found)
|
||||||
|
set(_print_msg)
|
||||||
|
foreach(_cache_variable ${_cache_variables})
|
||||||
|
# Skip "_" prefixed variables, these are used for internal book-keeping,
|
||||||
|
# not under user control.
|
||||||
|
string(FIND "${_cache_variable}" "_" _found)
|
||||||
|
if(NOT (_found EQUAL 0))
|
||||||
|
string(FIND "${${_cache_variable}}" "${contents}" _found)
|
||||||
|
if(NOT (_found EQUAL -1))
|
||||||
|
if(_found)
|
||||||
|
unset(${_cache_variable} CACHE)
|
||||||
|
set(_print_msg ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if(_print_msg)
|
||||||
|
message(STATUS ${msg})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
# Detect update from 3.1 to 3.2 libs.
|
# Detect update from 3.1 to 3.2 libs.
|
||||||
if(UNIX AND
|
if(UNIX AND
|
||||||
DEFINED OPENEXR_VERSION AND
|
DEFINED OPENEXR_VERSION AND
|
||||||
|
@ -63,3 +88,13 @@ if(UNIX AND
|
||||||
unset_cache_variables("^TBB")
|
unset_cache_variables("^TBB")
|
||||||
unset_cache_variables("^USD")
|
unset_cache_variables("^USD")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(UNIX AND (NOT APPLE) AND LIBDIR AND (EXISTS ${LIBDIR}))
|
||||||
|
# Only search for the path if it's found on the system.
|
||||||
|
set(_libdir_stale "/lib/linux_centos7_x86_64/")
|
||||||
|
unset_cached_varables_containting(
|
||||||
|
"${_libdir_stale}"
|
||||||
|
"Auto clearing old ${_libdir_stale} paths from CMake configuration"
|
||||||
|
)
|
||||||
|
unset(_libdir_stale)
|
||||||
|
endif()
|
||||||
|
|
|
@ -166,11 +166,9 @@ endif()
|
||||||
if(WITH_IMAGE_OPENEXR)
|
if(WITH_IMAGE_OPENEXR)
|
||||||
find_package_wrapper(OpenEXR) # our own module
|
find_package_wrapper(OpenEXR) # our own module
|
||||||
set_and_warn_library_found("OpenEXR" OPENEXR_FOUND WITH_IMAGE_OPENEXR)
|
set_and_warn_library_found("OpenEXR" OPENEXR_FOUND WITH_IMAGE_OPENEXR)
|
||||||
if(WITH_IMAGE_OPENEXR)
|
|
||||||
add_bundled_libraries(openexr/lib)
|
|
||||||
add_bundled_libraries(imath/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openexr/lib)
|
||||||
|
add_bundled_libraries(imath/lib)
|
||||||
|
|
||||||
if(WITH_IMAGE_OPENJPEG)
|
if(WITH_IMAGE_OPENJPEG)
|
||||||
find_package_wrapper(OpenJPEG)
|
find_package_wrapper(OpenJPEG)
|
||||||
|
@ -328,11 +326,8 @@ endif()
|
||||||
if(WITH_OPENVDB)
|
if(WITH_OPENVDB)
|
||||||
find_package(OpenVDB)
|
find_package(OpenVDB)
|
||||||
set_and_warn_library_found("OpenVDB" OPENVDB_FOUND WITH_OPENVDB)
|
set_and_warn_library_found("OpenVDB" OPENVDB_FOUND WITH_OPENVDB)
|
||||||
|
|
||||||
if(WITH_OPENVDB)
|
|
||||||
add_bundled_libraries(openvdb/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openvdb/lib)
|
||||||
|
|
||||||
if(WITH_NANOVDB)
|
if(WITH_NANOVDB)
|
||||||
find_package_wrapper(NanoVDB)
|
find_package_wrapper(NanoVDB)
|
||||||
|
@ -351,18 +346,14 @@ endif()
|
||||||
if(WITH_USD)
|
if(WITH_USD)
|
||||||
find_package_wrapper(USD)
|
find_package_wrapper(USD)
|
||||||
set_and_warn_library_found("USD" USD_FOUND WITH_USD)
|
set_and_warn_library_found("USD" USD_FOUND WITH_USD)
|
||||||
if(WITH_USD)
|
|
||||||
add_bundled_libraries(usd/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(usd/lib)
|
||||||
|
|
||||||
if(WITH_MATERIALX)
|
if(WITH_MATERIALX)
|
||||||
find_package_wrapper(MaterialX)
|
find_package_wrapper(MaterialX)
|
||||||
set_and_warn_library_found("MaterialX" MaterialX_FOUND WITH_MATERIALX)
|
set_and_warn_library_found("MaterialX" MaterialX_FOUND WITH_MATERIALX)
|
||||||
if(WITH_MATERIALX)
|
|
||||||
add_bundled_libraries(materialx/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(materialx/lib)
|
||||||
|
|
||||||
if(WITH_BOOST)
|
if(WITH_BOOST)
|
||||||
# uses in build instructions to override include and library variables
|
# uses in build instructions to override include and library variables
|
||||||
|
@ -418,9 +409,8 @@ if(WITH_BOOST)
|
||||||
find_package(IcuLinux)
|
find_package(IcuLinux)
|
||||||
list(APPEND BOOST_LIBRARIES ${ICU_LIBRARIES})
|
list(APPEND BOOST_LIBRARIES ${ICU_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_bundled_libraries(boost/lib)
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(boost/lib)
|
||||||
|
|
||||||
if(WITH_PUGIXML)
|
if(WITH_PUGIXML)
|
||||||
find_package_wrapper(PugiXML)
|
find_package_wrapper(PugiXML)
|
||||||
|
@ -455,21 +445,16 @@ if(WITH_OPENIMAGEIO)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_and_warn_library_found("OPENIMAGEIO" OPENIMAGEIO_FOUND WITH_OPENIMAGEIO)
|
set_and_warn_library_found("OPENIMAGEIO" OPENIMAGEIO_FOUND WITH_OPENIMAGEIO)
|
||||||
if(WITH_OPENIMAGEIO)
|
|
||||||
add_bundled_libraries(openimageio/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(openimageio/lib)
|
||||||
|
|
||||||
if(WITH_OPENCOLORIO)
|
if(WITH_OPENCOLORIO)
|
||||||
find_package_wrapper(OpenColorIO 2.0.0)
|
find_package_wrapper(OpenColorIO 2.0.0)
|
||||||
|
|
||||||
set(OPENCOLORIO_DEFINITIONS)
|
set(OPENCOLORIO_DEFINITIONS)
|
||||||
set_and_warn_library_found("OpenColorIO" OPENCOLORIO_FOUND WITH_OPENCOLORIO)
|
set_and_warn_library_found("OpenColorIO" OPENCOLORIO_FOUND WITH_OPENCOLORIO)
|
||||||
|
|
||||||
if(WITH_OPENCOLORIO)
|
|
||||||
add_bundled_libraries(opencolorio/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(opencolorio/lib)
|
||||||
|
|
||||||
if(WITH_CYCLES AND WITH_CYCLES_EMBREE)
|
if(WITH_CYCLES AND WITH_CYCLES_EMBREE)
|
||||||
find_package(Embree 3.8.0 REQUIRED)
|
find_package(Embree 3.8.0 REQUIRED)
|
||||||
|
@ -510,18 +495,14 @@ if(WITH_OPENSUBDIV)
|
||||||
set(OPENSUBDIV_LIBPATH) # TODO, remove and reference the absolute path everywhere
|
set(OPENSUBDIV_LIBPATH) # TODO, remove and reference the absolute path everywhere
|
||||||
|
|
||||||
set_and_warn_library_found("OpenSubdiv" OPENSUBDIV_FOUND WITH_OPENSUBDIV)
|
set_and_warn_library_found("OpenSubdiv" OPENSUBDIV_FOUND WITH_OPENSUBDIV)
|
||||||
if(WITH_OPENSUBDIV)
|
|
||||||
add_bundled_libraries(opensubdiv/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(opensubdiv/lib)
|
||||||
|
|
||||||
if(WITH_TBB)
|
if(WITH_TBB)
|
||||||
find_package_wrapper(TBB)
|
find_package_wrapper(TBB)
|
||||||
set_and_warn_library_found("TBB" TBB_FOUND WITH_TBB)
|
set_and_warn_library_found("TBB" TBB_FOUND WITH_TBB)
|
||||||
if(WITH_TBB)
|
|
||||||
add_bundled_libraries(tbb/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
add_bundled_libraries(tbb/lib)
|
||||||
|
|
||||||
if(WITH_XR_OPENXR)
|
if(WITH_XR_OPENXR)
|
||||||
find_package(XR_OpenXR_SDK)
|
find_package(XR_OpenXR_SDK)
|
||||||
|
@ -1013,18 +994,6 @@ endfunction()
|
||||||
|
|
||||||
configure_atomic_lib_if_needed()
|
configure_atomic_lib_if_needed()
|
||||||
|
|
||||||
# Handle library inter-dependencies.
|
|
||||||
# FIXME: find a better place to handle inter-library dependencies.
|
|
||||||
# This is done near the end of the file to ensure bundled libraries are not added multiple times.
|
|
||||||
if(WITH_USD)
|
|
||||||
if(NOT WITH_OPENIMAGEIO)
|
|
||||||
add_bundled_libraries(openimageio/lib)
|
|
||||||
endif()
|
|
||||||
if(NOT WITH_OPENVDB)
|
|
||||||
add_bundled_libraries(openvdb/lib)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(PLATFORM_BUNDLED_LIBRARIES)
|
if(PLATFORM_BUNDLED_LIBRARIES)
|
||||||
# For the installed Python module and installed Blender executable, we set the
|
# For the installed Python module and installed Blender executable, we set the
|
||||||
# rpath to the relative path where the install step will copy the shared libraries.
|
# rpath to the relative path where the install step will copy the shared libraries.
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
/* SPDX-License-Identifier: Apache-2.0
|
/* SPDX-License-Identifier: Apache-2.0
|
||||||
* Copyright 2021-2022 Blender Foundation */
|
* Copyright 2021-2022 Blender Foundation */
|
||||||
|
|
||||||
#include "blender/display_driver.h"
|
|
||||||
|
|
||||||
#include "device/device.h"
|
|
||||||
#include "util/log.h"
|
|
||||||
#include "util/math.h"
|
|
||||||
|
|
||||||
#include "GPU_context.h"
|
#include "GPU_context.h"
|
||||||
#include "GPU_immediate.h"
|
#include "GPU_immediate.h"
|
||||||
#include "GPU_shader.h"
|
#include "GPU_shader.h"
|
||||||
|
@ -15,6 +9,12 @@
|
||||||
|
|
||||||
#include "RE_engine.h"
|
#include "RE_engine.h"
|
||||||
|
|
||||||
|
#include "blender/display_driver.h"
|
||||||
|
|
||||||
|
#include "device/device.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/math.h"
|
||||||
|
|
||||||
CCL_NAMESPACE_BEGIN
|
CCL_NAMESPACE_BEGIN
|
||||||
|
|
||||||
/* --------------------------------------------------------------------
|
/* --------------------------------------------------------------------
|
||||||
|
|
|
@ -163,6 +163,9 @@ if(WITH_CYCLES_DEVICE_METAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_CYCLES_DEVICE_ONEAPI)
|
if(WITH_CYCLES_DEVICE_ONEAPI)
|
||||||
|
if(WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)
|
||||||
|
add_definitions(-DWITH_ONEAPI_SYCL_HOST_TASK)
|
||||||
|
endif()
|
||||||
if(WITH_CYCLES_ONEAPI_BINARIES)
|
if(WITH_CYCLES_ONEAPI_BINARIES)
|
||||||
set(cycles_kernel_oneapi_lib_suffix "_aot")
|
set(cycles_kernel_oneapi_lib_suffix "_aot")
|
||||||
else()
|
else()
|
||||||
|
|
|
@ -167,6 +167,17 @@ class Device {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Request cancellation of any long-running work. */
|
||||||
|
virtual void cancel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if device is ready for rendering, or report status if not. */
|
||||||
|
virtual bool is_ready(string & /*status*/) const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* GPU device only functions.
|
/* GPU device only functions.
|
||||||
* These may not be used on CPU or multi-devices. */
|
* These may not be used on CPU or multi-devices. */
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,20 @@ class MetalDevice : public Device {
|
||||||
|
|
||||||
bool use_metalrt = false;
|
bool use_metalrt = false;
|
||||||
MetalPipelineType kernel_specialization_level = PSO_GENERIC;
|
MetalPipelineType kernel_specialization_level = PSO_GENERIC;
|
||||||
std::atomic_bool async_compile_and_load = false;
|
|
||||||
|
int device_id = 0;
|
||||||
|
|
||||||
|
static thread_mutex existing_devices_mutex;
|
||||||
|
static std::map<int, MetalDevice *> active_device_ids;
|
||||||
|
|
||||||
|
static bool is_device_cancelled(int device_id);
|
||||||
|
|
||||||
|
static MetalDevice *get_device_by_ID(int device_idID,
|
||||||
|
thread_scoped_lock &existing_devices_mutex_lock);
|
||||||
|
|
||||||
|
virtual bool is_ready(string &status) const override;
|
||||||
|
|
||||||
|
virtual void cancel() override;
|
||||||
|
|
||||||
virtual BVHLayoutMask get_bvh_layout_mask() const override;
|
virtual BVHLayoutMask get_bvh_layout_mask() const override;
|
||||||
|
|
||||||
|
@ -92,14 +105,12 @@ class MetalDevice : public Device {
|
||||||
|
|
||||||
bool use_adaptive_compilation();
|
bool use_adaptive_compilation();
|
||||||
|
|
||||||
|
bool make_source_and_check_if_compile_needed(MetalPipelineType pso_type);
|
||||||
|
|
||||||
void make_source(MetalPipelineType pso_type, const uint kernel_features);
|
void make_source(MetalPipelineType pso_type, const uint kernel_features);
|
||||||
|
|
||||||
virtual bool load_kernels(const uint kernel_features) override;
|
virtual bool load_kernels(const uint kernel_features) override;
|
||||||
|
|
||||||
void reserve_local_memory(const uint kernel_features);
|
|
||||||
|
|
||||||
void init_host_memory();
|
|
||||||
|
|
||||||
void load_texture_info();
|
void load_texture_info();
|
||||||
|
|
||||||
void erase_allocation(device_memory &mem);
|
void erase_allocation(device_memory &mem);
|
||||||
|
@ -112,7 +123,7 @@ class MetalDevice : public Device {
|
||||||
|
|
||||||
virtual void optimize_for_scene(Scene *scene) override;
|
virtual void optimize_for_scene(Scene *scene) override;
|
||||||
|
|
||||||
bool compile_and_load(MetalPipelineType pso_type);
|
static void compile_and_load(int device_id, MetalPipelineType pso_type);
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* low-level memory management */
|
/* low-level memory management */
|
||||||
|
|
|
@ -13,10 +13,32 @@
|
||||||
# include "util/path.h"
|
# include "util/path.h"
|
||||||
# include "util/time.h"
|
# include "util/time.h"
|
||||||
|
|
||||||
|
# include <crt_externs.h>
|
||||||
|
|
||||||
CCL_NAMESPACE_BEGIN
|
CCL_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class MetalDevice;
|
class MetalDevice;
|
||||||
|
|
||||||
|
thread_mutex MetalDevice::existing_devices_mutex;
|
||||||
|
std::map<int, MetalDevice *> MetalDevice::active_device_ids;
|
||||||
|
|
||||||
|
/* Thread-safe device access for async work. Calling code must pass an appropriatelty scoped lock
|
||||||
|
* to existing_devices_mutex to safeguard against destruction of the returned instance. */
|
||||||
|
MetalDevice *MetalDevice::get_device_by_ID(int ID, thread_scoped_lock &existing_devices_mutex_lock)
|
||||||
|
{
|
||||||
|
auto it = active_device_ids.find(ID);
|
||||||
|
if (it != active_device_ids.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetalDevice::is_device_cancelled(int ID)
|
||||||
|
{
|
||||||
|
thread_scoped_lock lock(existing_devices_mutex);
|
||||||
|
return get_device_by_ID(ID, lock) == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
BVHLayoutMask MetalDevice::get_bvh_layout_mask() const
|
BVHLayoutMask MetalDevice::get_bvh_layout_mask() const
|
||||||
{
|
{
|
||||||
return use_metalrt ? BVH_LAYOUT_METAL : BVH_LAYOUT_BVH2;
|
return use_metalrt ? BVH_LAYOUT_METAL : BVH_LAYOUT_BVH2;
|
||||||
|
@ -40,6 +62,15 @@ void MetalDevice::set_error(const string &error)
|
||||||
MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler)
|
MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler)
|
||||||
: Device(info, stats, profiler), texture_info(this, "texture_info", MEM_GLOBAL)
|
: Device(info, stats, profiler), texture_info(this, "texture_info", MEM_GLOBAL)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
/* Assign an ID for this device which we can use to query whether async shader compilation
|
||||||
|
* requests are still relevant. */
|
||||||
|
thread_scoped_lock lock(existing_devices_mutex);
|
||||||
|
static int existing_devices_counter = 1;
|
||||||
|
device_id = existing_devices_counter++;
|
||||||
|
active_device_ids[device_id] = this;
|
||||||
|
}
|
||||||
|
|
||||||
mtlDevId = info.num;
|
mtlDevId = info.num;
|
||||||
|
|
||||||
/* select chosen device */
|
/* select chosen device */
|
||||||
|
@ -57,7 +88,6 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
||||||
if (@available(macos 11.0, *)) {
|
if (@available(macos 11.0, *)) {
|
||||||
if ([mtlDevice hasUnifiedMemory]) {
|
if ([mtlDevice hasUnifiedMemory]) {
|
||||||
default_storage_mode = MTLResourceStorageModeShared;
|
default_storage_mode = MTLResourceStorageModeShared;
|
||||||
init_host_memory();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +211,13 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
||||||
|
|
||||||
MetalDevice::~MetalDevice()
|
MetalDevice::~MetalDevice()
|
||||||
{
|
{
|
||||||
|
/* Cancel any async shader compilations that are in flight. */
|
||||||
|
cancel();
|
||||||
|
|
||||||
|
/* This lock safeguards against destruction during use (see other uses of
|
||||||
|
* existing_devices_mutex). */
|
||||||
|
thread_scoped_lock lock(existing_devices_mutex);
|
||||||
|
|
||||||
for (auto &tex : texture_slot_map) {
|
for (auto &tex : texture_slot_map) {
|
||||||
if (tex) {
|
if (tex) {
|
||||||
[tex release];
|
[tex release];
|
||||||
|
@ -326,21 +363,66 @@ bool MetalDevice::load_kernels(const uint _kernel_features)
|
||||||
* active, but may still need to be rendered without motion blur if that isn't active as well. */
|
* active, but may still need to be rendered without motion blur if that isn't active as well. */
|
||||||
motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION;
|
motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||||
|
|
||||||
bool result = compile_and_load(PSO_GENERIC);
|
/* Only request generic kernels if they aren't cached in memory. */
|
||||||
|
if (make_source_and_check_if_compile_needed(PSO_GENERIC)) {
|
||||||
|
/* If needed, load them asynchronously in order to responsively message progess to the user. */
|
||||||
|
int this_device_id = this->device_id;
|
||||||
|
auto compile_kernels_fn = ^() {
|
||||||
|
compile_and_load(this_device_id, PSO_GENERIC);
|
||||||
|
};
|
||||||
|
|
||||||
reserve_local_memory(kernel_features);
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||||
return result;
|
compile_kernels_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDevice::compile_and_load(MetalPipelineType pso_type)
|
bool MetalDevice::make_source_and_check_if_compile_needed(MetalPipelineType pso_type)
|
||||||
{
|
{
|
||||||
make_source(pso_type, kernel_features);
|
if (this->source[pso_type].empty()) {
|
||||||
|
make_source(pso_type, kernel_features);
|
||||||
if (!MetalDeviceKernels::should_load_kernels(this, pso_type)) {
|
|
||||||
/* We already have a full set of matching pipelines which are cached or queued. */
|
|
||||||
metal_printf("%s kernels already requested\n", kernel_type_as_string(pso_type));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return MetalDeviceKernels::should_load_kernels(this, pso_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
|
||||||
|
{
|
||||||
|
/* Thread-safe front-end compilation. Typically the MSL->AIR compilation can take a few seconds,
|
||||||
|
* so we avoid blocking device teardown if the user cancels a render immediately.
|
||||||
|
*/
|
||||||
|
|
||||||
|
id<MTLDevice> mtlDevice;
|
||||||
|
string source;
|
||||||
|
MetalGPUVendor device_vendor;
|
||||||
|
|
||||||
|
/* Safely gather any state required for the MSL->AIR compilation. */
|
||||||
|
{
|
||||||
|
thread_scoped_lock lock(existing_devices_mutex);
|
||||||
|
|
||||||
|
/* Check whether the device still exists. */
|
||||||
|
MetalDevice *instance = get_device_by_ID(device_id, lock);
|
||||||
|
if (!instance) {
|
||||||
|
metal_printf("Ignoring %s compilation request - device no longer exists\n",
|
||||||
|
kernel_type_as_string(pso_type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instance->make_source_and_check_if_compile_needed(pso_type)) {
|
||||||
|
/* We already have a full set of matching pipelines which are cached or queued. Return early
|
||||||
|
* to avoid redundant MTLLibrary compilation. */
|
||||||
|
metal_printf("Ignoreing %s compilation request - kernels already requested\n",
|
||||||
|
kernel_type_as_string(pso_type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtlDevice = instance->mtlDevice;
|
||||||
|
device_vendor = instance->device_vendor;
|
||||||
|
source = instance->source[pso_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform the actual compilation using our cached context. The MetalDevice can safely destruct
|
||||||
|
* in this time. */
|
||||||
|
|
||||||
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
||||||
|
|
||||||
|
@ -359,20 +441,15 @@ bool MetalDevice::compile_and_load(MetalPipelineType pso_type)
|
||||||
|
|
||||||
if (getenv("CYCLES_METAL_PROFILING") || getenv("CYCLES_METAL_DEBUG")) {
|
if (getenv("CYCLES_METAL_PROFILING") || getenv("CYCLES_METAL_DEBUG")) {
|
||||||
path_write_text(path_cache_get(string_printf("%s.metal", kernel_type_as_string(pso_type))),
|
path_write_text(path_cache_get(string_printf("%s.metal", kernel_type_as_string(pso_type))),
|
||||||
source[pso_type]);
|
source);
|
||||||
}
|
}
|
||||||
|
|
||||||
const double starttime = time_dt();
|
const double starttime = time_dt();
|
||||||
|
|
||||||
NSError *error = NULL;
|
NSError *error = NULL;
|
||||||
mtlLibrary[pso_type] = [mtlDevice newLibraryWithSource:@(source[pso_type].c_str())
|
id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str())
|
||||||
options:options
|
options:options
|
||||||
error:&error];
|
error:&error];
|
||||||
|
|
||||||
if (!mtlLibrary[pso_type]) {
|
|
||||||
NSString *err = [error localizedDescription];
|
|
||||||
set_error(string_printf("Failed to compile library:\n%s", [err UTF8String]));
|
|
||||||
}
|
|
||||||
|
|
||||||
metal_printf("Front-end compilation finished in %.1f seconds (%s)\n",
|
metal_printf("Front-end compilation finished in %.1f seconds (%s)\n",
|
||||||
time_dt() - starttime,
|
time_dt() - starttime,
|
||||||
|
@ -380,17 +457,21 @@ bool MetalDevice::compile_and_load(MetalPipelineType pso_type)
|
||||||
|
|
||||||
[options release];
|
[options release];
|
||||||
|
|
||||||
return MetalDeviceKernels::load(this, pso_type);
|
/* Save the compiled MTLLibrary and trigger the AIR->PSO builds (if the MetalDevice still
|
||||||
}
|
* exists). */
|
||||||
|
{
|
||||||
void MetalDevice::reserve_local_memory(const uint kernel_features)
|
thread_scoped_lock lock(existing_devices_mutex);
|
||||||
{
|
if (MetalDevice *instance = get_device_by_ID(device_id, lock)) {
|
||||||
/* METAL_WIP - implement this */
|
if (mtlLibrary) {
|
||||||
}
|
instance->mtlLibrary[pso_type] = mtlLibrary;
|
||||||
|
MetalDeviceKernels::load(instance, pso_type);
|
||||||
void MetalDevice::init_host_memory()
|
}
|
||||||
{
|
else {
|
||||||
/* METAL_WIP - implement this */
|
NSString *err = [error localizedDescription];
|
||||||
|
instance->set_error(string_printf("Failed to compile library:\n%s", [err UTF8String]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::load_texture_info()
|
void MetalDevice::load_texture_info()
|
||||||
|
@ -700,55 +781,74 @@ device_ptr MetalDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, siz
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MetalDevice::cancel()
|
||||||
|
{
|
||||||
|
/* Remove this device's ID from the list of active devices. Any pending compilation requests
|
||||||
|
* originating from this session will be cancelled. */
|
||||||
|
thread_scoped_lock lock(existing_devices_mutex);
|
||||||
|
if (device_id) {
|
||||||
|
active_device_ids.erase(device_id);
|
||||||
|
device_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetalDevice::is_ready(string &status) const
|
||||||
|
{
|
||||||
|
int num_loaded = MetalDeviceKernels::get_loaded_kernel_count(this, PSO_GENERIC);
|
||||||
|
if (num_loaded < DEVICE_KERNEL_NUM) {
|
||||||
|
status = string_printf("%d / %d render kernels loaded (may take a few minutes the first time)",
|
||||||
|
num_loaded,
|
||||||
|
DEVICE_KERNEL_NUM);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
metal_printf("MetalDevice::is_ready(...) --> true\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void MetalDevice::optimize_for_scene(Scene *scene)
|
void MetalDevice::optimize_for_scene(Scene *scene)
|
||||||
{
|
{
|
||||||
MetalPipelineType specialization_level = kernel_specialization_level;
|
MetalPipelineType specialization_level = kernel_specialization_level;
|
||||||
|
|
||||||
if (specialization_level < PSO_SPECIALIZED_INTERSECT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PSO_SPECIALIZED_INTERSECT kernels are fast to specialize, so we always load them
|
|
||||||
* synchronously. */
|
|
||||||
compile_and_load(PSO_SPECIALIZED_INTERSECT);
|
|
||||||
|
|
||||||
if (specialization_level < PSO_SPECIALIZED_SHADE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!scene->params.background) {
|
if (!scene->params.background) {
|
||||||
/* Don't load PSO_SPECIALIZED_SHADE kernels during viewport rendering as they are slower to
|
/* In live viewport, don't specialize beyond intersection kernels for responsiveness. */
|
||||||
* build. */
|
specialization_level = (MetalPipelineType)min(specialization_level, PSO_SPECIALIZED_INTERSECT);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PSO_SPECIALIZED_SHADE kernels are slower to specialize, so we load them asynchronously, and
|
/* For responsive rendering, specialize the kernels in the background, and only if there isn't an
|
||||||
* only if there isn't an existing load in flight.
|
* existing "optimize_for_scene" request in flight. */
|
||||||
*/
|
int this_device_id = this->device_id;
|
||||||
auto specialize_shade_fn = ^() {
|
auto specialize_kernels_fn = ^() {
|
||||||
compile_and_load(PSO_SPECIALIZED_SHADE);
|
for (int level = 1; level <= int(specialization_level); level++) {
|
||||||
async_compile_and_load = false;
|
compile_and_load(this_device_id, MetalPipelineType(level));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool async_specialize_shade = true;
|
/* In normal use, we always compile the specialized kernels in the background. */
|
||||||
|
bool specialize_in_background = true;
|
||||||
|
|
||||||
/* Block if a per-kernel profiling is enabled (ensure steady rendering rate). */
|
/* Block if a per-kernel profiling is enabled (ensure steady rendering rate). */
|
||||||
if (getenv("CYCLES_METAL_PROFILING") != nullptr) {
|
if (getenv("CYCLES_METAL_PROFILING") != nullptr) {
|
||||||
async_specialize_shade = false;
|
specialize_in_background = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (async_specialize_shade) {
|
/* Block during benchmark warm-up to ensure kernels are cached prior to the observed run. */
|
||||||
if (!async_compile_and_load) {
|
for (int i = 0; i < *_NSGetArgc(); i++) {
|
||||||
async_compile_and_load = true;
|
if (!strcmp((*_NSGetArgv())[i], "--warm-up")) {
|
||||||
|
specialize_in_background = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (specialize_in_background) {
|
||||||
|
if (!MetalDeviceKernels::any_specialization_happening_now()) {
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||||
specialize_shade_fn);
|
specialize_kernels_fn);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
metal_printf(
|
metal_printf("\"optimize_for_scene\" request already in flight - dropping request\n");
|
||||||
"Async PSO_SPECIALIZED_SHADE load request already in progress - dropping request\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
specialize_shade_fn();
|
specialize_kernels_fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,8 @@ struct MetalKernelPipeline {
|
||||||
|
|
||||||
void compile();
|
void compile();
|
||||||
|
|
||||||
|
int originating_device_id;
|
||||||
|
|
||||||
id<MTLLibrary> mtlLibrary = nil;
|
id<MTLLibrary> mtlLibrary = nil;
|
||||||
MetalPipelineType pso_type;
|
MetalPipelineType pso_type;
|
||||||
string source_md5;
|
string source_md5;
|
||||||
|
@ -94,7 +96,9 @@ struct MetalKernelPipeline {
|
||||||
/* Cache of Metal kernels for each DeviceKernel. */
|
/* Cache of Metal kernels for each DeviceKernel. */
|
||||||
namespace MetalDeviceKernels {
|
namespace MetalDeviceKernels {
|
||||||
|
|
||||||
bool should_load_kernels(MetalDevice *device, MetalPipelineType pso_type);
|
bool any_specialization_happening_now();
|
||||||
|
int get_loaded_kernel_count(MetalDevice const *device, MetalPipelineType pso_type);
|
||||||
|
bool should_load_kernels(MetalDevice const *device, MetalPipelineType pso_type);
|
||||||
bool load(MetalDevice *device, MetalPipelineType pso_type);
|
bool load(MetalDevice *device, MetalPipelineType pso_type);
|
||||||
const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel);
|
const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel);
|
||||||
|
|
||||||
|
|
|
@ -86,23 +86,17 @@ struct ShaderCache {
|
||||||
void load_kernel(DeviceKernel kernel, MetalDevice *device, MetalPipelineType pso_type);
|
void load_kernel(DeviceKernel kernel, MetalDevice *device, MetalPipelineType pso_type);
|
||||||
|
|
||||||
bool should_load_kernel(DeviceKernel device_kernel,
|
bool should_load_kernel(DeviceKernel device_kernel,
|
||||||
MetalDevice *device,
|
MetalDevice const *device,
|
||||||
MetalPipelineType pso_type);
|
MetalPipelineType pso_type);
|
||||||
|
|
||||||
void wait_for_all();
|
void wait_for_all();
|
||||||
|
|
||||||
private:
|
|
||||||
friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice);
|
friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice);
|
||||||
|
|
||||||
void compile_thread_func(int thread_index);
|
void compile_thread_func(int thread_index);
|
||||||
|
|
||||||
using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>;
|
using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>;
|
||||||
|
|
||||||
struct PipelineRequest {
|
|
||||||
MetalKernelPipeline *pipeline = nullptr;
|
|
||||||
std::function<void(MetalKernelPipeline *)> completionHandler;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct OccupancyTuningParameters {
|
struct OccupancyTuningParameters {
|
||||||
int threads_per_threadgroup = 0;
|
int threads_per_threadgroup = 0;
|
||||||
int num_threads_per_block = 0;
|
int num_threads_per_block = 0;
|
||||||
|
@ -113,13 +107,15 @@ struct ShaderCache {
|
||||||
PipelineCollection pipelines[DEVICE_KERNEL_NUM];
|
PipelineCollection pipelines[DEVICE_KERNEL_NUM];
|
||||||
id<MTLDevice> mtlDevice;
|
id<MTLDevice> mtlDevice;
|
||||||
|
|
||||||
bool running = false;
|
static bool running;
|
||||||
std::condition_variable cond_var;
|
std::condition_variable cond_var;
|
||||||
std::deque<PipelineRequest> request_queue;
|
std::deque<MetalKernelPipeline *> request_queue;
|
||||||
std::vector<std::thread> compile_threads;
|
std::vector<std::thread> compile_threads;
|
||||||
std::atomic_int incomplete_requests = 0;
|
std::atomic_int incomplete_requests = 0;
|
||||||
|
std::atomic_int incomplete_specialization_requests = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool ShaderCache::running = true;
|
||||||
std::mutex g_shaderCacheMutex;
|
std::mutex g_shaderCacheMutex;
|
||||||
std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache;
|
std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache;
|
||||||
|
|
||||||
|
@ -137,11 +133,25 @@ ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice)
|
||||||
|
|
||||||
ShaderCache::~ShaderCache()
|
ShaderCache::~ShaderCache()
|
||||||
{
|
{
|
||||||
metal_printf("ShaderCache shutting down with incomplete_requests = %d\n",
|
|
||||||
int(incomplete_requests));
|
|
||||||
|
|
||||||
running = false;
|
running = false;
|
||||||
cond_var.notify_all();
|
cond_var.notify_all();
|
||||||
|
|
||||||
|
int num_incomplete = int(incomplete_requests);
|
||||||
|
if (num_incomplete) {
|
||||||
|
/* Shutting down the app with incomplete shader compilation requests. Give 1 second's grace for
|
||||||
|
* clean shutdown. */
|
||||||
|
metal_printf("ShaderCache busy (incomplete_requests = %d)...\n", num_incomplete);
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
num_incomplete = int(incomplete_requests);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_incomplete) {
|
||||||
|
metal_printf("ShaderCache still busy (incomplete_requests = %d). Terminating...\n",
|
||||||
|
num_incomplete);
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
metal_printf("ShaderCache idle. Shutting down.\n");
|
||||||
for (auto &thread : compile_threads) {
|
for (auto &thread : compile_threads) {
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
|
@ -156,35 +166,69 @@ void ShaderCache::wait_for_all()
|
||||||
|
|
||||||
void ShaderCache::compile_thread_func(int thread_index)
|
void ShaderCache::compile_thread_func(int thread_index)
|
||||||
{
|
{
|
||||||
while (1) {
|
while (running) {
|
||||||
|
|
||||||
/* wait for / acquire next request */
|
/* wait for / acquire next request */
|
||||||
PipelineRequest request;
|
MetalKernelPipeline *pipeline;
|
||||||
{
|
{
|
||||||
thread_scoped_lock lock(cache_mutex);
|
thread_scoped_lock lock(cache_mutex);
|
||||||
cond_var.wait(lock, [&] { return !running || !request_queue.empty(); });
|
cond_var.wait(lock, [&] { return !running || !request_queue.empty(); });
|
||||||
if (!running) {
|
if (!running || request_queue.empty()) {
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request_queue.empty()) {
|
pipeline = request_queue.front();
|
||||||
request = request_queue.front();
|
request_queue.pop_front();
|
||||||
request_queue.pop_front();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* service request */
|
/* Service the request. */
|
||||||
if (request.pipeline) {
|
DeviceKernel device_kernel = pipeline->device_kernel;
|
||||||
request.pipeline->compile();
|
MetalPipelineType pso_type = pipeline->pso_type;
|
||||||
incomplete_requests--;
|
|
||||||
|
if (MetalDevice::is_device_cancelled(pipeline->originating_device_id)) {
|
||||||
|
/* The originating MetalDevice is no longer active, so this request is obsolete. */
|
||||||
|
metal_printf("Cancelling compilation of %s (%s)\n",
|
||||||
|
device_kernel_as_string(device_kernel),
|
||||||
|
kernel_type_as_string(pso_type));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Do the actual compilation. */
|
||||||
|
pipeline->compile();
|
||||||
|
|
||||||
|
thread_scoped_lock lock(cache_mutex);
|
||||||
|
auto &collection = pipelines[device_kernel];
|
||||||
|
|
||||||
|
/* Cache up to 3 kernel variants with the same pso_type in memory, purging oldest first. */
|
||||||
|
int max_entries_of_same_pso_type = 3;
|
||||||
|
for (int i = (int)collection.size() - 1; i >= 0; i--) {
|
||||||
|
if (collection[i]->pso_type == pso_type) {
|
||||||
|
max_entries_of_same_pso_type -= 1;
|
||||||
|
if (max_entries_of_same_pso_type == 0) {
|
||||||
|
metal_printf("Purging oldest %s:%s kernel from ShaderCache\n",
|
||||||
|
kernel_type_as_string(pso_type),
|
||||||
|
device_kernel_as_string(device_kernel));
|
||||||
|
collection.erase(collection.begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collection.push_back(unique_ptr<MetalKernelPipeline>(pipeline));
|
||||||
|
}
|
||||||
|
incomplete_requests--;
|
||||||
|
if (pso_type != PSO_GENERIC) {
|
||||||
|
incomplete_specialization_requests--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderCache::should_load_kernel(DeviceKernel device_kernel,
|
bool ShaderCache::should_load_kernel(DeviceKernel device_kernel,
|
||||||
MetalDevice *device,
|
MetalDevice const *device,
|
||||||
MetalPipelineType pso_type)
|
MetalPipelineType pso_type)
|
||||||
{
|
{
|
||||||
|
if (!running) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) {
|
if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) {
|
||||||
/* Skip megakernel. */
|
/* Skip megakernel. */
|
||||||
return false;
|
return false;
|
||||||
|
@ -240,7 +284,6 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
||||||
/* create compiler threads on first run */
|
/* create compiler threads on first run */
|
||||||
thread_scoped_lock lock(cache_mutex);
|
thread_scoped_lock lock(cache_mutex);
|
||||||
if (compile_threads.empty()) {
|
if (compile_threads.empty()) {
|
||||||
running = true;
|
|
||||||
for (int i = 0; i < max_mtlcompiler_threads; i++) {
|
for (int i = 0; i < max_mtlcompiler_threads; i++) {
|
||||||
compile_threads.push_back(std::thread([&] { compile_thread_func(i); }));
|
compile_threads.push_back(std::thread([&] { compile_thread_func(i); }));
|
||||||
}
|
}
|
||||||
|
@ -252,53 +295,39 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
||||||
}
|
}
|
||||||
|
|
||||||
incomplete_requests++;
|
incomplete_requests++;
|
||||||
|
if (pso_type != PSO_GENERIC) {
|
||||||
|
incomplete_specialization_requests++;
|
||||||
|
}
|
||||||
|
|
||||||
PipelineRequest request;
|
MetalKernelPipeline *pipeline = new MetalKernelPipeline;
|
||||||
request.pipeline = new MetalKernelPipeline;
|
|
||||||
memcpy(&request.pipeline->kernel_data_,
|
/* Keep track of the originating device's ID so that we can cancel requests if the device ceases
|
||||||
&device->launch_params.data,
|
* to be active. */
|
||||||
sizeof(request.pipeline->kernel_data_));
|
pipeline->originating_device_id = device->device_id;
|
||||||
request.pipeline->pso_type = pso_type;
|
memcpy(&pipeline->kernel_data_, &device->launch_params.data, sizeof(pipeline->kernel_data_));
|
||||||
request.pipeline->mtlDevice = mtlDevice;
|
pipeline->pso_type = pso_type;
|
||||||
request.pipeline->source_md5 = device->source_md5[pso_type];
|
pipeline->mtlDevice = mtlDevice;
|
||||||
request.pipeline->mtlLibrary = device->mtlLibrary[pso_type];
|
pipeline->source_md5 = device->source_md5[pso_type];
|
||||||
request.pipeline->device_kernel = device_kernel;
|
pipeline->mtlLibrary = device->mtlLibrary[pso_type];
|
||||||
request.pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
|
pipeline->device_kernel = device_kernel;
|
||||||
|
pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
|
||||||
|
|
||||||
if (occupancy_tuning[device_kernel].threads_per_threadgroup) {
|
if (occupancy_tuning[device_kernel].threads_per_threadgroup) {
|
||||||
request.pipeline->threads_per_threadgroup =
|
pipeline->threads_per_threadgroup =
|
||||||
occupancy_tuning[device_kernel].threads_per_threadgroup;
|
occupancy_tuning[device_kernel].threads_per_threadgroup;
|
||||||
request.pipeline->num_threads_per_block =
|
pipeline->num_threads_per_block =
|
||||||
occupancy_tuning[device_kernel].num_threads_per_block;
|
occupancy_tuning[device_kernel].num_threads_per_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* metalrt options */
|
/* metalrt options */
|
||||||
request.pipeline->use_metalrt = device->use_metalrt;
|
pipeline->use_metalrt = device->use_metalrt;
|
||||||
request.pipeline->metalrt_features = device->use_metalrt ?
|
pipeline->metalrt_features = device->use_metalrt ?
|
||||||
(device->kernel_features & METALRT_FEATURE_MASK) :
|
(device->kernel_features & METALRT_FEATURE_MASK) :
|
||||||
0;
|
0;
|
||||||
|
|
||||||
{
|
{
|
||||||
thread_scoped_lock lock(cache_mutex);
|
thread_scoped_lock lock(cache_mutex);
|
||||||
auto &collection = pipelines[device_kernel];
|
request_queue.push_back(pipeline);
|
||||||
|
|
||||||
/* Cache up to 3 kernel variants with the same pso_type, purging oldest first. */
|
|
||||||
int max_entries_of_same_pso_type = 3;
|
|
||||||
for (int i = (int)collection.size() - 1; i >= 0; i--) {
|
|
||||||
if (collection[i]->pso_type == pso_type) {
|
|
||||||
max_entries_of_same_pso_type -= 1;
|
|
||||||
if (max_entries_of_same_pso_type == 0) {
|
|
||||||
metal_printf("Purging oldest %s:%s kernel from ShaderCache\n",
|
|
||||||
kernel_type_as_string(pso_type),
|
|
||||||
device_kernel_as_string(device_kernel));
|
|
||||||
collection.erase(collection.begin() + i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collection.push_back(unique_ptr<MetalKernelPipeline>(request.pipeline));
|
|
||||||
request_queue.push_back(request);
|
|
||||||
}
|
}
|
||||||
cond_var.notify_one();
|
cond_var.notify_one();
|
||||||
}
|
}
|
||||||
|
@ -664,51 +693,61 @@ void MetalKernelPipeline::compile()
|
||||||
|
|
||||||
double starttime = time_dt();
|
double starttime = time_dt();
|
||||||
|
|
||||||
MTLNewComputePipelineStateWithReflectionCompletionHandler completionHandler = ^(
|
/* Block on load to ensure we continue with a valid kernel function */
|
||||||
id<MTLComputePipelineState> computePipelineState,
|
if (creating_new_archive) {
|
||||||
MTLComputePipelineReflection *reflection,
|
starttime = time_dt();
|
||||||
NSError *error) {
|
NSError *error;
|
||||||
bool recreate_archive = false;
|
if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor
|
||||||
if (computePipelineState == nil && archive) {
|
error:&error]) {
|
||||||
NSString *errStr = [error localizedDescription];
|
NSString *errStr = [error localizedDescription];
|
||||||
metal_printf(
|
metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil");
|
||||||
"Failed to create compute pipeline state \"%s\" from archive - attempting recreation... "
|
|
||||||
"(error: %s)\n",
|
|
||||||
device_kernel_as_string((DeviceKernel)device_kernel),
|
|
||||||
errStr ? [errStr UTF8String] : "nil");
|
|
||||||
computePipelineState = [mtlDevice
|
|
||||||
newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
|
|
||||||
options:MTLPipelineOptionNone
|
|
||||||
reflection:nullptr
|
|
||||||
error:&error];
|
|
||||||
recreate_archive = true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double duration = time_dt() - starttime;
|
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
|
||||||
|
options:pipelineOptions
|
||||||
|
reflection:nullptr
|
||||||
|
error:&error];
|
||||||
|
|
||||||
if (computePipelineState == nil) {
|
bool recreate_archive = false;
|
||||||
NSString *errStr = [error localizedDescription];
|
if (pipeline == nil && archive) {
|
||||||
error_str = string_printf("Failed to create compute pipeline state \"%s\", error: \n",
|
NSString *errStr = [error localizedDescription];
|
||||||
device_kernel_as_string((DeviceKernel)device_kernel));
|
metal_printf(
|
||||||
error_str += (errStr ? [errStr UTF8String] : "nil");
|
"Failed to create compute pipeline state \"%s\" from archive - attempting recreation... "
|
||||||
metal_printf("%16s | %2d | %-55s | %7.2fs | FAILED!\n",
|
"(error: %s)\n",
|
||||||
kernel_type_as_string(pso_type),
|
device_kernel_as_string((DeviceKernel)device_kernel),
|
||||||
device_kernel,
|
errStr ? [errStr UTF8String] : "nil");
|
||||||
device_kernel_as_string((DeviceKernel)device_kernel),
|
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
|
||||||
duration);
|
options:MTLPipelineOptionNone
|
||||||
return;
|
reflection:nullptr
|
||||||
}
|
error:&error];
|
||||||
|
recreate_archive = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!num_threads_per_block) {
|
double duration = time_dt() - starttime;
|
||||||
num_threads_per_block = round_down(computePipelineState.maxTotalThreadsPerThreadgroup,
|
|
||||||
computePipelineState.threadExecutionWidth);
|
|
||||||
num_threads_per_block = std::max(num_threads_per_block,
|
|
||||||
(int)computePipelineState.threadExecutionWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->pipeline = computePipelineState;
|
if (pipeline == nil) {
|
||||||
|
NSString *errStr = [error localizedDescription];
|
||||||
|
error_str = string_printf("Failed to create compute pipeline state \"%s\", error: \n",
|
||||||
|
device_kernel_as_string((DeviceKernel)device_kernel));
|
||||||
|
error_str += (errStr ? [errStr UTF8String] : "nil");
|
||||||
|
metal_printf("%16s | %2d | %-55s | %7.2fs | FAILED!\n",
|
||||||
|
kernel_type_as_string(pso_type),
|
||||||
|
device_kernel,
|
||||||
|
device_kernel_as_string((DeviceKernel)device_kernel),
|
||||||
|
duration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (@available(macOS 11.0, *)) {
|
if (!num_threads_per_block) {
|
||||||
|
num_threads_per_block = round_down(pipeline.maxTotalThreadsPerThreadgroup,
|
||||||
|
pipeline.threadExecutionWidth);
|
||||||
|
num_threads_per_block = std::max(num_threads_per_block,
|
||||||
|
(int)pipeline.threadExecutionWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@available(macOS 11.0, *)) {
|
||||||
|
if (ShaderCache::running) {
|
||||||
if (creating_new_archive || recreate_archive) {
|
if (creating_new_archive || recreate_archive) {
|
||||||
if (![archive serializeToURL:[NSURL fileURLWithPath:@(metalbin_path.c_str())]
|
if (![archive serializeToURL:[NSURL fileURLWithPath:@(metalbin_path.c_str())]
|
||||||
error:&error]) {
|
error:&error]) {
|
||||||
|
@ -720,24 +759,7 @@ void MetalKernelPipeline::compile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/* Block on load to ensure we continue with a valid kernel function */
|
|
||||||
if (creating_new_archive) {
|
|
||||||
starttime = time_dt();
|
|
||||||
NSError *error;
|
|
||||||
if (![archive addComputePipelineFunctionsWithDescriptor:computePipelineStateDescriptor
|
|
||||||
error:&error]) {
|
|
||||||
NSString *errStr = [error localizedDescription];
|
|
||||||
metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
id<MTLComputePipelineState> pipeline = [mtlDevice
|
|
||||||
newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
|
|
||||||
options:pipelineOptions
|
|
||||||
reflection:nullptr
|
|
||||||
error:&error];
|
|
||||||
completionHandler(pipeline, nullptr, error);
|
|
||||||
|
|
||||||
this->loaded = true;
|
this->loaded = true;
|
||||||
[computePipelineStateDescriptor release];
|
[computePipelineStateDescriptor release];
|
||||||
|
@ -763,8 +785,6 @@ void MetalKernelPipeline::compile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double duration = time_dt() - starttime;
|
|
||||||
|
|
||||||
if (!use_binary_archive) {
|
if (!use_binary_archive) {
|
||||||
metal_printf("%16s | %2d | %-55s | %7.2fs\n",
|
metal_printf("%16s | %2d | %-55s | %7.2fs\n",
|
||||||
kernel_type_as_string(pso_type),
|
kernel_type_as_string(pso_type),
|
||||||
|
@ -791,24 +811,46 @@ bool MetalDeviceKernels::load(MetalDevice *device, MetalPipelineType pso_type)
|
||||||
shader_cache->load_kernel((DeviceKernel)i, device, pso_type);
|
shader_cache->load_kernel((DeviceKernel)i, device, pso_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
shader_cache->wait_for_all();
|
if (getenv("CYCLES_METAL_PROFILING")) {
|
||||||
metal_printf("Back-end compilation finished in %.1f seconds (%s)\n",
|
shader_cache->wait_for_all();
|
||||||
time_dt() - starttime,
|
metal_printf("Back-end compilation finished in %.1f seconds (%s)\n",
|
||||||
kernel_type_as_string(pso_type));
|
time_dt() - starttime,
|
||||||
|
kernel_type_as_string(pso_type));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDeviceKernels::should_load_kernels(MetalDevice *device, MetalPipelineType pso_type)
|
bool MetalDeviceKernels::any_specialization_happening_now()
|
||||||
{
|
{
|
||||||
auto shader_cache = get_shader_cache(device->mtlDevice);
|
/* Return true if any ShaderCaches have ongoing specialization requests (typically there will be
|
||||||
for (int i = 0; i < DEVICE_KERNEL_NUM; i++) {
|
* only 1). */
|
||||||
if (shader_cache->should_load_kernel((DeviceKernel)i, device, pso_type)) {
|
thread_scoped_lock lock(g_shaderCacheMutex);
|
||||||
|
for (auto &it : g_shaderCache) {
|
||||||
|
if (it.second->incomplete_specialization_requests > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MetalDeviceKernels::get_loaded_kernel_count(MetalDevice const *device,
|
||||||
|
MetalPipelineType pso_type)
|
||||||
|
{
|
||||||
|
auto shader_cache = get_shader_cache(device->mtlDevice);
|
||||||
|
int loaded_count = DEVICE_KERNEL_NUM;
|
||||||
|
for (int i = 0; i < DEVICE_KERNEL_NUM; i++) {
|
||||||
|
if (shader_cache->should_load_kernel((DeviceKernel)i, device, pso_type)) {
|
||||||
|
loaded_count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loaded_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetalDeviceKernels::should_load_kernels(MetalDevice const *device, MetalPipelineType pso_type)
|
||||||
|
{
|
||||||
|
return get_loaded_kernel_count(device, pso_type) != DEVICE_KERNEL_NUM;
|
||||||
|
}
|
||||||
|
|
||||||
const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevice *device,
|
const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevice *device,
|
||||||
DeviceKernel kernel)
|
DeviceKernel kernel)
|
||||||
{
|
{
|
||||||
|
|
|
@ -702,6 +702,10 @@ bool MetalDeviceQueue::synchronize()
|
||||||
|
|
||||||
void MetalDeviceQueue::zero_to_device(device_memory &mem)
|
void MetalDeviceQueue::zero_to_device(device_memory &mem)
|
||||||
{
|
{
|
||||||
|
if (metal_device_->have_error()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
|
assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
|
||||||
|
|
||||||
if (mem.memory_size() == 0) {
|
if (mem.memory_size() == 0) {
|
||||||
|
@ -729,6 +733,10 @@ void MetalDeviceQueue::zero_to_device(device_memory &mem)
|
||||||
|
|
||||||
void MetalDeviceQueue::copy_to_device(device_memory &mem)
|
void MetalDeviceQueue::copy_to_device(device_memory &mem)
|
||||||
{
|
{
|
||||||
|
if (metal_device_->have_error()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mem.memory_size() == 0) {
|
if (mem.memory_size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -771,6 +779,10 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem)
|
||||||
|
|
||||||
void MetalDeviceQueue::copy_from_device(device_memory &mem)
|
void MetalDeviceQueue::copy_from_device(device_memory &mem)
|
||||||
{
|
{
|
||||||
|
if (metal_device_->have_error()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
|
assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
|
||||||
|
|
||||||
if (mem.memory_size() == 0) {
|
if (mem.memory_size() == 0) {
|
||||||
|
|
|
@ -429,7 +429,12 @@ void OneapiDevice::check_usm(SyclQueue *queue_, const void *usm_ptr, bool allow_
|
||||||
queue->get_device().get_info<sycl::info::device::device_type>();
|
queue->get_device().get_info<sycl::info::device::device_type>();
|
||||||
sycl::usm::alloc usm_type = get_pointer_type(usm_ptr, queue->get_context());
|
sycl::usm::alloc usm_type = get_pointer_type(usm_ptr, queue->get_context());
|
||||||
(void)usm_type;
|
(void)usm_type;
|
||||||
assert(usm_type == sycl::usm::alloc::device ||
|
# ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
|
const sycl::usm::alloc main_memory_type = sycl::usm::alloc::device;
|
||||||
|
# else
|
||||||
|
const sycl::usm::alloc main_memory_type = sycl::usm::alloc::host;
|
||||||
|
# endif
|
||||||
|
assert(usm_type == main_memory_type ||
|
||||||
(usm_type == sycl::usm::alloc::host &&
|
(usm_type == sycl::usm::alloc::host &&
|
||||||
(allow_host || device_type == sycl::info::device_type::cpu)) ||
|
(allow_host || device_type == sycl::info::device_type::cpu)) ||
|
||||||
usm_type == sycl::usm::alloc::unknown);
|
usm_type == sycl::usm::alloc::unknown);
|
||||||
|
@ -478,7 +483,11 @@ void *OneapiDevice::usm_alloc_device(SyclQueue *queue_, size_t memory_size)
|
||||||
{
|
{
|
||||||
assert(queue_);
|
assert(queue_);
|
||||||
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
|
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
|
||||||
|
# ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
return sycl::malloc_device(memory_size, *queue);
|
return sycl::malloc_device(memory_size, *queue);
|
||||||
|
# else
|
||||||
|
return sycl::malloc_host(memory_size, *queue);
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneapiDevice::usm_free(SyclQueue *queue_, void *usm_ptr)
|
void OneapiDevice::usm_free(SyclQueue *queue_, void *usm_ptr)
|
||||||
|
@ -736,7 +745,11 @@ char *OneapiDevice::device_capabilities()
|
||||||
|
|
||||||
const std::vector<sycl::device> &oneapi_devices = available_devices();
|
const std::vector<sycl::device> &oneapi_devices = available_devices();
|
||||||
for (const sycl::device &device : oneapi_devices) {
|
for (const sycl::device &device : oneapi_devices) {
|
||||||
|
# ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
const std::string &name = device.get_info<sycl::info::device::name>();
|
const std::string &name = device.get_info<sycl::info::device::name>();
|
||||||
|
# else
|
||||||
|
const std::string &name = "SYCL Host Task (Debug)";
|
||||||
|
# endif
|
||||||
|
|
||||||
capabilities << std::string("\t") << name << "\n";
|
capabilities << std::string("\t") << name << "\n";
|
||||||
# define WRITE_ATTR(attribute_name, attribute_variable) \
|
# define WRITE_ATTR(attribute_name, attribute_variable) \
|
||||||
|
@ -813,7 +826,11 @@ void OneapiDevice::iterate_devices(OneAPIDeviceIteratorCallback cb, void *user_p
|
||||||
for (sycl::device &device : devices) {
|
for (sycl::device &device : devices) {
|
||||||
const std::string &platform_name =
|
const std::string &platform_name =
|
||||||
device.get_platform().get_info<sycl::info::platform::name>();
|
device.get_platform().get_info<sycl::info::platform::name>();
|
||||||
|
# ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
std::string name = device.get_info<sycl::info::device::name>();
|
std::string name = device.get_info<sycl::info::device::name>();
|
||||||
|
# else
|
||||||
|
std::string name = "SYCL Host Task (Debug)";
|
||||||
|
# endif
|
||||||
std::string id = "ONEAPI_" + platform_name + "_" + name;
|
std::string id = "ONEAPI_" + platform_name + "_" + name;
|
||||||
if (device.has(sycl::aspect::ext_intel_pci_address)) {
|
if (device.has(sycl::aspect::ext_intel_pci_address)) {
|
||||||
id.append("_" + device.get_info<sycl::ext::intel::info::device::pci_address>());
|
id.append("_" + device.get_info<sycl::ext::intel::info::device::pci_address>());
|
||||||
|
|
|
@ -390,6 +390,9 @@ void PathTrace::path_trace(RenderWork &render_work)
|
||||||
const int num_samples = render_work.path_trace.num_samples;
|
const int num_samples = render_work.path_trace.num_samples;
|
||||||
|
|
||||||
PathTraceWork *path_trace_work = path_trace_works_[i].get();
|
PathTraceWork *path_trace_work = path_trace_works_[i].get();
|
||||||
|
if (path_trace_work->get_device()->have_error()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PathTraceWork::RenderStatistics statistics;
|
PathTraceWork::RenderStatistics statistics;
|
||||||
path_trace_work->render_samples(statistics,
|
path_trace_work->render_samples(statistics,
|
||||||
|
|
|
@ -752,6 +752,10 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
||||||
${SYCL_CPP_FLAGS}
|
${SYCL_CPP_FLAGS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)
|
||||||
|
list(APPEND sycl_compiler_flags -DWITH_ONEAPI_SYCL_HOST_TASK)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Set defaults for spir64 and spir64_gen options
|
# Set defaults for spir64 and spir64_gen options
|
||||||
if(NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64)
|
if(NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64)
|
||||||
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64 "-options '-ze-opt-large-register-file -ze-opt-regular-grf-kernel integrator_intersect'")
|
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64 "-options '-ze-opt-large-register-file -ze-opt-regular-grf-kernel integrator_intersect'")
|
||||||
|
@ -763,7 +767,8 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
||||||
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "--format zebin ")
|
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "--format zebin ")
|
||||||
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "-device ${CYCLES_ONEAPI_SPIR64_GEN_DEVICES} ")
|
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "-device ${CYCLES_ONEAPI_SPIR64_GEN_DEVICES} ")
|
||||||
|
|
||||||
if(WITH_CYCLES_ONEAPI_BINARIES)
|
# Host execution won't use GPU binaries, no need to compile them.
|
||||||
|
if(WITH_CYCLES_ONEAPI_BINARIES AND NOT WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)
|
||||||
# AoT binaries aren't currently reused when calling sycl::build.
|
# AoT binaries aren't currently reused when calling sycl::build.
|
||||||
list(APPEND sycl_compiler_flags -DSYCL_SKIP_KERNELS_PRELOAD)
|
list(APPEND sycl_compiler_flags -DSYCL_SKIP_KERNELS_PRELOAD)
|
||||||
# Iterate over all targest and their options
|
# Iterate over all targest and their options
|
||||||
|
|
|
@ -30,6 +30,16 @@ void gpu_parallel_active_index_array_impl(const uint num_states,
|
||||||
ccl_global int *ccl_restrict num_indices,
|
ccl_global int *ccl_restrict num_indices,
|
||||||
IsActiveOp is_active_op)
|
IsActiveOp is_active_op)
|
||||||
{
|
{
|
||||||
|
# ifdef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
|
int write_index = 0;
|
||||||
|
for (int state_index = 0; state_index < num_states; state_index++) {
|
||||||
|
if (is_active_op(state_index))
|
||||||
|
indices[write_index++] = state_index;
|
||||||
|
}
|
||||||
|
*num_indices = write_index;
|
||||||
|
return;
|
||||||
|
# endif /* WITH_ONEAPI_SYCL_HOST_TASK */
|
||||||
|
|
||||||
const sycl::nd_item<1> &item_id = sycl::ext::oneapi::experimental::this_nd_item<1>();
|
const sycl::nd_item<1> &item_id = sycl::ext::oneapi::experimental::this_nd_item<1>();
|
||||||
const uint blocksize = item_id.get_local_range(0);
|
const uint blocksize = item_id.get_local_range(0);
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,8 @@
|
||||||
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
|
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
|
||||||
#define ccl_gpu_kernel_threads(block_num_threads)
|
#define ccl_gpu_kernel_threads(block_num_threads)
|
||||||
|
|
||||||
#define ccl_gpu_kernel_signature(name, ...) \
|
#ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
|
# define ccl_gpu_kernel_signature(name, ...) \
|
||||||
void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
||||||
size_t kernel_global_size, \
|
size_t kernel_global_size, \
|
||||||
size_t kernel_local_size, \
|
size_t kernel_local_size, \
|
||||||
|
@ -67,9 +68,37 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
||||||
sycl::nd_range<1>(kernel_global_size, kernel_local_size), \
|
sycl::nd_range<1>(kernel_global_size, kernel_local_size), \
|
||||||
[=](sycl::nd_item<1> item) {
|
[=](sycl::nd_item<1> item) {
|
||||||
|
|
||||||
#define ccl_gpu_kernel_postfix \
|
# define ccl_gpu_kernel_postfix \
|
||||||
}); \
|
}); \
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
/* Additional anonymous lambda is required to handle all "return" statements in the kernel code */
|
||||||
|
# define ccl_gpu_kernel_signature(name, ...) \
|
||||||
|
void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
||||||
|
size_t kernel_global_size, \
|
||||||
|
size_t kernel_local_size, \
|
||||||
|
sycl::handler &cgh, \
|
||||||
|
__VA_ARGS__) { \
|
||||||
|
(kg); \
|
||||||
|
(kernel_local_size); \
|
||||||
|
cgh.host_task( \
|
||||||
|
[=]() {\
|
||||||
|
for (size_t gid = (size_t)0; gid < kernel_global_size; gid++) { \
|
||||||
|
kg->nd_item_local_id_0 = 0; \
|
||||||
|
kg->nd_item_local_range_0 = 1; \
|
||||||
|
kg->nd_item_group_id_0 = gid; \
|
||||||
|
kg->nd_item_group_range_0 = kernel_global_size; \
|
||||||
|
kg->nd_item_global_id_0 = gid; \
|
||||||
|
kg->nd_item_global_range_0 = kernel_global_size; \
|
||||||
|
auto kernel = [=]() {
|
||||||
|
|
||||||
|
# define ccl_gpu_kernel_postfix \
|
||||||
|
}; \
|
||||||
|
kernel(); \
|
||||||
|
} \
|
||||||
|
}); \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ccl_gpu_kernel_call(x) ((ONEAPIKernelContext*)kg)->x
|
#define ccl_gpu_kernel_call(x) ((ONEAPIKernelContext*)kg)->x
|
||||||
|
|
||||||
|
@ -83,23 +112,40 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
||||||
} ccl_gpu_kernel_lambda_pass((ONEAPIKernelContext *)kg)
|
} ccl_gpu_kernel_lambda_pass((ONEAPIKernelContext *)kg)
|
||||||
|
|
||||||
/* GPU thread, block, grid size and index */
|
/* GPU thread, block, grid size and index */
|
||||||
#define ccl_gpu_thread_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_id(0))
|
|
||||||
#define ccl_gpu_block_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_range(0))
|
|
||||||
#define ccl_gpu_block_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group(0))
|
|
||||||
#define ccl_gpu_grid_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group_range(0))
|
|
||||||
#define ccl_gpu_warp_size (sycl::ext::oneapi::experimental::this_sub_group().get_local_range()[0])
|
|
||||||
#define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
|
|
||||||
|
|
||||||
#define ccl_gpu_global_id_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_id(0))
|
#ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
#define ccl_gpu_global_size_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_range(0))
|
# define ccl_gpu_thread_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_id(0))
|
||||||
|
# define ccl_gpu_block_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_local_range(0))
|
||||||
|
# define ccl_gpu_block_idx_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group(0))
|
||||||
|
# define ccl_gpu_grid_dim_x (sycl::ext::oneapi::experimental::this_nd_item<1>().get_group_range(0))
|
||||||
|
# define ccl_gpu_warp_size (sycl::ext::oneapi::experimental::this_sub_group().get_local_range()[0])
|
||||||
|
# define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
|
||||||
|
|
||||||
|
# define ccl_gpu_global_id_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_id(0))
|
||||||
|
# define ccl_gpu_global_size_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_range(0))
|
||||||
|
|
||||||
/* GPU warp synchronization */
|
/* GPU warp synchronization */
|
||||||
#define ccl_gpu_syncthreads() sycl::ext::oneapi::experimental::this_nd_item<1>().barrier()
|
# define ccl_gpu_syncthreads() sycl::ext::oneapi::experimental::this_nd_item<1>().barrier()
|
||||||
#define ccl_gpu_local_syncthreads() sycl::ext::oneapi::experimental::this_nd_item<1>().barrier(sycl::access::fence_space::local_space)
|
# define ccl_gpu_local_syncthreads() sycl::ext::oneapi::experimental::this_nd_item<1>().barrier(sycl::access::fence_space::local_space)
|
||||||
#ifdef __SYCL_DEVICE_ONLY__
|
# ifdef __SYCL_DEVICE_ONLY__
|
||||||
#define ccl_gpu_ballot(predicate) (sycl::ext::oneapi::group_ballot(sycl::ext::oneapi::experimental::this_sub_group(), predicate).count())
|
# define ccl_gpu_ballot(predicate) (sycl::ext::oneapi::group_ballot(sycl::ext::oneapi::experimental::this_sub_group(), predicate).count())
|
||||||
|
# else
|
||||||
|
# define ccl_gpu_ballot(predicate) (predicate ? 1 : 0)
|
||||||
|
# endif
|
||||||
#else
|
#else
|
||||||
#define ccl_gpu_ballot(predicate) (predicate ? 1 : 0)
|
# define ccl_gpu_thread_idx_x (kg->nd_item_local_id_0)
|
||||||
|
# define ccl_gpu_block_dim_x (kg->nd_item_local_range_0)
|
||||||
|
# define ccl_gpu_block_idx_x (kg->nd_item_group_id_0)
|
||||||
|
# define ccl_gpu_grid_dim_x (kg->nd_item_group_range_0)
|
||||||
|
# define ccl_gpu_warp_size (1)
|
||||||
|
# define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
|
||||||
|
|
||||||
|
# define ccl_gpu_global_id_x() (kg->nd_item_global_id_0)
|
||||||
|
# define ccl_gpu_global_size_x() (kg->nd_item_global_range_0)
|
||||||
|
|
||||||
|
# define ccl_gpu_syncthreads()
|
||||||
|
# define ccl_gpu_local_syncthreads()
|
||||||
|
# define ccl_gpu_ballot(predicate) (predicate ? 1 : 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Debug defines */
|
/* Debug defines */
|
||||||
|
|
|
@ -23,6 +23,15 @@ typedef struct KernelGlobalsGPU {
|
||||||
#undef KERNEL_DATA_ARRAY
|
#undef KERNEL_DATA_ARRAY
|
||||||
IntegratorStateGPU *integrator_state;
|
IntegratorStateGPU *integrator_state;
|
||||||
const KernelData *__data;
|
const KernelData *__data;
|
||||||
|
|
||||||
|
#ifdef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
|
size_t nd_item_local_id_0;
|
||||||
|
size_t nd_item_local_range_0;
|
||||||
|
size_t nd_item_group_id_0;
|
||||||
|
size_t nd_item_group_range_0;
|
||||||
|
size_t nd_item_global_id_0;
|
||||||
|
size_t nd_item_global_range_0;
|
||||||
|
#endif
|
||||||
} KernelGlobalsGPU;
|
} KernelGlobalsGPU;
|
||||||
|
|
||||||
typedef ccl_global KernelGlobalsGPU *ccl_restrict KernelGlobals;
|
typedef ccl_global KernelGlobalsGPU *ccl_restrict KernelGlobals;
|
||||||
|
|
|
@ -230,6 +230,12 @@ bool oneapi_enqueue_kernel(KernelContext *kernel_context,
|
||||||
/* NOTE(@nsirgien): As for now non-uniform work-groups don't work on most oneAPI devices,
|
/* NOTE(@nsirgien): As for now non-uniform work-groups don't work on most oneAPI devices,
|
||||||
* we extend work size to fit uniformity requirements. */
|
* we extend work size to fit uniformity requirements. */
|
||||||
global_size = groups_count * local_size;
|
global_size = groups_count * local_size;
|
||||||
|
|
||||||
|
# ifdef WITH_ONEAPI_SYCL_HOST_TASK
|
||||||
|
/* Path array implementation is serial in case of SYCL Host Task execution. */
|
||||||
|
global_size = 1;
|
||||||
|
local_size = 1;
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let the compiler throw an error if there are any kernels missing in this implementation. */
|
/* Let the compiler throw an error if there are any kernels missing in this implementation. */
|
||||||
|
|
|
@ -1532,7 +1532,7 @@ ccl_device_extern void osl_texture_set_missingcolor_alpha(ccl_private OSLTexture
|
||||||
ccl_device_extern bool osl_texture(ccl_private ShaderGlobals *sg,
|
ccl_device_extern bool osl_texture(ccl_private ShaderGlobals *sg,
|
||||||
DeviceString filename,
|
DeviceString filename,
|
||||||
ccl_private void *texture_handle,
|
ccl_private void *texture_handle,
|
||||||
OSLTextureOptions *opt,
|
ccl_private OSLTextureOptions *opt,
|
||||||
float s,
|
float s,
|
||||||
float t,
|
float t,
|
||||||
float dsdx,
|
float dsdx,
|
||||||
|
@ -1557,13 +1557,14 @@ ccl_device_extern bool osl_texture(ccl_private ShaderGlobals *sg,
|
||||||
|
|
||||||
const float4 rgba = kernel_tex_image_interp(nullptr, id, s, 1.0f - t);
|
const float4 rgba = kernel_tex_image_interp(nullptr, id, s, 1.0f - t);
|
||||||
|
|
||||||
result[0] = rgba.x;
|
if (nchannels > 0)
|
||||||
|
result[0] = rgba.x;
|
||||||
if (nchannels > 1)
|
if (nchannels > 1)
|
||||||
result[1] = rgba.y;
|
result[1] = rgba.y;
|
||||||
if (nchannels > 2)
|
if (nchannels > 2)
|
||||||
result[2] = rgba.z;
|
result[2] = rgba.z;
|
||||||
if (nchannels > 3)
|
if (alpha)
|
||||||
result[3] = rgba.w;
|
*alpha = rgba.w;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1571,7 +1572,7 @@ ccl_device_extern bool osl_texture(ccl_private ShaderGlobals *sg,
|
||||||
ccl_device_extern bool osl_texture3d(ccl_private ShaderGlobals *sg,
|
ccl_device_extern bool osl_texture3d(ccl_private ShaderGlobals *sg,
|
||||||
DeviceString filename,
|
DeviceString filename,
|
||||||
ccl_private void *texture_handle,
|
ccl_private void *texture_handle,
|
||||||
OSLTextureOptions *opt,
|
ccl_private OSLTextureOptions *opt,
|
||||||
ccl_private const float3 *P,
|
ccl_private const float3 *P,
|
||||||
ccl_private const float3 *dPdx,
|
ccl_private const float3 *dPdx,
|
||||||
ccl_private const float3 *dPdy,
|
ccl_private const float3 *dPdy,
|
||||||
|
@ -1594,13 +1595,14 @@ ccl_device_extern bool osl_texture3d(ccl_private ShaderGlobals *sg,
|
||||||
|
|
||||||
const float4 rgba = kernel_tex_image_interp_3d(nullptr, id, *P, INTERPOLATION_NONE);
|
const float4 rgba = kernel_tex_image_interp_3d(nullptr, id, *P, INTERPOLATION_NONE);
|
||||||
|
|
||||||
result[0] = rgba.x;
|
if (nchannels > 0)
|
||||||
|
result[0] = rgba.x;
|
||||||
if (nchannels > 1)
|
if (nchannels > 1)
|
||||||
result[1] = rgba.y;
|
result[1] = rgba.y;
|
||||||
if (nchannels > 2)
|
if (nchannels > 2)
|
||||||
result[2] = rgba.z;
|
result[2] = rgba.z;
|
||||||
if (nchannels > 3)
|
if (alpha)
|
||||||
result[3] = rgba.w;
|
*alpha = rgba.w;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1608,7 +1610,7 @@ ccl_device_extern bool osl_texture3d(ccl_private ShaderGlobals *sg,
|
||||||
ccl_device_extern bool osl_environment(ccl_private ShaderGlobals *sg,
|
ccl_device_extern bool osl_environment(ccl_private ShaderGlobals *sg,
|
||||||
DeviceString filename,
|
DeviceString filename,
|
||||||
ccl_private void *texture_handle,
|
ccl_private void *texture_handle,
|
||||||
OSLTextureOptions *opt,
|
ccl_private OSLTextureOptions *opt,
|
||||||
ccl_private const float3 *R,
|
ccl_private const float3 *R,
|
||||||
ccl_private const float3 *dRdx,
|
ccl_private const float3 *dRdx,
|
||||||
ccl_private const float3 *dRdy,
|
ccl_private const float3 *dRdy,
|
||||||
|
@ -1621,13 +1623,14 @@ ccl_device_extern bool osl_environment(ccl_private ShaderGlobals *sg,
|
||||||
ccl_private float *dalphay,
|
ccl_private float *dalphay,
|
||||||
ccl_private void *errormessage)
|
ccl_private void *errormessage)
|
||||||
{
|
{
|
||||||
result[0] = 1.0f;
|
if (nchannels > 0)
|
||||||
|
result[0] = 1.0f;
|
||||||
if (nchannels > 1)
|
if (nchannels > 1)
|
||||||
result[1] = 0.0f;
|
result[1] = 0.0f;
|
||||||
if (nchannels > 2)
|
if (nchannels > 2)
|
||||||
result[2] = 1.0f;
|
result[2] = 1.0f;
|
||||||
if (nchannels > 3)
|
if (alpha)
|
||||||
result[3] = 1.0f;
|
*alpha = 1.0f;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,14 +113,18 @@ static void oiio_load_pixels(const ImageMetaData &metadata,
|
||||||
|
|
||||||
if (depth <= 1) {
|
if (depth <= 1) {
|
||||||
size_t scanlinesize = width * components * sizeof(StorageType);
|
size_t scanlinesize = width * components * sizeof(StorageType);
|
||||||
in->read_image(FileFormat,
|
in->read_image(0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
components,
|
||||||
|
FileFormat,
|
||||||
(uchar *)readpixels + (height - 1) * scanlinesize,
|
(uchar *)readpixels + (height - 1) * scanlinesize,
|
||||||
AutoStride,
|
AutoStride,
|
||||||
-scanlinesize,
|
-scanlinesize,
|
||||||
AutoStride);
|
AutoStride);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
in->read_image(FileFormat, (uchar *)readpixels);
|
in->read_image(0, 0, 0, components, FileFormat, (uchar *)readpixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (components > 4) {
|
if (components > 4) {
|
||||||
|
|
|
@ -439,9 +439,12 @@ bool DenoiseImage::read_previous_pixels(const DenoiseImageLayer &layer,
|
||||||
{
|
{
|
||||||
/* Load pixels from neighboring frames, and copy them into device buffer
|
/* Load pixels from neighboring frames, and copy them into device buffer
|
||||||
* with channels reshuffled. */
|
* with channels reshuffled. */
|
||||||
size_t num_pixels = (size_t)width * (size_t)height;
|
const size_t num_pixels = (size_t)width * (size_t)height;
|
||||||
|
const int num_channels = in_previous->spec().nchannels;
|
||||||
|
|
||||||
array<float> neighbor_pixels(num_pixels * num_channels);
|
array<float> neighbor_pixels(num_pixels * num_channels);
|
||||||
if (!in_previous->read_image(TypeDesc::FLOAT, neighbor_pixels.data())) {
|
|
||||||
|
if (!in_previous->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, neighbor_pixels.data())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +494,7 @@ bool DenoiseImage::load(const string &in_filepath, string &error)
|
||||||
|
|
||||||
/* Read all channels into buffer. Reading all channels at once is faster
|
/* Read all channels into buffer. Reading all channels at once is faster
|
||||||
* than individually due to interleaved EXR channel storage. */
|
* than individually due to interleaved EXR channel storage. */
|
||||||
if (!in->read_image(TypeDesc::FLOAT, pixels.data())) {
|
if (!in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, pixels.data())) {
|
||||||
error = "Failed to read image: " + in_filepath;
|
error = "Failed to read image: " + in_filepath;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,8 +401,8 @@ static bool merge_pixels(const vector<MergeImage> &images,
|
||||||
* faster than individually due to interleaved EXR channel storage. */
|
* faster than individually due to interleaved EXR channel storage. */
|
||||||
array<float> pixels;
|
array<float> pixels;
|
||||||
alloc_pixels(image.in->spec(), pixels);
|
alloc_pixels(image.in->spec(), pixels);
|
||||||
|
const int num_channels = image.in->spec().nchannels;
|
||||||
if (!image.in->read_image(TypeDesc::FLOAT, pixels.data())) {
|
if (!image.in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, pixels.data())) {
|
||||||
error = "Failed to read image: " + image.filepath;
|
error = "Failed to read image: " + image.filepath;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -538,6 +538,7 @@ static void read_layer_samples(vector<MergeImage> &images,
|
||||||
/* Load the "Debug Sample Count" pass and add the samples to the layer's sample count. */
|
/* Load the "Debug Sample Count" pass and add the samples to the layer's sample count. */
|
||||||
array<float> sample_count_buffer;
|
array<float> sample_count_buffer;
|
||||||
sample_count_buffer.resize(in_spec.width * in_spec.height);
|
sample_count_buffer.resize(in_spec.width * in_spec.height);
|
||||||
|
|
||||||
image.in->read_image(0,
|
image.in->read_image(0,
|
||||||
0,
|
0,
|
||||||
layer.sample_pass_offset,
|
layer.sample_pass_offset,
|
||||||
|
|
|
@ -113,6 +113,9 @@ void Session::start()
|
||||||
|
|
||||||
void Session::cancel(bool quick)
|
void Session::cancel(bool quick)
|
||||||
{
|
{
|
||||||
|
/* Cancel any long running device operations (e.g. shader compilations). */
|
||||||
|
device->cancel();
|
||||||
|
|
||||||
/* Check if session thread is rendering. */
|
/* Check if session thread is rendering. */
|
||||||
const bool rendering = is_session_thread_rendering();
|
const bool rendering = is_session_thread_rendering();
|
||||||
|
|
||||||
|
@ -401,6 +404,16 @@ RenderWork Session::run_update_for_next_iteration()
|
||||||
path_trace_->load_kernels();
|
path_trace_->load_kernels();
|
||||||
path_trace_->alloc_work_memory();
|
path_trace_->alloc_work_memory();
|
||||||
|
|
||||||
|
/* Wait for device to be ready (e.g. finish any background compilations). */
|
||||||
|
string device_status;
|
||||||
|
while (!device->is_ready(device_status)) {
|
||||||
|
progress.set_status(device_status);
|
||||||
|
if (progress.get_cancel()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
}
|
||||||
|
|
||||||
progress.add_skip_time(update_timer, params.background);
|
progress.add_skip_time(update_timer, params.background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -646,7 +646,8 @@ bool TileManager::read_full_buffer_from_disk(const string_view filename,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in->read_image(TypeDesc::FLOAT, buffers->buffer.data())) {
|
const int num_channels = in->spec().nchannels;
|
||||||
|
if (!in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, buffers->buffer.data())) {
|
||||||
LOG(ERROR) << "Error reading pixels from the tile file " << in->geterror();
|
LOG(ERROR) << "Error reading pixels from the tile file " << in->geterror();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1223,13 +1223,12 @@ static void gwl_registry_entry_update_all(GWL_Display *display, const int interf
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
GWL_RegisteryUpdate_Params params = {
|
GWL_RegisteryUpdate_Params params{};
|
||||||
.name = reg->name,
|
params.name = reg->name;
|
||||||
.interface_slot = reg->interface_slot,
|
params.interface_slot = reg->interface_slot;
|
||||||
.version = reg->version,
|
params.version = reg->version;
|
||||||
|
params.user_data = reg->user_data;
|
||||||
|
|
||||||
.user_data = reg->user_data,
|
|
||||||
};
|
|
||||||
handler->update_fn(display, ¶ms);
|
handler->update_fn(display, ¶ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4535,18 +4534,7 @@ static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, co
|
||||||
CLOG_INFO(LOG, 2, "scale");
|
CLOG_INFO(LOG, 2, "scale");
|
||||||
GWL_Output *output = static_cast<GWL_Output *>(data);
|
GWL_Output *output = static_cast<GWL_Output *>(data);
|
||||||
output->scale = factor;
|
output->scale = factor;
|
||||||
|
output->system->output_scale_update_maybe_leave(output, false);
|
||||||
GHOST_WindowManager *window_manager = output->system->getWindowManager();
|
|
||||||
if (window_manager) {
|
|
||||||
for (GHOST_IWindow *iwin : window_manager->getWindows()) {
|
|
||||||
GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
|
|
||||||
const std::vector<GWL_Output *> &outputs = win->outputs();
|
|
||||||
if (std::find(outputs.begin(), outputs.end(), output) == outputs.cend()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
win->outputs_changed_update_scale();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_output_listener output_listener = {
|
static const struct wl_output_listener output_listener = {
|
||||||
|
@ -4736,11 +4724,24 @@ static void gwl_registry_wl_output_update(GWL_Display *display,
|
||||||
}
|
}
|
||||||
static void gwl_registry_wl_output_remove(GWL_Display *display,
|
static void gwl_registry_wl_output_remove(GWL_Display *display,
|
||||||
void *user_data,
|
void *user_data,
|
||||||
const bool /*on_exit*/)
|
const bool on_exit)
|
||||||
{
|
{
|
||||||
/* While windows & cursors hold references to outputs, there is no need to manually remove
|
/* While windows & cursors hold references to outputs, there is no need to manually remove
|
||||||
* these references as the compositor will remove references via #wl_surface_listener.leave. */
|
* these references as the compositor will remove references via #wl_surface_listener.leave.
|
||||||
|
*
|
||||||
|
* WARNING: this is not the case for WLROOTS based compositors which have a (bug?)
|
||||||
|
* where surface leave events don't run. So `system->output_leave(..)` is needed
|
||||||
|
* until the issue is resolved in WLROOTS. */
|
||||||
GWL_Output *output = static_cast<GWL_Output *>(user_data);
|
GWL_Output *output = static_cast<GWL_Output *>(user_data);
|
||||||
|
|
||||||
|
if (!on_exit) {
|
||||||
|
/* Needed for WLROOTS, does nothing if surface leave callbacks have already run. */
|
||||||
|
output->system->output_scale_update_maybe_leave(output, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->xdg_output) {
|
||||||
|
zxdg_output_v1_destroy(output->xdg_output);
|
||||||
|
}
|
||||||
wl_output_destroy(output->wl_output);
|
wl_output_destroy(output->wl_output);
|
||||||
std::vector<GWL_Output *>::iterator iter = std::find(
|
std::vector<GWL_Output *>::iterator iter = std::find(
|
||||||
display->outputs.begin(), display->outputs.end(), output);
|
display->outputs.begin(), display->outputs.end(), output);
|
||||||
|
@ -5176,11 +5177,10 @@ static void global_handle_add(void *data,
|
||||||
const GWL_RegistryEntry *registry_entry_prev = display->registry_entry;
|
const GWL_RegistryEntry *registry_entry_prev = display->registry_entry;
|
||||||
|
|
||||||
/* The interface name that is ensured not to be freed. */
|
/* The interface name that is ensured not to be freed. */
|
||||||
GWL_RegisteryAdd_Params params = {
|
GWL_RegisteryAdd_Params params{};
|
||||||
.name = name,
|
params.name = name;
|
||||||
.interface_slot = interface_slot,
|
params.interface_slot = interface_slot;
|
||||||
.version = version,
|
params.version = version;
|
||||||
};
|
|
||||||
|
|
||||||
handler->add_fn(display, ¶ms);
|
handler->add_fn(display, ¶ms);
|
||||||
|
|
||||||
|
@ -6762,6 +6762,49 @@ void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
|
||||||
#undef SURFACE_CLEAR_PTR
|
#undef SURFACE_CLEAR_PTR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GHOST_SystemWayland::output_scale_update_maybe_leave(GWL_Output *output, bool leave)
|
||||||
|
{
|
||||||
|
/* Update scale, optionally leaving the outputs beforehand. */
|
||||||
|
GHOST_WindowManager *window_manager = output->system->getWindowManager();
|
||||||
|
if (window_manager) {
|
||||||
|
for (GHOST_IWindow *iwin : window_manager->getWindows()) {
|
||||||
|
GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin);
|
||||||
|
const std::vector<GWL_Output *> &outputs = win->outputs();
|
||||||
|
bool found = leave ? win->outputs_leave(output) :
|
||||||
|
!(std::find(outputs.begin(), outputs.end(), output) == outputs.cend());
|
||||||
|
if (found) {
|
||||||
|
win->outputs_changed_update_scale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GWL_Seat *seat : display_->seats) {
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
found = leave ? seat->pointer.outputs.erase(output) : seat->pointer.outputs.count(output);
|
||||||
|
if (found) {
|
||||||
|
if (seat->cursor.wl_surface_cursor != nullptr) {
|
||||||
|
update_cursor_scale(
|
||||||
|
seat->cursor, seat->system->wl_shm(), &seat->pointer, seat->cursor.wl_surface_cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found = leave ? seat->tablet.outputs.erase(output) : seat->tablet.outputs.count(output);
|
||||||
|
if (found) {
|
||||||
|
for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) {
|
||||||
|
GWL_TabletTool *tablet_tool = static_cast<GWL_TabletTool *>(
|
||||||
|
zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2));
|
||||||
|
if (tablet_tool->wl_surface_cursor != nullptr) {
|
||||||
|
update_cursor_scale(seat->cursor,
|
||||||
|
seat->system->wl_shm(),
|
||||||
|
&seat->pointer,
|
||||||
|
tablet_tool->wl_surface_cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mode,
|
bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mode,
|
||||||
const GHOST_TGrabCursorMode mode_current,
|
const GHOST_TGrabCursorMode mode_current,
|
||||||
int32_t init_grab_xy[2],
|
int32_t init_grab_xy[2],
|
||||||
|
|
|
@ -194,6 +194,8 @@ class GHOST_SystemWayland : public GHOST_System {
|
||||||
/** Set this seat to be active. */
|
/** Set this seat to be active. */
|
||||||
void seat_active_set(const struct GWL_Seat *seat);
|
void seat_active_set(const struct GWL_Seat *seat);
|
||||||
|
|
||||||
|
void output_scale_update_maybe_leave(GWL_Output *output, bool leave);
|
||||||
|
|
||||||
/** Clear all references to this surface to prevent accessing NULL pointers. */
|
/** Clear all references to this surface to prevent accessing NULL pointers. */
|
||||||
void window_surface_unref(const wl_surface *wl_surface);
|
void window_surface_unref(const wl_surface *wl_surface);
|
||||||
|
|
||||||
|
|
|
@ -1361,9 +1361,6 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
|
||||||
* Functionality only used for the WAYLAND implementation.
|
* Functionality only used for the WAYLAND implementation.
|
||||||
* \{ */
|
* \{ */
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true when the windows scale or DPI changes.
|
|
||||||
*/
|
|
||||||
bool GHOST_WindowWayland::outputs_changed_update_scale()
|
bool GHOST_WindowWayland::outputs_changed_update_scale()
|
||||||
{
|
{
|
||||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||||
|
|
|
@ -156,6 +156,9 @@ class GHOST_WindowWayland : public GHOST_Window {
|
||||||
bool outputs_enter(GWL_Output *output);
|
bool outputs_enter(GWL_Output *output);
|
||||||
bool outputs_leave(GWL_Output *output);
|
bool outputs_leave(GWL_Output *output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true when the windows scale or DPI changes.
|
||||||
|
*/
|
||||||
bool outputs_changed_update_scale();
|
bool outputs_changed_update_scale();
|
||||||
|
|
||||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||||
|
|
|
@ -20,6 +20,7 @@ set(SRC
|
||||||
./intern/mallocn.c
|
./intern/mallocn.c
|
||||||
./intern/mallocn_guarded_impl.c
|
./intern/mallocn_guarded_impl.c
|
||||||
./intern/mallocn_lockfree_impl.c
|
./intern/mallocn_lockfree_impl.c
|
||||||
|
./intern/memory_usage.cc
|
||||||
|
|
||||||
MEM_guardedalloc.h
|
MEM_guardedalloc.h
|
||||||
./intern/mallocn_inline.h
|
./intern/mallocn_inline.h
|
||||||
|
|
|
@ -53,6 +53,9 @@ class MemLeakPrinter {
|
||||||
|
|
||||||
void MEM_init_memleak_detection()
|
void MEM_init_memleak_detection()
|
||||||
{
|
{
|
||||||
|
/* Calling this ensures that the memory usage counters outlive the memory leak detection. */
|
||||||
|
memory_usage_init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This variable is constructed when this function is first called. This should happen as soon as
|
* This variable is constructed when this function is first called. This should happen as soon as
|
||||||
* possible when the program starts.
|
* possible when the program starts.
|
||||||
|
|
|
@ -89,6 +89,14 @@ void aligned_free(void *ptr);
|
||||||
extern bool leak_detector_has_run;
|
extern bool leak_detector_has_run;
|
||||||
extern char free_after_leak_detection_message[];
|
extern char free_after_leak_detection_message[];
|
||||||
|
|
||||||
|
void memory_usage_init(void);
|
||||||
|
void memory_usage_block_alloc(size_t size);
|
||||||
|
void memory_usage_block_free(size_t size);
|
||||||
|
size_t memory_usage_block_num(void);
|
||||||
|
size_t memory_usage_current(void);
|
||||||
|
size_t memory_usage_peak(void);
|
||||||
|
void memory_usage_peak_reset(void);
|
||||||
|
|
||||||
/* Prototypes for counted allocator functions */
|
/* Prototypes for counted allocator functions */
|
||||||
size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
|
size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
|
||||||
void MEM_lockfree_freeN(void *vmemh);
|
void MEM_lockfree_freeN(void *vmemh);
|
||||||
|
|
|
@ -30,8 +30,6 @@ typedef struct MemHeadAligned {
|
||||||
size_t len;
|
size_t len;
|
||||||
} MemHeadAligned;
|
} MemHeadAligned;
|
||||||
|
|
||||||
static unsigned int totblock = 0;
|
|
||||||
static size_t mem_in_use = 0, peak_mem = 0;
|
|
||||||
static bool malloc_debug_memset = false;
|
static bool malloc_debug_memset = false;
|
||||||
|
|
||||||
static void (*error_callback)(const char *) = NULL;
|
static void (*error_callback)(const char *) = NULL;
|
||||||
|
@ -46,18 +44,6 @@ enum {
|
||||||
#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG)
|
#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG)
|
||||||
#define MEMHEAD_LEN(memhead) ((memhead)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG)))
|
#define MEMHEAD_LEN(memhead) ((memhead)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG)))
|
||||||
|
|
||||||
/* Uncomment this to have proper peak counter. */
|
|
||||||
#define USE_ATOMIC_MAX
|
|
||||||
|
|
||||||
MEM_INLINE void update_maximum(size_t *maximum_value, size_t value)
|
|
||||||
{
|
|
||||||
#ifdef USE_ATOMIC_MAX
|
|
||||||
atomic_fetch_and_update_max_z(maximum_value, value);
|
|
||||||
#else
|
|
||||||
*maximum_value = value > *maximum_value ? value : *maximum_value;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
__attribute__((format(printf, 1, 2)))
|
__attribute__((format(printf, 1, 2)))
|
||||||
#endif
|
#endif
|
||||||
|
@ -103,8 +89,7 @@ void MEM_lockfree_freeN(void *vmemh)
|
||||||
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
|
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
|
||||||
size_t len = MEMHEAD_LEN(memh);
|
size_t len = MEMHEAD_LEN(memh);
|
||||||
|
|
||||||
atomic_sub_and_fetch_u(&totblock, 1);
|
memory_usage_block_free(len);
|
||||||
atomic_sub_and_fetch_z(&mem_in_use, len);
|
|
||||||
|
|
||||||
if (UNLIKELY(malloc_debug_memset && len)) {
|
if (UNLIKELY(malloc_debug_memset && len)) {
|
||||||
memset(memh + 1, 255, len);
|
memset(memh + 1, 255, len);
|
||||||
|
@ -224,16 +209,14 @@ void *MEM_lockfree_callocN(size_t len, const char *str)
|
||||||
|
|
||||||
if (LIKELY(memh)) {
|
if (LIKELY(memh)) {
|
||||||
memh->len = len;
|
memh->len = len;
|
||||||
atomic_add_and_fetch_u(&totblock, 1);
|
memory_usage_block_alloc(len);
|
||||||
atomic_add_and_fetch_z(&mem_in_use, len);
|
|
||||||
update_maximum(&peak_mem, mem_in_use);
|
|
||||||
|
|
||||||
return PTR_FROM_MEMHEAD(memh);
|
return PTR_FROM_MEMHEAD(memh);
|
||||||
}
|
}
|
||||||
print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
||||||
SIZET_ARG(len),
|
SIZET_ARG(len),
|
||||||
str,
|
str,
|
||||||
(uint)mem_in_use);
|
(uint)memory_usage_current());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +230,7 @@ void *MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
|
||||||
SIZET_ARG(len),
|
SIZET_ARG(len),
|
||||||
SIZET_ARG(size),
|
SIZET_ARG(size),
|
||||||
str,
|
str,
|
||||||
(unsigned int)mem_in_use);
|
(unsigned int)memory_usage_current());
|
||||||
abort();
|
abort();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -269,16 +252,14 @@ void *MEM_lockfree_mallocN(size_t len, const char *str)
|
||||||
}
|
}
|
||||||
|
|
||||||
memh->len = len;
|
memh->len = len;
|
||||||
atomic_add_and_fetch_u(&totblock, 1);
|
memory_usage_block_alloc(len);
|
||||||
atomic_add_and_fetch_z(&mem_in_use, len);
|
|
||||||
update_maximum(&peak_mem, mem_in_use);
|
|
||||||
|
|
||||||
return PTR_FROM_MEMHEAD(memh);
|
return PTR_FROM_MEMHEAD(memh);
|
||||||
}
|
}
|
||||||
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
||||||
SIZET_ARG(len),
|
SIZET_ARG(len),
|
||||||
str,
|
str,
|
||||||
(uint)mem_in_use);
|
(uint)memory_usage_current());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +273,7 @@ void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
|
||||||
SIZET_ARG(len),
|
SIZET_ARG(len),
|
||||||
SIZET_ARG(size),
|
SIZET_ARG(size),
|
||||||
str,
|
str,
|
||||||
(uint)mem_in_use);
|
(uint)memory_usage_current());
|
||||||
abort();
|
abort();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -340,16 +321,14 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str
|
||||||
|
|
||||||
memh->len = len | (size_t)MEMHEAD_ALIGN_FLAG;
|
memh->len = len | (size_t)MEMHEAD_ALIGN_FLAG;
|
||||||
memh->alignment = (short)alignment;
|
memh->alignment = (short)alignment;
|
||||||
atomic_add_and_fetch_u(&totblock, 1);
|
memory_usage_block_alloc(len);
|
||||||
atomic_add_and_fetch_z(&mem_in_use, len);
|
|
||||||
update_maximum(&peak_mem, mem_in_use);
|
|
||||||
|
|
||||||
return PTR_FROM_MEMHEAD(memh);
|
return PTR_FROM_MEMHEAD(memh);
|
||||||
}
|
}
|
||||||
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
||||||
SIZET_ARG(len),
|
SIZET_ARG(len),
|
||||||
str,
|
str,
|
||||||
(uint)mem_in_use);
|
(uint)memory_usage_current());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,8 +348,8 @@ void MEM_lockfree_callbackmemlist(void (*func)(void *))
|
||||||
|
|
||||||
void MEM_lockfree_printmemlist_stats(void)
|
void MEM_lockfree_printmemlist_stats(void)
|
||||||
{
|
{
|
||||||
printf("\ntotal memory len: %.3f MB\n", (double)mem_in_use / (double)(1024 * 1024));
|
printf("\ntotal memory len: %.3f MB\n", (double)memory_usage_current() / (double)(1024 * 1024));
|
||||||
printf("peak memory len: %.3f MB\n", (double)peak_mem / (double)(1024 * 1024));
|
printf("peak memory len: %.3f MB\n", (double)memory_usage_peak() / (double)(1024 * 1024));
|
||||||
printf(
|
printf(
|
||||||
"\nFor more detailed per-block statistics run Blender with memory debugging command line "
|
"\nFor more detailed per-block statistics run Blender with memory debugging command line "
|
||||||
"argument.\n");
|
"argument.\n");
|
||||||
|
@ -398,23 +377,23 @@ void MEM_lockfree_set_memory_debug(void)
|
||||||
|
|
||||||
size_t MEM_lockfree_get_memory_in_use(void)
|
size_t MEM_lockfree_get_memory_in_use(void)
|
||||||
{
|
{
|
||||||
return mem_in_use;
|
return memory_usage_current();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint MEM_lockfree_get_memory_blocks_in_use(void)
|
uint MEM_lockfree_get_memory_blocks_in_use(void)
|
||||||
{
|
{
|
||||||
return totblock;
|
return (uint)memory_usage_block_num();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dummy */
|
/* dummy */
|
||||||
void MEM_lockfree_reset_peak_memory(void)
|
void MEM_lockfree_reset_peak_memory(void)
|
||||||
{
|
{
|
||||||
peak_mem = mem_in_use;
|
memory_usage_peak_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t MEM_lockfree_get_peak_memory(void)
|
size_t MEM_lockfree_get_peak_memory(void)
|
||||||
{
|
{
|
||||||
return peak_mem;
|
return memory_usage_peak();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
#include "mallocn_intern.h"
|
||||||
|
|
||||||
|
#include "../../source/blender/blenlib/BLI_strict_flags.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is stored per thread. Align to cache line size to avoid false sharing.
|
||||||
|
*/
|
||||||
|
struct alignas(64) Local {
|
||||||
|
/** Helps to find bugs during program shutdown. */
|
||||||
|
bool destructed = false;
|
||||||
|
/**
|
||||||
|
* This is the first created #Local and on the main thread. When the main local data is
|
||||||
|
* destructed, we know that Blender is quitting and that we can't rely on thread locals being
|
||||||
|
* available still.
|
||||||
|
*/
|
||||||
|
bool is_main = false;
|
||||||
|
/**
|
||||||
|
* Number of bytes. This can be negative when e.g. one thread allocates a lot of memory, and
|
||||||
|
* another frees it. It has to be an atomic, because it may be accessed by other threads when the
|
||||||
|
* total memory usage is counted.
|
||||||
|
*/
|
||||||
|
std::atomic<int64_t> mem_in_use = 0;
|
||||||
|
/**
|
||||||
|
* Number of allocated blocks. Can be negative and is atomic for the same reason as above.
|
||||||
|
*/
|
||||||
|
std::atomic<int64_t> blocks_num = 0;
|
||||||
|
/**
|
||||||
|
* Amount of memory used when the peak was last updated. This is used so that we don't have to
|
||||||
|
* update the peak memory usage after every memory allocation. Instead it's only updated when "a
|
||||||
|
* lot" of new memory has been allocated. This makes the peak memory usage a little bit less
|
||||||
|
* accurate, but it's still good enough for practical purposes.
|
||||||
|
*/
|
||||||
|
std::atomic<int64_t> mem_in_use_during_peak_update = 0;
|
||||||
|
|
||||||
|
Local();
|
||||||
|
~Local();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a singleton that stores global data.
|
||||||
|
*/
|
||||||
|
struct Global {
|
||||||
|
/**
|
||||||
|
* Mutex that protects the vector below.
|
||||||
|
*/
|
||||||
|
std::mutex locals_mutex;
|
||||||
|
/**
|
||||||
|
* All currently constructed #Local. This must only be accessed when the mutex above is
|
||||||
|
* locked. Individual threads insert and remove themselves here.
|
||||||
|
*/
|
||||||
|
std::vector<Local *> locals;
|
||||||
|
/**
|
||||||
|
* Number of bytes that are not tracked by #Local. This is necessary because when a thread exits,
|
||||||
|
* its #Local data is freed. The memory counts stored there would be lost. The memory counts may
|
||||||
|
* be non-zero during thread destruction, if the thread did an unequal amount of allocations and
|
||||||
|
* frees (which is perfectly valid behavior as long as other threads have the responsibility to
|
||||||
|
* free any memory that the thread allocated).
|
||||||
|
*
|
||||||
|
* To solve this, the memory counts are added to these global counters when the thread
|
||||||
|
* exists. The global counters are also used when the entire process starts to exit, because the
|
||||||
|
* #Local data of the main thread is already destructed when the leak detection happens (during
|
||||||
|
* destruction of static variables which happens after destruction of threadlocals).
|
||||||
|
*/
|
||||||
|
std::atomic<int64_t> mem_in_use_outside_locals = 0;
|
||||||
|
/**
|
||||||
|
* Number of blocks that are not tracked by #Local, for the same reason as above.
|
||||||
|
*/
|
||||||
|
std::atomic<int64_t> blocks_num_outside_locals = 0;
|
||||||
|
/**
|
||||||
|
* Peak memory usage since the last reset.
|
||||||
|
*/
|
||||||
|
std::atomic<size_t> peak = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is true for most of the lifetime of the program. Only when it starts exiting this becomes
|
||||||
|
* false indicating that global counters should be used for correctness.
|
||||||
|
*/
|
||||||
|
static std::atomic<bool> use_local_counters = true;
|
||||||
|
/**
|
||||||
|
* When a thread allocated this amount of memory, the peak memory usage is updated. An alternative
|
||||||
|
* would be to update the global peak memory after every allocation, but that would cause much more
|
||||||
|
* overhead with little benefit.
|
||||||
|
*/
|
||||||
|
static constexpr int64_t peak_update_threshold = 1024 * 1024;
|
||||||
|
|
||||||
|
static Global &get_global()
|
||||||
|
{
|
||||||
|
static Global global;
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Local &get_local_data()
|
||||||
|
{
|
||||||
|
static thread_local Local local;
|
||||||
|
assert(!local.destructed);
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local::Local()
|
||||||
|
{
|
||||||
|
Global &global = get_global();
|
||||||
|
std::lock_guard lock{global.locals_mutex};
|
||||||
|
|
||||||
|
if (global.locals.empty()) {
|
||||||
|
/* This is the first thread creating #Local, it is therefore the main thread because it's
|
||||||
|
* created through #memory_usage_init. */
|
||||||
|
this->is_main = true;
|
||||||
|
}
|
||||||
|
/* Register self in the global list. */
|
||||||
|
global.locals.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Local::~Local()
|
||||||
|
{
|
||||||
|
Global &global = get_global();
|
||||||
|
std::lock_guard lock{global.locals_mutex};
|
||||||
|
|
||||||
|
/* Unregister self from the global list. */
|
||||||
|
global.locals.erase(std::find(global.locals.begin(), global.locals.end(), this));
|
||||||
|
/* Don't forget the memory counts stored locally. */
|
||||||
|
global.blocks_num_outside_locals.fetch_add(this->blocks_num, std::memory_order_relaxed);
|
||||||
|
global.mem_in_use_outside_locals.fetch_add(this->mem_in_use, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (this->is_main) {
|
||||||
|
/* The main thread started shutting down. Use global counters from now on to avoid accessing
|
||||||
|
* threadlocals after they have been destructed. */
|
||||||
|
use_local_counters.store(false, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
/* Helps to detect when thread locals are accidentally accessed after destruction. */
|
||||||
|
this->destructed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if the current memory usage is higher than the peak and update it if yes. */
|
||||||
|
static void update_global_peak()
|
||||||
|
{
|
||||||
|
Global &global = get_global();
|
||||||
|
/* Update peak. */
|
||||||
|
global.peak = std::max<size_t>(global.peak, memory_usage_current());
|
||||||
|
|
||||||
|
std::lock_guard lock{global.locals_mutex};
|
||||||
|
|
||||||
|
for (Local *local : global.locals) {
|
||||||
|
assert(!local->destructed);
|
||||||
|
/* Updating this makes sure that the peak is not updated too often, which would degrade
|
||||||
|
* performance. */
|
||||||
|
local->mem_in_use_during_peak_update = local->mem_in_use.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_usage_init()
|
||||||
|
{
|
||||||
|
/* Makes sure that the static and threadlocal variables on the main thread are initialized. */
|
||||||
|
get_local_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_usage_block_alloc(const size_t size)
|
||||||
|
{
|
||||||
|
if (LIKELY(use_local_counters.load(std::memory_order_relaxed))) {
|
||||||
|
Local &local = get_local_data();
|
||||||
|
/* Increase local memory counts. This does not cause thread synchronization in the majority of
|
||||||
|
* cases, because each thread has these counters on a separate cache line. It may only cause
|
||||||
|
* synchronization if another thread is computing the total current memory usage at the same
|
||||||
|
* time, which is very rare compared to doing allocations. */
|
||||||
|
local.blocks_num.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
local.mem_in_use.fetch_add(int64_t(size), std::memory_order_relaxed);
|
||||||
|
|
||||||
|
/* If a certain amount of new memory has been allocated, update the peak. */
|
||||||
|
if (local.mem_in_use - local.mem_in_use_during_peak_update > peak_update_threshold) {
|
||||||
|
update_global_peak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Global &global = get_global();
|
||||||
|
/* Increase global memory counts. */
|
||||||
|
global.blocks_num_outside_locals.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
global.mem_in_use_outside_locals.fetch_add(int64_t(size), std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_usage_block_free(const size_t size)
|
||||||
|
{
|
||||||
|
if (LIKELY(use_local_counters)) {
|
||||||
|
/* Decrease local memory counts. See comment in #memory_usage_block_alloc for details regarding
|
||||||
|
* thread synchronization. */
|
||||||
|
Local &local = get_local_data();
|
||||||
|
local.mem_in_use.fetch_sub(int64_t(size), std::memory_order_relaxed);
|
||||||
|
local.blocks_num.fetch_sub(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Global &global = get_global();
|
||||||
|
/* Decrease global memory counts. */
|
||||||
|
global.blocks_num_outside_locals.fetch_sub(1, std::memory_order_relaxed);
|
||||||
|
global.mem_in_use_outside_locals.fetch_sub(int64_t(size), std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t memory_usage_block_num()
|
||||||
|
{
|
||||||
|
Global &global = get_global();
|
||||||
|
std::lock_guard lock{global.locals_mutex};
|
||||||
|
|
||||||
|
/* Count the number of active blocks. */
|
||||||
|
int64_t blocks_num = global.blocks_num_outside_locals;
|
||||||
|
for (Local *local : global.locals) {
|
||||||
|
blocks_num += local->blocks_num;
|
||||||
|
}
|
||||||
|
return size_t(blocks_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t memory_usage_current()
|
||||||
|
{
|
||||||
|
Global &global = get_global();
|
||||||
|
std::lock_guard lock{global.locals_mutex};
|
||||||
|
|
||||||
|
/* Count the memory that's currently in use. */
|
||||||
|
int64_t mem_in_use = global.mem_in_use_outside_locals;
|
||||||
|
for (Local *local : global.locals) {
|
||||||
|
mem_in_use += local->mem_in_use;
|
||||||
|
}
|
||||||
|
return size_t(mem_in_use);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the approximate peak memory usage since the last call to #memory_usage_peak_reset.
|
||||||
|
* This is approximate, because the peak usage is not updated after every allocation (see
|
||||||
|
* #peak_update_threshold).
|
||||||
|
*
|
||||||
|
* In the worst case, the peak memory usage is underestimated by
|
||||||
|
* `peak_update_threshold * #threads`. After large allocations (larger than the threshold), the
|
||||||
|
* peak usage is always updated so those allocations will always be taken into account.
|
||||||
|
*/
|
||||||
|
size_t memory_usage_peak()
|
||||||
|
{
|
||||||
|
update_global_peak();
|
||||||
|
Global &global = get_global();
|
||||||
|
return global.peak;
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_usage_peak_reset()
|
||||||
|
{
|
||||||
|
Global &global = get_global();
|
||||||
|
global.peak = memory_usage_current();
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <boost/locale.hpp>
|
#include <boost/locale.hpp>
|
||||||
|
#include <iostream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "boost_locale_wrapper.h"
|
#include "boost_locale_wrapper.h"
|
||||||
|
|
|
@ -5050,30 +5050,33 @@ def km_sculpt(params):
|
||||||
# Expand
|
# Expand
|
||||||
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True},
|
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True},
|
||||||
{"properties": [
|
{"properties": [
|
||||||
("target", "MASK"),
|
("target", "MASK"),
|
||||||
("falloff_type", "GEODESIC"),
|
("falloff_type", "GEODESIC"),
|
||||||
("invert", True),
|
("invert", True),
|
||||||
("use_auto_mask", True),
|
("use_auto_mask", True),
|
||||||
("use_mask_preserve" , True)]}),
|
("use_mask_preserve", True),
|
||||||
|
]}),
|
||||||
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True, "alt": True},
|
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True, "alt": True},
|
||||||
{"properties": [
|
{"properties": [
|
||||||
("target", "MASK"),
|
("target", "MASK"),
|
||||||
("falloff_type", "NORMALS"),
|
("falloff_type", "NORMALS"),
|
||||||
("invert", False),
|
("invert", False),
|
||||||
("use_mask_preserve" , True)]}),
|
("use_mask_preserve", True),
|
||||||
|
]}),
|
||||||
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True},
|
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True},
|
||||||
{"properties": [
|
{"properties": [
|
||||||
("target", "FACE_SETS"),
|
("target", "FACE_SETS"),
|
||||||
("falloff_type", "GEODESIC"),
|
("falloff_type", "GEODESIC"),
|
||||||
("invert", False),
|
("invert", False),
|
||||||
("use_mask_preserve" , False),
|
("use_mask_preserve", False),
|
||||||
("use_modify_active", False)]}),
|
("use_modify_active", False),
|
||||||
|
]}),
|
||||||
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True},
|
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True},
|
||||||
{"properties": [
|
{"properties": [
|
||||||
("target", "FACE_SETS"),
|
("target", "FACE_SETS"),
|
||||||
("falloff_type", "BOUNDARY_FACE_SET"),
|
("falloff_type", "BOUNDARY_FACE_SET"),
|
||||||
("invert", False),
|
("invert", False),
|
||||||
("use_mask_preserve" , False),
|
("use_mask_preserve", False),
|
||||||
("use_modify_active", True),
|
("use_modify_active", True),
|
||||||
]}),
|
]}),
|
||||||
# Partial Visibility Show/hide
|
# Partial Visibility Show/hide
|
||||||
|
|
|
@ -540,7 +540,7 @@ class CLIP_OT_setup_tracking_scene(Operator):
|
||||||
sc = context.space_data
|
sc = context.space_data
|
||||||
if sc and sc.type == 'CLIP_EDITOR':
|
if sc and sc.type == 'CLIP_EDITOR':
|
||||||
clip = sc.clip
|
clip = sc.clip
|
||||||
if clip and clip.tracking.reconstruction.is_valid:
|
if clip and clip.tracking.objects.active.reconstruction.is_valid:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -57,19 +57,19 @@ class MotionPathButtonsPanel:
|
||||||
# Update Selected.
|
# Update Selected.
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.operator(f"{op_category}.paths_update", text="Update Path", icon=icon)
|
row.operator(op_category + ".paths_update", text="Update Path", icon=icon)
|
||||||
row.operator(f"{op_category}.paths_clear", text="", icon='X').only_selected = True
|
row.operator(op_category + ".paths_clear", text="", icon='X').only_selected = True
|
||||||
else:
|
else:
|
||||||
# Calculate.
|
# Calculate.
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.label(text="Nothing to show yet...", icon='ERROR')
|
col.label(text="Nothing to show yet...", icon='ERROR')
|
||||||
col.operator(f"{op_category}.paths_calculate", text="Calculate...", icon=icon)
|
col.operator(op_category + ".paths_calculate", text="Calculate...", icon=icon)
|
||||||
|
|
||||||
# Update All & Clear All.
|
# Update All & Clear All.
|
||||||
# Note that 'col' is from inside the preceeding `if` or `else` block.
|
# Note that 'col' is from inside the preceeding `if` or `else` block.
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
|
row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD')
|
||||||
row.operator(f"{op_category}.paths_clear", text="", icon='X').only_selected = False
|
row.operator(op_category + ".paths_clear", text="", icon='X').only_selected = False
|
||||||
|
|
||||||
|
|
||||||
class MotionPathButtonsPanel_display:
|
class MotionPathButtonsPanel_display:
|
||||||
|
|
|
@ -326,7 +326,7 @@ class GPENCIL_MT_cleanup(Menu):
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
layout.operator("gpencil.frame_clean_duplicate", text="Delete Duplicated Frames")
|
layout.operator("gpencil.frame_clean_duplicate", text="Delete Duplicate Frames")
|
||||||
layout.operator("gpencil.recalc_geometry", text="Recalculate Geometry")
|
layout.operator("gpencil.recalc_geometry", text="Recalculate Geometry")
|
||||||
if ob.mode != 'PAINT_GPENCIL':
|
if ob.mode != 'PAINT_GPENCIL':
|
||||||
layout.operator("gpencil.reproject")
|
layout.operator("gpencil.reproject")
|
||||||
|
|
|
@ -238,8 +238,7 @@ class DOPESHEET_HT_editor_buttons:
|
||||||
# Layer management
|
# Layer management
|
||||||
if st.mode == 'GPENCIL':
|
if st.mode == 'GPENCIL':
|
||||||
ob = context.active_object
|
ob = context.active_object
|
||||||
selected = st.dopesheet.show_only_selected
|
enable_but = ob is not None and ob.type == 'GPENCIL'
|
||||||
enable_but = selected and ob is not None and ob.type == 'GPENCIL'
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.enabled = enable_but
|
row.enabled = enable_but
|
||||||
|
|
|
@ -318,7 +318,9 @@ class NODE_MT_node(Menu):
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("node.clipboard_copy", text="Copy")
|
layout.operator("node.clipboard_copy", text="Copy")
|
||||||
layout.operator("node.clipboard_paste", text="Paste")
|
row = layout.row()
|
||||||
|
row.operator_context = 'EXEC_DEFAULT'
|
||||||
|
row.operator("node.clipboard_paste", text="Paste")
|
||||||
layout.operator("node.duplicate_move")
|
layout.operator("node.duplicate_move")
|
||||||
layout.operator("node.duplicate_move_linked")
|
layout.operator("node.duplicate_move_linked")
|
||||||
layout.operator("node.delete")
|
layout.operator("node.delete")
|
||||||
|
|
|
@ -723,8 +723,18 @@ class VIEW3D_HT_header(Header):
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
domain = curves.selection_domain
|
domain = curves.selection_domain
|
||||||
row.operator("curves.set_selection_domain", text="", icon='CURVE_BEZCIRCLE', depress=(domain == 'POINT')).domain = 'POINT'
|
row.operator(
|
||||||
row.operator("curves.set_selection_domain", text="", icon='CURVE_PATH', depress=(domain == 'CURVE')).domain = 'CURVE'
|
"curves.set_selection_domain",
|
||||||
|
text="",
|
||||||
|
icon='CURVE_BEZCIRCLE',
|
||||||
|
depress=(domain == 'POINT'),
|
||||||
|
).domain = 'POINT'
|
||||||
|
row.operator(
|
||||||
|
"curves.set_selection_domain",
|
||||||
|
text="",
|
||||||
|
icon='CURVE_PATH',
|
||||||
|
depress=(domain == 'CURVE'),
|
||||||
|
).domain = 'CURVE'
|
||||||
|
|
||||||
# Grease Pencil
|
# Grease Pencil
|
||||||
if obj and obj.type == 'GPENCIL' and context.gpencil_data:
|
if obj and obj.type == 'GPENCIL' and context.gpencil_data:
|
||||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
||||||
|
|
||||||
/* Blender file format version. */
|
/* Blender file format version. */
|
||||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||||
#define BLENDER_FILE_SUBVERSION 6
|
#define BLENDER_FILE_SUBVERSION 7
|
||||||
|
|
||||||
/* Minimum Blender version that supports reading file written with the current
|
/* Minimum Blender version that supports reading file written with the current
|
||||||
* version. Older Blender versions will test this and show a warning if the file
|
* version. Older Blender versions will test this and show a warning if the file
|
||||||
|
|
|
@ -287,11 +287,6 @@ class CurvesGeometry : public ::CurvesGeometry {
|
||||||
Span<float2> surface_uv_coords() const;
|
Span<float2> surface_uv_coords() const;
|
||||||
MutableSpan<float2> surface_uv_coords_for_write();
|
MutableSpan<float2> surface_uv_coords_for_write();
|
||||||
|
|
||||||
VArray<float> selection_point_float() const;
|
|
||||||
MutableSpan<float> selection_point_float_for_write();
|
|
||||||
VArray<float> selection_curve_float() const;
|
|
||||||
MutableSpan<float> selection_curve_float_for_write();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the largest and smallest position values, only including control points
|
* Calculate the largest and smallest position values, only including control points
|
||||||
* (rather than evaluated points). The existing values of `min` and `max` are taken into account.
|
* (rather than evaluated points). The existing values of `min` and `max` are taken into account.
|
||||||
|
|
|
@ -713,6 +713,11 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
||||||
|
|
||||||
bNode *node_copy(bNodeTree *dst_tree, const bNode &src_node, int flag, bool use_unique);
|
bNode *node_copy(bNodeTree *dst_tree, const bNode &src_node, int flag, bool use_unique);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the node itself.
|
||||||
|
*
|
||||||
|
* \note ID user reference-counting and changing the `nodes_by_id` vector are up to the caller.
|
||||||
|
*/
|
||||||
void node_free_node(bNodeTree *tree, bNode *node);
|
void node_free_node(bNodeTree *tree, bNode *node);
|
||||||
|
|
||||||
} // namespace blender::bke
|
} // namespace blender::bke
|
||||||
|
|
|
@ -169,7 +169,10 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
|
||||||
float locx = 0;
|
float locx = 0;
|
||||||
float locy = 0;
|
float locy = 0;
|
||||||
|
|
||||||
/* Runtime-only cache of the number of input links, for multi-input sockets. */
|
/**
|
||||||
|
* Runtime-only cache of the number of input links, for multi-input sockets,
|
||||||
|
* including dragged node links that aren't actually in the tree.
|
||||||
|
*/
|
||||||
short total_inputs = 0;
|
short total_inputs = 0;
|
||||||
|
|
||||||
/** Only valid when #topology_cache_is_dirty is false. */
|
/** Only valid when #topology_cache_is_dirty is false. */
|
||||||
|
@ -652,6 +655,11 @@ inline bool bNodeLink::is_available() const
|
||||||
return this->fromsock->is_available() && this->tosock->is_available();
|
return this->fromsock->is_available() && this->tosock->is_available();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool bNodeLink::is_used() const
|
||||||
|
{
|
||||||
|
return !this->is_muted() && this->is_available();
|
||||||
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -670,6 +678,20 @@ inline int bNodeSocket::index_in_tree() const
|
||||||
return this->runtime->index_in_all_sockets;
|
return this->runtime->index_in_all_sockets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int bNodeSocket::index_in_all_inputs() const
|
||||||
|
{
|
||||||
|
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||||
|
BLI_assert(this->is_input());
|
||||||
|
return this->runtime->index_in_inout_sockets;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int bNodeSocket::index_in_all_outputs() const
|
||||||
|
{
|
||||||
|
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||||
|
BLI_assert(this->is_output());
|
||||||
|
return this->runtime->index_in_inout_sockets;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool bNodeSocket::is_hidden() const
|
inline bool bNodeSocket::is_hidden() const
|
||||||
{
|
{
|
||||||
return (this->flag & SOCK_HIDDEN) != 0;
|
return (this->flag & SOCK_HIDDEN) != 0;
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup bke
|
||||||
|
*
|
||||||
|
* Pose Backups can be created from the current pose, and later restored. The
|
||||||
|
* backup is restricted to those bones animated by a given Action, so that
|
||||||
|
* operations are as fast as possible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "BLI_listbase.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct PoseBackup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a backup of those bones that are selected AND animated in the given action.
|
||||||
|
*
|
||||||
|
* The backup is owned by the caller, and should be freed with `BKE_pose_backup_free()`.
|
||||||
|
*/
|
||||||
|
struct PoseBackup *BKE_pose_backup_create_selected_bones(
|
||||||
|
const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a backup of those bones that are animated in the given action.
|
||||||
|
*
|
||||||
|
* The backup is owned by the caller, and should be freed with `BKE_pose_backup_free()`.
|
||||||
|
*/
|
||||||
|
struct PoseBackup *BKE_pose_backup_create_all_bones(
|
||||||
|
const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT;
|
||||||
|
bool BKE_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup);
|
||||||
|
void BKE_pose_backup_restore(const struct PoseBackup *pbd);
|
||||||
|
void BKE_pose_backup_free(struct PoseBackup *pbd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a backup of those bones that are animated in the given action.
|
||||||
|
*
|
||||||
|
* The backup is owned by the Object, and there can be only one backup at a time.
|
||||||
|
* It should be freed with `BKE_pose_backup_clear(ob)`.
|
||||||
|
*/
|
||||||
|
void BKE_pose_backup_create_on_object(struct Object *ob, const struct bAction *action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the pose backup owned by this OBject.
|
||||||
|
*
|
||||||
|
* \return true on success, false if there was no pose backup to restore.
|
||||||
|
*
|
||||||
|
* \see #BKE_pose_backup_create_on_object
|
||||||
|
*/
|
||||||
|
bool BKE_pose_backup_restore_on_object(struct Object *ob);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the pose backup that was stored on this object's runtime data.
|
||||||
|
*/
|
||||||
|
void BKE_pose_backup_clear(struct Object *ob);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -254,6 +254,7 @@ set(SRC
|
||||||
intern/pbvh_uv_islands.cc
|
intern/pbvh_uv_islands.cc
|
||||||
intern/pointcache.c
|
intern/pointcache.c
|
||||||
intern/pointcloud.cc
|
intern/pointcloud.cc
|
||||||
|
intern/pose_backup.cc
|
||||||
intern/preferences.c
|
intern/preferences.c
|
||||||
intern/report.c
|
intern/report.c
|
||||||
intern/rigidbody.c
|
intern/rigidbody.c
|
||||||
|
@ -451,6 +452,7 @@ set(SRC
|
||||||
BKE_pbvh_pixels.hh
|
BKE_pbvh_pixels.hh
|
||||||
BKE_pointcache.h
|
BKE_pointcache.h
|
||||||
BKE_pointcloud.h
|
BKE_pointcloud.h
|
||||||
|
BKE_pose_backup.h
|
||||||
BKE_preferences.h
|
BKE_preferences.h
|
||||||
BKE_report.h
|
BKE_report.h
|
||||||
BKE_rigidbody.h
|
BKE_rigidbody.h
|
||||||
|
|
|
@ -108,7 +108,7 @@ static void action_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src,
|
||||||
/* Duplicate F-Curve. */
|
/* Duplicate F-Curve. */
|
||||||
|
|
||||||
/* XXX TODO: pass subdata flag?
|
/* XXX TODO: pass subdata flag?
|
||||||
* But surprisingly does not seem to be doing any ID refcounting... */
|
* But surprisingly does not seem to be doing any ID reference-counting. */
|
||||||
fcurve_dst = BKE_fcurve_copy(fcurve_src);
|
fcurve_dst = BKE_fcurve_copy(fcurve_src);
|
||||||
|
|
||||||
BLI_addtail(&action_dst->curves, fcurve_dst);
|
BLI_addtail(&action_dst->curves, fcurve_dst);
|
||||||
|
|
|
@ -416,9 +416,9 @@ static void setup_app_data(bContext *C,
|
||||||
* means that we do not reset their user count, however we do increase that one when doing
|
* means that we do not reset their user count, however we do increase that one when doing
|
||||||
* lib_link on local IDs using linked ones.
|
* lib_link on local IDs using linked ones.
|
||||||
* There is no real way to predict amount of changes here, so we have to fully redo
|
* There is no real way to predict amount of changes here, so we have to fully redo
|
||||||
* refcounting.
|
* reference-counting.
|
||||||
* Now that we re-use (and do not liblink in readfile.c) most local datablocks as well, we have
|
* Now that we re-use (and do not liblink in readfile.c) most local data-blocks as well,
|
||||||
* to recompute refcount for all local IDs too. */
|
* we have to recompute reference-counts for all local IDs too. */
|
||||||
BKE_main_id_refcount_recompute(bmain, false);
|
BKE_main_id_refcount_recompute(bmain, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1460,9 +1460,9 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
|
||||||
Cloth *cloth = clmd->clothObject;
|
Cloth *cloth = clmd->clothObject;
|
||||||
ClothSpring *spring = nullptr, *tspring = nullptr, *tspring2 = nullptr;
|
ClothSpring *spring = nullptr, *tspring = nullptr, *tspring2 = nullptr;
|
||||||
uint struct_springs = 0, shear_springs = 0, bend_springs = 0, struct_springs_real = 0;
|
uint struct_springs = 0, shear_springs = 0, bend_springs = 0, struct_springs_real = 0;
|
||||||
uint mvert_num = (uint)mesh->totvert;
|
uint mvert_num = uint(mesh->totvert);
|
||||||
uint numedges = uint(mesh->totedge);
|
uint numedges = uint(mesh->totedge);
|
||||||
uint numpolys = (uint)mesh->totpoly;
|
uint numpolys = uint(mesh->totpoly);
|
||||||
float shrink_factor;
|
float shrink_factor;
|
||||||
const MEdge *medge = BKE_mesh_edges(mesh);
|
const MEdge *medge = BKE_mesh_edges(mesh);
|
||||||
const MPoly *mpoly = BKE_mesh_polys(mesh);
|
const MPoly *mpoly = BKE_mesh_polys(mesh);
|
||||||
|
@ -1647,7 +1647,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
|
||||||
for (int i = 0; i < mvert_num; i++) {
|
for (int i = 0; i < mvert_num; i++) {
|
||||||
if (cloth->verts[i].spring_count > 0) {
|
if (cloth->verts[i].spring_count > 0) {
|
||||||
cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f /
|
cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f /
|
||||||
(float(cloth->verts[i].spring_count));
|
float(cloth->verts[i].spring_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1511,8 +1511,8 @@ Depsgraph *CTX_data_expect_evaluated_depsgraph(const bContext *C)
|
||||||
{
|
{
|
||||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||||
/* TODO(sergey): Assert that the dependency graph is fully evaluated.
|
/* TODO(sergey): Assert that the dependency graph is fully evaluated.
|
||||||
* Note that first the depsgraph and scene post-eval hooks needs to run extra round of updates
|
* Note that first the depsgraph and scene post-evaluation hooks needs to run extra round of
|
||||||
* first to make check here really reliable. */
|
* updates first to make check here really reliable. */
|
||||||
return depsgraph;
|
return depsgraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,6 @@ static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right";
|
||||||
static const std::string ATTR_NURBS_ORDER = "nurbs_order";
|
static const std::string ATTR_NURBS_ORDER = "nurbs_order";
|
||||||
static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight";
|
static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight";
|
||||||
static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode";
|
static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode";
|
||||||
static const std::string ATTR_SELECTION_POINT_FLOAT = ".selection_point_float";
|
|
||||||
static const std::string ATTR_SELECTION_CURVE_FLOAT = ".selection_curve_float";
|
|
||||||
static const std::string ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
|
static const std::string ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -433,26 +431,6 @@ MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
|
||||||
return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE);
|
return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
VArray<float> CurvesGeometry::selection_point_float() const
|
|
||||||
{
|
|
||||||
return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
MutableSpan<float> CurvesGeometry::selection_point_float_for_write()
|
|
||||||
{
|
|
||||||
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
VArray<float> CurvesGeometry::selection_curve_float() const
|
|
||||||
{
|
|
||||||
return get_varray_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
MutableSpan<float> CurvesGeometry::selection_curve_float_for_write()
|
|
||||||
{
|
|
||||||
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
|
@ -1117,15 +1117,18 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (MDeformVert &dvert : mesh->deform_verts_for_write()) {
|
MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
|
||||||
MDeformWeight *weight = BKE_defvert_find_index(&dvert, index);
|
threading::parallel_for(dverts.index_range(), 1024, [&](IndexRange range) {
|
||||||
BKE_defvert_remove_group(&dvert, weight);
|
for (MDeformVert &dvert : dverts.slice(range)) {
|
||||||
for (MDeformWeight &weight : MutableSpan(dvert.dw, dvert.totweight)) {
|
MDeformWeight *weight = BKE_defvert_find_index(&dvert, index);
|
||||||
if (weight.def_nr > index) {
|
BKE_defvert_remove_group(&dvert, weight);
|
||||||
weight.def_nr--;
|
for (MDeformWeight &weight : MutableSpan(dvert.dw, dvert.totweight)) {
|
||||||
|
if (weight.def_nr > index) {
|
||||||
|
weight.def_nr--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -732,7 +732,7 @@ static void cp_key(const int start,
|
||||||
|
|
||||||
if (flagflo) {
|
if (flagflo) {
|
||||||
ktot += start * kd;
|
ktot += start * kd;
|
||||||
a = (int)floor(ktot);
|
a = int(floor(ktot));
|
||||||
if (a) {
|
if (a) {
|
||||||
ktot -= a;
|
ktot -= a;
|
||||||
k1 += a * key->elemsize;
|
k1 += a * key->elemsize;
|
||||||
|
@ -1078,7 +1078,7 @@ static void do_key(const int start,
|
||||||
if (flagdo & 1) {
|
if (flagdo & 1) {
|
||||||
if (flagflo & 1) {
|
if (flagflo & 1) {
|
||||||
k1tot += start * k1d;
|
k1tot += start * k1d;
|
||||||
a = (int)floor(k1tot);
|
a = int(floor(k1tot));
|
||||||
if (a) {
|
if (a) {
|
||||||
k1tot -= a;
|
k1tot -= a;
|
||||||
k1 += a * key->elemsize;
|
k1 += a * key->elemsize;
|
||||||
|
@ -1091,7 +1091,7 @@ static void do_key(const int start,
|
||||||
if (flagdo & 2) {
|
if (flagdo & 2) {
|
||||||
if (flagflo & 2) {
|
if (flagflo & 2) {
|
||||||
k2tot += start * k2d;
|
k2tot += start * k2d;
|
||||||
a = (int)floor(k2tot);
|
a = int(floor(k2tot));
|
||||||
if (a) {
|
if (a) {
|
||||||
k2tot -= a;
|
k2tot -= a;
|
||||||
k2 += a * key->elemsize;
|
k2 += a * key->elemsize;
|
||||||
|
@ -1104,7 +1104,7 @@ static void do_key(const int start,
|
||||||
if (flagdo & 4) {
|
if (flagdo & 4) {
|
||||||
if (flagflo & 4) {
|
if (flagflo & 4) {
|
||||||
k3tot += start * k3d;
|
k3tot += start * k3d;
|
||||||
a = (int)floor(k3tot);
|
a = int(floor(k3tot));
|
||||||
if (a) {
|
if (a) {
|
||||||
k3tot -= a;
|
k3tot -= a;
|
||||||
k3 += a * key->elemsize;
|
k3 += a * key->elemsize;
|
||||||
|
@ -1117,7 +1117,7 @@ static void do_key(const int start,
|
||||||
if (flagdo & 8) {
|
if (flagdo & 8) {
|
||||||
if (flagflo & 8) {
|
if (flagflo & 8) {
|
||||||
k4tot += start * k4d;
|
k4tot += start * k4d;
|
||||||
a = (int)floor(k4tot);
|
a = int(floor(k4tot));
|
||||||
if (a) {
|
if (a) {
|
||||||
k4tot -= a;
|
k4tot -= a;
|
||||||
k4 += a * key->elemsize;
|
k4 += a * key->elemsize;
|
||||||
|
@ -1661,7 +1661,7 @@ int BKE_keyblock_element_count(const Key *key)
|
||||||
|
|
||||||
size_t BKE_keyblock_element_calc_size_from_shape(const Key *key, const int shape_index)
|
size_t BKE_keyblock_element_calc_size_from_shape(const Key *key, const int shape_index)
|
||||||
{
|
{
|
||||||
return (size_t)BKE_keyblock_element_count_from_shape(key, shape_index) * key->elemsize;
|
return size_t(BKE_keyblock_element_count_from_shape(key, shape_index)) * key->elemsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BKE_keyblock_element_calc_size(const Key *key)
|
size_t BKE_keyblock_element_calc_size(const Key *key)
|
||||||
|
|
|
@ -1057,7 +1057,7 @@ static void layer_collection_objects_sync(ViewLayer *view_layer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Holdout and indirect only */
|
/* Holdout and indirect only */
|
||||||
if ((layer->flag & LAYER_COLLECTION_HOLDOUT)) {
|
if (layer->flag & LAYER_COLLECTION_HOLDOUT) {
|
||||||
base->flag_from_collection |= BASE_HOLDOUT;
|
base->flag_from_collection |= BASE_HOLDOUT;
|
||||||
}
|
}
|
||||||
if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) {
|
if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) {
|
||||||
|
|
|
@ -322,8 +322,8 @@ void id_us_min(ID *id)
|
||||||
|
|
||||||
if (id->us <= limit) {
|
if (id->us <= limit) {
|
||||||
if (!ID_TYPE_IS_DEPRECATED(GS(id->name))) {
|
if (!ID_TYPE_IS_DEPRECATED(GS(id->name))) {
|
||||||
/* Do not assert on deprecated ID types, we cannot really ensure that their ID refcounting
|
/* Do not assert on deprecated ID types, we cannot really ensure that their ID
|
||||||
* is valid... */
|
* reference-counting is valid. */
|
||||||
CLOG_ERROR(&LOG,
|
CLOG_ERROR(&LOG,
|
||||||
"ID user decrement error: %s (from '%s'): %d <= %d",
|
"ID user decrement error: %s (from '%s'): %d <= %d",
|
||||||
id->name,
|
id->name,
|
||||||
|
|
|
@ -261,7 +261,7 @@ static bool library_foreach_ID_link(Main *bmain,
|
||||||
* (the node tree), but re-use those generated for the 'owner' ID (the material). */
|
* (the node tree), but re-use those generated for the 'owner' ID (the material). */
|
||||||
if (inherit_data == NULL) {
|
if (inherit_data == NULL) {
|
||||||
data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0;
|
data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0;
|
||||||
/* When an ID is defined as not refcounting its ID usages, it should never do it. */
|
/* When an ID is defined as not reference-counting its ID usages, it should never do it. */
|
||||||
data.cb_flag_clear = (id->tag & LIB_TAG_NO_USER_REFCOUNT) ?
|
data.cb_flag_clear = (id->tag & LIB_TAG_NO_USER_REFCOUNT) ?
|
||||||
IDWALK_CB_USER | IDWALK_CB_USER_ONE :
|
IDWALK_CB_USER | IDWALK_CB_USER_ONE :
|
||||||
0;
|
0;
|
||||||
|
|
|
@ -143,9 +143,7 @@ static void make_edges_mdata_extend(Mesh &mesh)
|
||||||
static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispbase)
|
static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispbase)
|
||||||
{
|
{
|
||||||
using namespace blender::bke;
|
using namespace blender::bke;
|
||||||
const float *data;
|
int a, b, ofs;
|
||||||
int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0;
|
|
||||||
int p1, p2, p3, p4, *index;
|
|
||||||
const bool conv_polys = (
|
const bool conv_polys = (
|
||||||
/* 2D polys are filled with #DispList.type == #DL_INDEX3. */
|
/* 2D polys are filled with #DispList.type == #DL_INDEX3. */
|
||||||
(CU_DO_2DFILL(cu) == false) ||
|
(CU_DO_2DFILL(cu) == false) ||
|
||||||
|
@ -153,6 +151,10 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba
|
||||||
BKE_curve_type_get(cu) == OB_SURF);
|
BKE_curve_type_get(cu) == OB_SURF);
|
||||||
|
|
||||||
/* count */
|
/* count */
|
||||||
|
int totvert = 0;
|
||||||
|
int totedge = 0;
|
||||||
|
int totpoly = 0;
|
||||||
|
int totloop = 0;
|
||||||
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
|
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
|
||||||
if (dl->type == DL_SEGM) {
|
if (dl->type == DL_SEGM) {
|
||||||
totvert += dl->parts * dl->nr;
|
totvert += dl->parts * dl->nr;
|
||||||
|
@ -193,117 +195,110 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba
|
||||||
MutableSpan<MPoly> polys = mesh->polys_for_write();
|
MutableSpan<MPoly> polys = mesh->polys_for_write();
|
||||||
MutableSpan<MLoop> loops = mesh->loops_for_write();
|
MutableSpan<MLoop> loops = mesh->loops_for_write();
|
||||||
|
|
||||||
MVert *mvert = verts.data();
|
|
||||||
MEdge *medge = edges.data();
|
|
||||||
MPoly *mpoly = polys.data();
|
|
||||||
MLoop *mloop = loops.data();
|
|
||||||
MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||||
SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>(
|
SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>(
|
||||||
"material_index", ATTR_DOMAIN_FACE);
|
"material_index", ATTR_DOMAIN_FACE);
|
||||||
MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named(
|
MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named(
|
||||||
&mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh->totloop, DATA_("UVMap")));
|
&mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh->totloop, DATA_("UVMap")));
|
||||||
|
|
||||||
/* verts and faces */
|
int dst_vert = 0;
|
||||||
vertcount = 0;
|
int dst_edge = 0;
|
||||||
|
int dst_poly = 0;
|
||||||
|
int dst_loop = 0;
|
||||||
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
|
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
|
||||||
const bool is_smooth = (dl->rt & CU_SMOOTH) != 0;
|
const bool is_smooth = (dl->rt & CU_SMOOTH) != 0;
|
||||||
|
|
||||||
if (dl->type == DL_SEGM) {
|
if (dl->type == DL_SEGM) {
|
||||||
startvert = vertcount;
|
const int startvert = dst_vert;
|
||||||
a = dl->parts * dl->nr;
|
a = dl->parts * dl->nr;
|
||||||
data = dl->verts;
|
const float *data = dl->verts;
|
||||||
while (a--) {
|
while (a--) {
|
||||||
copy_v3_v3(mvert->co, data);
|
copy_v3_v3(verts[dst_vert].co, data);
|
||||||
data += 3;
|
data += 3;
|
||||||
vertcount++;
|
dst_vert++;
|
||||||
mvert++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (a = 0; a < dl->parts; a++) {
|
for (a = 0; a < dl->parts; a++) {
|
||||||
ofs = a * dl->nr;
|
ofs = a * dl->nr;
|
||||||
for (b = 1; b < dl->nr; b++) {
|
for (b = 1; b < dl->nr; b++) {
|
||||||
medge->v1 = startvert + ofs + b - 1;
|
edges[dst_edge].v1 = startvert + ofs + b - 1;
|
||||||
medge->v2 = startvert + ofs + b;
|
edges[dst_edge].v2 = startvert + ofs + b;
|
||||||
medge->flag = ME_EDGEDRAW;
|
edges[dst_edge].flag = ME_EDGEDRAW;
|
||||||
|
|
||||||
medge++;
|
dst_edge++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dl->type == DL_POLY) {
|
else if (dl->type == DL_POLY) {
|
||||||
if (conv_polys) {
|
if (conv_polys) {
|
||||||
startvert = vertcount;
|
const int startvert = dst_vert;
|
||||||
a = dl->parts * dl->nr;
|
a = dl->parts * dl->nr;
|
||||||
data = dl->verts;
|
const float *data = dl->verts;
|
||||||
while (a--) {
|
while (a--) {
|
||||||
copy_v3_v3(mvert->co, data);
|
copy_v3_v3(verts[dst_vert].co, data);
|
||||||
data += 3;
|
data += 3;
|
||||||
vertcount++;
|
dst_vert++;
|
||||||
mvert++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (a = 0; a < dl->parts; a++) {
|
for (a = 0; a < dl->parts; a++) {
|
||||||
ofs = a * dl->nr;
|
ofs = a * dl->nr;
|
||||||
for (b = 0; b < dl->nr; b++) {
|
for (b = 0; b < dl->nr; b++) {
|
||||||
medge->v1 = startvert + ofs + b;
|
edges[dst_edge].v1 = startvert + ofs + b;
|
||||||
if (b == dl->nr - 1) {
|
if (b == dl->nr - 1) {
|
||||||
medge->v2 = startvert + ofs;
|
edges[dst_edge].v2 = startvert + ofs;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
medge->v2 = startvert + ofs + b + 1;
|
edges[dst_edge].v2 = startvert + ofs + b + 1;
|
||||||
}
|
}
|
||||||
medge->flag = ME_EDGEDRAW;
|
edges[dst_edge].flag = ME_EDGEDRAW;
|
||||||
medge++;
|
dst_edge++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dl->type == DL_INDEX3) {
|
else if (dl->type == DL_INDEX3) {
|
||||||
startvert = vertcount;
|
const int startvert = dst_vert;
|
||||||
a = dl->nr;
|
a = dl->nr;
|
||||||
data = dl->verts;
|
const float *data = dl->verts;
|
||||||
while (a--) {
|
while (a--) {
|
||||||
copy_v3_v3(mvert->co, data);
|
copy_v3_v3(verts[dst_vert].co, data);
|
||||||
data += 3;
|
data += 3;
|
||||||
vertcount++;
|
dst_vert++;
|
||||||
mvert++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a = dl->parts;
|
a = dl->parts;
|
||||||
index = dl->index;
|
const int *index = dl->index;
|
||||||
while (a--) {
|
while (a--) {
|
||||||
mloop[0].v = startvert + index[0];
|
loops[dst_loop + 0].v = startvert + index[0];
|
||||||
mloop[1].v = startvert + index[2];
|
loops[dst_loop + 1].v = startvert + index[2];
|
||||||
mloop[2].v = startvert + index[1];
|
loops[dst_loop + 2].v = startvert + index[1];
|
||||||
mpoly->loopstart = int(mloop - loops.data());
|
polys[dst_poly].loopstart = dst_loop;
|
||||||
mpoly->totloop = 3;
|
polys[dst_poly].totloop = 3;
|
||||||
material_indices.span[mpoly - polys.data()] = dl->col;
|
material_indices.span[dst_poly] = dl->col;
|
||||||
|
|
||||||
if (mloopuv) {
|
if (mloopuv) {
|
||||||
for (int i = 0; i < 3; i++, mloopuv++) {
|
for (int i = 0; i < 3; i++, mloopuv++) {
|
||||||
mloopuv->uv[0] = (mloop[i].v - startvert) / float(dl->nr - 1);
|
mloopuv->uv[0] = (loops[dst_loop + i].v - startvert) / float(dl->nr - 1);
|
||||||
mloopuv->uv[1] = 0.0f;
|
mloopuv->uv[1] = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_smooth) {
|
if (is_smooth) {
|
||||||
mpoly->flag |= ME_SMOOTH;
|
polys[dst_poly].flag |= ME_SMOOTH;
|
||||||
}
|
}
|
||||||
mpoly++;
|
dst_poly++;
|
||||||
mloop += 3;
|
dst_loop += 3;
|
||||||
index += 3;
|
index += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dl->type == DL_SURF) {
|
else if (dl->type == DL_SURF) {
|
||||||
startvert = vertcount;
|
const int startvert = dst_vert;
|
||||||
a = dl->parts * dl->nr;
|
a = dl->parts * dl->nr;
|
||||||
data = dl->verts;
|
const float *data = dl->verts;
|
||||||
while (a--) {
|
while (a--) {
|
||||||
copy_v3_v3(mvert->co, data);
|
copy_v3_v3(verts[dst_vert].co, data);
|
||||||
data += 3;
|
data += 3;
|
||||||
vertcount++;
|
dst_vert++;
|
||||||
mvert++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (a = 0; a < dl->parts; a++) {
|
for (a = 0; a < dl->parts; a++) {
|
||||||
|
@ -312,6 +307,7 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int p1, p2, p3, p4;
|
||||||
if (dl->flag & DL_CYCL_U) { /* p2 -> p1 -> */
|
if (dl->flag & DL_CYCL_U) { /* p2 -> p1 -> */
|
||||||
p1 = startvert + dl->nr * a; /* p4 -> p3 -> */
|
p1 = startvert + dl->nr * a; /* p4 -> p3 -> */
|
||||||
p2 = p1 + dl->nr - 1; /* -----> next row */
|
p2 = p1 + dl->nr - 1; /* -----> next row */
|
||||||
|
@ -332,13 +328,13 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; b < dl->nr; b++) {
|
for (; b < dl->nr; b++) {
|
||||||
mloop[0].v = p1;
|
loops[dst_loop + 0].v = p1;
|
||||||
mloop[1].v = p3;
|
loops[dst_loop + 1].v = p3;
|
||||||
mloop[2].v = p4;
|
loops[dst_loop + 2].v = p4;
|
||||||
mloop[3].v = p2;
|
loops[dst_loop + 3].v = p2;
|
||||||
mpoly->loopstart = int(mloop - loops.data());
|
polys[dst_poly].loopstart = dst_loop;
|
||||||
mpoly->totloop = 4;
|
polys[dst_poly].totloop = 4;
|
||||||
material_indices.span[mpoly - polys.data()] = dl->col;
|
material_indices.span[dst_poly] = dl->col;
|
||||||
|
|
||||||
if (mloopuv) {
|
if (mloopuv) {
|
||||||
int orco_sizeu = dl->nr - 1;
|
int orco_sizeu = dl->nr - 1;
|
||||||
|
@ -357,7 +353,7 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++, mloopuv++) {
|
for (int i = 0; i < 4; i++, mloopuv++) {
|
||||||
/* find uv based on vertex index into grid array */
|
/* find uv based on vertex index into grid array */
|
||||||
int v = mloop[i].v - startvert;
|
int v = loops[dst_loop + i].v - startvert;
|
||||||
|
|
||||||
mloopuv->uv[0] = (v / dl->nr) / float(orco_sizev);
|
mloopuv->uv[0] = (v / dl->nr) / float(orco_sizev);
|
||||||
mloopuv->uv[1] = (v % dl->nr) / float(orco_sizeu);
|
mloopuv->uv[1] = (v % dl->nr) / float(orco_sizeu);
|
||||||
|
@ -373,10 +369,10 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_smooth) {
|
if (is_smooth) {
|
||||||
mpoly->flag |= ME_SMOOTH;
|
polys[dst_poly].flag |= ME_SMOOTH;
|
||||||
}
|
}
|
||||||
mpoly++;
|
dst_poly++;
|
||||||
mloop += 4;
|
dst_loop += 4;
|
||||||
|
|
||||||
p4 = p3;
|
p4 = p3;
|
||||||
p3++;
|
p3++;
|
||||||
|
@ -1066,7 +1062,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
|
||||||
* everything is only allowed to reference original data-blocks.
|
* everything is only allowed to reference original data-blocks.
|
||||||
*
|
*
|
||||||
* Note that user-count updates has to be done *after* mesh has been transferred to Main database
|
* Note that user-count updates has to be done *after* mesh has been transferred to Main database
|
||||||
* (since doing refcounting on non-Main IDs is forbidden). */
|
* (since doing reference-counting on non-Main IDs is forbidden). */
|
||||||
BKE_library_foreach_ID_link(
|
BKE_library_foreach_ID_link(
|
||||||
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
|
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
|
||||||
|
|
||||||
|
|
|
@ -972,7 +972,7 @@ static void loop_manifold_fan_around_vert_next(const Span<MLoop> loops,
|
||||||
const uint vert_fan_next = loops[*r_mlfan_curr_index].v;
|
const uint vert_fan_next = loops[*r_mlfan_curr_index].v;
|
||||||
const MPoly &mpfan_next = polys[*r_mpfan_curr_index];
|
const MPoly &mpfan_next = polys[*r_mpfan_curr_index];
|
||||||
if ((vert_fan_orig == vert_fan_next && vert_fan_orig == mv_pivot_index) ||
|
if ((vert_fan_orig == vert_fan_next && vert_fan_orig == mv_pivot_index) ||
|
||||||
(vert_fan_orig != vert_fan_next && vert_fan_orig != mv_pivot_index)) {
|
(!ELEM(vert_fan_orig, vert_fan_next, mv_pivot_index))) {
|
||||||
/* We need the previous loop, but current one is our vertex's loop. */
|
/* We need the previous loop, but current one is our vertex's loop. */
|
||||||
*r_mlfan_vert_index = *r_mlfan_curr_index;
|
*r_mlfan_vert_index = *r_mlfan_curr_index;
|
||||||
if (--(*r_mlfan_curr_index) < mpfan_next.loopstart) {
|
if (--(*r_mlfan_curr_index) < mpfan_next.loopstart) {
|
||||||
|
|
|
@ -1592,7 +1592,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((size_t)mp_dst->totloop > islands_res_buff_size) {
|
if (size_t(mp_dst->totloop) > islands_res_buff_size) {
|
||||||
islands_res_buff_size = size_t(mp_dst->totloop) + MREMAP_DEFAULT_BUFSIZE;
|
islands_res_buff_size = size_t(mp_dst->totloop) + MREMAP_DEFAULT_BUFSIZE;
|
||||||
for (tindex = 0; tindex < num_trees; tindex++) {
|
for (tindex = 0; tindex < num_trees; tindex++) {
|
||||||
islands_res[tindex] = static_cast<IslandResult *>(
|
islands_res[tindex] = static_cast<IslandResult *>(
|
||||||
|
@ -2257,7 +2257,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
|
||||||
*/
|
*/
|
||||||
RNG *rng = BLI_rng_new(0);
|
RNG *rng = BLI_rng_new(0);
|
||||||
|
|
||||||
const size_t numpolys_src = (size_t)me_src->totpoly;
|
const size_t numpolys_src = size_t(me_src->totpoly);
|
||||||
|
|
||||||
/* Here it's simpler to just allocate for all polys :/ */
|
/* Here it's simpler to just allocate for all polys :/ */
|
||||||
int *indices = static_cast<int *>(MEM_mallocN(sizeof(*indices) * numpolys_src, __func__));
|
int *indices = static_cast<int *>(MEM_mallocN(sizeof(*indices) * numpolys_src, __func__));
|
||||||
|
|
|
@ -2952,11 +2952,6 @@ void nodeRebuildIDVector(bNodeTree *node_tree)
|
||||||
|
|
||||||
namespace blender::bke {
|
namespace blender::bke {
|
||||||
|
|
||||||
/**
|
|
||||||
* Free the node itself.
|
|
||||||
*
|
|
||||||
* \note: ID user refcounting and changing the `nodes_by_id` vector are up to the caller.
|
|
||||||
*/
|
|
||||||
void node_free_node(bNodeTree *ntree, bNode *node)
|
void node_free_node(bNodeTree *ntree, bNode *node)
|
||||||
{
|
{
|
||||||
/* since it is called while free database, node->id is undefined */
|
/* since it is called while free database, node->id is undefined */
|
||||||
|
@ -3031,7 +3026,7 @@ void ntreeFreeLocalNode(bNodeTree *ntree, bNode *node)
|
||||||
void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
|
void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
|
||||||
{
|
{
|
||||||
/* This function is not for localized node trees, we do not want
|
/* This function is not for localized node trees, we do not want
|
||||||
* do to ID user refcounting and removal of animdation data then. */
|
* do to ID user reference-counting and removal of animdation data then. */
|
||||||
BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) == 0);
|
BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) == 0);
|
||||||
|
|
||||||
bool node_has_id = false;
|
bool node_has_id = false;
|
||||||
|
@ -3558,8 +3553,6 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
|
||||||
node->flag |= flags_to_set;
|
node->flag |= flags_to_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
|
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
|
||||||
{
|
{
|
||||||
const bool was_available = (sock->flag & SOCK_UNAVAIL) == 0;
|
const bool was_available = (sock->flag & SOCK_UNAVAIL) == 0;
|
||||||
|
|
|
@ -51,6 +51,10 @@ static void update_link_vector(const bNodeTree &ntree)
|
||||||
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
|
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
|
||||||
tree_runtime.links.clear();
|
tree_runtime.links.clear();
|
||||||
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
||||||
|
/* Check that the link connects nodes within this tree. */
|
||||||
|
BLI_assert(tree_runtime.nodes_by_id.contains(link->fromnode));
|
||||||
|
BLI_assert(tree_runtime.nodes_by_id.contains(link->tonode));
|
||||||
|
|
||||||
tree_runtime.links.append(link);
|
tree_runtime.links.append(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,7 @@
|
||||||
#include "BKE_pbvh.h"
|
#include "BKE_pbvh.h"
|
||||||
#include "BKE_pointcache.h"
|
#include "BKE_pointcache.h"
|
||||||
#include "BKE_pointcloud.h"
|
#include "BKE_pointcloud.h"
|
||||||
|
#include "BKE_pose_backup.h"
|
||||||
#include "BKE_rigidbody.h"
|
#include "BKE_rigidbody.h"
|
||||||
#include "BKE_scene.h"
|
#include "BKE_scene.h"
|
||||||
#include "BKE_shader_fx.h"
|
#include "BKE_shader_fx.h"
|
||||||
|
@ -1814,6 +1815,7 @@ void BKE_object_free_derived_caches(Object *ob)
|
||||||
}
|
}
|
||||||
|
|
||||||
BKE_object_to_mesh_clear(ob);
|
BKE_object_to_mesh_clear(ob);
|
||||||
|
BKE_pose_backup_clear(ob);
|
||||||
BKE_object_to_curve_clear(ob);
|
BKE_object_to_curve_clear(ob);
|
||||||
BKE_object_free_curve_cache(ob);
|
BKE_object_free_curve_cache(ob);
|
||||||
|
|
||||||
|
@ -2889,6 +2891,7 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size)
|
||||||
case OB_LAMP: {
|
case OB_LAMP: {
|
||||||
Light *lamp = (Light *)ob->data;
|
Light *lamp = (Light *)ob->data;
|
||||||
lamp->dist *= size;
|
lamp->dist *= size;
|
||||||
|
lamp->radius *= size;
|
||||||
lamp->area_size *= size;
|
lamp->area_size *= size;
|
||||||
lamp->area_sizey *= size;
|
lamp->area_sizey *= size;
|
||||||
lamp->area_sizez *= size;
|
lamp->area_sizez *= size;
|
||||||
|
@ -5132,6 +5135,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int /*flag*/)
|
||||||
runtime->mesh_deform_eval = nullptr;
|
runtime->mesh_deform_eval = nullptr;
|
||||||
runtime->curve_cache = nullptr;
|
runtime->curve_cache = nullptr;
|
||||||
runtime->object_as_temp_mesh = nullptr;
|
runtime->object_as_temp_mesh = nullptr;
|
||||||
|
runtime->pose_backup = nullptr;
|
||||||
runtime->object_as_temp_curve = nullptr;
|
runtime->object_as_temp_curve = nullptr;
|
||||||
runtime->geometry_set_eval = nullptr;
|
runtime->geometry_set_eval = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ MeshUVVert *MeshPrimitive::get_other_uv_vertex(const MeshVertex *v1, const MeshV
|
||||||
BLI_assert(vertices[0].vertex == v1 || vertices[1].vertex == v1 || vertices[2].vertex == v1);
|
BLI_assert(vertices[0].vertex == v1 || vertices[1].vertex == v1 || vertices[2].vertex == v1);
|
||||||
BLI_assert(vertices[0].vertex == v2 || vertices[1].vertex == v2 || vertices[2].vertex == v2);
|
BLI_assert(vertices[0].vertex == v2 || vertices[1].vertex == v2 || vertices[2].vertex == v2);
|
||||||
for (MeshUVVert &uv_vertex : vertices) {
|
for (MeshUVVert &uv_vertex : vertices) {
|
||||||
if (uv_vertex.vertex != v1 && uv_vertex.vertex != v2) {
|
if (!ELEM(uv_vertex.vertex, v1, v2)) {
|
||||||
return &uv_vertex;
|
return &uv_vertex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
/** \file
|
/** \file
|
||||||
* \ingroup edarmature
|
* \ingroup bke
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ED_armature.h"
|
#include "BKE_pose_backup.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -38,6 +38,14 @@ struct PoseBackup {
|
||||||
ListBase /* PoseChannelBackup* */ backups;
|
ListBase /* PoseChannelBackup* */ backups;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a backup of the pose, for only those bones that are animated in the
|
||||||
|
* given Action. If `selected_bone_names` is not empty, the set of bones to back
|
||||||
|
* up is intersected with these bone names such that only the selected subset is
|
||||||
|
* backed up.
|
||||||
|
*
|
||||||
|
* The returned pointer is owned by the caller.
|
||||||
|
*/
|
||||||
static PoseBackup *pose_backup_create(const Object *ob,
|
static PoseBackup *pose_backup_create(const Object *ob,
|
||||||
const bAction *action,
|
const bAction *action,
|
||||||
const BoneNameSet &selected_bone_names)
|
const BoneNameSet &selected_bone_names)
|
||||||
|
@ -86,24 +94,24 @@ static PoseBackup *pose_backup_create(const Object *ob,
|
||||||
return pose_backup;
|
return pose_backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
PoseBackup *ED_pose_backup_create_all_bones(const Object *ob, const bAction *action)
|
PoseBackup *BKE_pose_backup_create_all_bones(const Object *ob, const bAction *action)
|
||||||
{
|
{
|
||||||
return pose_backup_create(ob, action, BoneNameSet());
|
return pose_backup_create(ob, action, BoneNameSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
PoseBackup *ED_pose_backup_create_selected_bones(const Object *ob, const bAction *action)
|
PoseBackup *BKE_pose_backup_create_selected_bones(const Object *ob, const bAction *action)
|
||||||
{
|
{
|
||||||
const bArmature *armature = static_cast<const bArmature *>(ob->data);
|
const bArmature *armature = static_cast<const bArmature *>(ob->data);
|
||||||
const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature);
|
const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature);
|
||||||
return pose_backup_create(ob, action, selected_bone_names);
|
return pose_backup_create(ob, action, selected_bone_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup)
|
bool BKE_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup)
|
||||||
{
|
{
|
||||||
return pose_backup->is_bone_selection_relevant;
|
return pose_backup->is_bone_selection_relevant;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ED_pose_backup_restore(const PoseBackup *pbd)
|
void BKE_pose_backup_restore(const PoseBackup *pbd)
|
||||||
{
|
{
|
||||||
LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) {
|
LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) {
|
||||||
memcpy(chan_bak->pchan, &chan_bak->olddata, sizeof(chan_bak->olddata));
|
memcpy(chan_bak->pchan, &chan_bak->olddata, sizeof(chan_bak->olddata));
|
||||||
|
@ -117,7 +125,7 @@ void ED_pose_backup_restore(const PoseBackup *pbd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ED_pose_backup_free(PoseBackup *pbd)
|
void BKE_pose_backup_free(PoseBackup *pbd)
|
||||||
{
|
{
|
||||||
LISTBASE_FOREACH_MUTABLE (PoseChannelBackup *, chan_bak, &pbd->backups) {
|
LISTBASE_FOREACH_MUTABLE (PoseChannelBackup *, chan_bak, &pbd->backups) {
|
||||||
if (chan_bak->oldprops) {
|
if (chan_bak->oldprops) {
|
||||||
|
@ -127,3 +135,29 @@ void ED_pose_backup_free(PoseBackup *pbd)
|
||||||
}
|
}
|
||||||
MEM_freeN(pbd);
|
MEM_freeN(pbd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BKE_pose_backup_create_on_object(Object *ob, const bAction *action)
|
||||||
|
{
|
||||||
|
BKE_pose_backup_clear(ob);
|
||||||
|
PoseBackup *pose_backup = BKE_pose_backup_create_all_bones(ob, action);
|
||||||
|
ob->runtime.pose_backup = pose_backup;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BKE_pose_backup_restore_on_object(struct Object *ob)
|
||||||
|
{
|
||||||
|
if (ob->runtime.pose_backup == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BKE_pose_backup_restore(ob->runtime.pose_backup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BKE_pose_backup_clear(Object *ob)
|
||||||
|
{
|
||||||
|
if (ob->runtime.pose_backup == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BKE_pose_backup_free(ob->runtime.pose_backup);
|
||||||
|
ob->runtime.pose_backup = nullptr;
|
||||||
|
}
|
|
@ -112,4 +112,6 @@ inline void gather(const VArray<T> &src,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void invert_booleans(MutableSpan<bool> span);
|
||||||
|
|
||||||
} // namespace blender::array_utils
|
} // namespace blender::array_utils
|
||||||
|
|
|
@ -33,4 +33,13 @@ void gather(const GSpan src, const IndexMask indices, GMutableSpan dst, const in
|
||||||
gather(GVArray::ForSpan(src), indices, dst, grain_size);
|
gather(GVArray::ForSpan(src), indices, dst, grain_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void invert_booleans(MutableSpan<bool> span)
|
||||||
|
{
|
||||||
|
threading::parallel_for(span.index_range(), 4096, [&](IndexRange range) {
|
||||||
|
for (const int i : range) {
|
||||||
|
span[i] = !span[i];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blender::array_utils
|
} // namespace blender::array_utils
|
||||||
|
|
|
@ -1782,12 +1782,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
|
if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
|
||||||
if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "bleedexp")) {
|
|
||||||
for (Light *la = bmain->lights.first; la; la = la->id.next) {
|
|
||||||
la->bleedexp = 2.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DNA_struct_elem_find(fd->filesdna, "GPUDOFSettings", "float", "ratio")) {
|
if (!DNA_struct_elem_find(fd->filesdna, "GPUDOFSettings", "float", "ratio")) {
|
||||||
for (Camera *ca = bmain->cameras.first; ca; ca = ca->id.next) {
|
for (Camera *ca = bmain->cameras.first; ca; ca = ca->id.next) {
|
||||||
ca->gpu_dof.ratio = 1.0f;
|
ca->gpu_dof.ratio = 1.0f;
|
||||||
|
@ -1820,7 +1814,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||||
for (Light *la = bmain->lights.first; la; la = la->id.next) {
|
for (Light *la = bmain->lights.first; la; la = la->id.next) {
|
||||||
la->contact_dist = 0.2f;
|
la->contact_dist = 0.2f;
|
||||||
la->contact_bias = 0.03f;
|
la->contact_bias = 0.03f;
|
||||||
la->contact_spread = 0.2f;
|
|
||||||
la->contact_thickness = 0.2f;
|
la->contact_thickness = 0.2f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "DNA_curves_types.h"
|
#include "DNA_curves_types.h"
|
||||||
#include "DNA_genfile.h"
|
#include "DNA_genfile.h"
|
||||||
#include "DNA_gpencil_modifier_types.h"
|
#include "DNA_gpencil_modifier_types.h"
|
||||||
|
#include "DNA_light_types.h"
|
||||||
#include "DNA_lineart_types.h"
|
#include "DNA_lineart_types.h"
|
||||||
#include "DNA_listBase.h"
|
#include "DNA_listBase.h"
|
||||||
#include "DNA_mask_types.h"
|
#include "DNA_mask_types.h"
|
||||||
|
@ -50,6 +51,7 @@
|
||||||
#include "BKE_collection.h"
|
#include "BKE_collection.h"
|
||||||
#include "BKE_colortools.h"
|
#include "BKE_colortools.h"
|
||||||
#include "BKE_curve.h"
|
#include "BKE_curve.h"
|
||||||
|
#include "BKE_curves.hh"
|
||||||
#include "BKE_data_transfer.h"
|
#include "BKE_data_transfer.h"
|
||||||
#include "BKE_deform.h"
|
#include "BKE_deform.h"
|
||||||
#include "BKE_fcurve.h"
|
#include "BKE_fcurve.h"
|
||||||
|
@ -3789,7 +3791,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||||
LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) {
|
LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) {
|
||||||
MovieTracking *tracking = &clip->tracking;
|
MovieTracking *tracking = &clip->tracking;
|
||||||
|
|
||||||
const float frame_center_x = (float(clip->lastsize[0])) / 2;
|
const float frame_center_x = float(clip->lastsize[0]) / 2;
|
||||||
const float frame_center_y = float(clip->lastsize[1]) / 2;
|
const float frame_center_y = float(clip->lastsize[1]) / 2;
|
||||||
|
|
||||||
tracking->camera.principal_point[0] = (tracking->camera.principal_legacy[0] -
|
tracking->camera.principal_point[0] = (tracking->camera.principal_legacy[0] -
|
||||||
|
@ -3828,13 +3830,20 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||||
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
|
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
|
||||||
if (sl->spacetype == SPACE_VIEW3D) {
|
if (sl->spacetype == SPACE_VIEW3D) {
|
||||||
View3D *v3d = (View3D *)sl;
|
View3D *v3d = (View3D *)sl;
|
||||||
v3d->overlay.flag |= (int)(V3D_OVERLAY_SCULPT_SHOW_MASK |
|
v3d->overlay.flag |= int(V3D_OVERLAY_SCULPT_SHOW_MASK |
|
||||||
V3D_OVERLAY_SCULPT_SHOW_FACE_SETS);
|
V3D_OVERLAY_SCULPT_SHOW_FACE_SETS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!MAIN_VERSION_ATLEAST(bmain, 305, 7)) {
|
||||||
|
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
|
||||||
|
light->radius = light->area_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Versioning code until next subversion bump goes here.
|
* Versioning code until next subversion bump goes here.
|
||||||
*
|
*
|
||||||
|
@ -3850,5 +3859,9 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||||
LISTBASE_FOREACH (Curves *, curves_id, &bmain->hair_curves) {
|
LISTBASE_FOREACH (Curves *, curves_id, &bmain->hair_curves) {
|
||||||
curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED;
|
curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED;
|
||||||
}
|
}
|
||||||
|
LISTBASE_FOREACH (Curves *, curves_id, &bmain->hair_curves) {
|
||||||
|
BKE_id_attribute_rename(&curves_id->id, ".selection_point_float", ".selection", nullptr);
|
||||||
|
BKE_id_attribute_rename(&curves_id->id, ".selection_curve_float", ".selection", nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ struct bNodeSocket *version_node_add_socket_if_not_exist(struct bNodeTree *ntree
|
||||||
const char *name);
|
const char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The versioning code generally expects `SOCK_IS_LINKED` to be set correctly. This function updates
|
* The versioning code generally expects `SOCK_IS_LINKED` to be set correctly. This function
|
||||||
* the flag on all sockets after changes to the node tree.
|
* updates the flag on all sockets after changes to the node tree.
|
||||||
*/
|
*/
|
||||||
void version_socket_update_is_used(bNodeTree *ntree);
|
void version_socket_update_is_used(bNodeTree *ntree);
|
||||||
ARegion *do_versions_add_region(int regiontype, const char *name);
|
ARegion *do_versions_add_region(int regiontype, const char *name);
|
||||||
|
|
|
@ -919,7 +919,7 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm,
|
||||||
BLI_linklist_prepend_alloca(&loops_of_vert, l_curr);
|
BLI_linklist_prepend_alloca(&loops_of_vert, l_curr);
|
||||||
loops_of_vert_count += 1;
|
loops_of_vert_count += 1;
|
||||||
|
|
||||||
const uint index_test = (uint)BM_elem_index_get(l_curr);
|
const uint index_test = uint(BM_elem_index_get(l_curr));
|
||||||
if (index_best > index_test) {
|
if (index_best > index_test) {
|
||||||
index_best = index_test;
|
index_best = index_test;
|
||||||
link_best = loops_of_vert;
|
link_best = loops_of_vert;
|
||||||
|
|
|
@ -104,7 +104,13 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter,
|
||||||
char path[FILE_MAX];
|
char path[FILE_MAX];
|
||||||
|
|
||||||
/* combine file path for the input */
|
/* combine file path for the input */
|
||||||
BLI_path_join(path, FILE_MAX, storage->base_path, sockdata->path);
|
if (sockdata->path[0]) {
|
||||||
|
BLI_path_join(path, FILE_MAX, storage->base_path, sockdata->path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BLI_strncpy(path, storage->base_path, FILE_MAX);
|
||||||
|
BLI_path_slash_ensure(path, FILE_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
NodeOperation *output_operation = nullptr;
|
NodeOperation *output_operation = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -1675,8 +1675,11 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
OperationCode target_op = driver_targets_bbone ? OperationCode::BONE_SEGMENTS :
|
OperationCode target_op = OperationCode::BONE_LOCAL;
|
||||||
OperationCode::BONE_LOCAL;
|
if (driver_targets_bbone) {
|
||||||
|
target_op = check_pchan_has_bbone_segments(object, pchan) ? OperationCode::BONE_SEGMENTS :
|
||||||
|
OperationCode::BONE_DONE;
|
||||||
|
}
|
||||||
OperationKey bone_key(&object->id, NodeType::BONE, pchan->name, target_op);
|
OperationKey bone_key(&object->id, NodeType::BONE, pchan->name, target_op);
|
||||||
add_relation(driver_key, bone_key, "Arm Bone -> Driver -> Bone");
|
add_relation(driver_key, bone_key, "Arm Bone -> Driver -> Bone");
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,8 +105,7 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod
|
||||||
* times.
|
* times.
|
||||||
* This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
|
* This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
|
||||||
* and this node has been scheduled. */
|
* and this node has been scheduled. */
|
||||||
operation_node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE |
|
operation_node->flag &= ~DEPSOP_FLAG_CLEAR_ON_EVAL;
|
||||||
DEPSOP_FLAG_USER_MODIFIED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deg_task_run_func(TaskPool *pool, void *taskdata)
|
void deg_task_run_func(TaskPool *pool, void *taskdata)
|
||||||
|
@ -270,6 +269,10 @@ void schedule_node(DepsgraphEvalState *state,
|
||||||
bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, uint8_t(true));
|
bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, uint8_t(true));
|
||||||
if (!is_scheduled) {
|
if (!is_scheduled) {
|
||||||
if (node->is_noop()) {
|
if (node->is_noop()) {
|
||||||
|
/* Clear flags to avoid affecting subsequent update propagation.
|
||||||
|
* For normal nodes these are cleared when it is evaluated. */
|
||||||
|
node->flag &= ~DEPSOP_FLAG_CLEAR_ON_EVAL;
|
||||||
|
|
||||||
/* skip NOOP node, schedule children right away */
|
/* skip NOOP node, schedule children right away */
|
||||||
schedule_children(state, node, schedule_fn);
|
schedule_children(state, node, schedule_fn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,10 @@ enum OperationFlag {
|
||||||
|
|
||||||
/* Set of flags which gets flushed along the relations. */
|
/* Set of flags which gets flushed along the relations. */
|
||||||
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
|
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
|
||||||
|
|
||||||
|
/* Set of flags which get cleared upon evaluation. */
|
||||||
|
DEPSOP_FLAG_CLEAR_ON_EVAL = (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE |
|
||||||
|
DEPSOP_FLAG_USER_MODIFIED),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Atomic Operation - Base type for all operations */
|
/* Atomic Operation - Base type for all operations */
|
||||||
|
|
|
@ -45,7 +45,7 @@ static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const
|
||||||
evli->sizey = scale[1] / scale[2];
|
evli->sizey = scale[1] / scale[2];
|
||||||
evli->spotsize = cosf(la->spotsize * 0.5f);
|
evli->spotsize = cosf(la->spotsize * 0.5f);
|
||||||
evli->spotblend = (1.0f - evli->spotsize) * la->spotblend;
|
evli->spotblend = (1.0f - evli->spotsize) * la->spotblend;
|
||||||
evli->radius = max_ff(0.001f, la->area_size);
|
evli->radius = max_ff(0.001f, la->radius);
|
||||||
}
|
}
|
||||||
else if (la->type == LA_AREA) {
|
else if (la->type == LA_AREA) {
|
||||||
evli->sizex = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
|
evli->sizex = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
|
||||||
|
@ -62,7 +62,7 @@ static void light_shape_parameters_set(EEVEE_Light *evli, const Light *la, const
|
||||||
evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
|
evli->radius = max_ff(0.001f, tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
evli->radius = max_ff(0.001f, la->area_size);
|
evli->radius = max_ff(0.001f, la->radius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,7 @@ void Light::shape_parameters_set(const ::Light *la, const float scale[3])
|
||||||
_area_size_x = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f);
|
_area_size_x = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_area_size_x = la->area_size;
|
_area_size_x = la->radius;
|
||||||
}
|
}
|
||||||
_area_size_y = _area_size_x = max_ff(0.001f, _area_size_x);
|
_area_size_y = _area_size_x = max_ff(0.001f, _area_size_x);
|
||||||
radius_squared = square_f(_area_size_x);
|
radius_squared = square_f(_area_size_x);
|
||||||
|
|
|
@ -92,10 +92,10 @@ static void OVERLAY_engine_init(void *vedata)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts->sculpt) {
|
if (ts->sculpt) {
|
||||||
if (!(v3d->overlay.flag & (int)V3D_OVERLAY_SCULPT_SHOW_FACE_SETS)) {
|
if (!(v3d->overlay.flag & int(V3D_OVERLAY_SCULPT_SHOW_FACE_SETS))) {
|
||||||
pd->overlay.sculpt_mode_face_sets_opacity = 0.0f;
|
pd->overlay.sculpt_mode_face_sets_opacity = 0.0f;
|
||||||
}
|
}
|
||||||
if (!(v3d->overlay.flag & (int)V3D_OVERLAY_SCULPT_SHOW_MASK)) {
|
if (!(v3d->overlay.flag & int(V3D_OVERLAY_SCULPT_SHOW_MASK))) {
|
||||||
pd->overlay.sculpt_mode_mask_opacity = 0.0f;
|
pd->overlay.sculpt_mode_mask_opacity = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,7 +637,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
|
||||||
DRW_buffer_add_entry(cb->groundline, instdata.pos);
|
DRW_buffer_add_entry(cb->groundline, instdata.pos);
|
||||||
|
|
||||||
if (la->type == LA_LOCAL) {
|
if (la->type == LA_LOCAL) {
|
||||||
instdata.area_size_x = instdata.area_size_y = la->area_size;
|
instdata.area_size_x = instdata.area_size_y = la->radius;
|
||||||
DRW_buffer_add_entry(cb->light_point, color, &instdata);
|
DRW_buffer_add_entry(cb->light_point, color, &instdata);
|
||||||
}
|
}
|
||||||
else if (la->type == LA_SUN) {
|
else if (la->type == LA_SUN) {
|
||||||
|
@ -661,7 +661,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
|
||||||
instdata.spot_blend = sqrtf((-a - c * a) / (c - c * a));
|
instdata.spot_blend = sqrtf((-a - c * a) / (c - c * a));
|
||||||
instdata.spot_cosine = a;
|
instdata.spot_cosine = a;
|
||||||
/* HACK: We pack the area size in alpha color. This is decoded by the shader. */
|
/* HACK: We pack the area size in alpha color. This is decoded by the shader. */
|
||||||
color[3] = -max_ff(la->area_size, FLT_MIN);
|
color[3] = -max_ff(la->radius, FLT_MIN);
|
||||||
DRW_buffer_add_entry(cb->light_spot, color, &instdata);
|
DRW_buffer_add_entry(cb->light_spot, color, &instdata);
|
||||||
|
|
||||||
if ((la->mode & LA_SHOW_CONE) && !DRW_state_is_select()) {
|
if ((la->mode & LA_SHOW_CONE) && !DRW_state_is_select()) {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "draw_cache_impl.h"
|
#include "draw_cache_impl.h"
|
||||||
#include "overlay_private.hh"
|
#include "overlay_private.hh"
|
||||||
|
|
||||||
|
#include "BKE_attribute.hh"
|
||||||
#include "BKE_curves.hh"
|
#include "BKE_curves.hh"
|
||||||
|
|
||||||
void OVERLAY_sculpt_curves_cache_init(OVERLAY_Data *vedata)
|
void OVERLAY_sculpt_curves_cache_init(OVERLAY_Data *vedata)
|
||||||
|
@ -31,18 +32,11 @@ void OVERLAY_sculpt_curves_cache_init(OVERLAY_Data *vedata)
|
||||||
|
|
||||||
static bool everything_selected(const Curves &curves_id)
|
static bool everything_selected(const Curves &curves_id)
|
||||||
{
|
{
|
||||||
const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
|
using namespace blender;
|
||||||
curves_id.geometry);
|
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
|
||||||
blender::VArray<float> selection;
|
const VArray<bool> selection = curves.attributes().lookup_or_default<bool>(
|
||||||
switch (curves_id.selection_domain) {
|
".selection", ATTR_DOMAIN_POINT, true);
|
||||||
case ATTR_DOMAIN_POINT:
|
return selection.is_single() && selection.get_internal_single();
|
||||||
selection = curves.selection_point_float();
|
|
||||||
break;
|
|
||||||
case ATTR_DOMAIN_CURVE:
|
|
||||||
selection = curves.selection_curve_float();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return selection.is_single() && selection.get_internal_single() == 1.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OVERLAY_sculpt_curves_cache_populate(OVERLAY_Data *vedata, Object *object)
|
void OVERLAY_sculpt_curves_cache_populate(OVERLAY_Data *vedata, Object *object)
|
||||||
|
@ -56,12 +50,9 @@ void OVERLAY_sculpt_curves_cache_populate(OVERLAY_Data *vedata, Object *object)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the location of the texture. */
|
/* Retrieve the location of the texture. */
|
||||||
const char *name = curves->selection_domain == ATTR_DOMAIN_POINT ? ".selection_point_float" :
|
|
||||||
".selection_curve_float";
|
|
||||||
|
|
||||||
bool is_point_domain;
|
bool is_point_domain;
|
||||||
GPUVertBuf **texture = DRW_curves_texture_for_evaluated_attribute(
|
GPUVertBuf **texture = DRW_curves_texture_for_evaluated_attribute(
|
||||||
curves, name, &is_point_domain);
|
curves, ".selection", &is_point_domain);
|
||||||
if (texture == nullptr) {
|
if (texture == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
#include "BLI_devirtualize_parameters.hh"
|
||||||
#include "BLI_listbase.h"
|
#include "BLI_listbase.h"
|
||||||
#include "BLI_math_base.h"
|
#include "BLI_math_base.h"
|
||||||
#include "BLI_math_vec_types.hh"
|
#include "BLI_math_vec_types.hh"
|
||||||
|
@ -334,17 +335,16 @@ static void curves_batch_cache_ensure_edit_points_data(const Curves &curves_id,
|
||||||
GPU_vertbuf_init_with_format(cache.edit_points_data, &format_data);
|
GPU_vertbuf_init_with_format(cache.edit_points_data, &format_data);
|
||||||
GPU_vertbuf_data_alloc(cache.edit_points_data, curves.points_num());
|
GPU_vertbuf_data_alloc(cache.edit_points_data, curves.points_num());
|
||||||
|
|
||||||
VArray<float> selection;
|
const VArray<bool> selection = curves.attributes().lookup_or_default<bool>(
|
||||||
|
".selection", eAttrDomain(curves_id.selection_domain), true);
|
||||||
switch (curves_id.selection_domain) {
|
switch (curves_id.selection_domain) {
|
||||||
case ATTR_DOMAIN_POINT:
|
case ATTR_DOMAIN_POINT:
|
||||||
selection = curves.selection_point_float();
|
|
||||||
for (const int point_i : selection.index_range()) {
|
for (const int point_i : selection.index_range()) {
|
||||||
const float point_selection = (selection[point_i] > 0.0f) ? 1.0f : 0.0f;
|
const float point_selection = (selection[point_i] > 0.0f) ? 1.0f : 0.0f;
|
||||||
GPU_vertbuf_attr_set(cache.edit_points_data, color, point_i, &point_selection);
|
GPU_vertbuf_attr_set(cache.edit_points_data, color, point_i, &point_selection);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ATTR_DOMAIN_CURVE:
|
case ATTR_DOMAIN_CURVE:
|
||||||
selection = curves.selection_curve_float();
|
|
||||||
for (const int curve_i : curves.curves_range()) {
|
for (const int curve_i : curves.curves_range()) {
|
||||||
const float curve_selection = (selection[curve_i] > 0.0f) ? 1.0f : 0.0f;
|
const float curve_selection = (selection[curve_i] > 0.0f) ? 1.0f : 0.0f;
|
||||||
const IndexRange points = curves.points_for_curve(curve_i);
|
const IndexRange points = curves.points_for_curve(curve_i);
|
||||||
|
|
|
@ -276,7 +276,7 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
|
||||||
int32_t packed = 0;
|
int32_t packed = 0;
|
||||||
/* Aspect uses 9 bits */
|
/* Aspect uses 9 bits */
|
||||||
float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
|
float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
|
||||||
packed |= (int32_t)unit_float_to_uchar_clamp(asp_normalized);
|
packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
|
||||||
/* Store if inversed in the 9th bit. */
|
/* Store if inversed in the 9th bit. */
|
||||||
if (asp > 1.0f) {
|
if (asp > 1.0f) {
|
||||||
packed |= 1 << 8;
|
packed |= 1 << 8;
|
||||||
|
@ -284,13 +284,13 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
|
||||||
/* Rotation uses 9 bits */
|
/* Rotation uses 9 bits */
|
||||||
/* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine
|
/* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine
|
||||||
* because the cosine will always be positive. */
|
* because the cosine will always be positive. */
|
||||||
packed |= (int32_t)unit_float_to_uchar_clamp(cosf(rot)) << 9;
|
packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
|
||||||
/* Store sine sign in 9th bit. */
|
/* Store sine sign in 9th bit. */
|
||||||
if (rot < 0.0f) {
|
if (rot < 0.0f) {
|
||||||
packed |= 1 << 17;
|
packed |= 1 << 17;
|
||||||
}
|
}
|
||||||
/* Hardness uses 8 bits */
|
/* Hardness uses 8 bits */
|
||||||
packed |= (int32_t)unit_float_to_uchar_clamp(hard) << 18;
|
packed |= int32_t(unit_float_to_uchar_clamp(hard)) << 18;
|
||||||
return packed;
|
return packed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +315,7 @@ static void gpencil_buffer_add_point(GPUIndexBufBuilder *ibo,
|
||||||
/* Encode fill opacity defined by opacity modifier in vertex color alpha. If
|
/* Encode fill opacity defined by opacity modifier in vertex color alpha. If
|
||||||
* no opacity modifier, the value will be always 1.0f. The opacity factor can be any
|
* no opacity modifier, the value will be always 1.0f. The opacity factor can be any
|
||||||
* value between 0.0f and 2.0f */
|
* value between 0.0f and 2.0f */
|
||||||
col->fcol[3] = ((int)(col->fcol[3] * 10000.0f) * 10.0f) + gps->fill_opacity_fac;
|
col->fcol[3] = (int(col->fcol[3] * 10000.0f) * 10.0f) + gps->fill_opacity_fac;
|
||||||
|
|
||||||
vert->strength = (round_cap0) ? pt->strength : -pt->strength;
|
vert->strength = (round_cap0) ? pt->strength : -pt->strength;
|
||||||
vert->u_stroke = pt->uv_fac;
|
vert->u_stroke = pt->uv_fac;
|
||||||
|
@ -579,7 +579,7 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob)
|
||||||
gps->runtime.stroke_start = 0;
|
gps->runtime.stroke_start = 0;
|
||||||
copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill);
|
copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill);
|
||||||
/* Caps. */
|
/* Caps. */
|
||||||
gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type;
|
gps->caps[0] = gps->caps[1] = short(brush->gpencil_settings->caps_type);
|
||||||
|
|
||||||
gpd->runtime.sbuffer_gps = gps;
|
gpd->runtime.sbuffer_gps = gps;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ set(SRC
|
||||||
armature_utils.c
|
armature_utils.c
|
||||||
editarmature_undo.c
|
editarmature_undo.c
|
||||||
meshlaplacian.c
|
meshlaplacian.c
|
||||||
pose_backup.cc
|
|
||||||
pose_edit.c
|
pose_edit.c
|
||||||
pose_group.c
|
pose_group.c
|
||||||
pose_lib_2.c
|
pose_lib_2.c
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "BKE_context.h"
|
#include "BKE_context.h"
|
||||||
#include "BKE_lib_id.h"
|
#include "BKE_lib_id.h"
|
||||||
#include "BKE_object.h"
|
#include "BKE_object.h"
|
||||||
|
#include "BKE_pose_backup.h"
|
||||||
#include "BKE_report.h"
|
#include "BKE_report.h"
|
||||||
|
|
||||||
#include "DEG_depsgraph.h"
|
#include "DEG_depsgraph.h"
|
||||||
|
@ -37,7 +38,6 @@
|
||||||
|
|
||||||
#include "UI_interface.h"
|
#include "UI_interface.h"
|
||||||
|
|
||||||
#include "ED_armature.h"
|
|
||||||
#include "ED_asset.h"
|
#include "ED_asset.h"
|
||||||
#include "ED_keyframing.h"
|
#include "ED_keyframing.h"
|
||||||
#include "ED_screen.h"
|
#include "ED_screen.h"
|
||||||
|
@ -66,13 +66,14 @@ typedef struct PoseBlendData {
|
||||||
/* For temp-loading the Action from the pose library. */
|
/* For temp-loading the Action from the pose library. */
|
||||||
AssetTempIDConsumer *temp_id_consumer;
|
AssetTempIDConsumer *temp_id_consumer;
|
||||||
|
|
||||||
/* Blend factor, interval [0, 1] for interpolating between current and given pose. */
|
/* Blend factor, interval [-1, 1] for interpolating between current and given pose.
|
||||||
|
* Positive factors will blend in `act`, whereas negative factors will blend in `act_flipped`. */
|
||||||
float blend_factor;
|
float blend_factor;
|
||||||
struct PoseBackup *pose_backup;
|
struct PoseBackup *pose_backup;
|
||||||
|
|
||||||
Object *ob; /* Object to work on. */
|
Object *ob; /* Object to work on. */
|
||||||
bAction *act; /* Pose to blend into the current pose. */
|
bAction *act; /* Pose to blend into the current pose. */
|
||||||
bool free_action;
|
bAction *act_flipped; /* Flipped copy of `act`. */
|
||||||
|
|
||||||
Scene *scene; /* For auto-keying. */
|
Scene *scene; /* For auto-keying. */
|
||||||
ScrArea *area; /* For drawing status text. */
|
ScrArea *area; /* For drawing status text. */
|
||||||
|
@ -83,10 +84,19 @@ typedef struct PoseBlendData {
|
||||||
char headerstr[UI_MAX_DRAW_STR];
|
char headerstr[UI_MAX_DRAW_STR];
|
||||||
} PoseBlendData;
|
} PoseBlendData;
|
||||||
|
|
||||||
|
/** Return the bAction that should be blended.
|
||||||
|
* This is either pbd->act or pbd->act_flipped, depending on the sign of the blend factor.
|
||||||
|
*/
|
||||||
|
static bAction *poselib_action_to_blend(PoseBlendData *pbd)
|
||||||
|
{
|
||||||
|
return (pbd->blend_factor >= 0) ? pbd->act : pbd->act_flipped;
|
||||||
|
}
|
||||||
|
|
||||||
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
|
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
|
||||||
static void poselib_backup_posecopy(PoseBlendData *pbd)
|
static void poselib_backup_posecopy(PoseBlendData *pbd)
|
||||||
{
|
{
|
||||||
pbd->pose_backup = ED_pose_backup_create_selected_bones(pbd->ob, pbd->act);
|
const bAction *action = poselib_action_to_blend(pbd);
|
||||||
|
pbd->pose_backup = BKE_pose_backup_create_selected_bones(pbd->ob, action);
|
||||||
|
|
||||||
if (pbd->state == POSE_BLEND_INIT) {
|
if (pbd->state == POSE_BLEND_INIT) {
|
||||||
/* Ready for blending now. */
|
/* Ready for blending now. */
|
||||||
|
@ -125,7 +135,7 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ED_pose_backup_is_selection_relevant(pbd->pose_backup) &&
|
if (BKE_pose_backup_is_selection_relevant(pbd->pose_backup) &&
|
||||||
!PBONE_SELECTED(armature, pchan->bone)) {
|
!PBONE_SELECTED(armature, pchan->bone)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +162,7 @@ static void poselib_blend_apply(bContext *C, wmOperator *op)
|
||||||
}
|
}
|
||||||
pbd->needs_redraw = false;
|
pbd->needs_redraw = false;
|
||||||
|
|
||||||
ED_pose_backup_restore(pbd->pose_backup);
|
BKE_pose_backup_restore(pbd->pose_backup);
|
||||||
|
|
||||||
/* The pose needs updating, whether it's for restoring the original pose or for showing the
|
/* The pose needs updating, whether it's for restoring the original pose or for showing the
|
||||||
* result of the blend. */
|
* result of the blend. */
|
||||||
|
@ -166,15 +176,28 @@ static void poselib_blend_apply(bContext *C, wmOperator *op)
|
||||||
/* Perform the actual blending. */
|
/* Perform the actual blending. */
|
||||||
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||||
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
|
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
|
||||||
BKE_pose_apply_action_blend(pbd->ob, pbd->act, &anim_eval_context, pbd->blend_factor);
|
bAction *to_blend = poselib_action_to_blend(pbd);
|
||||||
|
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, fabs(pbd->blend_factor));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------- */
|
/* ---------------------------- */
|
||||||
|
|
||||||
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
|
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
|
||||||
{
|
{
|
||||||
pbd->blend_factor = CLAMPIS(new_factor, 0.0f, 1.0f);
|
const bool sign_changed = signf(new_factor) != signf(pbd->blend_factor);
|
||||||
|
if (sign_changed) {
|
||||||
|
/* The zero point was crossed, meaning that the pose will be flipped. This means the pose
|
||||||
|
* backup has to change, as it only contains the bones for one side. */
|
||||||
|
BKE_pose_backup_restore(pbd->pose_backup);
|
||||||
|
BKE_pose_backup_free(pbd->pose_backup);
|
||||||
|
}
|
||||||
|
|
||||||
|
pbd->blend_factor = CLAMPIS(new_factor, -1.0f, 1.0f);
|
||||||
pbd->needs_redraw = true;
|
pbd->needs_redraw = true;
|
||||||
|
|
||||||
|
if (sign_changed) {
|
||||||
|
poselib_backup_posecopy(pbd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return operator return value. */
|
/* Return operator return value. */
|
||||||
|
@ -224,8 +247,6 @@ static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const
|
||||||
pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING;
|
pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING;
|
||||||
pbd->needs_redraw = true;
|
pbd->needs_redraw = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* TODO(Sybren): use better UI for slider. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return OPERATOR_RUNNING_MODAL;
|
return OPERATOR_RUNNING_MODAL;
|
||||||
|
@ -292,18 +313,21 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
||||||
PoseBlendData *pbd;
|
PoseBlendData *pbd;
|
||||||
op->customdata = pbd = MEM_callocN(sizeof(PoseBlendData), "PoseLib Preview Data");
|
op->customdata = pbd = MEM_callocN(sizeof(PoseBlendData), "PoseLib Preview Data");
|
||||||
|
|
||||||
bAction *action = poselib_blend_init_get_action(C, op);
|
pbd->act = poselib_blend_init_get_action(C, op);
|
||||||
if (action == NULL) {
|
if (pbd->act == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Maybe flip the Action. */
|
/* Passing `flipped=True` is the same as flipping the sign of the blend factor. */
|
||||||
const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
|
const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
|
||||||
if (apply_flipped) {
|
const float multiply_factor = apply_flipped ? -1.0f : 1.0f;
|
||||||
action = flip_pose(C, ob, action);
|
pbd->blend_factor = multiply_factor * RNA_float_get(op->ptr, "blend_factor");
|
||||||
pbd->free_action = true;
|
|
||||||
|
/* Only construct the flipped pose if there is a chance it's actually needed. */
|
||||||
|
const bool is_interactive = (event != NULL);
|
||||||
|
if (is_interactive || pbd->blend_factor < 0) {
|
||||||
|
pbd->act_flipped = flip_pose(C, ob, pbd->act);
|
||||||
}
|
}
|
||||||
pbd->act = action;
|
|
||||||
|
|
||||||
/* Get the basic data. */
|
/* Get the basic data. */
|
||||||
pbd->ob = ob;
|
pbd->ob = ob;
|
||||||
|
@ -314,12 +338,12 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
||||||
|
|
||||||
pbd->state = POSE_BLEND_INIT;
|
pbd->state = POSE_BLEND_INIT;
|
||||||
pbd->needs_redraw = true;
|
pbd->needs_redraw = true;
|
||||||
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
|
|
||||||
/* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */
|
/* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */
|
||||||
pbd->release_confirm_info.use_release_confirm = false;
|
pbd->release_confirm_info.use_release_confirm = false;
|
||||||
|
|
||||||
/* Release confirm data. Only available if there's an event to work with. */
|
/* Release confirm data. Only available if there's an event to work with. */
|
||||||
if (event != NULL) {
|
if (is_interactive) {
|
||||||
PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm");
|
PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm");
|
||||||
pbd->release_confirm_info.use_release_confirm = (release_confirm_prop != NULL) &&
|
pbd->release_confirm_info.use_release_confirm = (release_confirm_prop != NULL) &&
|
||||||
RNA_property_boolean_get(op->ptr,
|
RNA_property_boolean_get(op->ptr,
|
||||||
|
@ -328,10 +352,11 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
|
||||||
ED_slider_init(pbd->slider, event);
|
ED_slider_init(pbd->slider, event);
|
||||||
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
|
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
|
||||||
ED_slider_allow_overshoot_set(pbd->slider, false);
|
ED_slider_allow_overshoot_set(pbd->slider, false);
|
||||||
|
ED_slider_is_bidirectional_set(pbd->slider, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pbd->release_confirm_info.use_release_confirm) {
|
if (pbd->release_confirm_info.use_release_confirm) {
|
||||||
BLI_assert(event != NULL);
|
BLI_assert(is_interactive);
|
||||||
pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type(
|
pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type(
|
||||||
event->type);
|
event->type);
|
||||||
}
|
}
|
||||||
|
@ -369,7 +394,8 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
|
||||||
poselib_keytag_pose(C, scene, pbd);
|
poselib_keytag_pose(C, scene, pbd);
|
||||||
|
|
||||||
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
|
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
|
||||||
RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
|
RNA_float_set(op->ptr, "blend_factor", fabs(pbd->blend_factor));
|
||||||
|
RNA_boolean_set(op->ptr, "flipped", pbd->blend_factor < 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +407,7 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
|
||||||
BKE_report(op->reports, RPT_ERROR, "Internal pose library error, canceling operator");
|
BKE_report(op->reports, RPT_ERROR, "Internal pose library error, canceling operator");
|
||||||
ATTR_FALLTHROUGH;
|
ATTR_FALLTHROUGH;
|
||||||
case POSE_BLEND_CANCEL:
|
case POSE_BLEND_CANCEL:
|
||||||
ED_pose_backup_restore(pbd->pose_backup);
|
BKE_pose_backup_restore(pbd->pose_backup);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,15 +424,13 @@ static void poselib_blend_free(wmOperator *op)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pbd->free_action) {
|
if (pbd->act_flipped) {
|
||||||
/* Run before #poselib_tempload_exit to avoid any problems from indirectly
|
BKE_id_free(NULL, pbd->act_flipped);
|
||||||
* referenced ID pointers. */
|
|
||||||
BKE_id_free(NULL, pbd->act);
|
|
||||||
}
|
}
|
||||||
poselib_tempload_exit(pbd);
|
poselib_tempload_exit(pbd);
|
||||||
|
|
||||||
/* Free temp data for operator */
|
/* Free temp data for operator */
|
||||||
ED_pose_backup_free(pbd->pose_backup);
|
BKE_pose_backup_free(pbd->pose_backup);
|
||||||
pbd->pose_backup = NULL;
|
pbd->pose_backup = NULL;
|
||||||
|
|
||||||
MEM_SAFE_FREE(op->customdata);
|
MEM_SAFE_FREE(op->customdata);
|
||||||
|
@ -526,6 +550,8 @@ static bool poselib_blend_poll(bContext *C)
|
||||||
|
|
||||||
void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
|
void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
|
||||||
{
|
{
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
/* Identifiers: */
|
/* Identifiers: */
|
||||||
ot->name = "Apply Pose Asset";
|
ot->name = "Apply Pose Asset";
|
||||||
ot->idname = "POSELIB_OT_apply_pose_asset";
|
ot->idname = "POSELIB_OT_apply_pose_asset";
|
||||||
|
@ -542,17 +568,21 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
|
||||||
RNA_def_float_factor(ot->srna,
|
RNA_def_float_factor(ot->srna,
|
||||||
"blend_factor",
|
"blend_factor",
|
||||||
1.0f,
|
1.0f,
|
||||||
0.0f,
|
-1.0f,
|
||||||
1.0f,
|
1.0f,
|
||||||
"Blend Factor",
|
"Blend Factor",
|
||||||
"Amount that the pose is applied on top of the existing poses",
|
"Amount that the pose is applied on top of the existing poses. A negative "
|
||||||
0.0f,
|
"value will apply the pose flipped over the X-axis",
|
||||||
|
-1.0f,
|
||||||
1.0f);
|
1.0f);
|
||||||
RNA_def_boolean(ot->srna,
|
prop = RNA_def_boolean(
|
||||||
"flipped",
|
ot->srna,
|
||||||
false,
|
"flipped",
|
||||||
"Apply Flipped",
|
false,
|
||||||
"When enabled, applies the pose flipped over the X-axis");
|
"Apply Flipped",
|
||||||
|
"When enabled, applies the pose flipped over the X-axis. This is the same as "
|
||||||
|
"passing a negative `blend_factor`");
|
||||||
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
||||||
|
@ -578,22 +608,26 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
|
||||||
prop = RNA_def_float_factor(ot->srna,
|
prop = RNA_def_float_factor(ot->srna,
|
||||||
"blend_factor",
|
"blend_factor",
|
||||||
0.0f,
|
0.0f,
|
||||||
0.0f,
|
-1.0f,
|
||||||
1.0f,
|
1.0f,
|
||||||
"Blend Factor",
|
"Blend Factor",
|
||||||
"Amount that the pose is applied on top of the existing poses",
|
"Amount that the pose is applied on top of the existing poses. A "
|
||||||
0.0f,
|
"negative value will apply the pose flipped over the X-axis",
|
||||||
|
-1.0f,
|
||||||
1.0f);
|
1.0f);
|
||||||
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
|
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
|
||||||
* property just exists for symmetry with the Apply operator (and thus simplicity of the rest of
|
* property just exists for symmetry with the Apply operator (and thus simplicity of the rest of
|
||||||
* the code, which can assume this property exists). */
|
* the code, which can assume this property exists). */
|
||||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||||
|
|
||||||
RNA_def_boolean(ot->srna,
|
prop = RNA_def_boolean(ot->srna,
|
||||||
"flipped",
|
"flipped",
|
||||||
false,
|
false,
|
||||||
"Apply Flipped",
|
"Apply Flipped",
|
||||||
"When enabled, applies the pose flipped over the X-axis");
|
"When enabled, applies the pose flipped over the X-axis. This is the "
|
||||||
|
"same as passing a negative `blend_factor`");
|
||||||
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||||
|
|
||||||
prop = RNA_def_boolean(ot->srna,
|
prop = RNA_def_boolean(ot->srna,
|
||||||
"release_confirm",
|
"release_confirm",
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -22,6 +22,7 @@ set(INC
|
||||||
set(SRC
|
set(SRC
|
||||||
intern/curves_add.cc
|
intern/curves_add.cc
|
||||||
intern/curves_ops.cc
|
intern/curves_ops.cc
|
||||||
|
intern/curves_selection.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIB
|
set(LIB
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "BLI_array_utils.hh"
|
||||||
#include "BLI_devirtualize_parameters.hh"
|
#include "BLI_devirtualize_parameters.hh"
|
||||||
|
#include "BLI_index_mask_ops.hh"
|
||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
#include "BLI_vector_set.hh"
|
#include "BLI_vector_set.hh"
|
||||||
|
|
||||||
|
@ -748,7 +750,6 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const eAttrDomain old_domain = eAttrDomain(curves_id->selection_domain);
|
|
||||||
curves_id->selection_domain = domain;
|
curves_id->selection_domain = domain;
|
||||||
|
|
||||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
||||||
|
@ -756,18 +757,21 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
|
||||||
if (curves.points_num() == 0) {
|
if (curves.points_num() == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const GVArray src = attributes.lookup(".selection", domain);
|
||||||
if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
|
if (src.is_empty()) {
|
||||||
VArray<float> curve_selection = curves.adapt_domain(
|
continue;
|
||||||
curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
|
|
||||||
curve_selection.materialize(curves.selection_curve_float_for_write());
|
|
||||||
attributes.remove(".selection_point_float");
|
|
||||||
}
|
}
|
||||||
else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) {
|
|
||||||
VArray<float> point_selection = curves.adapt_domain(
|
const CPPType &type = src.type();
|
||||||
curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
|
void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
|
||||||
point_selection.materialize(curves.selection_point_float_for_write());
|
src.materialize(dst);
|
||||||
attributes.remove(".selection_curve_float");
|
|
||||||
|
attributes.remove(".selection");
|
||||||
|
if (!attributes.add(".selection",
|
||||||
|
domain,
|
||||||
|
bke::cpp_type_to_custom_data_type(type),
|
||||||
|
bke::AttributeInitMoveArray(dst))) {
|
||||||
|
MEM_freeN(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
||||||
|
@ -801,46 +805,54 @@ static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
|
||||||
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool varray_contains_nonzero(const VArray<float> &data)
|
static bool contains(const VArray<bool> &varray, const bool value)
|
||||||
{
|
{
|
||||||
bool contains_nonzero = false;
|
const CommonVArrayInfo info = varray.common_info();
|
||||||
devirtualize_varray(data, [&](const auto array) {
|
if (info.type == CommonVArrayInfo::Type::Single) {
|
||||||
for (const int i : data.index_range()) {
|
return *static_cast<const bool *>(info.data) == value;
|
||||||
if (array[i] != 0.0f) {
|
}
|
||||||
contains_nonzero = true;
|
if (info.type == CommonVArrayInfo::Type::Span) {
|
||||||
break;
|
const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
|
||||||
}
|
return threading::parallel_reduce(
|
||||||
}
|
span.index_range(),
|
||||||
});
|
4096,
|
||||||
return contains_nonzero;
|
false,
|
||||||
|
[&](const IndexRange range, const bool init) {
|
||||||
|
return init || span.slice(range).contains(value);
|
||||||
|
},
|
||||||
|
[&](const bool a, const bool b) { return a || b; });
|
||||||
|
}
|
||||||
|
return threading::parallel_reduce(
|
||||||
|
varray.index_range(),
|
||||||
|
2048,
|
||||||
|
false,
|
||||||
|
[&](const IndexRange range, const bool init) {
|
||||||
|
if (init) {
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
/* Alternatively, this could use #materialize to retrieve many values at once. */
|
||||||
|
for (const int64_t i : range) {
|
||||||
|
if (varray[i] == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
[&](const bool a, const bool b) { return a || b; });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_anything_selected(const Curves &curves_id)
|
bool has_anything_selected(const Curves &curves_id)
|
||||||
{
|
{
|
||||||
const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
|
const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
|
||||||
switch (curves_id.selection_domain) {
|
const VArray<bool> selection = curves.attributes().lookup<bool>(".selection");
|
||||||
case ATTR_DOMAIN_POINT:
|
return !selection || contains(selection, true);
|
||||||
return varray_contains_nonzero(curves.selection_point_float());
|
|
||||||
case ATTR_DOMAIN_CURVE:
|
|
||||||
return varray_contains_nonzero(curves.selection_curve_float());
|
|
||||||
}
|
|
||||||
BLI_assert_unreachable();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool any_point_selected(const CurvesGeometry &curves)
|
static bool has_anything_selected(const Span<Curves *> curves_ids)
|
||||||
{
|
{
|
||||||
return varray_contains_nonzero(curves.selection_point_float());
|
return std::any_of(curves_ids.begin(), curves_ids.end(), [](const Curves *curves_id) {
|
||||||
}
|
return has_anything_selected(*curves_id);
|
||||||
|
});
|
||||||
static bool any_point_selected(const Span<Curves *> curves_ids)
|
|
||||||
{
|
|
||||||
for (const Curves *curves_id : curves_ids) {
|
|
||||||
if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace select_all {
|
namespace select_all {
|
||||||
|
@ -854,6 +866,16 @@ static void invert_selection(MutableSpan<float> selection)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void invert_selection(GMutableSpan selection)
|
||||||
|
{
|
||||||
|
if (selection.type().is<bool>()) {
|
||||||
|
array_utils::invert_booleans(selection.typed<bool>());
|
||||||
|
}
|
||||||
|
else if (selection.type().is<float>()) {
|
||||||
|
invert_selection(selection.typed<float>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int select_all_exec(bContext *C, wmOperator *op)
|
static int select_all_exec(bContext *C, wmOperator *op)
|
||||||
{
|
{
|
||||||
int action = RNA_enum_get(op->ptr, "action");
|
int action = RNA_enum_get(op->ptr, "action");
|
||||||
|
@ -861,27 +883,34 @@ static int select_all_exec(bContext *C, wmOperator *op)
|
||||||
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
|
VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C);
|
||||||
|
|
||||||
if (action == SEL_TOGGLE) {
|
if (action == SEL_TOGGLE) {
|
||||||
action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
|
action = has_anything_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Curves *curves_id : unique_curves) {
|
for (Curves *curves_id : unique_curves) {
|
||||||
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
|
||||||
|
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||||
if (action == SEL_SELECT) {
|
if (action == SEL_SELECT) {
|
||||||
/* As an optimization, just remove the selection attributes when everything is selected. */
|
/* As an optimization, just remove the selection attributes when everything is selected. */
|
||||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
attributes.remove(".selection");
|
||||||
attributes.remove(".selection_point_float");
|
}
|
||||||
attributes.remove(".selection_curve_float");
|
else if (!attributes.contains(".selection")) {
|
||||||
|
BLI_assert(ELEM(action, SEL_INVERT, SEL_DESELECT));
|
||||||
|
/* If the attribute doesn't exist and it's either deleted or inverted, create
|
||||||
|
* it with nothing selected, since that means everything was selected before. */
|
||||||
|
attributes.add(".selection",
|
||||||
|
eAttrDomain(curves_id->selection_domain),
|
||||||
|
CD_PROP_BOOL,
|
||||||
|
bke::AttributeInitDefaultValue());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ?
|
bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection");
|
||||||
curves.selection_point_float_for_write() :
|
|
||||||
curves.selection_curve_float_for_write();
|
|
||||||
if (action == SEL_DESELECT) {
|
if (action == SEL_DESELECT) {
|
||||||
selection.fill(0.0f);
|
fill_selection_false(selection.span);
|
||||||
}
|
}
|
||||||
else if (action == SEL_INVERT) {
|
else if (action == SEL_INVERT) {
|
||||||
invert_selection(selection);
|
invert_selection(selection.span);
|
||||||
}
|
}
|
||||||
|
selection.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup edcurves
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BLI_index_mask_ops.hh"
|
||||||
|
|
||||||
|
#include "BKE_attribute.hh"
|
||||||
|
#include "BKE_curves.hh"
|
||||||
|
|
||||||
|
#include "ED_curves.h"
|
||||||
|
#include "ED_object.h"
|
||||||
|
|
||||||
|
namespace blender::ed::curves {
|
||||||
|
|
||||||
|
static IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves,
|
||||||
|
Vector<int64_t> &r_indices)
|
||||||
|
{
|
||||||
|
const IndexRange curves_range = curves.curves_range();
|
||||||
|
const bke::AttributeAccessor attributes = curves.attributes();
|
||||||
|
|
||||||
|
/* Interpolate from points to curves manually as a performance improvement, since we are only
|
||||||
|
* interested in whether any point in each curve is selected. Retrieve meta data since
|
||||||
|
* #lookup_or_default from the attribute API doesn't give the domain of the attribute. */
|
||||||
|
std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(".selection");
|
||||||
|
if (meta_data && meta_data->domain == ATTR_DOMAIN_POINT) {
|
||||||
|
/* Avoid the interpolation from interpolating the attribute to the
|
||||||
|
* curve domain by retrieving the point domain values directly. */
|
||||||
|
const VArray<bool> selection = attributes.lookup_or_default<bool>(
|
||||||
|
".selection", ATTR_DOMAIN_POINT, true);
|
||||||
|
if (selection.is_single()) {
|
||||||
|
return selection.get_internal_single() ? IndexMask(curves_range) : IndexMask();
|
||||||
|
}
|
||||||
|
return index_mask_ops::find_indices_based_on_predicate(
|
||||||
|
curves_range, 512, r_indices, [&](const int64_t curve_i) {
|
||||||
|
const IndexRange points = curves.points_for_curve(curve_i);
|
||||||
|
/* The curve is selected if any of its points are selected. */
|
||||||
|
Array<bool, 32> point_selection(points.size());
|
||||||
|
selection.materialize_compressed(points, point_selection);
|
||||||
|
return point_selection.as_span().contains(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const VArray<bool> selection = attributes.lookup_or_default<bool>(
|
||||||
|
".selection", ATTR_DOMAIN_CURVE, true);
|
||||||
|
return index_mask_ops::find_indices_from_virtual_array(curves_range, selection, 2048, r_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices)
|
||||||
|
{
|
||||||
|
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
|
||||||
|
return retrieve_selected_curves(curves, r_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
static IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves,
|
||||||
|
Vector<int64_t> &r_indices)
|
||||||
|
{
|
||||||
|
return index_mask_ops::find_indices_from_virtual_array(
|
||||||
|
curves.points_range(),
|
||||||
|
curves.attributes().lookup_or_default<bool>(".selection", ATTR_DOMAIN_POINT, true),
|
||||||
|
2048,
|
||||||
|
r_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
|
||||||
|
{
|
||||||
|
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
|
||||||
|
return retrieve_selected_points(curves, r_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensure_selection_attribute(Curves &curves_id, const eCustomDataType create_type)
|
||||||
|
{
|
||||||
|
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
|
||||||
|
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||||
|
if (attributes.contains(".selection")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const eAttrDomain domain = eAttrDomain(curves_id.selection_domain);
|
||||||
|
const int domain_size = attributes.domain_size(domain);
|
||||||
|
switch (create_type) {
|
||||||
|
case CD_PROP_BOOL:
|
||||||
|
attributes.add(".selection",
|
||||||
|
domain,
|
||||||
|
CD_PROP_BOOL,
|
||||||
|
bke::AttributeInitVArray(VArray<bool>::ForSingle(true, domain_size)));
|
||||||
|
break;
|
||||||
|
case CD_PROP_FLOAT:
|
||||||
|
attributes.add(".selection",
|
||||||
|
domain,
|
||||||
|
CD_PROP_FLOAT,
|
||||||
|
bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, domain_size)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_selection_false(GMutableSpan selection)
|
||||||
|
{
|
||||||
|
if (selection.type().is<bool>()) {
|
||||||
|
selection.typed<bool>().fill(false);
|
||||||
|
}
|
||||||
|
else if (selection.type().is<float>()) {
|
||||||
|
selection.typed<float>().fill(0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void fill_selection_true(GMutableSpan selection)
|
||||||
|
{
|
||||||
|
if (selection.type().is<bool>()) {
|
||||||
|
selection.typed<bool>().fill(true);
|
||||||
|
}
|
||||||
|
else if (selection.type().is<float>()) {
|
||||||
|
selection.typed<float>().fill(1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blender::ed::curves
|
|
@ -881,7 +881,7 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot)
|
||||||
INT_MAX);
|
INT_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********************* Clean Duplicated Frames ************************** */
|
/* ********************* Clean Duplicate Frames ************************** */
|
||||||
static bool gpencil_frame_is_equal(const bGPDframe *gpf_a, const bGPDframe *gpf_b)
|
static bool gpencil_frame_is_equal(const bGPDframe *gpf_a, const bGPDframe *gpf_b)
|
||||||
{
|
{
|
||||||
if ((gpf_a == NULL) || (gpf_b == NULL)) {
|
if ((gpf_a == NULL) || (gpf_b == NULL)) {
|
||||||
|
@ -1015,9 +1015,9 @@ void GPENCIL_OT_frame_clean_duplicate(wmOperatorType *ot)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* identifiers */
|
/* identifiers */
|
||||||
ot->name = "Clean Duplicated Frames";
|
ot->name = "Clean Duplicate Frames";
|
||||||
ot->idname = "GPENCIL_OT_frame_clean_duplicate";
|
ot->idname = "GPENCIL_OT_frame_clean_duplicate";
|
||||||
ot->description = "Remove any duplicated frame";
|
ot->description = "Remove duplicate keyframes";
|
||||||
|
|
||||||
/* callbacks */
|
/* callbacks */
|
||||||
ot->exec = gpencil_frame_clean_duplicate_exec;
|
ot->exec = gpencil_frame_clean_duplicate_exec;
|
||||||
|
|
|
@ -3356,7 +3356,7 @@ void ED_gpencil_layer_merge(bGPdata *gpd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpencil_layer_new_name_get(bGPdata *gpd, char *rname)
|
static void gpencil_layer_new_name_get(bGPdata *gpd, char *rname)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||||
|
|
|
@ -369,19 +369,6 @@ void ED_mesh_deform_bind_callback(struct Object *object,
|
||||||
int verts_num,
|
int verts_num,
|
||||||
float cagemat[4][4]);
|
float cagemat[4][4]);
|
||||||
|
|
||||||
/* Pose backups, pose_backup.c */
|
|
||||||
struct PoseBackup;
|
|
||||||
/**
|
|
||||||
* Create a backup of those bones that are animated in the given action.
|
|
||||||
*/
|
|
||||||
struct PoseBackup *ED_pose_backup_create_selected_bones(
|
|
||||||
const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT;
|
|
||||||
struct PoseBackup *ED_pose_backup_create_all_bones(
|
|
||||||
const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT;
|
|
||||||
bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup);
|
|
||||||
void ED_pose_backup_restore(const struct PoseBackup *pbd);
|
|
||||||
void ED_pose_backup_free(struct PoseBackup *pbd);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,20 +20,69 @@ void ED_operatortypes_curves(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
# include "BKE_curves.hh"
|
# include "BKE_attribute.hh"
|
||||||
|
# include "BLI_index_mask.hh"
|
||||||
|
# include "BLI_vector.hh"
|
||||||
# include "BLI_vector_set.hh"
|
# include "BLI_vector_set.hh"
|
||||||
|
|
||||||
|
# include "BKE_curves.hh"
|
||||||
|
|
||||||
namespace blender::ed::curves {
|
namespace blender::ed::curves {
|
||||||
|
|
||||||
bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
|
bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
|
||||||
bool has_anything_selected(const Curves &curves_id);
|
|
||||||
VectorSet<Curves *> get_unique_editable_curves(const bContext &C);
|
VectorSet<Curves *> get_unique_editable_curves(const bContext &C);
|
||||||
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob);
|
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob);
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/** \name Poll Functions
|
||||||
|
* \{ */
|
||||||
|
|
||||||
bool editable_curves_with_surface_poll(bContext *C);
|
bool editable_curves_with_surface_poll(bContext *C);
|
||||||
bool curves_with_surface_poll(bContext *C);
|
bool curves_with_surface_poll(bContext *C);
|
||||||
bool editable_curves_poll(bContext *C);
|
bool editable_curves_poll(bContext *C);
|
||||||
bool curves_poll(bContext *C);
|
bool curves_poll(bContext *C);
|
||||||
|
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/** \name Selection
|
||||||
|
*
|
||||||
|
* Selection on curves can be stored on either attribute domain: either per-curve or per-point. It
|
||||||
|
* can be stored with a float or boolean data-type. The boolean data-type is faster, smaller, and
|
||||||
|
* corresponds better to edit-mode selections, but the float data type is useful for soft selection
|
||||||
|
* (like masking) in sculpt mode.
|
||||||
|
*
|
||||||
|
* The attribute API is used to do the necessary type and domain conversions when necessary, and
|
||||||
|
* can handle most interaction with the selection attribute, but these functions implement some
|
||||||
|
* helpful utilities on top of that.
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
void fill_selection_false(GMutableSpan span);
|
||||||
|
void fill_selection_true(GMutableSpan span);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if any element is selected, on either domain with either type.
|
||||||
|
*/
|
||||||
|
bool has_anything_selected(const Curves &curves_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find curves that have any point selected (a selection factor greater than zero),
|
||||||
|
* or curves that have their own selection factor greater than zero.
|
||||||
|
*/
|
||||||
|
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find points that are selected (a selection factor greater than zero),
|
||||||
|
* or points in curves with a selection factor greater than zero).
|
||||||
|
*/
|
||||||
|
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the ".selection" attribute doesn't exist, create it with the requested type (bool or float).
|
||||||
|
*/
|
||||||
|
void ensure_selection_attribute(Curves &curves_id, const eCustomDataType create_type);
|
||||||
|
|
||||||
|
/** \} */
|
||||||
|
|
||||||
} // namespace blender::ed::curves
|
} // namespace blender::ed::curves
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,26 +17,3 @@ void ED_operatortypes_sculpt_curves(void);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
# include "BLI_index_mask.hh"
|
|
||||||
# include "BLI_vector.hh"
|
|
||||||
|
|
||||||
namespace blender::ed::sculpt_paint {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find curves that have any point selected (a selection factor greater than zero),
|
|
||||||
* or curves that have their own selection factor greater than zero.
|
|
||||||
*/
|
|
||||||
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find points that are selected (a selection factor greater than zero),
|
|
||||||
* or points in curves with a selection factor greater than zero).
|
|
||||||
*/
|
|
||||||
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
|
|
||||||
|
|
||||||
} // namespace blender::ed::sculpt_paint
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -78,15 +78,6 @@ void ED_node_draw_snap(
|
||||||
|
|
||||||
/* node_draw.cc */
|
/* node_draw.cc */
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw a single node socket at default size.
|
|
||||||
* \note this is only called from external code, internally #node_socket_draw_nested() is used for
|
|
||||||
* optimized drawing of multiple/all sockets of a node.
|
|
||||||
*/
|
|
||||||
void ED_node_socket_draw(struct bNodeSocket *sock,
|
|
||||||
const struct rcti *rect,
|
|
||||||
const float color[4],
|
|
||||||
float scale);
|
|
||||||
void ED_node_tree_update(const struct bContext *C);
|
void ED_node_tree_update(const struct bContext *C);
|
||||||
void ED_node_tag_update_id(struct ID *id);
|
void ED_node_tag_update_id(struct ID *id);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ struct SpaceNode;
|
||||||
struct ARegion;
|
struct ARegion;
|
||||||
struct Main;
|
struct Main;
|
||||||
struct bNodeTree;
|
struct bNodeTree;
|
||||||
|
struct rcti;
|
||||||
|
|
||||||
namespace blender::ed::space_node {
|
namespace blender::ed::space_node {
|
||||||
|
|
||||||
|
@ -22,4 +23,11 @@ void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion);
|
||||||
void node_insert_on_link_flags(Main &bmain, SpaceNode &snode);
|
void node_insert_on_link_flags(Main &bmain, SpaceNode &snode);
|
||||||
void node_insert_on_link_flags_clear(bNodeTree &node_tree);
|
void node_insert_on_link_flags_clear(bNodeTree &node_tree);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw a single node socket at default size.
|
||||||
|
* \note this is only called from external code, internally #node_socket_draw_nested() is used for
|
||||||
|
* optimized drawing of multiple/all sockets of a node.
|
||||||
|
*/
|
||||||
|
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale);
|
||||||
|
|
||||||
} // namespace blender::ed::space_node
|
} // namespace blender::ed::space_node
|
||||||
|
|
|
@ -217,7 +217,8 @@ void ED_area_tag_refresh(ScrArea *area);
|
||||||
void ED_area_do_refresh(struct bContext *C, ScrArea *area);
|
void ED_area_do_refresh(struct bContext *C, ScrArea *area);
|
||||||
struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]);
|
struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]);
|
||||||
/**
|
/**
|
||||||
* Use NULL to disable it.
|
* Show the given text in the area's header, instead of its regular contents.
|
||||||
|
* Use NULL to disable this and show the regular header contents again.
|
||||||
*/
|
*/
|
||||||
void ED_area_status_text(ScrArea *area, const char *str);
|
void ED_area_status_text(ScrArea *area, const char *str);
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -98,6 +98,9 @@ void ED_slider_factor_set(struct tSlider *slider, float factor);
|
||||||
bool ED_slider_allow_overshoot_get(struct tSlider *slider);
|
bool ED_slider_allow_overshoot_get(struct tSlider *slider);
|
||||||
void ED_slider_allow_overshoot_set(struct tSlider *slider, bool value);
|
void ED_slider_allow_overshoot_set(struct tSlider *slider, bool value);
|
||||||
|
|
||||||
|
bool ED_slider_is_bidirectional_get(struct tSlider *slider);
|
||||||
|
void ED_slider_is_bidirectional_set(struct tSlider *slider, bool value);
|
||||||
|
|
||||||
/* ************** XXX OLD CRUFT WARNING ************* */
|
/* ************** XXX OLD CRUFT WARNING ************* */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -175,8 +175,8 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay
|
||||||
if (STRPREFIX(render_pass->name, render_pass_name_prefix) &&
|
if (STRPREFIX(render_pass->name, render_pass_name_prefix) &&
|
||||||
!STREQLEN(render_pass->name, render_pass_name_prefix, sizeof(render_pass->name))) {
|
!STREQLEN(render_pass->name, render_pass_name_prefix, sizeof(render_pass->name))) {
|
||||||
BLI_assert(render_pass->channels == 4);
|
BLI_assert(render_pass->channels == 4);
|
||||||
const int x = (int)(fpos[0] * render_pass->rectx);
|
const int x = int(fpos[0] * render_pass->rectx);
|
||||||
const int y = (int)(fpos[1] * render_pass->recty);
|
const int y = int(fpos[1] * render_pass->recty);
|
||||||
const int offset = 4 * (y * render_pass->rectx + x);
|
const int offset = 4 * (y * render_pass->rectx + x);
|
||||||
zero_v3(r_col);
|
zero_v3(r_col);
|
||||||
r_col[0] = render_pass->rect[offset];
|
r_col[0] = render_pass->rect[offset];
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue