Cycles: new Microfacet-based Hair BSDF with elliptical cross-section support #105600
|
@ -61,17 +61,17 @@ ContinuationIndentWidth: 4
|
|||
# This tries to match Blender's style as much as possible. One
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping: {
|
||||
AfterClass: 'false'
|
||||
AfterControlStatement: 'false'
|
||||
AfterEnum : 'false'
|
||||
AfterFunction : 'true'
|
||||
AfterNamespace : 'false'
|
||||
AfterStruct : 'false'
|
||||
AfterUnion : 'false'
|
||||
BeforeCatch : 'true'
|
||||
BeforeElse : 'true'
|
||||
IndentBraces : 'false'
|
||||
AfterObjCDeclaration: 'true'
|
||||
AfterClass: 'false',
|
||||
AfterControlStatement: 'false',
|
||||
AfterEnum : 'false',
|
||||
AfterFunction : 'true',
|
||||
AfterNamespace : 'false',
|
||||
AfterStruct : 'false',
|
||||
AfterUnion : 'false',
|
||||
BeforeCatch : 'true',
|
||||
BeforeElse : 'true',
|
||||
IndentBraces : 'false',
|
||||
AfterObjCDeclaration: 'true',
|
||||
}
|
||||
|
||||
# For switch statements, indent the cases.
|
||||
|
|
|
@ -118,14 +118,18 @@ enable_testing()
|
|||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
if("${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "11.0.0")
|
||||
message(FATAL_ERROR "The minimum supported version of GCC is 11.0.0")
|
||||
message(FATAL_ERROR "The minimum supported version of GCC is 11.0.0, found ${CMAKE_C_COMPILER_VERSION}")
|
||||
endif()
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(CMAKE_COMPILER_IS_GNUCC AND ("${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "8.0"))
|
||||
message(FATAL_ERROR "The minimum supported version of CLANG is 8.0")
|
||||
message(FATAL_ERROR "The minimum supported version of CLANG is 8.0, found ${CMAKE_C_COMPILER_VERSION}")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
if(MSVC_VERSION VERSION_LESS "1928")
|
||||
# MSVC_VERSION is an internal version number, it doesn't map to something
|
||||
# the end user would recognize as a version. Because of this, for MSVC we do
|
||||
# not show the found number. When using our make.bat the actual VS version
|
||||
# will be displayed on the console before starting the build, anyway.
|
||||
message(FATAL_ERROR "The minimum supported version of MSVC is 2019 (16.9.16)")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -501,12 +505,14 @@ endif()
|
|||
if(NOT APPLE)
|
||||
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_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
|
||||
# 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_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_SYCL_TARGETS)
|
||||
endif()
|
||||
|
@ -830,27 +836,17 @@ endif()
|
|||
# enable boost for cycles, audaspace or i18n
|
||||
# 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_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_USD OFF)
|
||||
set_and_warn_dependency(WITH_BOOST WITH_ALEMBIC OFF)
|
||||
if(WITH_CYCLES)
|
||||
set_and_warn_dependency(WITH_BOOST WITH_CYCLES_OSL OFF)
|
||||
set_and_warn_dependency(WITH_PUGIXML WITH_CYCLES_OSL OFF)
|
||||
endif()
|
||||
set_and_warn_dependency(WITH_PUGIXML WITH_OPENIMAGEIO OFF)
|
||||
|
||||
if(WITH_BOOST AND NOT (WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_INTERNATIONAL OR
|
||||
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_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_MOD_FLUID OFF)
|
||||
|
||||
|
@ -859,14 +855,10 @@ set_and_warn_dependency(WITH_OPENVDB WITH_NANOVDB OFF)
|
|||
|
||||
# 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_OPENCOLORIO OFF)
|
||||
|
||||
# Haru needs `TIFFFaxBlackCodes` & `TIFFFaxWhiteCodes` symbols from TIFF.
|
||||
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
|
||||
if(WITH_CYCLES)
|
||||
set(WITH_OPENIMAGEIO ON)
|
||||
|
@ -880,17 +872,6 @@ else()
|
|||
set(WITH_CYCLES_OSL OFF)
|
||||
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
|
||||
if(WITH_INSTALL_PORTABLE)
|
||||
set(CMAKE_SKIP_BUILD_RPATH TRUE)
|
||||
|
@ -1093,14 +1074,6 @@ if(WITH_CYCLES)
|
|||
"Configure OIIO or disable WITH_CYCLES"
|
||||
)
|
||||
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(NOT WITH_LLVM)
|
||||
message(
|
||||
|
@ -2007,24 +1980,6 @@ if(0)
|
|||
print_all_vars()
|
||||
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.
|
||||
if(POSTCONFIGURE_SCRIPT)
|
||||
include(${POSTCONFIGURE_SCRIPT})
|
||||
|
|
|
@ -39,15 +39,15 @@ with-all,with-opencollada,with-jack,with-pulseaudio,with-embree,with-oidn,with-n
|
|||
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,ver-xr-openxr:,ver-level-zero:\
|
||||
force-all,force-python,force-boost,force-tbb,\
|
||||
force-ocio,force-imath,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
|
||||
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,force-usd,\
|
||||
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,force-materialx,force-usd,\
|
||||
force-xr-openxr,force-level-zero,force-openpgl,\
|
||||
build-all,build-python,build-boost,build-tbb,\
|
||||
build-ocio,build-imath,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\
|
||||
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,build-usd,\
|
||||
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,build-materialx,build-usd,\
|
||||
build-xr-openxr,build-level-zero,build-openpgl,\
|
||||
skip-python,skip-boost,skip-tbb,\
|
||||
skip-ocio,skip-imath,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\
|
||||
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn,skip-usd,\
|
||||
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn,skip-materialx,skip-usd,\
|
||||
skip-xr-openxr,skip-level-zero,skip-openpgl \
|
||||
-- "$@" \
|
||||
)
|
||||
|
@ -223,6 +223,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
|
|||
--build-ffmpeg
|
||||
Force the build of FFMpeg.
|
||||
|
||||
--build-materialx
|
||||
Force the build of MaterialX.
|
||||
|
||||
--build-usd
|
||||
Force the build of Universal Scene Description.
|
||||
|
||||
|
@ -296,6 +299,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
|
|||
--force-ffmpeg
|
||||
Force the rebuild of FFMpeg.
|
||||
|
||||
--force-materialx
|
||||
Force the rebuild of MaterialX.
|
||||
|
||||
--force-usd
|
||||
Force the rebuild of Universal Scene Description.
|
||||
|
||||
|
@ -362,6 +368,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
|
|||
--skip-ffmpeg
|
||||
Unconditionally skip FFMpeg installation/building.
|
||||
|
||||
--skip-materialx
|
||||
Unconditionally skip MaterialX installation/building.
|
||||
|
||||
--skip-usd
|
||||
Unconditionally skip Universal Scene Description installation/building.
|
||||
|
||||
|
@ -394,7 +403,7 @@ CLANG_FORMAT_VERSION="10.0"
|
|||
CLANG_FORMAT_VERSION_MIN="6.0"
|
||||
CLANG_FORMAT_VERSION_MEX="14.0"
|
||||
|
||||
PYTHON_VERSION="3.10.8"
|
||||
PYTHON_VERSION="3.10.9"
|
||||
PYTHON_VERSION_SHORT="3.10"
|
||||
PYTHON_VERSION_MIN="3.10"
|
||||
PYTHON_VERSION_MEX="3.12"
|
||||
|
@ -402,6 +411,7 @@ PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
|
|||
PYTHON_FORCE_BUILD=false
|
||||
PYTHON_FORCE_REBUILD=false
|
||||
PYTHON_SKIP=false
|
||||
_with_built_python=false
|
||||
|
||||
# Additional Python modules.
|
||||
PYTHON_IDNA_VERSION="3.3"
|
||||
|
@ -434,7 +444,7 @@ PYTHON_ZSTANDARD_VERSION_MIN="0.15.2"
|
|||
PYTHON_ZSTANDARD_VERSION_MEX="0.20.0"
|
||||
PYTHON_ZSTANDARD_NAME="zstandard"
|
||||
|
||||
PYTHON_NUMPY_VERSION="1.22.0"
|
||||
PYTHON_NUMPY_VERSION="1.23.5"
|
||||
PYTHON_NUMPY_VERSION_MIN="1.14"
|
||||
PYTHON_NUMPY_VERSION_MEX="2.0"
|
||||
PYTHON_NUMPY_NAME="numpy"
|
||||
|
@ -462,11 +472,13 @@ PYTHON_MODULES_PIP=(
|
|||
)
|
||||
|
||||
|
||||
BOOST_VERSION="1.78.0"
|
||||
BOOST_VERSION_SHORT="1.78"
|
||||
BOOST_VERSION="1.80.0"
|
||||
BOOST_VERSION_SHORT="1.80"
|
||||
BOOST_VERSION_MIN="1.49"
|
||||
BOOST_VERSION_MEX="2.0"
|
||||
BOOST_FORCE_BUILD=false
|
||||
# XXX Boost currently has an issue with python/tbb, see rB019b930 for details and patch used to fix it.
|
||||
# So for now it has to be built, system packages are not usable. :(
|
||||
BOOST_FORCE_BUILD=true
|
||||
BOOST_FORCE_REBUILD=false
|
||||
BOOST_SKIP=false
|
||||
|
||||
|
@ -479,8 +491,8 @@ TBB_FORCE_BUILD=false
|
|||
TBB_FORCE_REBUILD=false
|
||||
TBB_SKIP=false
|
||||
|
||||
OCIO_VERSION="2.1.1"
|
||||
OCIO_VERSION_SHORT="2.1"
|
||||
OCIO_VERSION="2.2.0"
|
||||
OCIO_VERSION_SHORT="2.2"
|
||||
OCIO_VERSION_MIN="2.0"
|
||||
OCIO_VERSION_MEX="3.0"
|
||||
OCIO_FORCE_BUILD=false
|
||||
|
@ -505,10 +517,10 @@ OPENEXR_FORCE_REBUILD=false
|
|||
OPENEXR_SKIP=false
|
||||
_with_built_openexr=false
|
||||
|
||||
OIIO_VERSION="2.3.20.0"
|
||||
OIIO_VERSION_SHORT="2.3"
|
||||
OIIO_VERSION_MIN="2.1.12"
|
||||
OIIO_VERSION_MEX="2.4.0"
|
||||
OIIO_VERSION="2.4.6.0"
|
||||
OIIO_VERSION_SHORT="2.4"
|
||||
OIIO_VERSION_MIN="2.2.0"
|
||||
OIIO_VERSION_MEX="2.5.0"
|
||||
OIIO_FORCE_BUILD=false
|
||||
OIIO_FORCE_REBUILD=false
|
||||
OIIO_SKIP=false
|
||||
|
@ -523,8 +535,8 @@ LLVM_FORCE_REBUILD=false
|
|||
LLVM_SKIP=false
|
||||
|
||||
# OSL needs to be compiled for now!
|
||||
OSL_VERSION="1.12.6.2"
|
||||
OSL_VERSION_SHORT="1.12"
|
||||
OSL_VERSION="1.13.0.2"
|
||||
OSL_VERSION_SHORT="1.13"
|
||||
OSL_VERSION_MIN="1.11"
|
||||
OSL_VERSION_MEX="2.0"
|
||||
OSL_FORCE_BUILD=false
|
||||
|
@ -532,9 +544,9 @@ OSL_FORCE_REBUILD=false
|
|||
OSL_SKIP=false
|
||||
|
||||
# OpenSubdiv needs to be compiled for now
|
||||
OSD_VERSION="3.4.4"
|
||||
OSD_VERSION_SHORT="3.4"
|
||||
OSD_VERSION_MIN="3.4"
|
||||
OSD_VERSION="3.5.0"
|
||||
OSD_VERSION_SHORT="3.5"
|
||||
OSD_VERSION_MIN="3.5"
|
||||
OSD_VERSION_MEX="4.0"
|
||||
OSD_FORCE_BUILD=false
|
||||
OSD_FORCE_REBUILD=false
|
||||
|
@ -543,10 +555,10 @@ OSD_SKIP=false
|
|||
# OpenVDB needs to be compiled for now
|
||||
OPENVDB_BLOSC_VERSION="1.21.1"
|
||||
|
||||
OPENVDB_VERSION="9.0.0"
|
||||
OPENVDB_VERSION_SHORT="9.0"
|
||||
OPENVDB_VERSION_MIN="9.0"
|
||||
OPENVDB_VERSION_MEX="9.1"
|
||||
OPENVDB_VERSION="10.0.0"
|
||||
OPENVDB_VERSION_SHORT="10.0"
|
||||
OPENVDB_VERSION_MIN="10.0"
|
||||
OPENVDB_VERSION_MEX="11.0"
|
||||
OPENVDB_FORCE_BUILD=false
|
||||
OPENVDB_FORCE_REBUILD=false
|
||||
OPENVDB_SKIP=false
|
||||
|
@ -560,8 +572,16 @@ ALEMBIC_FORCE_BUILD=false
|
|||
ALEMBIC_FORCE_REBUILD=false
|
||||
ALEMBIC_SKIP=false
|
||||
|
||||
USD_VERSION="22.03"
|
||||
USD_VERSION_SHORT="22.03"
|
||||
MATERIALX_VERSION="1.38.6"
|
||||
MATERIALX_VERSION_SHORT="1.38"
|
||||
MATERIALX_VERSION_MIN="1.38"
|
||||
MATERIALX_VERSION_MEX="1.40"
|
||||
MATERIALX_FORCE_BUILD=false
|
||||
MATERIALX_FORCE_REBUILD=false
|
||||
MATERIALX_SKIP=false
|
||||
|
||||
USD_VERSION="22.11"
|
||||
USD_VERSION_SHORT="22.11"
|
||||
USD_VERSION_MIN="20.05"
|
||||
USD_VERSION_MEX="23.00"
|
||||
USD_FORCE_BUILD=false
|
||||
|
@ -896,6 +916,9 @@ while true; do
|
|||
--build-alembic)
|
||||
ALEMBIC_FORCE_BUILD=true; shift; continue
|
||||
;;
|
||||
--build-materialx)
|
||||
MATERIALX_FORCE_BUILD=true; shift; continue
|
||||
;;
|
||||
--build-usd)
|
||||
USD_FORCE_BUILD=true; shift; continue
|
||||
;;
|
||||
|
@ -925,6 +948,7 @@ while true; do
|
|||
OIDN_FORCE_REBUILD=true
|
||||
FFMPEG_FORCE_REBUILD=true
|
||||
ALEMBIC_FORCE_REBUILD=true
|
||||
MATERIALX_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
XR_OPENXR_FORCE_REBUILD=true
|
||||
LEVEL_ZERO_FORCE_REBUILD=true
|
||||
|
@ -980,6 +1004,9 @@ while true; do
|
|||
--force-alembic)
|
||||
ALEMBIC_FORCE_REBUILD=true; shift; continue
|
||||
;;
|
||||
--force-materialx)
|
||||
MATERIALX_FORCE_REBUILD=true; shift; continue
|
||||
;;
|
||||
--force-usd)
|
||||
USD_FORCE_REBUILD=true; shift; continue
|
||||
;;
|
||||
|
@ -1043,6 +1070,9 @@ while true; do
|
|||
--skip-usd)
|
||||
USD_SKIP=true; shift; continue
|
||||
;;
|
||||
--skip-materialx)
|
||||
MATERIALX_SKIP=true; shift; continue
|
||||
;;
|
||||
--skip-xr-openxr)
|
||||
XR_OPENXR_SKIP=true; shift; continue
|
||||
;;
|
||||
|
@ -1108,7 +1138,9 @@ PYTHON_SOURCE=( "https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHO
|
|||
|
||||
_boost_version_nodots=`echo "$BOOST_VERSION" | sed -r 's/\./_/g'`
|
||||
BOOST_SOURCE=( "https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$_boost_version_nodots.tar.bz2" )
|
||||
BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams --with-python --with-program_options --with-serialization --with-atomic"
|
||||
BOOST_BUILD_MODULES="--with-filesystem --with-locale --with-thread --with-regex --with-system --with-date_time --with-wave --with-atomic --with-serialization --with-program_options --with-iostreams --with-python"
|
||||
# Used by debian distros.
|
||||
BOOST_DEB_PACKAGE_MODULES=( "libboost-filesystem" "libboost-locale" "libboost-thread" "libboost-regex" "libboost-system" "libboost-date-time" "libboost-wave" "libboost-atomic" "libboost-serialization" "libboost-program-options" "libboost-iostreams" "libboost-python" "libboost-numpy" )
|
||||
|
||||
TBB_SOURCE=( "https://github.com/oneapi-src/oneTBB/archive/$TBB_VERSION$TBB_VERSION_UPDATE.tar.gz" )
|
||||
TBB_SOURCE_CMAKE=( "https://raw.githubusercontent.com/wjakob/tbb/master/CMakeLists.txt" )
|
||||
|
@ -1138,17 +1170,11 @@ _LLVM_SOURCE_ROOT="https://github.com/llvm/llvm-project/releases/download/llvmor
|
|||
LLVM_SOURCE=( "$_LLVM_SOURCE_ROOT/llvm-$LLVM_VERSION.src.tar.xz" )
|
||||
LLVM_CLANG_SOURCE=( "$_LLVM_SOURCE_ROOT/clang-$LLVM_VERSION.src.tar.xz" "$_LLVM_SOURCE_ROOT/cfe-$LLVM_VERSION.src.tar.xz" )
|
||||
|
||||
OSL_USE_REPO=false
|
||||
OSL_USE_REPO=true
|
||||
OSL_SOURCE=( "https://github.com/imageworks/OpenShadingLanguage/archive/v$OSL_VERSION.tar.gz" )
|
||||
#~ OSL_SOURCE_REPO=( "https://github.com/imageworks/OpenShadingLanguage.git" )
|
||||
#~ OSL_SOURCE_REPO_BRANCH="master"
|
||||
#~ OSL_SOURCE_REPO_UID="85179714e1bc69cd25ecb6bb711c1a156685d395"
|
||||
#~ OSL_SOURCE=( "https://github.com/Nazg-Gul/OpenShadingLanguage/archive/Release-1.5.11.tar.gz" )
|
||||
#~ OSL_SOURCE_REPO=( "https://github.com/mont29/OpenShadingLanguage.git" )
|
||||
#~ OSL_SOURCE_REPO_UID="85179714e1bc69cd25ecb6bb711c1a156685d395"
|
||||
#~ OSL_SOURCE_REPO=( "https://github.com/Nazg-Gul/OpenShadingLanguage.git" )
|
||||
#~ OSL_SOURCE_REPO_UID="7d40ff5fe8e47b030042afb92d0e955f5aa96f48"
|
||||
#~ OSL_SOURCE_REPO_BRANCH="blender-fixes"
|
||||
OSL_SOURCE_REPO=( "https://github.com/AcademySoftwareFoundation/OpenShadingLanguage.git" )
|
||||
OSL_SOURCE_REPO_BRANCH="main"
|
||||
OSL_SOURCE_REPO_UID="1a7670600c8b08c2443a78d03c8c27e9a1149140"
|
||||
|
||||
OSD_USE_REPO=false
|
||||
# Script foo to make the version string compliant with the archive name:
|
||||
|
@ -1171,6 +1197,8 @@ ALEMBIC_SOURCE=( "https://github.com/alembic/alembic/archive/${ALEMBIC_VERSION}.
|
|||
# ALEMBIC_SOURCE_REPO_UID="e6c90d4faa32c4550adeaaf3f556dad4b73a92bb"
|
||||
# ALEMBIC_SOURCE_REPO_BRANCH="master"
|
||||
|
||||
MATERIALX_SOURCE=( "https://github.com/AcademySoftwareFoundation/MaterialX/archive/refs/tags/v${MATERIALX_VERSION}.tar.gz" )
|
||||
|
||||
USD_SOURCE=( "https://github.com/PixarAnimationStudios/USD/archive/v${USD_VERSION}.tar.gz" )
|
||||
|
||||
OPENCOLLADA_USE_REPO=false
|
||||
|
@ -1223,8 +1251,10 @@ Those libraries should be available as packages in all recent distributions (opt
|
|||
* libjpeg, libpng, libtiff, [openjpeg2], [libopenal].
|
||||
* libx11, libxcursor, libxi, libxrandr, libxinerama (and other libx... as needed).
|
||||
* libwayland-client0, libdecor, libwayland-cursor0, libwayland-egl1, libxkbcommon0, libdbus-1-3, libegl1 (Wayland)
|
||||
* libsqlite3, libzstd, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp, flex.
|
||||
* libsdl2, libepoxy, libpugixml, libpotrace, [libgmp], fontconfig, [libharu/libhpdf].\""
|
||||
* libsqlite3, libzstd, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp, flex, pybind11.
|
||||
* libsdl2, libepoxy, libpugixml, libpotrace, [libgmp], fontconfig, [libharu/libhpdf].
|
||||
* [libvulkan/vulkan-loader].
|
||||
* [libfribidi], [libharfbuzz].\""
|
||||
|
||||
DEPS_SPECIFIC_INFO="\"BUILDABLE DEPENDENCIES:
|
||||
|
||||
|
@ -1485,9 +1515,17 @@ _init_python() {
|
|||
_update_deps_python() {
|
||||
if [ "$1" = true ]; then
|
||||
BOOST_FORCE_BUILD=true
|
||||
OCIO_FORCE_BUILD=true
|
||||
OIIO_FORCE_BUILD=true
|
||||
OPENVDB_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
BOOST_FORCE_REBUILD=true
|
||||
OCIO_FORCE_REBUILD=true
|
||||
OIIO_FORCE_REBUILD=true
|
||||
OPENVDB_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -1573,6 +1611,9 @@ compile_Python() {
|
|||
PRINT ""
|
||||
$_python -m pip install $module --no-binary :all:
|
||||
done
|
||||
|
||||
_with_built_python=true
|
||||
_with_built_python_execpath="$INST/python-$PYTHON_VERSION_SHORT/bin/python3"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -1591,12 +1632,14 @@ _update_deps_boost() {
|
|||
OSL_FORCE_BUILD=true
|
||||
OPENVDB_FORCE_BUILD=true
|
||||
ALEMBIC_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
OIIO_FORCE_REBUILD=true
|
||||
OSL_FORCE_REBUILD=true
|
||||
OPENVDB_FORCE_REBUILD=true
|
||||
ALEMBIC_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -1616,7 +1659,7 @@ compile_Boost() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
boost_magic=14
|
||||
boost_magic=15
|
||||
|
||||
_init_boost
|
||||
|
||||
|
@ -1642,11 +1685,13 @@ compile_Boost() {
|
|||
mkdir -p $SRC
|
||||
download BOOST_SOURCE[@] $_src.tar.bz2
|
||||
tar -C $SRC --transform "s,\w*,boost-$BOOST_VERSION,x" -xf $_src.tar.bz2
|
||||
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/boost.diff
|
||||
fi
|
||||
|
||||
cd $_src
|
||||
if [ ! -f $_src/b2 ]; then
|
||||
if [ -d $INST/python-$PYTHON_VERSION_INSTALLED ]; then
|
||||
if [ -d $_with_built_python ]; then
|
||||
./bootstrap.sh --with-python-root="$INST/python-$PYTHON_VERSION_INSTALLED"
|
||||
else
|
||||
./bootstrap.sh
|
||||
|
@ -1841,7 +1886,7 @@ compile_OCIO() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
ocio_magic=3
|
||||
ocio_magic=5
|
||||
_init_ocio
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -1896,9 +1941,13 @@ compile_OCIO() {
|
|||
cmake_d="$cmake_d -D CMAKE_PREFIX_PATH=$_inst"
|
||||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_APPS=OFF"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=ON"
|
||||
cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF"
|
||||
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
if [ $(uname -m) == "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D OCIO_USE_SSE=OFF"
|
||||
fi
|
||||
|
@ -2088,11 +2137,13 @@ _update_deps_openexr() {
|
|||
OIIO_FORCE_BUILD=true
|
||||
OPENVDB_FORCE_BUILD=true
|
||||
ALEMBIC_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
OIIO_FORCE_REBUILD=true
|
||||
OPENVDB_FORCE_REBUILD=true
|
||||
ALEMBIC_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -2221,9 +2272,11 @@ _init_oiio() {
|
|||
_update_deps_oiio() {
|
||||
if [ "$1" = true ]; then
|
||||
OSL_FORCE_BUILD=true
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
OSL_FORCE_REBUILD=true
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -2243,7 +2296,7 @@ compile_OIIO() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
oiio_magic=19
|
||||
oiio_magic=20
|
||||
_init_oiio
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -2297,6 +2350,7 @@ compile_OIIO() {
|
|||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
|
||||
cmake_d="$cmake_d -D LINKSTATIC=OFF"
|
||||
cmake_d="$cmake_d -D BUILD_SHARED_LIBS=ON"
|
||||
|
||||
if [ $(uname -m) != "aarch64" ]; then
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
|
@ -2312,21 +2366,37 @@ compile_OIIO() {
|
|||
cmake_d="$cmake_d -D OpenEXR_ROOT=$INST/openexr"
|
||||
fi
|
||||
|
||||
# ptex is only needed when nicholas bishop is ready
|
||||
cmake_d="$cmake_d -D USE_PTEX=OFF"
|
||||
cmake_d="$cmake_d -D USE_PYTHON=ON"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
# Optional tests and cmd tools
|
||||
cmake_d="$cmake_d -D USE_QT=OFF"
|
||||
cmake_d="$cmake_d -D USE_PYTHON=OFF"
|
||||
cmake_d="$cmake_d -D USE_QT5=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENGL=OFF"
|
||||
cmake_d="$cmake_d -D USE_TBB=OFF"
|
||||
cmake_d="$cmake_d -D USE_BZIP2=OFF"
|
||||
cmake_d="$cmake_d -D USE_FREETYPE=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENCOLORIO=OFF"
|
||||
|
||||
cmake_d="$cmake_d -D USE_WEBP=ON"
|
||||
cmake_d="$cmake_d -D USE_OPENJPEG=ON"
|
||||
|
||||
cmake_d="$cmake_d -D USE_FFMPEG=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENCV=OFF"
|
||||
cmake_d="$cmake_d -D USE_OPENVDB=OFF"
|
||||
cmake_d="$cmake_d -D USE_NUKE=OFF"
|
||||
cmake_d="$cmake_d -D USE_DCMTK=OFF"
|
||||
cmake_d="$cmake_d -D USE_LIBHEIF=OFF"
|
||||
cmake_d="$cmake_d -D USE_GIF=OFF"
|
||||
cmake_d="$cmake_d -D USE_LIBRAW=OFF"
|
||||
cmake_d="$cmake_d -D USE_LIBSQUISH=OFF"
|
||||
|
||||
cmake_d="$cmake_d -D BUILD_TESTING=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON"
|
||||
cmake_d="$cmake_d -D TXT2MAN="
|
||||
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
|
||||
|
||||
if [ -d $INST/boost ]; then
|
||||
cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost -D Boost_NO_SYSTEM_PATHS=ON -D Boost_NO_BOOST_CMAKE=ON"
|
||||
|
@ -2404,7 +2474,7 @@ compile_LLVM() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
llvm_magic=3
|
||||
llvm_magic=4
|
||||
_init_llvm
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -2453,9 +2523,9 @@ compile_LLVM() {
|
|||
mkdir build
|
||||
cd build
|
||||
|
||||
LLVM_TARGETS="X86"
|
||||
LLVM_TARGETS="X86;NVPTX"
|
||||
if [ $(uname -m) == "aarch64" ]; then
|
||||
LLVM_TARGETS="AArch64"
|
||||
LLVM_TARGETS="AArch64;NVPTX"
|
||||
fi
|
||||
|
||||
cmake_d="-D CMAKE_BUILD_TYPE=Release"
|
||||
|
@ -2522,7 +2592,7 @@ compile_OSL() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
osl_magic=21
|
||||
osl_magic=22
|
||||
_init_osl
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -2553,8 +2623,9 @@ compile_OSL() {
|
|||
INFO "Unpacking OpenShadingLanguage-$OSL_VERSION"
|
||||
tar -C $SRC --transform "s,(.*/?)OpenShadingLanguage-[^/]*(.*),\1OpenShadingLanguage-$OSL_VERSION\2,x" \
|
||||
-xf $_src.tar.gz
|
||||
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
|
||||
fi
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
|
||||
fi
|
||||
|
||||
cd $_src
|
||||
|
@ -2566,6 +2637,8 @@ compile_OSL() {
|
|||
# Stick to same rev as windows' libs...
|
||||
git checkout $OSL_SOURCE_REPO_UID
|
||||
git reset --hard
|
||||
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
|
||||
fi
|
||||
|
||||
# Always refresh the whole build!
|
||||
|
@ -2881,7 +2954,12 @@ _init_openvdb() {
|
|||
}
|
||||
|
||||
_update_deps_openvdb() {
|
||||
:
|
||||
if [ "$1" = true ]; then
|
||||
USD_FORCE_BUILD=true
|
||||
fi
|
||||
if [ "$2" = true ]; then
|
||||
USD_FORCE_REBUILD=true
|
||||
fi
|
||||
}
|
||||
|
||||
clean_OPENVDB() {
|
||||
|
@ -2903,7 +2981,7 @@ compile_OPENVDB() {
|
|||
PRINT ""
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
openvdb_magic=4
|
||||
openvdb_magic=5
|
||||
_init_openvdb
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -2952,12 +3030,18 @@ compile_OPENVDB() {
|
|||
cmake_d="-D CMAKE_BUILD_TYPE=Release"
|
||||
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
cmake_d="$cmake_d -D USE_STATIC_DEPENDENCIES=OFF"
|
||||
cmake_d="$cmake_d -D OPENVDB_CORE_SHARED=ON"
|
||||
cmake_d="$cmake_d -D OPENVDB_CORE_STATIC=OFF"
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_BINARIES=OFF"
|
||||
|
||||
if [ "$WITH_NANOVDB" = true ]; then
|
||||
cmake_d="$cmake_d -D USE_NANOVDB=ON"
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_NANOVDB=ON"
|
||||
cmake_d="$cmake_d -D NANOVDB_BUILD_TOOLS=OFF"
|
||||
else
|
||||
cmake_d="$cmake_d -D USE_NANOVDB=OFF"
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_NANOVDB=OFF"
|
||||
cmake_d="$cmake_d -D NANOVDB_BUILD_TOOLS=OFF"
|
||||
fi
|
||||
|
||||
if [ -d $INST/boost ]; then
|
||||
|
@ -2969,6 +3053,9 @@ compile_OPENVDB() {
|
|||
fi
|
||||
if [ -d $INST/tbb ]; then
|
||||
cmake_d="$cmake_d -D TBB_ROOT=$INST/tbb"
|
||||
# Work around until we use oneTBB, otherwise OpenVDB forcefully
|
||||
# uses oneTBB if it can find it on the system.
|
||||
cmake_d="$cmake_d -D Tbb_INCLUDE_DIR=$INST/tbb/include"
|
||||
fi
|
||||
|
||||
if [ "$_with_built_imath" = true ]; then
|
||||
|
@ -2982,6 +3069,13 @@ compile_OPENVDB() {
|
|||
cmake_d="$cmake_d -D Blosc_ROOT=$INST/blosc"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -D OPENVDB_BUILD_PYTHON_MODULE=ON"
|
||||
cmake_d="$cmake_d -D OPENVDB_PYTHON_WRAP_ALL_GRID_TYPES=ON"
|
||||
cmake_d="$cmake_d -D USE_NUMPY=ON"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
cmake $cmake_d ..
|
||||
|
||||
make -j$THREADS install
|
||||
|
@ -3119,6 +3213,103 @@ compile_ALEMBIC() {
|
|||
run_ldconfig "alembic"
|
||||
}
|
||||
|
||||
#### Build materialX ####
|
||||
_init_materialx() {
|
||||
_src=$SRC/MaterialX-$MATERIALX_VERSION
|
||||
_git=false
|
||||
_inst=$INST/materialx-$MATERIALX_VERSION_SHORT
|
||||
_inst_shortcut=$INST/materialx
|
||||
}
|
||||
|
||||
_update_deps_materialx() {
|
||||
:
|
||||
}
|
||||
|
||||
clean_MATERIALX() {
|
||||
_init_materialx
|
||||
if [ -d $_inst ]; then
|
||||
# Force rebuilding the dependencies if needed.
|
||||
_update_deps_materialx false true
|
||||
fi
|
||||
_clean
|
||||
}
|
||||
|
||||
compile_MATERIALX() {
|
||||
if [ "$NO_BUILD" = true ]; then
|
||||
WARNING "--no-build enabled, MaterialX will not be compiled!"
|
||||
return
|
||||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
materialx_magic=1
|
||||
_init_materialx
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
_update_deps_materialx true false
|
||||
|
||||
# Clean install if needed!
|
||||
magic_compile_check materialx-$MATERIALX_VERSION $materialx_magic
|
||||
if [ $? -eq 1 -o "$MATERIALX_FORCE_REBUILD" = true ]; then
|
||||
clean_MATERIALX
|
||||
fi
|
||||
|
||||
if [ ! -d $_inst ]; then
|
||||
INFO "Building MaterialX-$MATERIALX_VERSION"
|
||||
|
||||
# Force rebuilding the dependencies.
|
||||
_update_deps_materialx true true
|
||||
|
||||
prepare_inst
|
||||
|
||||
if [ ! -d $_src ]; then
|
||||
mkdir -p $SRC
|
||||
download MATERIALX_SOURCE[@] "$_src.tar.gz"
|
||||
|
||||
INFO "Unpacking MaterialX-$MATERIALX_VERSION"
|
||||
tar -C $SRC -xf $_src.tar.gz
|
||||
|
||||
patch -d $_src -p1 < $SCRIPT_DIR/patches/materialx.diff
|
||||
fi
|
||||
|
||||
cd $_src
|
||||
|
||||
cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
|
||||
cmake_d="$cmake_d -DMATERIALX_BUILD_SHARED_LIBS=ON"
|
||||
cmake_d="$cmake_d -DCMAKE_DEBUG_POSTFIX=_d"
|
||||
|
||||
cmake_d="$cmake_d -DMATERIALX_BUILD_RENDER=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DMATERIALX_BUILD_PYTHON=ON"
|
||||
cmake_d="$cmake_d -DMATERIALX_INSTALL_PYTHON=OFF"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D Python_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
cmake $cmake_d ./
|
||||
make -j$THREADS install
|
||||
make clean
|
||||
|
||||
if [ ! -d $_inst ]; then
|
||||
ERROR "MaterialX-$MATERIALX_VERSION failed to compile, exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
magic_compile_set materialx-$MATERIALX_VERSION $materialx_magic
|
||||
|
||||
cd $CWD
|
||||
INFO "Done compiling MaterialX-$MATERIALX_VERSION!"
|
||||
else
|
||||
INFO "Own MaterialX-$MATERIALX_VERSION is up to date, nothing to do!"
|
||||
INFO "If you want to force rebuild of this lib, use the --force-materialx option."
|
||||
fi
|
||||
|
||||
if [ -d $_inst ]; then
|
||||
_create_inst_shortcut
|
||||
fi
|
||||
run_ldconfig "materialx"
|
||||
}
|
||||
|
||||
#### Build USD ####
|
||||
_init_usd() {
|
||||
_src=$SRC/USD-$USD_VERSION
|
||||
|
@ -3147,7 +3338,7 @@ compile_USD() {
|
|||
fi
|
||||
|
||||
# To be changed each time we make edits that would modify the compiled result!
|
||||
usd_magic=1
|
||||
usd_magic=2
|
||||
_init_usd
|
||||
|
||||
# Force having own builds for the dependencies.
|
||||
|
@ -3181,18 +3372,46 @@ compile_USD() {
|
|||
cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst"
|
||||
# For the reasoning behind these options, please see usd.cmake.
|
||||
if [ -d $INST/boost ]; then
|
||||
cmake_d="$cmake_d $cmake_d -D BOOST_ROOT=$INST/boost"
|
||||
cmake_d="$cmake_d -DBOOST_ROOT=$INST/boost"
|
||||
fi
|
||||
|
||||
if [ -d $INST/tbb ]; then
|
||||
cmake_d="$cmake_d $cmake_d -D TBB_ROOT_DIR=$INST/tbb"
|
||||
cmake_d="$cmake_d -DTBB_ROOT_DIR=$INST/tbb"
|
||||
fi
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_PYTHON_SUPPORT=OFF"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_IMAGING=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_PYTHON_SUPPORT=ON"
|
||||
cmake_d="$cmake_d -DPXR_USE_PYTHON_3=ON"
|
||||
if [ "$_with_built_python" = true ]; then
|
||||
cmake_d="$cmake_d -D PYTHON_EXECUTABLE=$_with_built_python_execpath"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_IMAGING=ON"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_OPENIMAGEIO_PLUGIN=ON"
|
||||
if [ -d $INST/openexr ]; then
|
||||
cmake_d="$cmake_d -DOPENEXR_LOCATION=$INST/openexr"
|
||||
fi
|
||||
if [ -d $INST/oiio ]; then
|
||||
cmake_d="$cmake_d -DOpenImageIO_ROOT=$INST/oiio"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_OPENVDB_SUPPORT=ON"
|
||||
if [ -d $INST/openvdb ]; then
|
||||
cmake_d="$cmake_d -DOPENVDB_LOCATION=$INST/openvdb"
|
||||
fi
|
||||
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_GL_SUPPORT=ON"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -DBUILD_SHARED_LIBS=ON"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_MONOLITHIC=ON"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_EXAMPLES=OFF"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_TUTORIALS=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_USD_TOOLS=OFF"
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_HDF5_SUPPORT=OFF"
|
||||
cmake_d="$cmake_d -DPXR_ENABLE_MATERIALX_SUPPORT=OFF"
|
||||
cmake_d="$cmake_d -DPXR_BUILD_USDVIEW=OFF"
|
||||
|
||||
cmake_d="$cmake_d -DPXR_BUILD_MONOLITHIC=ON"
|
||||
cmake_d="$cmake_d -DBUILD_SHARED_LIBS=ON"
|
||||
cmake_d="$cmake_d -DCMAKE_DEBUG_POSTFIX=_d"
|
||||
|
||||
cmake $cmake_d ./
|
||||
|
@ -4205,11 +4424,12 @@ install_DEB() {
|
|||
git libfreetype6-dev libfontconfig-dev libx11-dev flex bison libxxf86vm-dev \
|
||||
libxcursor-dev libxi-dev wget libsqlite3-dev libxrandr-dev libxinerama-dev \
|
||||
libwayland-dev libdecor-0-dev wayland-protocols libegl-dev libxkbcommon-dev libdbus-1-dev linux-libc-dev \
|
||||
libvulkan-dev libshaderc-dev \
|
||||
libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \
|
||||
libopenal-dev libepoxy-dev yasm \
|
||||
libopenal-dev libepoxy-dev yasm pybind11-dev \
|
||||
libsdl2-dev libfftw3-dev patch bzip2 libxml2-dev libtinyxml-dev libjemalloc-dev \
|
||||
libgmp-dev libpugixml-dev libpotrace-dev libhpdf-dev libzstd-dev libpystring-dev \
|
||||
libglfw3-dev"
|
||||
libglfw3-dev libfribidi-dev libharfbuzz-dev"
|
||||
|
||||
VORBIS_USE=true
|
||||
OGG_USE=true
|
||||
|
@ -4393,7 +4613,7 @@ install_DEB() {
|
|||
|
||||
boost_version=$(echo `get_package_version_DEB libboost-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/')
|
||||
|
||||
install_packages_DEB libboost-{filesystem,iostreams,locale,regex,system,thread,wave,program-options}$boost_version-dev
|
||||
install_packages_DEB ${BOOST_DEB_PACKAGE_MODULES[@]/%/$boost_version-dev}
|
||||
clean_Boost
|
||||
else
|
||||
compile_Boost
|
||||
|
@ -4585,6 +4805,16 @@ install_DEB() {
|
|||
compile_ALEMBIC
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
|
@ -4928,10 +5158,12 @@ install_RPM() {
|
|||
libtiff-devel libjpeg-devel libpng-devel sqlite-devel fftw-devel SDL2-devel \
|
||||
libX11-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel \
|
||||
wayland-devel libdecor-devel wayland-protocols-devel mesa-libEGL-devel libxkbcommon-devel dbus-devel kernel-headers \
|
||||
vulkan-loader-devel libshaderc-devel \
|
||||
wget ncurses-devel readline-devel $OPENJPEG_DEV openal-soft-devel \
|
||||
libepoxy-devel yasm patch \
|
||||
libepoxy-devel yasm patch pybind11-devel \
|
||||
libxml2-devel yaml-cpp-devel tinyxml-devel jemalloc-devel \
|
||||
gmp-devel pugixml-devel potrace-devel libharu-devel libzstd-devel pystring-devel"
|
||||
gmp-devel pugixml-devel potrace-devel libharu-devel libzstd-devel pystring-devel \
|
||||
fribidi-devel harfbuzz-devel"
|
||||
|
||||
OPENJPEG_USE=true
|
||||
VORBIS_USE=true
|
||||
|
@ -5312,6 +5544,16 @@ install_RPM() {
|
|||
compile_ALEMBIC
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
|
@ -5582,9 +5824,10 @@ install_ARCH() {
|
|||
|
||||
_packages="$BASE_DEVEL git cmake fontconfig flex \
|
||||
libxi libxcursor libxrandr libxinerama libepoxy libdecor libpng libtiff wget openal \
|
||||
$OPENJPEG_DEV yasm sdl2 fftw \
|
||||
vulkan-icd-loader vulkan-headers shaderc \
|
||||
$OPENJPEG_DEV yasm sdl2 fftw pybind11 \
|
||||
libxml2 yaml-cpp tinyxml python-requests jemalloc gmp potrace pugixml libharu \
|
||||
zstd pystring"
|
||||
zstd pystring fribidi harfbuzz"
|
||||
|
||||
OPENJPEG_USE=true
|
||||
VORBIS_USE=true
|
||||
|
@ -5916,6 +6159,16 @@ install_ARCH() {
|
|||
compile_ALEMBIC
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
|
@ -6207,6 +6460,27 @@ install_OTHER() {
|
|||
fi
|
||||
|
||||
|
||||
PRINT ""
|
||||
if [ "$MATERIALX_SKIP" = true ]; then
|
||||
WARNING "Skipping MaterialX installation, as requested..."
|
||||
elif [ "$MATERIALX_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced MaterialX building, as requested..."
|
||||
compile_MATERIALX
|
||||
else
|
||||
compile_MATERIALX
|
||||
fi
|
||||
|
||||
PRINT ""
|
||||
if [ "$USD_SKIP" = true ]; then
|
||||
WARNING "Skipping USD installation, as requested..."
|
||||
elif [ "$USD_FORCE_BUILD" = true ]; then
|
||||
INFO "Forced USD building, as requested..."
|
||||
compile_USD
|
||||
else
|
||||
compile_USD
|
||||
fi
|
||||
|
||||
|
||||
if [ "$WITH_OPENCOLLADA" = true ]; then
|
||||
PRINT ""
|
||||
if [ "$OPENCOLLADA_SKIP" = true ]; then
|
||||
|
@ -6284,7 +6558,8 @@ print_info() {
|
|||
|
||||
_buildargs="-U *SNDFILE* -U PYTHON* -U *BOOST* -U *Boost* -U *TBB*"
|
||||
_buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CLANG* -U *CYCLES*"
|
||||
_buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *BLOSC* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC* -U *USD*"
|
||||
_buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *BLOSC* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC*"
|
||||
_buildargs="$_buildargs -U *MATERIALX* -U *USD*"
|
||||
_buildargs="$_buildargs -U *EMBREE* -U *OPENIMAGEDENOISE* -U *OPENXR* -U *OPENPGL*"
|
||||
|
||||
_1="-D WITH_CODEC_SNDFILE=ON"
|
||||
|
@ -6471,6 +6746,17 @@ print_info() {
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ "$MATERIALX_SKIP" = false ]; then
|
||||
_1="-D WITH_MATERIALX=ON"
|
||||
PRINT " $_1"
|
||||
_buildargs="$_buildargs $_1"
|
||||
if [ -d $INST/materialx ]; then
|
||||
_1="-D MaterialX_DIR=$INST/materialx/lib/cmake/MaterialX"
|
||||
PRINT " $_1"
|
||||
_buildargs="$_buildargs $_1"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$USD_SKIP" = false ]; then
|
||||
_1="-D WITH_USD=ON"
|
||||
PRINT " $_1"
|
||||
|
|
|
@ -1209,43 +1209,6 @@ function(print_all_vars)
|
|||
endforeach()
|
||||
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
|
||||
projectname
|
||||
)
|
||||
|
|
|
@ -86,16 +86,14 @@ endif()
|
|||
|
||||
if(WITH_USD)
|
||||
find_package(USD REQUIRED)
|
||||
add_bundled_libraries(usd/lib)
|
||||
endif()
|
||||
add_bundled_libraries(usd/lib)
|
||||
|
||||
if(WITH_MATERIALX)
|
||||
find_package(MaterialX)
|
||||
set_and_warn_library_found("MaterialX" MaterialX_FOUND WITH_MATERIALX)
|
||||
if(WITH_MATERIALX)
|
||||
add_bundled_libraries(materialx/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(materialx/lib)
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
find_package(MoltenVK REQUIRED)
|
||||
|
@ -117,8 +115,8 @@ endif()
|
|||
|
||||
if(WITH_OPENSUBDIV)
|
||||
find_package(OpenSubdiv)
|
||||
add_bundled_libraries(opensubdiv/lib)
|
||||
endif()
|
||||
add_bundled_libraries(opensubdiv/lib)
|
||||
|
||||
if(WITH_CODEC_SNDFILE)
|
||||
find_package(SndFile)
|
||||
|
@ -156,9 +154,9 @@ list(APPEND FREETYPE_LIBRARIES
|
|||
|
||||
if(WITH_IMAGE_OPENEXR)
|
||||
find_package(OpenEXR)
|
||||
add_bundled_libraries(openexr/lib)
|
||||
add_bundled_libraries(imath/lib)
|
||||
endif()
|
||||
add_bundled_libraries(openexr/lib)
|
||||
add_bundled_libraries(imath/lib)
|
||||
|
||||
if(WITH_CODEC_FFMPEG)
|
||||
set(FFMPEG_ROOT_DIR ${LIBDIR}/ffmpeg)
|
||||
|
@ -270,12 +268,11 @@ if(WITH_BOOST)
|
|||
set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS})
|
||||
set(BOOST_DEFINITIONS)
|
||||
|
||||
add_bundled_libraries(boost/lib)
|
||||
|
||||
mark_as_advanced(Boost_LIBRARIES)
|
||||
mark_as_advanced(Boost_INCLUDE_DIRS)
|
||||
unset(_boost_FIND_COMPONENTS)
|
||||
endif()
|
||||
add_bundled_libraries(boost/lib)
|
||||
|
||||
if(WITH_INTERNATIONAL OR WITH_CODEC_FFMPEG)
|
||||
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_IDIFF "${LIBDIR}/openimageio/bin/idiff")
|
||||
add_bundled_libraries(openimageio/lib)
|
||||
endif()
|
||||
add_bundled_libraries(openimageio/lib)
|
||||
|
||||
if(WITH_OPENCOLORIO)
|
||||
find_package(OpenColorIO 2.0.0 REQUIRED)
|
||||
add_bundled_libraries(opencolorio/lib)
|
||||
endif()
|
||||
add_bundled_libraries(opencolorio/lib)
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
find_package(OpenVDB)
|
||||
|
@ -314,8 +311,8 @@ if(WITH_OPENVDB)
|
|||
unset(BLOSC_LIBRARIES CACHE)
|
||||
endif()
|
||||
set(OPENVDB_DEFINITIONS)
|
||||
add_bundled_libraries(openvdb/lib)
|
||||
endif()
|
||||
add_bundled_libraries(openvdb/lib)
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
find_package(NanoVDB)
|
||||
|
@ -363,8 +360,8 @@ endif()
|
|||
|
||||
if(WITH_TBB)
|
||||
find_package(TBB REQUIRED)
|
||||
add_bundled_libraries(tbb/lib)
|
||||
endif()
|
||||
add_bundled_libraries(tbb/lib)
|
||||
|
||||
if(WITH_POTRACE)
|
||||
find_package(Potrace REQUIRED)
|
||||
|
@ -382,9 +379,9 @@ if(WITH_OPENMP)
|
|||
set(OpenMP_LIBRARY_DIR "${LIBDIR}/openmp/lib/")
|
||||
set(OpenMP_LINKER_FLAGS "-L'${OpenMP_LIBRARY_DIR}' -lomp")
|
||||
set(OpenMP_LIBRARY "${OpenMP_LIBRARY_DIR}/libomp.dylib")
|
||||
add_bundled_libraries(openmp/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(openmp/lib)
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
find_package(XR_OpenXR_SDK REQUIRED)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
# Auto update existing CMake caches for new libraries
|
||||
|
||||
# Clear cached variables whose name matches `pattern`.
|
||||
function(unset_cache_variables pattern)
|
||||
get_cmake_property(_cache_variables CACHE_VARIABLES)
|
||||
foreach(_cache_variable ${_cache_variables})
|
||||
|
@ -12,6 +13,30 @@ function(unset_cache_variables pattern)
|
|||
endforeach()
|
||||
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.
|
||||
if(UNIX AND
|
||||
DEFINED OPENEXR_VERSION AND
|
||||
|
@ -63,3 +88,13 @@ if(UNIX AND
|
|||
unset_cache_variables("^TBB")
|
||||
unset_cache_variables("^USD")
|
||||
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)
|
||||
find_package_wrapper(OpenEXR) # our own module
|
||||
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()
|
||||
add_bundled_libraries(openexr/lib)
|
||||
add_bundled_libraries(imath/lib)
|
||||
|
||||
if(WITH_IMAGE_OPENJPEG)
|
||||
find_package_wrapper(OpenJPEG)
|
||||
|
@ -328,11 +326,8 @@ endif()
|
|||
if(WITH_OPENVDB)
|
||||
find_package(OpenVDB)
|
||||
set_and_warn_library_found("OpenVDB" OPENVDB_FOUND WITH_OPENVDB)
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
add_bundled_libraries(openvdb/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(openvdb/lib)
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
find_package_wrapper(NanoVDB)
|
||||
|
@ -351,18 +346,14 @@ endif()
|
|||
if(WITH_USD)
|
||||
find_package_wrapper(USD)
|
||||
set_and_warn_library_found("USD" USD_FOUND WITH_USD)
|
||||
if(WITH_USD)
|
||||
add_bundled_libraries(usd/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(usd/lib)
|
||||
|
||||
if(WITH_MATERIALX)
|
||||
find_package_wrapper(MaterialX)
|
||||
set_and_warn_library_found("MaterialX" MaterialX_FOUND WITH_MATERIALX)
|
||||
if(WITH_MATERIALX)
|
||||
add_bundled_libraries(materialx/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(materialx/lib)
|
||||
|
||||
if(WITH_BOOST)
|
||||
# uses in build instructions to override include and library variables
|
||||
|
@ -418,9 +409,8 @@ if(WITH_BOOST)
|
|||
find_package(IcuLinux)
|
||||
list(APPEND BOOST_LIBRARIES ${ICU_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_bundled_libraries(boost/lib)
|
||||
endif()
|
||||
add_bundled_libraries(boost/lib)
|
||||
|
||||
if(WITH_PUGIXML)
|
||||
find_package_wrapper(PugiXML)
|
||||
|
@ -455,21 +445,16 @@ if(WITH_OPENIMAGEIO)
|
|||
endif()
|
||||
|
||||
set_and_warn_library_found("OPENIMAGEIO" OPENIMAGEIO_FOUND WITH_OPENIMAGEIO)
|
||||
if(WITH_OPENIMAGEIO)
|
||||
add_bundled_libraries(openimageio/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(openimageio/lib)
|
||||
|
||||
if(WITH_OPENCOLORIO)
|
||||
find_package_wrapper(OpenColorIO 2.0.0)
|
||||
|
||||
set(OPENCOLORIO_DEFINITIONS)
|
||||
set_and_warn_library_found("OpenColorIO" OPENCOLORIO_FOUND WITH_OPENCOLORIO)
|
||||
|
||||
if(WITH_OPENCOLORIO)
|
||||
add_bundled_libraries(opencolorio/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(opencolorio/lib)
|
||||
|
||||
if(WITH_CYCLES AND WITH_CYCLES_EMBREE)
|
||||
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_and_warn_library_found("OpenSubdiv" OPENSUBDIV_FOUND WITH_OPENSUBDIV)
|
||||
if(WITH_OPENSUBDIV)
|
||||
add_bundled_libraries(opensubdiv/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(opensubdiv/lib)
|
||||
|
||||
if(WITH_TBB)
|
||||
find_package_wrapper(TBB)
|
||||
set_and_warn_library_found("TBB" TBB_FOUND WITH_TBB)
|
||||
if(WITH_TBB)
|
||||
add_bundled_libraries(tbb/lib)
|
||||
endif()
|
||||
endif()
|
||||
add_bundled_libraries(tbb/lib)
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
find_package(XR_OpenXR_SDK)
|
||||
|
@ -1013,18 +994,6 @@ endfunction()
|
|||
|
||||
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)
|
||||
# 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.
|
||||
|
|
|
@ -49,7 +49,7 @@ update-code:
|
|||
#
|
||||
buildbot:
|
||||
gcc:
|
||||
version: '9.0.0'
|
||||
version: '11.0.0'
|
||||
cuda10:
|
||||
version: '10.1.243'
|
||||
cuda11:
|
||||
|
|
|
@ -1543,6 +1543,17 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
default=False,
|
||||
)
|
||||
|
||||
kernel_optimization_level: EnumProperty(
|
||||
name="Kernel Optimization",
|
||||
description="Kernels can be optimized based on scene content. Optimized kernels are requested at the start of a render. If optimized kernels are not available, rendering will proceed using generic kernels until the optimized set is available in the cache. This can result in additional CPU usage for a brief time (tens of seconds).",
|
||||
default='FULL',
|
||||
items=(
|
||||
('OFF', "Off", "Disable kernel optimization. Slowest rendering, no extra background CPU usage"),
|
||||
('INTERSECT', "Intersection only", "Optimize only intersection kernels. Faster rendering, negligible extra background CPU usage"),
|
||||
('FULL', "Full", "Optimize all kernels. Fastest rendering, may result in extra background CPU usage"),
|
||||
),
|
||||
)
|
||||
|
||||
def find_existing_device_entry(self, device):
|
||||
for device_entry in self.devices:
|
||||
if device_entry.id == device[2] and device_entry.type == device[1]:
|
||||
|
@ -1711,10 +1722,12 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
if compute_device_type == 'METAL':
|
||||
import platform
|
||||
# MetalRT only works on Apple Silicon at present, pending argument encoding fixes on AMD
|
||||
# Kernel specialization is only viable on Apple Silicon at present due to relative compilation speed
|
||||
if platform.machine() == 'arm64':
|
||||
row = layout.row()
|
||||
row.use_property_split = True
|
||||
row.prop(self, "use_metalrt")
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.prop(self, "kernel_optimization_level")
|
||||
col.prop(self, "use_metalrt")
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_impl(self.layout, context)
|
||||
|
|
|
@ -30,7 +30,10 @@ int blender_device_threads(BL::Scene &b_scene)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background)
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences,
|
||||
BL::Scene &b_scene,
|
||||
bool background,
|
||||
bool preview)
|
||||
{
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
|
||||
|
@ -113,6 +116,18 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
|
|||
device.use_metalrt = true;
|
||||
}
|
||||
|
||||
if (preview) {
|
||||
/* Disable specialization for preview renders. */
|
||||
device.kernel_optimization_level = KERNEL_OPTIMIZATION_LEVEL_OFF;
|
||||
}
|
||||
else {
|
||||
device.kernel_optimization_level = (KernelOptimizationLevel)get_enum(
|
||||
cpreferences,
|
||||
"kernel_optimization_level",
|
||||
KERNEL_OPTIMIZATION_NUM_LEVELS,
|
||||
KERNEL_OPTIMIZATION_LEVEL_FULL);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ int blender_device_threads(BL::Scene &b_scene);
|
|||
/* Convert Blender settings to device specification. */
|
||||
DeviceInfo blender_device_info(BL::Preferences &b_preferences,
|
||||
BL::Scene &b_scene,
|
||||
bool background);
|
||||
bool background,
|
||||
bool preview);
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0
|
||||
* 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_immediate.h"
|
||||
#include "GPU_shader.h"
|
||||
|
@ -15,6 +9,12 @@
|
|||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "blender/display_driver.h"
|
||||
|
||||
#include "device/device.h"
|
||||
#include "util/log.h"
|
||||
#include "util/math.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
|
|
|
@ -754,7 +754,7 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key
|
|||
RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene), &sceneptr);
|
||||
BL::Scene b_scene(sceneptr);
|
||||
|
||||
DeviceInfo device = blender_device_info(b_preferences, b_scene, true);
|
||||
DeviceInfo device = blender_device_info(b_preferences, b_scene, true, true);
|
||||
|
||||
/* Get denoising parameters from view layer. */
|
||||
PointerRNA viewlayerptr;
|
||||
|
|
|
@ -866,7 +866,8 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
|
|||
|
||||
/* Device */
|
||||
params.threads = blender_device_threads(b_scene);
|
||||
params.device = blender_device_info(b_preferences, b_scene, params.background);
|
||||
params.device = blender_device_info(
|
||||
b_preferences, b_scene, params.background, b_engine.is_preview());
|
||||
|
||||
/* samples */
|
||||
int samples = get_int(cscene, "samples");
|
||||
|
|
|
@ -163,6 +163,9 @@ if(WITH_CYCLES_DEVICE_METAL)
|
|||
endif()
|
||||
|
||||
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)
|
||||
set(cycles_kernel_oneapi_lib_suffix "_aot")
|
||||
else()
|
||||
|
|
|
@ -57,22 +57,32 @@ enum DeviceTypeMask {
|
|||
|
||||
#define DEVICE_MASK(type) (DeviceTypeMask)(1 << type)
|
||||
|
||||
enum KernelOptimizationLevel {
|
||||
KERNEL_OPTIMIZATION_LEVEL_OFF = 0,
|
||||
KERNEL_OPTIMIZATION_LEVEL_INTERSECT = 1,
|
||||
KERNEL_OPTIMIZATION_LEVEL_FULL = 2,
|
||||
|
||||
KERNEL_OPTIMIZATION_NUM_LEVELS
|
||||
};
|
||||
|
||||
class DeviceInfo {
|
||||
public:
|
||||
DeviceType type;
|
||||
string description;
|
||||
string id; /* used for user preferences, should stay fixed with changing hardware config */
|
||||
int num;
|
||||
bool display_device; /* GPU is used as a display device. */
|
||||
bool has_nanovdb; /* Support NanoVDB volumes. */
|
||||
bool has_light_tree; /* Support light tree. */
|
||||
bool has_osl; /* Support Open Shading Language. */
|
||||
bool has_guiding; /* Support path guiding. */
|
||||
bool has_profiling; /* Supports runtime collection of profiling info. */
|
||||
bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */
|
||||
bool has_gpu_queue; /* Device supports GPU queue. */
|
||||
bool use_metalrt; /* Use MetalRT to accelerate ray queries (Metal only). */
|
||||
DenoiserTypeMask denoisers; /* Supported denoiser types. */
|
||||
bool display_device; /* GPU is used as a display device. */
|
||||
bool has_nanovdb; /* Support NanoVDB volumes. */
|
||||
bool has_light_tree; /* Support light tree. */
|
||||
bool has_osl; /* Support Open Shading Language. */
|
||||
bool has_guiding; /* Support path guiding. */
|
||||
bool has_profiling; /* Supports runtime collection of profiling info. */
|
||||
bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */
|
||||
bool has_gpu_queue; /* Device supports GPU queue. */
|
||||
bool use_metalrt; /* Use MetalRT to accelerate ray queries (Metal only). */
|
||||
KernelOptimizationLevel kernel_optimization_level; /* Optimization level applied to path tracing
|
||||
kernels (Metal only). */
|
||||
DenoiserTypeMask denoisers; /* Supported denoiser types. */
|
||||
int cpu_threads;
|
||||
vector<DeviceInfo> multi_devices;
|
||||
string error_msg;
|
||||
|
@ -167,6 +177,17 @@ class Device {
|
|||
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.
|
||||
* These may not be used on CPU or multi-devices. */
|
||||
|
||||
|
|
|
@ -76,7 +76,20 @@ class MetalDevice : public Device {
|
|||
|
||||
bool use_metalrt = false;
|
||||
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;
|
||||
|
||||
|
@ -92,14 +105,12 @@ class MetalDevice : public Device {
|
|||
|
||||
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);
|
||||
|
||||
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 erase_allocation(device_memory &mem);
|
||||
|
@ -112,7 +123,7 @@ class MetalDevice : public Device {
|
|||
|
||||
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 */
|
||||
|
|
|
@ -13,10 +13,32 @@
|
|||
# include "util/path.h"
|
||||
# include "util/time.h"
|
||||
|
||||
# include <crt_externs.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
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 appropriately 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
|
||||
{
|
||||
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)
|
||||
: 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;
|
||||
|
||||
/* select chosen device */
|
||||
|
@ -57,7 +88,6 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
|||
if (@available(macos 11.0, *)) {
|
||||
if ([mtlDevice hasUnifiedMemory]) {
|
||||
default_storage_mode = MTLResourceStorageModeShared;
|
||||
init_host_memory();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,10 +110,6 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
|||
case METAL_GPU_APPLE: {
|
||||
max_threads_per_threadgroup = 512;
|
||||
use_metalrt = info.use_metalrt;
|
||||
|
||||
/* Specialize the intersection kernels on Apple GPUs by default as these can be built very
|
||||
* quickly. */
|
||||
kernel_specialization_level = PSO_SPECIALIZED_INTERSECT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +122,22 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
|||
capture_enabled = true;
|
||||
}
|
||||
|
||||
if (device_vendor == METAL_GPU_APPLE) {
|
||||
/* Set kernel_specialization_level based on user prefs. */
|
||||
switch (info.kernel_optimization_level) {
|
||||
case KERNEL_OPTIMIZATION_LEVEL_OFF:
|
||||
kernel_specialization_level = PSO_GENERIC;
|
||||
break;
|
||||
default:
|
||||
case KERNEL_OPTIMIZATION_LEVEL_INTERSECT:
|
||||
kernel_specialization_level = PSO_SPECIALIZED_INTERSECT;
|
||||
break;
|
||||
case KERNEL_OPTIMIZATION_LEVEL_FULL:
|
||||
kernel_specialization_level = PSO_SPECIALIZED_SHADE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto envstr = getenv("CYCLES_METAL_SPECIALIZATION_LEVEL")) {
|
||||
kernel_specialization_level = (MetalPipelineType)atoi(envstr);
|
||||
}
|
||||
|
@ -181,6 +223,13 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
|
|||
|
||||
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) {
|
||||
if (tex) {
|
||||
[tex release];
|
||||
|
@ -326,21 +375,65 @@ bool MetalDevice::load_kernels(const uint _kernel_features)
|
|||
* active, but may still need to be rendered without motion blur if that isn't active as well. */
|
||||
motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
|
||||
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 progress 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);
|
||||
return result;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
|
||||
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 (!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;
|
||||
if (this->source[pso_type].empty()) {
|
||||
make_source(pso_type, kernel_features);
|
||||
}
|
||||
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 tear-down 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];
|
||||
|
||||
|
@ -359,20 +452,15 @@ bool MetalDevice::compile_and_load(MetalPipelineType pso_type)
|
|||
|
||||
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))),
|
||||
source[pso_type]);
|
||||
source);
|
||||
}
|
||||
|
||||
const double starttime = time_dt();
|
||||
double starttime = time_dt();
|
||||
|
||||
NSError *error = NULL;
|
||||
mtlLibrary[pso_type] = [mtlDevice newLibraryWithSource:@(source[pso_type].c_str())
|
||||
options:options
|
||||
error:&error];
|
||||
|
||||
if (!mtlLibrary[pso_type]) {
|
||||
NSString *err = [error localizedDescription];
|
||||
set_error(string_printf("Failed to compile library:\n%s", [err UTF8String]));
|
||||
}
|
||||
id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str())
|
||||
options:options
|
||||
error:&error];
|
||||
|
||||
metal_printf("Front-end compilation finished in %.1f seconds (%s)\n",
|
||||
time_dt() - starttime,
|
||||
|
@ -380,17 +468,38 @@ bool MetalDevice::compile_and_load(MetalPipelineType pso_type)
|
|||
|
||||
[options release];
|
||||
|
||||
return MetalDeviceKernels::load(this, pso_type);
|
||||
}
|
||||
bool blocking_pso_build = (getenv("CYCLES_METAL_PROFILING") ||
|
||||
MetalDeviceKernels::is_benchmark_warmup());
|
||||
if (blocking_pso_build) {
|
||||
MetalDeviceKernels::wait_for_all();
|
||||
starttime = 0.0;
|
||||
}
|
||||
|
||||
void MetalDevice::reserve_local_memory(const uint kernel_features)
|
||||
{
|
||||
/* METAL_WIP - implement this */
|
||||
}
|
||||
/* Save the compiled MTLLibrary and trigger the AIR->PSO builds (if the MetalDevice still
|
||||
* exists). */
|
||||
{
|
||||
thread_scoped_lock lock(existing_devices_mutex);
|
||||
if (MetalDevice *instance = get_device_by_ID(device_id, lock)) {
|
||||
if (mtlLibrary) {
|
||||
instance->mtlLibrary[pso_type] = mtlLibrary;
|
||||
|
||||
void MetalDevice::init_host_memory()
|
||||
{
|
||||
/* METAL_WIP - implement this */
|
||||
starttime = time_dt();
|
||||
MetalDeviceKernels::load(instance, pso_type);
|
||||
}
|
||||
else {
|
||||
NSString *err = [error localizedDescription];
|
||||
instance->set_error(string_printf("Failed to compile library:\n%s", [err UTF8String]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (starttime && blocking_pso_build) {
|
||||
MetalDeviceKernels::wait_for_all();
|
||||
|
||||
metal_printf("Back-end compilation finished in %.1f seconds (%s)\n",
|
||||
time_dt() - starttime,
|
||||
kernel_type_as_string(pso_type));
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDevice::load_texture_info()
|
||||
|
@ -700,55 +809,72 @@ device_ptr MetalDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, siz
|
|||
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)
|
||||
{
|
||||
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) {
|
||||
/* Don't load PSO_SPECIALIZED_SHADE kernels during viewport rendering as they are slower to
|
||||
* build. */
|
||||
return;
|
||||
/* In live viewport, don't specialize beyond intersection kernels for responsiveness. */
|
||||
specialization_level = (MetalPipelineType)min(specialization_level, PSO_SPECIALIZED_INTERSECT);
|
||||
}
|
||||
|
||||
/* PSO_SPECIALIZED_SHADE kernels are slower to specialize, so we load them asynchronously, and
|
||||
* only if there isn't an existing load in flight.
|
||||
*/
|
||||
auto specialize_shade_fn = ^() {
|
||||
compile_and_load(PSO_SPECIALIZED_SHADE);
|
||||
async_compile_and_load = false;
|
||||
/* For responsive rendering, specialize the kernels in the background, and only if there isn't an
|
||||
* existing "optimize_for_scene" request in flight. */
|
||||
int this_device_id = this->device_id;
|
||||
auto specialize_kernels_fn = ^() {
|
||||
for (int level = 1; level <= int(specialization_level); level++) {
|
||||
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). */
|
||||
if (getenv("CYCLES_METAL_PROFILING") != nullptr) {
|
||||
async_specialize_shade = false;
|
||||
specialize_in_background = false;
|
||||
}
|
||||
|
||||
if (async_specialize_shade) {
|
||||
if (!async_compile_and_load) {
|
||||
async_compile_and_load = true;
|
||||
/* Block during benchmark warm-up to ensure kernels are cached prior to the observed run. */
|
||||
if (MetalDeviceKernels::is_benchmark_warmup()) {
|
||||
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),
|
||||
specialize_shade_fn);
|
||||
specialize_kernels_fn);
|
||||
}
|
||||
else {
|
||||
metal_printf(
|
||||
"Async PSO_SPECIALIZED_SHADE load request already in progress - dropping request\n");
|
||||
metal_printf("\"optimize_for_scene\" request already in flight - dropping request\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
specialize_shade_fn();
|
||||
specialize_kernels_fn();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ struct MetalKernelPipeline {
|
|||
|
||||
void compile();
|
||||
|
||||
int originating_device_id;
|
||||
|
||||
id<MTLLibrary> mtlLibrary = nil;
|
||||
MetalPipelineType pso_type;
|
||||
string source_md5;
|
||||
|
@ -94,9 +96,13 @@ struct MetalKernelPipeline {
|
|||
/* Cache of Metal kernels for each DeviceKernel. */
|
||||
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);
|
||||
const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel);
|
||||
void wait_for_all();
|
||||
bool is_benchmark_warmup();
|
||||
|
||||
} /* namespace MetalDeviceKernels */
|
||||
|
||||
|
|
|
@ -86,23 +86,17 @@ struct ShaderCache {
|
|||
void load_kernel(DeviceKernel kernel, MetalDevice *device, MetalPipelineType pso_type);
|
||||
|
||||
bool should_load_kernel(DeviceKernel device_kernel,
|
||||
MetalDevice *device,
|
||||
MetalDevice const *device,
|
||||
MetalPipelineType pso_type);
|
||||
|
||||
void wait_for_all();
|
||||
|
||||
private:
|
||||
friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice);
|
||||
|
||||
void compile_thread_func(int thread_index);
|
||||
|
||||
using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>;
|
||||
|
||||
struct PipelineRequest {
|
||||
MetalKernelPipeline *pipeline = nullptr;
|
||||
std::function<void(MetalKernelPipeline *)> completionHandler;
|
||||
};
|
||||
|
||||
struct OccupancyTuningParameters {
|
||||
int threads_per_threadgroup = 0;
|
||||
int num_threads_per_block = 0;
|
||||
|
@ -113,35 +107,61 @@ struct ShaderCache {
|
|||
PipelineCollection pipelines[DEVICE_KERNEL_NUM];
|
||||
id<MTLDevice> mtlDevice;
|
||||
|
||||
bool running = false;
|
||||
static bool running;
|
||||
std::condition_variable cond_var;
|
||||
std::deque<PipelineRequest> request_queue;
|
||||
std::deque<MetalKernelPipeline *> request_queue;
|
||||
std::vector<std::thread> compile_threads;
|
||||
std::atomic_int incomplete_requests = 0;
|
||||
std::atomic_int incomplete_specialization_requests = 0;
|
||||
};
|
||||
|
||||
std::mutex g_shaderCacheMutex;
|
||||
std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache;
|
||||
bool ShaderCache::running = true;
|
||||
|
||||
const int MAX_POSSIBLE_GPUS_ON_SYSTEM = 8;
|
||||
using DeviceShaderCache = std::pair<id<MTLDevice>, unique_ptr<ShaderCache>>;
|
||||
int g_shaderCacheCount = 0;
|
||||
DeviceShaderCache g_shaderCache[MAX_POSSIBLE_GPUS_ON_SYSTEM];
|
||||
|
||||
ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice)
|
||||
{
|
||||
thread_scoped_lock lock(g_shaderCacheMutex);
|
||||
auto it = g_shaderCache.find(mtlDevice);
|
||||
if (it != g_shaderCache.end()) {
|
||||
return it->second.get();
|
||||
for (int i = 0; i < g_shaderCacheCount; i++) {
|
||||
if (g_shaderCache[i].first == mtlDevice) {
|
||||
return g_shaderCache[i].second.get();
|
||||
}
|
||||
}
|
||||
|
||||
g_shaderCache[mtlDevice] = make_unique<ShaderCache>(mtlDevice);
|
||||
return g_shaderCache[mtlDevice].get();
|
||||
static thread_mutex g_shaderCacheCountMutex;
|
||||
g_shaderCacheCountMutex.lock();
|
||||
int index = g_shaderCacheCount++;
|
||||
g_shaderCacheCountMutex.unlock();
|
||||
|
||||
assert(index < MAX_POSSIBLE_GPUS_ON_SYSTEM);
|
||||
g_shaderCache[index].first = mtlDevice;
|
||||
g_shaderCache[index].second = make_unique<ShaderCache>(mtlDevice);
|
||||
return g_shaderCache[index].second.get();
|
||||
}
|
||||
|
||||
ShaderCache::~ShaderCache()
|
||||
{
|
||||
metal_printf("ShaderCache shutting down with incomplete_requests = %d\n",
|
||||
int(incomplete_requests));
|
||||
|
||||
running = false;
|
||||
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 && !MetalDeviceKernels::is_benchmark_warmup()) {
|
||||
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) {
|
||||
thread.join();
|
||||
}
|
||||
|
@ -156,35 +176,69 @@ void ShaderCache::wait_for_all()
|
|||
|
||||
void ShaderCache::compile_thread_func(int thread_index)
|
||||
{
|
||||
while (1) {
|
||||
while (running) {
|
||||
|
||||
/* wait for / acquire next request */
|
||||
PipelineRequest request;
|
||||
MetalKernelPipeline *pipeline;
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
cond_var.wait(lock, [&] { return !running || !request_queue.empty(); });
|
||||
if (!running) {
|
||||
break;
|
||||
if (!running || request_queue.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!request_queue.empty()) {
|
||||
request = request_queue.front();
|
||||
request_queue.pop_front();
|
||||
}
|
||||
pipeline = request_queue.front();
|
||||
request_queue.pop_front();
|
||||
}
|
||||
|
||||
/* service request */
|
||||
if (request.pipeline) {
|
||||
request.pipeline->compile();
|
||||
incomplete_requests--;
|
||||
/* Service the request. */
|
||||
DeviceKernel device_kernel = pipeline->device_kernel;
|
||||
MetalPipelineType pso_type = pipeline->pso_type;
|
||||
|
||||
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,
|
||||
MetalDevice *device,
|
||||
MetalDevice const *device,
|
||||
MetalPipelineType pso_type)
|
||||
{
|
||||
if (!running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) {
|
||||
/* Skip megakernel. */
|
||||
return false;
|
||||
|
@ -240,7 +294,6 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
|||
/* create compiler threads on first run */
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
if (compile_threads.empty()) {
|
||||
running = true;
|
||||
for (int i = 0; i < max_mtlcompiler_threads; i++) {
|
||||
compile_threads.push_back(std::thread([&] { compile_thread_func(i); }));
|
||||
}
|
||||
|
@ -252,65 +305,43 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
|
|||
}
|
||||
|
||||
incomplete_requests++;
|
||||
if (pso_type != PSO_GENERIC) {
|
||||
incomplete_specialization_requests++;
|
||||
}
|
||||
|
||||
PipelineRequest request;
|
||||
request.pipeline = new MetalKernelPipeline;
|
||||
memcpy(&request.pipeline->kernel_data_,
|
||||
&device->launch_params.data,
|
||||
sizeof(request.pipeline->kernel_data_));
|
||||
request.pipeline->pso_type = pso_type;
|
||||
request.pipeline->mtlDevice = mtlDevice;
|
||||
request.pipeline->source_md5 = device->source_md5[pso_type];
|
||||
request.pipeline->mtlLibrary = device->mtlLibrary[pso_type];
|
||||
request.pipeline->device_kernel = device_kernel;
|
||||
request.pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
|
||||
MetalKernelPipeline *pipeline = new MetalKernelPipeline;
|
||||
|
||||
/* Keep track of the originating device's ID so that we can cancel requests if the device ceases
|
||||
* to be active. */
|
||||
pipeline->originating_device_id = device->device_id;
|
||||
memcpy(&pipeline->kernel_data_, &device->launch_params.data, sizeof(pipeline->kernel_data_));
|
||||
pipeline->pso_type = pso_type;
|
||||
pipeline->mtlDevice = mtlDevice;
|
||||
pipeline->source_md5 = device->source_md5[pso_type];
|
||||
pipeline->mtlLibrary = device->mtlLibrary[pso_type];
|
||||
pipeline->device_kernel = device_kernel;
|
||||
pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
|
||||
|
||||
if (occupancy_tuning[device_kernel].threads_per_threadgroup) {
|
||||
request.pipeline->threads_per_threadgroup =
|
||||
occupancy_tuning[device_kernel].threads_per_threadgroup;
|
||||
request.pipeline->num_threads_per_block =
|
||||
occupancy_tuning[device_kernel].num_threads_per_block;
|
||||
pipeline->threads_per_threadgroup = occupancy_tuning[device_kernel].threads_per_threadgroup;
|
||||
pipeline->num_threads_per_block = occupancy_tuning[device_kernel].num_threads_per_block;
|
||||
}
|
||||
|
||||
/* metalrt options */
|
||||
request.pipeline->use_metalrt = device->use_metalrt;
|
||||
request.pipeline->metalrt_features = device->use_metalrt ?
|
||||
(device->kernel_features & METALRT_FEATURE_MASK) :
|
||||
0;
|
||||
pipeline->use_metalrt = device->use_metalrt;
|
||||
pipeline->metalrt_features = device->use_metalrt ?
|
||||
(device->kernel_features & METALRT_FEATURE_MASK) :
|
||||
0;
|
||||
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
auto &collection = pipelines[device_kernel];
|
||||
|
||||
/* 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);
|
||||
request_queue.push_back(pipeline);
|
||||
}
|
||||
cond_var.notify_one();
|
||||
}
|
||||
|
||||
MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const MetalDevice *device)
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
auto &collection = pipelines[kernel];
|
||||
if (collection.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* metalrt options */
|
||||
bool use_metalrt = device->use_metalrt;
|
||||
bool device_metalrt_hair = use_metalrt && device->kernel_features & KERNEL_FEATURE_HAIR;
|
||||
|
@ -322,34 +353,43 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M
|
|||
device->kernel_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
|
||||
MetalKernelPipeline *best_pipeline = nullptr;
|
||||
for (auto &pipeline : collection) {
|
||||
if (!pipeline->loaded) {
|
||||
/* still loading - ignore */
|
||||
continue;
|
||||
}
|
||||
while (!best_pipeline) {
|
||||
{
|
||||
thread_scoped_lock lock(cache_mutex);
|
||||
for (auto &pipeline : pipelines[kernel]) {
|
||||
if (!pipeline->loaded) {
|
||||
/* still loading - ignore */
|
||||
continue;
|
||||
}
|
||||
|
||||
bool pipeline_metalrt_hair = pipeline->metalrt_features & KERNEL_FEATURE_HAIR;
|
||||
bool pipeline_metalrt_hair_thick = pipeline->metalrt_features & KERNEL_FEATURE_HAIR_THICK;
|
||||
bool pipeline_metalrt_pointcloud = pipeline->metalrt_features & KERNEL_FEATURE_POINTCLOUD;
|
||||
bool pipeline_metalrt_motion = use_metalrt &&
|
||||
pipeline->metalrt_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
bool pipeline_metalrt_hair = pipeline->metalrt_features & KERNEL_FEATURE_HAIR;
|
||||
bool pipeline_metalrt_hair_thick = pipeline->metalrt_features & KERNEL_FEATURE_HAIR_THICK;
|
||||
bool pipeline_metalrt_pointcloud = pipeline->metalrt_features & KERNEL_FEATURE_POINTCLOUD;
|
||||
bool pipeline_metalrt_motion = use_metalrt &&
|
||||
pipeline->metalrt_features & KERNEL_FEATURE_OBJECT_MOTION;
|
||||
|
||||
if (pipeline->use_metalrt != use_metalrt || pipeline_metalrt_hair != device_metalrt_hair ||
|
||||
pipeline_metalrt_hair_thick != device_metalrt_hair_thick ||
|
||||
pipeline_metalrt_pointcloud != device_metalrt_pointcloud ||
|
||||
pipeline_metalrt_motion != device_metalrt_motion) {
|
||||
/* wrong combination of metalrt options */
|
||||
continue;
|
||||
}
|
||||
if (pipeline->use_metalrt != use_metalrt || pipeline_metalrt_hair != device_metalrt_hair ||
|
||||
pipeline_metalrt_hair_thick != device_metalrt_hair_thick ||
|
||||
pipeline_metalrt_pointcloud != device_metalrt_pointcloud ||
|
||||
pipeline_metalrt_motion != device_metalrt_motion) {
|
||||
/* wrong combination of metalrt options */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pipeline->pso_type != PSO_GENERIC) {
|
||||
if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] ||
|
||||
pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) {
|
||||
best_pipeline = pipeline.get();
|
||||
if (pipeline->pso_type != PSO_GENERIC) {
|
||||
if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] ||
|
||||
pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) {
|
||||
best_pipeline = pipeline.get();
|
||||
}
|
||||
}
|
||||
else if (!best_pipeline) {
|
||||
best_pipeline = pipeline.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!best_pipeline) {
|
||||
best_pipeline = pipeline.get();
|
||||
|
||||
if (!best_pipeline) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -664,51 +704,60 @@ void MetalKernelPipeline::compile()
|
|||
|
||||
double starttime = time_dt();
|
||||
|
||||
MTLNewComputePipelineStateWithReflectionCompletionHandler completionHandler = ^(
|
||||
id<MTLComputePipelineState> computePipelineState,
|
||||
MTLComputePipelineReflection *reflection,
|
||||
NSError *error) {
|
||||
bool recreate_archive = false;
|
||||
if (computePipelineState == nil && archive) {
|
||||
/* 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 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;
|
||||
metal_printf("Failed to add PSO to archive:\n%s\n", errStr ? [errStr UTF8String] : "nil");
|
||||
}
|
||||
}
|
||||
|
||||
double duration = time_dt() - starttime;
|
||||
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
|
||||
options:pipelineOptions
|
||||
reflection:nullptr
|
||||
error:&error];
|
||||
|
||||
if (computePipelineState == 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;
|
||||
}
|
||||
bool recreate_archive = false;
|
||||
if (pipeline == nil && archive) {
|
||||
NSString *errStr = [error localizedDescription];
|
||||
metal_printf(
|
||||
"Failed to create compute pipeline state \"%s\" from archive - attempting recreation... "
|
||||
"(error: %s)\n",
|
||||
device_kernel_as_string((DeviceKernel)device_kernel),
|
||||
errStr ? [errStr UTF8String] : "nil");
|
||||
pipeline = [mtlDevice newComputePipelineStateWithDescriptor:computePipelineStateDescriptor
|
||||
options:MTLPipelineOptionNone
|
||||
reflection:nullptr
|
||||
error:&error];
|
||||
recreate_archive = true;
|
||||
}
|
||||
|
||||
if (!num_threads_per_block) {
|
||||
num_threads_per_block = round_down(computePipelineState.maxTotalThreadsPerThreadgroup,
|
||||
computePipelineState.threadExecutionWidth);
|
||||
num_threads_per_block = std::max(num_threads_per_block,
|
||||
(int)computePipelineState.threadExecutionWidth);
|
||||
}
|
||||
double duration = time_dt() - starttime;
|
||||
|
||||
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 (![archive serializeToURL:[NSURL fileURLWithPath:@(metalbin_path.c_str())]
|
||||
error:&error]) {
|
||||
|
@ -720,24 +769,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;
|
||||
[computePipelineStateDescriptor release];
|
||||
|
@ -763,8 +795,6 @@ void MetalKernelPipeline::compile()
|
|||
}
|
||||
}
|
||||
|
||||
double duration = time_dt() - starttime;
|
||||
|
||||
if (!use_binary_archive) {
|
||||
metal_printf("%16s | %2d | %-55s | %7.2fs\n",
|
||||
kernel_type_as_string(pso_type),
|
||||
|
@ -785,36 +815,69 @@ void MetalKernelPipeline::compile()
|
|||
|
||||
bool MetalDeviceKernels::load(MetalDevice *device, MetalPipelineType pso_type)
|
||||
{
|
||||
const double starttime = time_dt();
|
||||
auto shader_cache = get_shader_cache(device->mtlDevice);
|
||||
for (int i = 0; i < DEVICE_KERNEL_NUM; i++) {
|
||||
shader_cache->load_kernel((DeviceKernel)i, device, pso_type);
|
||||
}
|
||||
|
||||
shader_cache->wait_for_all();
|
||||
metal_printf("Back-end compilation finished in %.1f seconds (%s)\n",
|
||||
time_dt() - starttime,
|
||||
kernel_type_as_string(pso_type));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDeviceKernels::should_load_kernels(MetalDevice *device, MetalPipelineType pso_type)
|
||||
void MetalDeviceKernels::wait_for_all()
|
||||
{
|
||||
auto shader_cache = get_shader_cache(device->mtlDevice);
|
||||
for (int i = 0; i < DEVICE_KERNEL_NUM; i++) {
|
||||
if (shader_cache->should_load_kernel((DeviceKernel)i, device, pso_type)) {
|
||||
for (int i = 0; i < g_shaderCacheCount; i++) {
|
||||
g_shaderCache[i].second->wait_for_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalDeviceKernels::any_specialization_happening_now()
|
||||
{
|
||||
/* Return true if any ShaderCaches have ongoing specialization requests (typically there will be
|
||||
* only 1). */
|
||||
for (int i = 0; i < g_shaderCacheCount; i++) {
|
||||
if (g_shaderCache[i].second->incomplete_specialization_requests > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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,
|
||||
DeviceKernel kernel)
|
||||
{
|
||||
return get_shader_cache(device->mtlDevice)->get_best_pipeline(kernel, device);
|
||||
}
|
||||
|
||||
bool MetalDeviceKernels::is_benchmark_warmup()
|
||||
{
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
for (int i = 0; i < args.count; i++) {
|
||||
if (const char *arg = [[args objectAtIndex:i] cStringUsingEncoding:NSASCIIStringEncoding]) {
|
||||
if (!strcmp(arg, "--warm-up")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* WITH_METAL*/
|
||||
|
|
|
@ -202,6 +202,9 @@ MetalDeviceQueue::~MetalDeviceQueue()
|
|||
assert(mtlCommandBuffer_ == nil);
|
||||
assert(command_buffers_submitted_ == command_buffers_completed_);
|
||||
|
||||
close_compute_encoder();
|
||||
close_blit_encoder();
|
||||
|
||||
if (@available(macos 10.14, *)) {
|
||||
[shared_event_listener_ release];
|
||||
[shared_event_ release];
|
||||
|
@ -637,9 +640,7 @@ bool MetalDeviceQueue::synchronize()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mtlComputeEncoder_) {
|
||||
close_compute_encoder();
|
||||
}
|
||||
close_compute_encoder();
|
||||
close_blit_encoder();
|
||||
|
||||
if (mtlCommandBuffer_) {
|
||||
|
@ -702,6 +703,10 @@ bool MetalDeviceQueue::synchronize()
|
|||
|
||||
void MetalDeviceQueue::zero_to_device(device_memory &mem)
|
||||
{
|
||||
if (metal_device_->have_error()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE);
|
||||
|
||||
if (mem.memory_size() == 0) {
|
||||
|
@ -729,6 +734,10 @@ void MetalDeviceQueue::zero_to_device(device_memory &mem)
|
|||
|
||||
void MetalDeviceQueue::copy_to_device(device_memory &mem)
|
||||
{
|
||||
if (metal_device_->have_error()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem.memory_size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -771,6 +780,10 @@ void MetalDeviceQueue::copy_to_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);
|
||||
|
||||
if (mem.memory_size() == 0) {
|
||||
|
@ -843,9 +856,7 @@ id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel
|
|||
if (@available(macos 10.14, *)) {
|
||||
if (timing_shared_event_) {
|
||||
/* Close the current encoder to ensure we're able to capture per-encoder timing data. */
|
||||
if (mtlComputeEncoder_) {
|
||||
close_compute_encoder();
|
||||
}
|
||||
close_compute_encoder();
|
||||
}
|
||||
|
||||
if (mtlComputeEncoder_) {
|
||||
|
@ -885,9 +896,7 @@ id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder()
|
|||
return mtlBlitEncoder_;
|
||||
}
|
||||
|
||||
if (mtlComputeEncoder_) {
|
||||
close_compute_encoder();
|
||||
}
|
||||
close_compute_encoder();
|
||||
|
||||
if (!mtlCommandBuffer_) {
|
||||
mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer];
|
||||
|
@ -901,12 +910,14 @@ id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder()
|
|||
|
||||
void MetalDeviceQueue::close_compute_encoder()
|
||||
{
|
||||
[mtlComputeEncoder_ endEncoding];
|
||||
mtlComputeEncoder_ = nil;
|
||||
if (mtlComputeEncoder_) {
|
||||
[mtlComputeEncoder_ endEncoding];
|
||||
mtlComputeEncoder_ = nil;
|
||||
|
||||
if (@available(macos 10.14, *)) {
|
||||
if (timing_shared_event_) {
|
||||
[mtlCommandBuffer_ encodeSignalEvent:timing_shared_event_ value:timing_shared_event_id_++];
|
||||
if (@available(macos 10.14, *)) {
|
||||
if (timing_shared_event_) {
|
||||
[mtlCommandBuffer_ encodeSignalEvent:timing_shared_event_ value:timing_shared_event_id_++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
sycl::usm::alloc usm_type = get_pointer_type(usm_ptr, queue->get_context());
|
||||
(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 &&
|
||||
(allow_host || device_type == sycl::info::device_type::cpu)) ||
|
||||
usm_type == sycl::usm::alloc::unknown);
|
||||
|
@ -478,7 +483,11 @@ void *OneapiDevice::usm_alloc_device(SyclQueue *queue_, size_t memory_size)
|
|||
{
|
||||
assert(queue_);
|
||||
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
|
||||
# ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||
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)
|
||||
|
@ -736,7 +745,11 @@ char *OneapiDevice::device_capabilities()
|
|||
|
||||
const std::vector<sycl::device> &oneapi_devices = available_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>();
|
||||
# else
|
||||
const std::string &name = "SYCL Host Task (Debug)";
|
||||
# endif
|
||||
|
||||
capabilities << std::string("\t") << name << "\n";
|
||||
# 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) {
|
||||
const std::string &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>();
|
||||
# else
|
||||
std::string name = "SYCL Host Task (Debug)";
|
||||
# endif
|
||||
std::string id = "ONEAPI_" + platform_name + "_" + name;
|
||||
if (device.has(sycl::aspect::ext_intel_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;
|
||||
|
||||
PathTraceWork *path_trace_work = path_trace_works_[i].get();
|
||||
if (path_trace_work->get_device()->have_error()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PathTraceWork::RenderStatistics statistics;
|
||||
path_trace_work->render_samples(statistics,
|
||||
|
|
|
@ -753,6 +753,10 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
|||
${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
|
||||
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'")
|
||||
|
@ -764,7 +768,8 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
|||
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "--format zebin ")
|
||||
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.
|
||||
list(APPEND sycl_compiler_flags -DSYCL_SKIP_KERNELS_PRELOAD)
|
||||
# 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,
|
||||
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 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_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, \
|
||||
size_t kernel_global_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_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
|
||||
|
||||
|
@ -83,23 +112,40 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
|||
} ccl_gpu_kernel_lambda_pass((ONEAPIKernelContext *)kg)
|
||||
|
||||
/* 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))
|
||||
#define ccl_gpu_global_size_x() (sycl::ext::oneapi::experimental::this_nd_item<1>().get_global_range(0))
|
||||
#ifndef WITH_ONEAPI_SYCL_HOST_TASK
|
||||
# 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 */
|
||||
#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)
|
||||
#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_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)
|
||||
# ifdef __SYCL_DEVICE_ONLY__
|
||||
# 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
|
||||
#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
|
||||
|
||||
/* Debug defines */
|
||||
|
|
|
@ -23,6 +23,15 @@ typedef struct KernelGlobalsGPU {
|
|||
#undef KERNEL_DATA_ARRAY
|
||||
IntegratorStateGPU *integrator_state;
|
||||
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;
|
||||
|
||||
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,
|
||||
* we extend work size to fit uniformity requirements. */
|
||||
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. */
|
||||
|
|
|
@ -364,7 +364,9 @@ ccl_device_inline void film_write_emission_or_background_pass(
|
|||
}
|
||||
# endif /* __DENOISING_FEATURES__ */
|
||||
|
||||
if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
const bool is_shadowcatcher = (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) != 0;
|
||||
if (!is_shadowcatcher && lightgroup != LIGHTGROUP_NONE &&
|
||||
kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
film_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup,
|
||||
contribution);
|
||||
}
|
||||
|
@ -373,13 +375,12 @@ ccl_device_inline void film_write_emission_or_background_pass(
|
|||
/* Directly visible, write to emission or background pass. */
|
||||
pass_offset = pass;
|
||||
}
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
else if (is_shadowcatcher) {
|
||||
/* Don't write any light passes for shadow catcher, for easier
|
||||
* compositing back together of the combined pass. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const Spectrum diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
|
|
|
@ -180,8 +180,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
|
|||
|
||||
/* Write to render buffer. */
|
||||
guiding_record_background(kg, state, light_eval, mis_weight);
|
||||
film_write_surface_emission(
|
||||
kg, state, light_eval, mis_weight, render_buffer, kernel_data.background.lightgroup);
|
||||
film_write_surface_emission(kg, state, light_eval, mis_weight, render_buffer, ls.group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
DeviceString filename,
|
||||
ccl_private void *texture_handle,
|
||||
OSLTextureOptions *opt,
|
||||
ccl_private OSLTextureOptions *opt,
|
||||
float s,
|
||||
float t,
|
||||
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);
|
||||
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 0)
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 1)
|
||||
result[1] = rgba.y;
|
||||
if (nchannels > 2)
|
||||
result[2] = rgba.z;
|
||||
if (nchannels > 3)
|
||||
result[3] = rgba.w;
|
||||
if (alpha)
|
||||
*alpha = rgba.w;
|
||||
|
||||
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,
|
||||
DeviceString filename,
|
||||
ccl_private void *texture_handle,
|
||||
OSLTextureOptions *opt,
|
||||
ccl_private OSLTextureOptions *opt,
|
||||
ccl_private const float3 *P,
|
||||
ccl_private const float3 *dPdx,
|
||||
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);
|
||||
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 0)
|
||||
result[0] = rgba.x;
|
||||
if (nchannels > 1)
|
||||
result[1] = rgba.y;
|
||||
if (nchannels > 2)
|
||||
result[2] = rgba.z;
|
||||
if (nchannels > 3)
|
||||
result[3] = rgba.w;
|
||||
if (alpha)
|
||||
*alpha = rgba.w;
|
||||
|
||||
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,
|
||||
DeviceString filename,
|
||||
ccl_private void *texture_handle,
|
||||
OSLTextureOptions *opt,
|
||||
ccl_private OSLTextureOptions *opt,
|
||||
ccl_private const float3 *R,
|
||||
ccl_private const float3 *dRdx,
|
||||
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 void *errormessage)
|
||||
{
|
||||
result[0] = 1.0f;
|
||||
if (nchannels > 0)
|
||||
result[0] = 1.0f;
|
||||
if (nchannels > 1)
|
||||
result[1] = 0.0f;
|
||||
if (nchannels > 2)
|
||||
result[2] = 1.0f;
|
||||
if (nchannels > 3)
|
||||
result[3] = 1.0f;
|
||||
if (alpha)
|
||||
*alpha = 1.0f;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -113,14 +113,18 @@ static void oiio_load_pixels(const ImageMetaData &metadata,
|
|||
|
||||
if (depth <= 1) {
|
||||
size_t scanlinesize = width * components * sizeof(StorageType);
|
||||
in->read_image(FileFormat,
|
||||
in->read_image(0,
|
||||
0,
|
||||
0,
|
||||
components,
|
||||
FileFormat,
|
||||
(uchar *)readpixels + (height - 1) * scanlinesize,
|
||||
AutoStride,
|
||||
-scanlinesize,
|
||||
AutoStride);
|
||||
}
|
||||
else {
|
||||
in->read_image(FileFormat, (uchar *)readpixels);
|
||||
in->read_image(0, 0, 0, components, FileFormat, (uchar *)readpixels);
|
||||
}
|
||||
|
||||
if (components > 4) {
|
||||
|
|
|
@ -263,8 +263,9 @@ static float3 output_estimate_emission(ShaderOutput *output, bool &is_constant)
|
|||
|
||||
return estimate;
|
||||
}
|
||||
else if (node->type == LightFalloffNode::get_node_type()) {
|
||||
/* Light Falloff node. */
|
||||
else if (node->type == LightFalloffNode::get_node_type() ||
|
||||
node->type == IESLightNode::get_node_type()) {
|
||||
/* Get strength from Light Falloff and IES texture node. */
|
||||
ShaderInput *strength_in = node->input("Strength");
|
||||
is_constant = false;
|
||||
|
||||
|
|
|
@ -439,9 +439,12 @@ bool DenoiseImage::read_previous_pixels(const DenoiseImageLayer &layer,
|
|||
{
|
||||
/* Load pixels from neighboring frames, and copy them into device buffer
|
||||
* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* 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;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -401,8 +401,8 @@ static bool merge_pixels(const vector<MergeImage> &images,
|
|||
* faster than individually due to interleaved EXR channel storage. */
|
||||
array<float> pixels;
|
||||
alloc_pixels(image.in->spec(), pixels);
|
||||
|
||||
if (!image.in->read_image(TypeDesc::FLOAT, pixels.data())) {
|
||||
const int num_channels = image.in->spec().nchannels;
|
||||
if (!image.in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, pixels.data())) {
|
||||
error = "Failed to read image: " + image.filepath;
|
||||
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. */
|
||||
array<float> sample_count_buffer;
|
||||
sample_count_buffer.resize(in_spec.width * in_spec.height);
|
||||
|
||||
image.in->read_image(0,
|
||||
0,
|
||||
layer.sample_pass_offset,
|
||||
|
|
|
@ -113,6 +113,9 @@ void Session::start()
|
|||
|
||||
void Session::cancel(bool quick)
|
||||
{
|
||||
/* Cancel any long running device operations (e.g. shader compilations). */
|
||||
device->cancel();
|
||||
|
||||
/* Check if session thread is 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_->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);
|
||||
}
|
||||
|
||||
|
|
|
@ -646,7 +646,8 @@ bool TileManager::read_full_buffer_from_disk(const string_view filename,
|
|||
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();
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1223,13 +1223,12 @@ static void gwl_registry_entry_update_all(GWL_Display *display, const int interf
|
|||
continue;
|
||||
}
|
||||
|
||||
GWL_RegisteryUpdate_Params params = {
|
||||
.name = reg->name,
|
||||
.interface_slot = reg->interface_slot,
|
||||
.version = reg->version,
|
||||
GWL_RegisteryUpdate_Params params{};
|
||||
params.name = reg->name;
|
||||
params.interface_slot = reg->interface_slot;
|
||||
params.version = reg->version;
|
||||
params.user_data = reg->user_data;
|
||||
|
||||
.user_data = reg->user_data,
|
||||
};
|
||||
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");
|
||||
GWL_Output *output = static_cast<GWL_Output *>(data);
|
||||
output->scale = factor;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
output->system->output_scale_update_maybe_leave(output, false);
|
||||
}
|
||||
|
||||
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,
|
||||
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
|
||||
* 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);
|
||||
|
||||
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);
|
||||
std::vector<GWL_Output *>::iterator iter = std::find(
|
||||
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;
|
||||
|
||||
/* The interface name that is ensured not to be freed. */
|
||||
GWL_RegisteryAdd_Params params = {
|
||||
.name = name,
|
||||
.interface_slot = interface_slot,
|
||||
.version = version,
|
||||
};
|
||||
GWL_RegisteryAdd_Params params{};
|
||||
params.name = name;
|
||||
params.interface_slot = interface_slot;
|
||||
params.version = version;
|
||||
|
||||
handler->add_fn(display, ¶ms);
|
||||
|
||||
|
@ -6762,6 +6762,49 @@ void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
|
|||
#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,
|
||||
const GHOST_TGrabCursorMode mode_current,
|
||||
int32_t init_grab_xy[2],
|
||||
|
|
|
@ -194,6 +194,8 @@ class GHOST_SystemWayland : public GHOST_System {
|
|||
/** Set this seat to be active. */
|
||||
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. */
|
||||
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.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Return true when the windows scale or DPI changes.
|
||||
*/
|
||||
bool GHOST_WindowWayland::outputs_changed_update_scale()
|
||||
{
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
|
|
|
@ -156,6 +156,9 @@ class GHOST_WindowWayland : public GHOST_Window {
|
|||
bool outputs_enter(GWL_Output *output);
|
||||
bool outputs_leave(GWL_Output *output);
|
||||
|
||||
/**
|
||||
* Return true when the windows scale or DPI changes.
|
||||
*/
|
||||
bool outputs_changed_update_scale();
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
|
|
|
@ -20,6 +20,7 @@ set(SRC
|
|||
./intern/mallocn.c
|
||||
./intern/mallocn_guarded_impl.c
|
||||
./intern/mallocn_lockfree_impl.c
|
||||
./intern/memory_usage.cc
|
||||
|
||||
MEM_guardedalloc.h
|
||||
./intern/mallocn_inline.h
|
||||
|
|
|
@ -53,6 +53,9 @@ class MemLeakPrinter {
|
|||
|
||||
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
|
||||
* possible when the program starts.
|
||||
|
|
|
@ -89,6 +89,14 @@ void aligned_free(void *ptr);
|
|||
extern bool leak_detector_has_run;
|
||||
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 */
|
||||
size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
|
||||
void MEM_lockfree_freeN(void *vmemh);
|
||||
|
|
|
@ -30,8 +30,6 @@ typedef struct MemHeadAligned {
|
|||
size_t len;
|
||||
} MemHeadAligned;
|
||||
|
||||
static unsigned int totblock = 0;
|
||||
static size_t mem_in_use = 0, peak_mem = 0;
|
||||
static bool malloc_debug_memset = false;
|
||||
|
||||
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_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__
|
||||
__attribute__((format(printf, 1, 2)))
|
||||
#endif
|
||||
|
@ -103,8 +89,7 @@ void MEM_lockfree_freeN(void *vmemh)
|
|||
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
|
||||
size_t len = MEMHEAD_LEN(memh);
|
||||
|
||||
atomic_sub_and_fetch_u(&totblock, 1);
|
||||
atomic_sub_and_fetch_z(&mem_in_use, len);
|
||||
memory_usage_block_free(len);
|
||||
|
||||
if (UNLIKELY(malloc_debug_memset && len)) {
|
||||
memset(memh + 1, 255, len);
|
||||
|
@ -224,16 +209,14 @@ void *MEM_lockfree_callocN(size_t len, const char *str)
|
|||
|
||||
if (LIKELY(memh)) {
|
||||
memh->len = len;
|
||||
atomic_add_and_fetch_u(&totblock, 1);
|
||||
atomic_add_and_fetch_z(&mem_in_use, len);
|
||||
update_maximum(&peak_mem, mem_in_use);
|
||||
memory_usage_block_alloc(len);
|
||||
|
||||
return PTR_FROM_MEMHEAD(memh);
|
||||
}
|
||||
print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
||||
SIZET_ARG(len),
|
||||
str,
|
||||
(uint)mem_in_use);
|
||||
(uint)memory_usage_current());
|
||||
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(size),
|
||||
str,
|
||||
(unsigned int)mem_in_use);
|
||||
(unsigned int)memory_usage_current());
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
@ -269,16 +252,14 @@ void *MEM_lockfree_mallocN(size_t len, const char *str)
|
|||
}
|
||||
|
||||
memh->len = len;
|
||||
atomic_add_and_fetch_u(&totblock, 1);
|
||||
atomic_add_and_fetch_z(&mem_in_use, len);
|
||||
update_maximum(&peak_mem, mem_in_use);
|
||||
memory_usage_block_alloc(len);
|
||||
|
||||
return PTR_FROM_MEMHEAD(memh);
|
||||
}
|
||||
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
||||
SIZET_ARG(len),
|
||||
str,
|
||||
(uint)mem_in_use);
|
||||
(uint)memory_usage_current());
|
||||
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(size),
|
||||
str,
|
||||
(uint)mem_in_use);
|
||||
(uint)memory_usage_current());
|
||||
abort();
|
||||
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->alignment = (short)alignment;
|
||||
atomic_add_and_fetch_u(&totblock, 1);
|
||||
atomic_add_and_fetch_z(&mem_in_use, len);
|
||||
update_maximum(&peak_mem, mem_in_use);
|
||||
memory_usage_block_alloc(len);
|
||||
|
||||
return PTR_FROM_MEMHEAD(memh);
|
||||
}
|
||||
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
|
||||
SIZET_ARG(len),
|
||||
str,
|
||||
(uint)mem_in_use);
|
||||
(uint)memory_usage_current());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -369,8 +348,8 @@ void MEM_lockfree_callbackmemlist(void (*func)(void *))
|
|||
|
||||
void MEM_lockfree_printmemlist_stats(void)
|
||||
{
|
||||
printf("\ntotal memory len: %.3f MB\n", (double)mem_in_use / (double)(1024 * 1024));
|
||||
printf("peak memory len: %.3f MB\n", (double)peak_mem / (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)memory_usage_peak() / (double)(1024 * 1024));
|
||||
printf(
|
||||
"\nFor more detailed per-block statistics run Blender with memory debugging command line "
|
||||
"argument.\n");
|
||||
|
@ -398,23 +377,23 @@ void MEM_lockfree_set_memory_debug(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)
|
||||
{
|
||||
return totblock;
|
||||
return (uint)memory_usage_block_num();
|
||||
}
|
||||
|
||||
/* dummy */
|
||||
void MEM_lockfree_reset_peak_memory(void)
|
||||
{
|
||||
peak_mem = mem_in_use;
|
||||
memory_usage_peak_reset();
|
||||
}
|
||||
|
||||
size_t MEM_lockfree_get_peak_memory(void)
|
||||
{
|
||||
return peak_mem;
|
||||
return memory_usage_peak();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include "mallocn_intern.h"
|
||||
|
||||
#include "../../source/blender/blenlib/BLI_strict_flags.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct Local;
|
||||
struct Global;
|
||||
|
||||
/**
|
||||
* This is stored per thread. Align to cache line size to avoid false sharing.
|
||||
*/
|
||||
struct alignas(64) Local {
|
||||
/**
|
||||
* Retain shared ownership of #Global to make sure that it is not destructed.
|
||||
*/
|
||||
std::shared_ptr<Global> global;
|
||||
|
||||
/** 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. It's owned by a `std::shared_ptr` which is owned by
|
||||
* the static variable in #get_global_ptr and all #Local objects.
|
||||
*/
|
||||
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 thread-locals).
|
||||
*/
|
||||
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 std::shared_ptr<Global> &get_global_ptr()
|
||||
{
|
||||
static std::shared_ptr<Global> global = std::make_shared<Global>();
|
||||
return global;
|
||||
}
|
||||
|
||||
static Global &get_global()
|
||||
{
|
||||
return *get_global_ptr();
|
||||
}
|
||||
|
||||
static Local &get_local_data()
|
||||
{
|
||||
static thread_local Local local;
|
||||
assert(!local.destructed);
|
||||
return local;
|
||||
}
|
||||
|
||||
Local::Local()
|
||||
{
|
||||
this->global = get_global_ptr();
|
||||
|
||||
std::lock_guard lock{this->global->locals_mutex};
|
||||
if (this->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. */
|
||||
this->global->locals.push_back(this);
|
||||
}
|
||||
|
||||
Local::~Local()
|
||||
{
|
||||
std::lock_guard lock{this->global->locals_mutex};
|
||||
|
||||
/* Unregister self from the global list. */
|
||||
this->global->locals.erase(
|
||||
std::find(this->global->locals.begin(), this->global->locals.end(), this));
|
||||
/* Don't forget the memory counts stored locally. */
|
||||
this->global->blocks_num_outside_locals.fetch_add(this->blocks_num, std::memory_order_relaxed);
|
||||
this->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
|
||||
* thread-locals 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 thread-local 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 <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "boost_locale_wrapper.h"
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 042c799b7aef9634a33d24e78d4922706aca9a2b
|
||||
Subproject commit a9d4443c244f89399ec4bcc427e05a07950528cc
|
|
@ -5050,30 +5050,33 @@ def km_sculpt(params):
|
|||
# Expand
|
||||
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True},
|
||||
{"properties": [
|
||||
("target", "MASK"),
|
||||
("falloff_type", "GEODESIC"),
|
||||
("invert", True),
|
||||
("use_auto_mask", True),
|
||||
("use_mask_preserve" , True)]}),
|
||||
("target", "MASK"),
|
||||
("falloff_type", "GEODESIC"),
|
||||
("invert", True),
|
||||
("use_auto_mask", True),
|
||||
("use_mask_preserve", True),
|
||||
]}),
|
||||
("sculpt.expand", {"type": 'A', "value": 'PRESS', "shift": True, "alt": True},
|
||||
{"properties": [
|
||||
("target", "MASK"),
|
||||
("falloff_type", "NORMALS"),
|
||||
("invert", False),
|
||||
("use_mask_preserve" , True)]}),
|
||||
("target", "MASK"),
|
||||
("falloff_type", "NORMALS"),
|
||||
("invert", False),
|
||||
("use_mask_preserve", True),
|
||||
]}),
|
||||
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True},
|
||||
{"properties": [
|
||||
("target", "FACE_SETS"),
|
||||
("falloff_type", "GEODESIC"),
|
||||
("invert", False),
|
||||
("use_mask_preserve" , False),
|
||||
("use_modify_active", False)]}),
|
||||
("use_mask_preserve", False),
|
||||
("use_modify_active", False),
|
||||
]}),
|
||||
("sculpt.expand", {"type": 'W', "value": 'PRESS', "shift": True, "alt": True},
|
||||
{"properties": [
|
||||
("target", "FACE_SETS"),
|
||||
("falloff_type", "BOUNDARY_FACE_SET"),
|
||||
("invert", False),
|
||||
("use_mask_preserve" , False),
|
||||
("use_mask_preserve", False),
|
||||
("use_modify_active", True),
|
||||
]}),
|
||||
# Partial Visibility Show/hide
|
||||
|
|
|
@ -81,8 +81,8 @@ class ASSET_OT_open_containing_blend_file(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
asset_file_handle = getattr(context, 'asset_file_handle', None)
|
||||
asset_library_ref = getattr(context, 'asset_library_ref', None)
|
||||
asset_file_handle = getattr(context, "asset_file_handle", None)
|
||||
asset_library_ref = getattr(context, "asset_library_ref", None)
|
||||
|
||||
if not asset_library_ref:
|
||||
cls.poll_message_set("No asset library selected")
|
||||
|
|
|
@ -540,7 +540,7 @@ class CLIP_OT_setup_tracking_scene(Operator):
|
|||
sc = context.space_data
|
||||
if sc and sc.type == 'CLIP_EDITOR':
|
||||
clip = sc.clip
|
||||
if clip and clip.tracking.reconstruction.is_valid:
|
||||
if clip and clip.tracking.objects.active.reconstruction.is_valid:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -620,7 +620,7 @@ class CLIP_OT_setup_tracking_scene(Operator):
|
|||
if not view_layers.get("Foreground"):
|
||||
if len(view_layers) == 1:
|
||||
fg = view_layers[0]
|
||||
fg.name = 'Foreground'
|
||||
fg.name = "Foreground"
|
||||
else:
|
||||
fg = view_layers.new("Foreground")
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ class SCENE_OT_freestyle_add_edge_marks_to_keying_set(Operator):
|
|||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
for i, edge in enumerate(mesh.edges):
|
||||
if not edge.hide and edge.select:
|
||||
path = 'edges[%d].use_freestyle_mark' % i
|
||||
path = "edges[%d].use_freestyle_mark" % i
|
||||
ks.paths.add(mesh, path, index=0)
|
||||
bpy.ops.object.mode_set(mode=ob_mode, toggle=False)
|
||||
return {'FINISHED'}
|
||||
|
@ -173,7 +173,7 @@ class SCENE_OT_freestyle_add_face_marks_to_keying_set(Operator):
|
|||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
||||
for i, polygon in enumerate(mesh.polygons):
|
||||
if not polygon.hide and polygon.select:
|
||||
path = 'polygons[%d].use_freestyle_mark' % i
|
||||
path = "polygons[%d].use_freestyle_mark" % i
|
||||
ks.paths.add(mesh, path, index=0)
|
||||
bpy.ops.object.mode_set(mode=ob_mode, toggle=False)
|
||||
return {'FINISHED'}
|
||||
|
|
|
@ -223,7 +223,7 @@ class SequencerFadesAdd(Operator):
|
|||
if not self.is_long_enough(sequence, duration):
|
||||
continue
|
||||
|
||||
animated_property = 'volume' if hasattr(sequence, 'volume') else 'blend_alpha'
|
||||
animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
|
||||
fade_fcurve = self.fade_find_or_create_fcurve(context, sequence, animated_property)
|
||||
fades = self.calculate_fades(sequence, fade_fcurve, animated_property, duration)
|
||||
self.fade_animation_clear(fade_fcurve, fades)
|
||||
|
|
|
@ -2507,7 +2507,7 @@ class WM_OT_batch_rename(Operator):
|
|||
('COLLECTION', "Collections", ""),
|
||||
('MATERIAL', "Materials", ""),
|
||||
None,
|
||||
# Enum identifiers are compared with 'object.type'.
|
||||
# Enum identifiers are compared with `object.type`.
|
||||
# Follow order in "Add" menu.
|
||||
('MESH', "Meshes", ""),
|
||||
('CURVE', "Curves", ""),
|
||||
|
|
|
@ -57,19 +57,19 @@ class MotionPathButtonsPanel:
|
|||
# Update Selected.
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.operator(f"{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_update", text="Update Path", icon=icon)
|
||||
row.operator(op_category + ".paths_clear", text="", icon='X').only_selected = True
|
||||
else:
|
||||
# Calculate.
|
||||
col = layout.column(align=True)
|
||||
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.
|
||||
# Note that 'col' is from inside the preceeding `if` or `else` block.
|
||||
# Note that `col` is from inside the preceding `if` or `else` block.
|
||||
row = col.row(align=True)
|
||||
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:
|
||||
|
|
|
@ -87,7 +87,7 @@ class ConstraintButtonsPanel:
|
|||
@staticmethod
|
||||
def target_template(layout, con, subtargets=True):
|
||||
col = layout.column()
|
||||
col.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object
|
||||
col.prop(con, "target") # XXX: limiting settings for only `curves` or some type of object.
|
||||
|
||||
if con.target and subtargets:
|
||||
if con.target.type == 'ARMATURE':
|
||||
|
|
|
@ -87,7 +87,7 @@ class BONE_PT_transform(BoneButtonsPanel, Panel):
|
|||
row.use_property_decorate = False
|
||||
row.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
||||
row = layout.row(align=True)
|
||||
row.prop(pchan, "rotation_mode", text='Mode')
|
||||
row.prop(pchan, "rotation_mode", text="Mode")
|
||||
row.label(text="", icon='BLANK1')
|
||||
|
||||
col = layout.column()
|
||||
|
@ -403,9 +403,9 @@ class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
|
|||
col.prop(pchan, "ik_rotation_weight", text="IK Rotation Weight", slider=True)
|
||||
col.active = active
|
||||
# not supported yet
|
||||
#row = layout.row()
|
||||
#row.prop(pchan, "use_ik_linear_control", text="Joint Size")
|
||||
#row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
|
||||
# row = layout.row()
|
||||
# row.prop(pchan, "use_ik_linear_control", text="Joint Size")
|
||||
# row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
|
||||
|
||||
|
||||
class BONE_PT_deform(BoneButtonsPanel, Panel):
|
||||
|
|
|
@ -12,7 +12,7 @@ class DataButtonsPanel:
|
|||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return hasattr(context, 'curves') and context.curves and (engine in cls.COMPAT_ENGINES)
|
||||
return hasattr(context, "curves") and context.curves and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_curves(DataButtonsPanel, Panel):
|
||||
|
@ -73,8 +73,8 @@ class CURVES_MT_add_attribute(Menu):
|
|||
layout = self.layout
|
||||
curves = context.curves
|
||||
|
||||
self.add_standard_attribute(layout, curves, 'radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, curves, 'color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, curves, "radius", 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, curves, "color", 'FLOAT_COLOR', 'POINT')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -102,8 +102,8 @@ class CURVES_UL_attributes(UIList):
|
|||
return flags, indices
|
||||
|
||||
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
domain = attribute.bl_rna.properties['domain'].enum_items[attribute.domain]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
domain = attribute.bl_rna.properties["domain"].enum_items[attribute.domain]
|
||||
|
||||
split = layout.split(factor=0.5)
|
||||
split.emboss = 'NONE'
|
||||
|
|
|
@ -551,7 +551,7 @@ class MESH_UL_attributes(UIList):
|
|||
return flags, indices
|
||||
|
||||
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
|
||||
domain_name = self.display_domain_names.get(attribute.domain, "")
|
||||
|
||||
|
@ -658,7 +658,7 @@ class ColorAttributesListBase():
|
|||
|
||||
class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
|
||||
def draw_item(self, _context, layout, data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
|
||||
domain_name = self.display_domain_names.get(attribute.domain, "")
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ class DataButtonsPanel:
|
|||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return hasattr(context, 'pointcloud') and context.pointcloud and (engine in cls.COMPAT_ENGINES)
|
||||
return hasattr(context, "pointcloud") and context.pointcloud and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_pointcloud(DataButtonsPanel, Panel):
|
||||
|
@ -53,10 +53,10 @@ class POINTCLOUD_MT_add_attribute(Menu):
|
|||
layout = self.layout
|
||||
pointcloud = context.pointcloud
|
||||
|
||||
self.add_standard_attribute(layout, pointcloud, 'radius', 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'color', 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'id', 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, 'velocity', 'FLOAT_VECTOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "radius", 'FLOAT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "color", 'FLOAT_COLOR', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "id", 'INT', 'POINT')
|
||||
self.add_standard_attribute(layout, pointcloud, "velocity", 'FLOAT_VECTOR', 'POINT')
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -84,7 +84,7 @@ class POINTCLOUD_UL_attributes(UIList):
|
|||
return flags, indices
|
||||
|
||||
def draw_item(self, _context, layout, _data, attribute, _icon, _active_data, _active_propname, _index):
|
||||
data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type]
|
||||
data_type = attribute.bl_rna.properties["data_type"].enum_items[attribute.data_type]
|
||||
|
||||
split = layout.split(factor=0.75)
|
||||
split.emboss = 'NONE'
|
||||
|
|
|
@ -68,7 +68,7 @@ class DATA_PT_volume_file(DataButtonsPanel, Panel):
|
|||
class VOLUME_UL_grids(UIList):
|
||||
def draw_item(self, _context, layout, _data, grid, _icon, _active_data, _active_propname, _index):
|
||||
name = grid.name
|
||||
data_type = grid.bl_rna.properties['data_type'].enum_items[grid.data_type]
|
||||
data_type = grid.bl_rna.properties["data_type"].enum_items[grid.data_type]
|
||||
|
||||
layout.emboss = 'NONE'
|
||||
layout.label(text=name)
|
||||
|
|
|
@ -69,11 +69,8 @@ class GreasePencilDisplayPanel:
|
|||
ob = context.active_object
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if ob and ob.type == 'GPENCIL' and brush:
|
||||
if context.mode == 'PAINT_GPENCIL':
|
||||
return brush.gpencil_tool != 'ERASE'
|
||||
else:
|
||||
# GP Sculpt, Vertex and Weight Paint always have Brush Tip panel.
|
||||
return True
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def draw_header(self, context):
|
||||
|
@ -255,7 +252,7 @@ class GPENCIL_MT_layer_active(Menu):
|
|||
|
||||
gpd = context.gpencil_data
|
||||
if gpd:
|
||||
layout.operator("gpencil.layer_add", text="New Layer", icon='ADD')
|
||||
layout.operator("gpencil.layer_add", text="New Layer", icon='ADD').layer = -1
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -329,7 +326,7 @@ class GPENCIL_MT_cleanup(Menu):
|
|||
|
||||
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")
|
||||
if ob.mode != 'PAINT_GPENCIL':
|
||||
layout.operator("gpencil.reproject")
|
||||
|
|
|
@ -324,7 +324,7 @@ class RENDER_PT_output_color_management(RenderOutputButtonsPanel, Panel):
|
|||
col = flow.column()
|
||||
|
||||
if image_settings.has_linear_colorspace:
|
||||
if hasattr(owner, 'linear_colorspace_settings'):
|
||||
if hasattr(owner, "linear_colorspace_settings"):
|
||||
col.prop(owner.linear_colorspace_settings, "name", text="Color Space")
|
||||
else:
|
||||
col.prop(owner.display_settings, "display_device")
|
||||
|
|
|
@ -1287,9 +1287,6 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
|
|||
row = layout.row(align=True)
|
||||
row.prop(gp_settings, "eraser_thickness_factor")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(settings, "show_brush", text="Display Cursor")
|
||||
|
||||
# FIXME: tools must use their own UI drawing!
|
||||
elif brush.gpencil_tool == 'FILL':
|
||||
use_property_split_prev = layout.use_property_split
|
||||
|
|
|
@ -31,10 +31,10 @@ class CONSOLE_MT_view(Menu):
|
|||
layout = self.layout
|
||||
|
||||
props = layout.operator("wm.context_cycle_int", text="Zoom In")
|
||||
props.data_path = 'space_data.font_size'
|
||||
props.data_path = "space_data.font_size"
|
||||
props.reverse = False
|
||||
props = layout.operator("wm.context_cycle_int", text="Zoom Out")
|
||||
props.data_path = 'space_data.font_size'
|
||||
props.data_path = "space_data.font_size"
|
||||
props.reverse = True
|
||||
|
||||
layout.separator()
|
||||
|
@ -62,7 +62,7 @@ class CONSOLE_MT_language(Menu):
|
|||
layout = self.layout
|
||||
layout.column()
|
||||
|
||||
# Collect modules with 'console_*.execute'
|
||||
# Collect modules with `console_*.execute`.
|
||||
languages = []
|
||||
for modname, mod in sys.modules.items():
|
||||
if modname.startswith("console_") and hasattr(mod, "execute"):
|
||||
|
|
|
@ -238,8 +238,7 @@ class DOPESHEET_HT_editor_buttons:
|
|||
# Layer management
|
||||
if st.mode == 'GPENCIL':
|
||||
ob = context.active_object
|
||||
selected = st.dopesheet.show_only_selected
|
||||
enable_but = selected and ob is not None and ob.type == 'GPENCIL'
|
||||
enable_but = ob is not None and ob.type == 'GPENCIL'
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.enabled = enable_but
|
||||
|
@ -551,7 +550,7 @@ class DOPESHEET_PT_custom_props_action(PropertyPanel, Panel):
|
|||
bl_space_type = 'DOPESHEET_EDITOR'
|
||||
bl_category = "Action"
|
||||
bl_region_type = 'UI'
|
||||
bl_context = 'data'
|
||||
bl_context = "data"
|
||||
_context_path = "active_action"
|
||||
_property_type = bpy.types.Action
|
||||
|
||||
|
|
|
@ -332,6 +332,7 @@ class GRAPH_MT_slider(Menu):
|
|||
layout.operator("graph.breakdown", text="Breakdown")
|
||||
layout.operator("graph.blend_to_neighbor", text="Blend to Neighbor")
|
||||
layout.operator("graph.blend_to_default", text="Blend to Default Value")
|
||||
layout.operator("graph.ease", text="Ease")
|
||||
|
||||
|
||||
class GRAPH_MT_view_pie(Menu):
|
||||
|
|
|
@ -742,7 +742,7 @@ class IMAGE_HT_header(Header):
|
|||
|
||||
# Snap.
|
||||
snap_uv_element = tool_settings.snap_uv_element
|
||||
act_snap_uv_element = tool_settings.bl_rna.properties['snap_uv_element'].enum_items[snap_uv_element]
|
||||
act_snap_uv_element = tool_settings.bl_rna.properties["snap_uv_element"].enum_items[snap_uv_element]
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(tool_settings, "use_snap_uv", text="")
|
||||
|
|
|
@ -41,7 +41,7 @@ class NODE_HT_header(Header):
|
|||
|
||||
layout.template_header()
|
||||
|
||||
# Now expanded via the 'ui_type'
|
||||
# Now expanded via the `ui_type`.
|
||||
# layout.prop(snode, "tree_type", text="")
|
||||
|
||||
if snode.tree_type == 'ShaderNodeTree':
|
||||
|
@ -318,7 +318,9 @@ class NODE_MT_node(Menu):
|
|||
|
||||
layout.separator()
|
||||
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_linked")
|
||||
layout.operator("node.delete")
|
||||
|
@ -659,7 +661,7 @@ class NODE_PT_active_node_properties(Panel):
|
|||
)
|
||||
|
||||
def show_socket_input(self, socket):
|
||||
return hasattr(socket, 'draw') and socket.enabled and not socket.is_linked
|
||||
return hasattr(socket, "draw") and socket.enabled and not socket.is_linked
|
||||
|
||||
|
||||
class NODE_PT_texture_mapping(Panel):
|
||||
|
@ -959,7 +961,7 @@ def node_panel(cls):
|
|||
node_cls.bl_space_type = 'NODE_EDITOR'
|
||||
node_cls.bl_region_type = 'UI'
|
||||
node_cls.bl_category = "Options"
|
||||
if hasattr(node_cls, 'bl_parent_id'):
|
||||
if hasattr(node_cls, "bl_parent_id"):
|
||||
node_cls.bl_parent_id = 'NODE_' + node_cls.bl_parent_id
|
||||
|
||||
return node_cls
|
||||
|
|
|
@ -51,8 +51,8 @@ ToolDef = namedtuple(
|
|||
"idname",
|
||||
# The name to display in the interface.
|
||||
"label",
|
||||
# Description (for tool-tip), when not set, use the description of 'operator',
|
||||
# may be a string or a 'function(context, item, key-map) -> string'.
|
||||
# Description (for tool-tip), when not set, use the description of `operator`,
|
||||
# may be a string or a `function(context, item, key-map) -> string`.
|
||||
"description",
|
||||
# The name of the icon to use (found in `release/datafiles/icons`) or None for no icon.
|
||||
"icon",
|
||||
|
@ -88,7 +88,7 @@ ToolDef = namedtuple(
|
|||
# Note that this isn't used for Blender's built in tools which use the built-in key-map.
|
||||
# Keep this functionality since it's likely useful for add-on key-maps.
|
||||
#
|
||||
# Warning: currently 'from_dict' this is a list of one item,
|
||||
# Warning: currently `from_dict` this is a list of one item,
|
||||
# so internally we can swap the key-map function for the key-map itself.
|
||||
# This isn't very nice and may change, tool definitions shouldn't care about this.
|
||||
"keymap",
|
||||
|
@ -256,7 +256,7 @@ class ToolSelectPanelHelper:
|
|||
|
||||
# tool flattening
|
||||
#
|
||||
# usually 'tools' is already expanded into ToolDef
|
||||
# usually 'tools' is already expanded into `ToolDef`
|
||||
# but when registering a tool, this can still be a function
|
||||
# (_tools_flatten is usually called with cls.tools_from_context(context)
|
||||
# [that already yields from the function])
|
||||
|
@ -792,7 +792,7 @@ class ToolSelectPanelHelper:
|
|||
item, tool, icon_value = cls._tool_get_active(context, space_type, mode, with_icon=True)
|
||||
if item is None:
|
||||
return None
|
||||
# Note: we could show 'item.text' here but it makes the layout jitter when switching tools.
|
||||
# NOTE: we could show `item.text` here but it makes the layout jitter when switching tools.
|
||||
# Add some spacing since the icon is currently assuming regular small icon size.
|
||||
if show_tool_icon_always:
|
||||
layout.label(text=" " + iface_(item.label, "Operator"), icon_value=icon_value)
|
||||
|
|
|
@ -2706,7 +2706,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
)
|
||||
|
||||
# Private tools dictionary, store data to implement `tools_all` & `tools_from_context`.
|
||||
# The keys match image spaces modes: 'context.space_data.mode'.
|
||||
# The keys match image spaces modes: `context.space_data.mode`.
|
||||
# The values represent the tools, see `ToolSelectPanelHelper` for details.
|
||||
_tools = {
|
||||
None: [
|
||||
|
@ -2892,7 +2892,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
)
|
||||
|
||||
# Private tools dictionary, store data to implement `tools_all` & `tools_from_context`.
|
||||
# The keys match object-modes from: 'context.mode'.
|
||||
# The keys match object-modes from: `context.mode`.
|
||||
# The values represent the tools, see `ToolSelectPanelHelper` for details.
|
||||
_tools = {
|
||||
None: [
|
||||
|
@ -3230,7 +3230,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
)
|
||||
|
||||
# Private tools dictionary, store data to implement `tools_all` & `tools_from_context`.
|
||||
# The keys match sequence editors view type: 'context.space_data.view_type'.
|
||||
# The keys match sequence editors view type: `context.space_data.view_type`.
|
||||
# The values represent the tools, see `ToolSelectPanelHelper` for details.
|
||||
_tools = {
|
||||
None: [
|
||||
|
|
|
@ -1286,7 +1286,7 @@ class ThemeGenericClassGenerator:
|
|||
def generate_panel_classes_from_theme_areas():
|
||||
from bpy.types import Theme
|
||||
|
||||
for theme_area in Theme.bl_rna.properties['theme_area'].enum_items_static:
|
||||
for theme_area in Theme.bl_rna.properties["theme_area"].enum_items_static:
|
||||
if theme_area.identifier in {'USER_INTERFACE', 'STYLE', 'BONE_COLOR_SETS'}:
|
||||
continue
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class VIEW3D_HT_tool_header(Header):
|
|||
layout.popover("VIEW3D_PT_tools_brush_falloff")
|
||||
layout.popover("VIEW3D_PT_tools_brush_display")
|
||||
|
||||
# Note: general mode options should be added to 'draw_mode_settings'.
|
||||
# NOTE: general mode options should be added to `draw_mode_settings`.
|
||||
if tool_mode == 'SCULPT':
|
||||
if is_valid_context:
|
||||
draw_3d_brush_settings(layout, tool_mode)
|
||||
|
@ -99,12 +99,13 @@ class VIEW3D_HT_tool_header(Header):
|
|||
elif tool_mode == 'PAINT_GPENCIL':
|
||||
if is_valid_context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush and brush.gpencil_tool != 'ERASE':
|
||||
if brush.gpencil_tool != 'TINT':
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_brush_advanced")
|
||||
if brush:
|
||||
if brush.gpencil_tool != 'ERASE':
|
||||
if brush.gpencil_tool != 'TINT':
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_brush_advanced")
|
||||
|
||||
if brush.gpencil_tool not in {'FILL', 'TINT'}:
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_brush_stroke")
|
||||
if brush.gpencil_tool not in {'FILL', 'TINT'}:
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_brush_stroke")
|
||||
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_paint_appearance")
|
||||
elif tool_mode == 'SCULPT_GPENCIL':
|
||||
|
@ -722,8 +723,18 @@ class VIEW3D_HT_header(Header):
|
|||
|
||||
row = layout.row(align=True)
|
||||
domain = curves.selection_domain
|
||||
row.operator("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'
|
||||
row.operator(
|
||||
"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
|
||||
if obj and obj.type == 'GPENCIL' and context.gpencil_data:
|
||||
|
@ -878,7 +889,7 @@ class VIEW3D_HT_header(Header):
|
|||
row = layout.row()
|
||||
row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'})
|
||||
|
||||
# While exposing 'shading.show_xray(_wireframe)' is correct.
|
||||
# While exposing `shading.show_xray(_wireframe)` is correct.
|
||||
# this hides the key shortcut from users: T70433.
|
||||
if has_pose_mode:
|
||||
draw_depressed = overlay.show_xray_bone
|
||||
|
@ -2279,9 +2290,9 @@ class VIEW3D_MT_add(Menu):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# note, don't use 'EXEC_SCREEN' or operators won't get the 'v3d' context.
|
||||
# NOTE: don't use 'EXEC_SCREEN' or operators won't get the `v3d` context.
|
||||
|
||||
# Note: was EXEC_AREA, but this context does not have the 'rv3d', which prevents
|
||||
# NOTE: was `EXEC_AREA`, but this context does not have the `rv3d`, which prevents
|
||||
# "align_view" to work on first call (see T32719).
|
||||
layout.operator_context = 'EXEC_REGION_WIN'
|
||||
|
||||
|
@ -3280,23 +3291,23 @@ class VIEW3D_MT_mask(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Smooth Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Smooth Mask")
|
||||
props.filter_type = 'SMOOTH'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Sharpen Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Sharpen Mask")
|
||||
props.filter_type = 'SHARPEN'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Grow Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Grow Mask")
|
||||
props.filter_type = 'GROW'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Shrink Mask')
|
||||
props = layout.operator("sculpt.mask_filter", text="Shrink Mask")
|
||||
props.filter_type = 'SHRINK'
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Increase Contrast')
|
||||
props = layout.operator("sculpt.mask_filter", text="Increase Contrast")
|
||||
props.filter_type = 'CONTRAST_INCREASE'
|
||||
props.auto_iteration_count = False
|
||||
|
||||
props = layout.operator("sculpt.mask_filter", text='Decrease Contrast')
|
||||
props = layout.operator("sculpt.mask_filter", text="Decrease Contrast")
|
||||
props.filter_type = 'CONTRAST_DECREASE'
|
||||
props.auto_iteration_count = False
|
||||
|
||||
|
@ -3341,13 +3352,13 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
op = layout.operator("sculpt.face_sets_create", text='Face Set from Masked')
|
||||
op = layout.operator("sculpt.face_sets_create", text="Face Set from Masked")
|
||||
op.mode = 'MASKED'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_create", text='Face Set from Visible')
|
||||
op = layout.operator("sculpt.face_sets_create", text="Face Set from Visible")
|
||||
op.mode = 'VISIBLE'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_create", text='Face Set from Edit Mode Selection')
|
||||
op = layout.operator("sculpt.face_sets_create", text="Face Set from Edit Mode Selection")
|
||||
op.mode = 'SELECTION'
|
||||
|
||||
layout.separator()
|
||||
|
@ -3356,10 +3367,10 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("sculpt.face_set_edit", text='Grow Face Set')
|
||||
op = layout.operator("sculpt.face_set_edit", text="Grow Face Set")
|
||||
op.mode = 'GROW'
|
||||
|
||||
op = layout.operator("sculpt.face_set_edit", text='Shrink Face Set')
|
||||
op = layout.operator("sculpt.face_set_edit", text="Shrink Face Set")
|
||||
op.mode = 'SHRINK'
|
||||
|
||||
layout.separator()
|
||||
|
@ -3378,18 +3389,18 @@ class VIEW3D_MT_face_sets(Menu):
|
|||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("mesh.face_set_extract", text='Extract Face Set')
|
||||
op = layout.operator("mesh.face_set_extract", text="Extract Face Set")
|
||||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("sculpt.face_set_change_visibility", text='Invert Visible Face Sets')
|
||||
op = layout.operator("sculpt.face_set_change_visibility", text="Invert Visible Face Sets")
|
||||
op.mode = 'INVERT'
|
||||
|
||||
op = layout.operator("sculpt.reveal_all", text='Show All Face Sets')
|
||||
op = layout.operator("sculpt.reveal_all", text="Show All Face Sets")
|
||||
|
||||
layout.separator()
|
||||
|
||||
op = layout.operator("sculpt.face_sets_randomize_colors", text='Randomize Colors')
|
||||
op = layout.operator("sculpt.face_sets_randomize_colors", text="Randomize Colors")
|
||||
|
||||
|
||||
class VIEW3D_MT_sculpt_set_pivot(Menu):
|
||||
|
@ -3420,31 +3431,31 @@ class VIEW3D_MT_face_sets_init(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Loose Parts')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Loose Parts")
|
||||
op.mode = 'LOOSE_PARTS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Face Set Boundaries')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Face Set Boundaries")
|
||||
op.mode = 'FACE_SET_BOUNDARIES'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Materials')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Materials")
|
||||
op.mode = 'MATERIALS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Normals')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Normals")
|
||||
op.mode = 'NORMALS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By UV Seams')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By UV Seams")
|
||||
op.mode = 'UV_SEAMS'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Edge Creases')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Edge Creases")
|
||||
op.mode = 'CREASES'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Edge Bevel Weight')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Edge Bevel Weight")
|
||||
op.mode = 'BEVEL_WEIGHT'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Sharp Edges')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Sharp Edges")
|
||||
op.mode = 'SHARP_EDGES'
|
||||
|
||||
op = layout.operator("sculpt.face_sets_init", text='By Face Maps')
|
||||
op = layout.operator("sculpt.face_sets_init", text="By Face Maps")
|
||||
op.mode = 'FACE_MAPS'
|
||||
|
||||
|
||||
|
@ -3454,13 +3465,13 @@ class VIEW3D_MT_random_mask(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
op = layout.operator("sculpt.mask_init", text='Per Vertex')
|
||||
op = layout.operator("sculpt.mask_init", text="Per Vertex")
|
||||
op.mode = 'RANDOM_PER_VERTEX'
|
||||
|
||||
op = layout.operator("sculpt.mask_init", text='Per Face Set')
|
||||
op = layout.operator("sculpt.mask_init", text="Per Face Set")
|
||||
op.mode = 'RANDOM_PER_FACE_SET'
|
||||
|
||||
op = layout.operator("sculpt.mask_init", text='Per Loose Part')
|
||||
op = layout.operator("sculpt.mask_init", text="Per Loose Part")
|
||||
op.mode = 'RANDOM_PER_LOOSE_PART'
|
||||
|
||||
|
||||
|
@ -5489,23 +5500,23 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu):
|
|||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
op = pie.operator("paint.mask_flood_fill", text='Invert Mask')
|
||||
op = pie.operator("paint.mask_flood_fill", text="Invert Mask")
|
||||
op.mode = 'INVERT'
|
||||
op = pie.operator("paint.mask_flood_fill", text='Clear Mask')
|
||||
op = pie.operator("paint.mask_flood_fill", text="Clear Mask")
|
||||
op.mode = 'VALUE'
|
||||
op.value = 0.0
|
||||
op = pie.operator("sculpt.mask_filter", text='Smooth Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Smooth Mask")
|
||||
op.filter_type = 'SMOOTH'
|
||||
op = pie.operator("sculpt.mask_filter", text='Sharpen Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Sharpen Mask")
|
||||
op.filter_type = 'SHARPEN'
|
||||
op = pie.operator("sculpt.mask_filter", text='Grow Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Grow Mask")
|
||||
op.filter_type = 'GROW'
|
||||
op = pie.operator("sculpt.mask_filter", text='Shrink Mask')
|
||||
op = pie.operator("sculpt.mask_filter", text="Shrink Mask")
|
||||
op.filter_type = 'SHRINK'
|
||||
op = pie.operator("sculpt.mask_filter", text='Increase Contrast')
|
||||
op = pie.operator("sculpt.mask_filter", text="Increase Contrast")
|
||||
op.filter_type = 'CONTRAST_INCREASE'
|
||||
op.auto_iteration_count = False
|
||||
op = pie.operator("sculpt.mask_filter", text='Decrease Contrast')
|
||||
op = pie.operator("sculpt.mask_filter", text="Decrease Contrast")
|
||||
op.filter_type = 'CONTRAST_DECREASE'
|
||||
op.auto_iteration_count = False
|
||||
|
||||
|
@ -5554,16 +5565,16 @@ class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu):
|
|||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
op = pie.operator("sculpt.face_sets_create", text='Face Set from Masked')
|
||||
op = pie.operator("sculpt.face_sets_create", text="Face Set from Masked")
|
||||
op.mode = 'MASKED'
|
||||
|
||||
op = pie.operator("sculpt.face_sets_create", text='Face Set from Visible')
|
||||
op = pie.operator("sculpt.face_sets_create", text="Face Set from Visible")
|
||||
op.mode = 'VISIBLE'
|
||||
|
||||
op = pie.operator("sculpt.face_set_change_visibility", text='Invert Visible')
|
||||
op = pie.operator("sculpt.face_set_change_visibility", text="Invert Visible")
|
||||
op.mode = 'INVERT'
|
||||
|
||||
op = pie.operator("sculpt.reveal_all", text='Show All')
|
||||
op = pie.operator("sculpt.reveal_all", text="Show All")
|
||||
|
||||
|
||||
class VIEW3D_MT_wpaint_vgroup_lock_pie(Menu):
|
||||
|
@ -6963,9 +6974,9 @@ class VIEW3D_PT_snapping(Panel):
|
|||
col.prop(tool_settings, "use_snap_project")
|
||||
|
||||
if 'FACE_NEAREST' in snap_elements:
|
||||
col.prop(tool_settings, 'use_snap_to_same_target')
|
||||
col.prop(tool_settings, "use_snap_to_same_target")
|
||||
if object_mode == 'EDIT':
|
||||
col.prop(tool_settings, 'snap_face_nearest_steps')
|
||||
col.prop(tool_settings, "snap_face_nearest_steps")
|
||||
|
||||
if 'VOLUME' in snap_elements:
|
||||
col.prop(tool_settings, "use_snap_peel_object")
|
||||
|
|
|
@ -1084,7 +1084,7 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel):
|
|||
wpaint = tool_settings.weight_paint
|
||||
mesh = context.object.data
|
||||
|
||||
layout.prop(mesh, 'use_mirror_vertex_groups')
|
||||
layout.prop(mesh, "use_mirror_vertex_groups")
|
||||
|
||||
draw_vpaint_symmetry(layout, wpaint, context.object)
|
||||
|
||||
|
|
|
@ -597,7 +597,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
|
|||
path = keyingsets_utils.path_add_property(base_path, "delta_rotation_quaternion")
|
||||
elif data.rotation_mode == 'AXIS_ANGLE':
|
||||
# XXX: for now, this is not available yet
|
||||
#path = path_add_property(base_path, "delta_rotation_axis_angle")
|
||||
# path = path_add_property(base_path, "delta_rotation_axis_angle")
|
||||
return
|
||||
else:
|
||||
path = keyingsets_utils.path_add_property(base_path, "delta_rotation_euler")
|
||||
|
@ -637,7 +637,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
|
|||
###############################
|
||||
|
||||
|
||||
# Note that this controls order of options in 'insert keyframe' menu.
|
||||
# Note that this controls order of options in `insert keyframe` menu.
|
||||
# Better try to keep some logical order here beyond mere alphabetical one, also because of menu entries shortcut.
|
||||
# See also T51867.
|
||||
classes = (
|
||||
|
|
|
@ -73,7 +73,7 @@ static eThumbStatus extract_png_from_blend_file(const char *src_blend, const cha
|
|||
|
||||
std::optional<blender::Vector<uint8_t>> png_buf_opt = blendthumb_create_png_data_from_thumb(
|
||||
&thumb);
|
||||
if (png_buf_opt == std::nullopt) {
|
||||
if (!png_buf_opt) {
|
||||
err = BT_ERROR;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* An #AnonymousAttributeID is used to identify attributes that are not explicitly named.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct AnonymousAttributeID AnonymousAttributeID;
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name);
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name);
|
||||
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id);
|
||||
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id);
|
||||
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id);
|
||||
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,155 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Wrapper for #AnonymousAttributeID with RAII semantics.
|
||||
* This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or
|
||||
* #WeakAnonymousAttributeID.
|
||||
*/
|
||||
template<bool IsStrongReference> class OwnedAnonymousAttributeID {
|
||||
private:
|
||||
const AnonymousAttributeID *data_ = nullptr;
|
||||
|
||||
template<bool OtherIsStrongReference> friend class OwnedAnonymousAttributeID;
|
||||
|
||||
public:
|
||||
OwnedAnonymousAttributeID() = default;
|
||||
|
||||
/** Create a new anonymous attribute id. */
|
||||
explicit OwnedAnonymousAttributeID(StringRefNull debug_name)
|
||||
{
|
||||
if constexpr (IsStrongReference) {
|
||||
data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str());
|
||||
}
|
||||
else {
|
||||
data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This transfers ownership, so no incref is necessary.
|
||||
* The caller has to make sure that it owned the anonymous id.
|
||||
*/
|
||||
explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id)
|
||||
: data_(anonymous_id)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
this->incref();
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
|
||||
{
|
||||
data_ = other.data_;
|
||||
this->incref();
|
||||
other.decref();
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
~OwnedAnonymousAttributeID()
|
||||
{
|
||||
this->decref();
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OwnedAnonymousAttributeID();
|
||||
new (this) OwnedAnonymousAttributeID(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool OtherIsStrong>
|
||||
OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->~OwnedAnonymousAttributeID();
|
||||
new (this) OwnedAnonymousAttributeID(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return data_ != nullptr;
|
||||
}
|
||||
|
||||
StringRefNull debug_name() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return BKE_anonymous_attribute_id_debug_name(data_);
|
||||
}
|
||||
|
||||
bool has_strong_references() const
|
||||
{
|
||||
BLI_assert(data_ != nullptr);
|
||||
return BKE_anonymous_attribute_id_has_strong_references(data_);
|
||||
}
|
||||
|
||||
/** Extract the ownership of the currently wrapped anonymous id. */
|
||||
const AnonymousAttributeID *extract()
|
||||
{
|
||||
const AnonymousAttributeID *extracted_data = data_;
|
||||
/* Don't decref because the caller becomes the new owner. */
|
||||
data_ = nullptr;
|
||||
return extracted_data;
|
||||
}
|
||||
|
||||
/** Get the wrapped anonymous id, without taking ownership. */
|
||||
const AnonymousAttributeID *get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
void incref()
|
||||
{
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if constexpr (IsStrongReference) {
|
||||
BKE_anonymous_attribute_id_increment_strong(data_);
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_increment_weak(data_);
|
||||
}
|
||||
}
|
||||
|
||||
void decref()
|
||||
{
|
||||
if (data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if constexpr (IsStrongReference) {
|
||||
BKE_anonymous_attribute_id_decrement_strong(data_);
|
||||
}
|
||||
else {
|
||||
BKE_anonymous_attribute_id_decrement_weak(data_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>;
|
||||
using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>;
|
||||
|
||||
} // namespace blender::bke
|
|
@ -0,0 +1,103 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_user_counter.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* An #AnonymousAttributeID contains information about a specific anonymous attribute.
|
||||
* Like normal attributes, anonymous attributes are also identified by their name, so one should
|
||||
* not have to compare #AnonymousAttributeID pointers.
|
||||
*
|
||||
* Anonymous attributes don't need additional information besides their name, with a few
|
||||
* exceptions:
|
||||
* - The name of anonymous attributes is generated automatically, so it is generally not human
|
||||
* readable (just random characters). #AnonymousAttributeID can provide more context as where a
|
||||
* specific anonymous attribute was created which can simplify debugging.
|
||||
* - [Not yet supported.] When anonymous attributes are contained in on-disk caches, we have to map
|
||||
* those back to anonymous attributes at run-time. The issue is that (for various reasons) we
|
||||
* might change how anonymous attribute names are generated in the future, which would lead to a
|
||||
* mis-match between stored and new attribute names. To work around it, we should cache
|
||||
* additional information for anonymous attributes on disk (like which node created it). This
|
||||
* information can then be used to map stored attributes to their run-time counterpart.
|
||||
*
|
||||
* Once created, #AnonymousAttributeID is immutable. Also it is intrinsically reference counted so
|
||||
* that it can have shared ownership. `std::shared_ptr` can't be used for that purpose here,
|
||||
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
|
||||
* should be used to avoid manual reference counting in C++ code.
|
||||
*/
|
||||
class AnonymousAttributeID {
|
||||
private:
|
||||
mutable std::atomic<int> users_ = 1;
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
|
||||
public:
|
||||
virtual ~AnonymousAttributeID() = default;
|
||||
|
||||
StringRefNull name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
void user_add() const
|
||||
{
|
||||
users_.fetch_add(1);
|
||||
}
|
||||
|
||||
void user_remove() const
|
||||
{
|
||||
const int new_users = users_.fetch_sub(1) - 1;
|
||||
if (new_users == 0) {
|
||||
MEM_delete(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
|
||||
using AutoAnonymousAttributeID = UserCounter<const AnonymousAttributeID>;
|
||||
|
||||
/**
|
||||
* A set of anonymous attribute names that is passed around in geometry nodes.
|
||||
*/
|
||||
class AnonymousAttributeSet {
|
||||
public:
|
||||
/**
|
||||
* This uses `std::shared_ptr` because attributes sets are passed around by value during geometry
|
||||
* nodes evaluation, and this makes it very small if there is no name. Also it makes copying very
|
||||
* cheap.
|
||||
*/
|
||||
std::shared_ptr<Set<std::string>> names;
|
||||
};
|
||||
|
||||
/**
|
||||
* Can be passed to algorithms which propagate attributes. It can tell the algorithm which
|
||||
* anonymous attributes should be propagated and can be skipped.
|
||||
*/
|
||||
class AnonymousAttributePropagationInfo {
|
||||
public:
|
||||
/**
|
||||
* This uses `std::shared_ptr` because it's usually initialized from an #AnonymousAttributeSet
|
||||
* and then the set doesn't have to be copied.
|
||||
*/
|
||||
std::shared_ptr<Set<std::string>> names;
|
||||
|
||||
/**
|
||||
* Propagate all anonymous attributes even if the set above is empty.
|
||||
*/
|
||||
bool propagate_all = true;
|
||||
|
||||
/**
|
||||
* Return true when the anonymous attribute should be propagated and false otherwise.
|
||||
*/
|
||||
bool propagate(const AnonymousAttributeID &anonymous_id) const;
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
|
@ -11,7 +11,7 @@
|
|||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_attribute.h"
|
||||
|
||||
struct Mesh;
|
||||
|
@ -24,7 +24,7 @@ class GField;
|
|||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Identifies an attribute that is either named or anonymous.
|
||||
* Identifies an attribute with optional anonymous attribute information.
|
||||
* It does not own the identifier, so it is just a reference.
|
||||
*/
|
||||
class AttributeIDRef {
|
||||
|
@ -38,15 +38,14 @@ class AttributeIDRef {
|
|||
AttributeIDRef(StringRefNull name);
|
||||
AttributeIDRef(const char *name);
|
||||
AttributeIDRef(const std::string &name);
|
||||
AttributeIDRef(const AnonymousAttributeID &anonymous_id);
|
||||
AttributeIDRef(const AnonymousAttributeID *anonymous_id);
|
||||
|
||||
operator bool() const;
|
||||
uint64_t hash() const;
|
||||
bool is_named() const;
|
||||
bool is_anonymous() const;
|
||||
StringRef name() const;
|
||||
const AnonymousAttributeID &anonymous_id() const;
|
||||
bool should_be_kept() const;
|
||||
|
||||
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
|
||||
friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
|
||||
|
@ -749,6 +748,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
|
|||
const bke::AttributeAccessor src_attributes,
|
||||
bke::MutableAttributeAccessor dst_attributes,
|
||||
eAttrDomainMask domain_mask,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip = {});
|
||||
|
||||
/**
|
||||
|
@ -762,6 +762,7 @@ void copy_attribute_domain(AttributeAccessor src_attributes,
|
|||
MutableAttributeAccessor dst_attributes,
|
||||
IndexMask selection,
|
||||
eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip = {});
|
||||
|
||||
bool allow_procedural_attribute_access(StringRef attribute_name);
|
||||
|
@ -852,29 +853,31 @@ inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name)
|
|||
}
|
||||
|
||||
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
|
||||
: anonymous_id_(anonymous_id)
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID &anonymous_id)
|
||||
: AttributeIDRef(anonymous_id.name())
|
||||
{
|
||||
anonymous_id_ = &anonymous_id;
|
||||
}
|
||||
|
||||
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
|
||||
: AttributeIDRef(anonymous_id ? anonymous_id->name() : "")
|
||||
{
|
||||
anonymous_id_ = anonymous_id;
|
||||
}
|
||||
|
||||
inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
|
||||
{
|
||||
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
|
||||
return a.name_ == b.name_;
|
||||
}
|
||||
|
||||
inline AttributeIDRef::operator bool() const
|
||||
{
|
||||
return this->is_named() || this->is_anonymous();
|
||||
return !name_.is_empty();
|
||||
}
|
||||
|
||||
inline uint64_t AttributeIDRef::hash() const
|
||||
{
|
||||
return get_default_hash_2(name_, anonymous_id_);
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_named() const
|
||||
{
|
||||
return !name_.is_empty();
|
||||
return get_default_hash(name_);
|
||||
}
|
||||
|
||||
inline bool AttributeIDRef::is_anonymous() const
|
||||
|
@ -884,7 +887,6 @@ inline bool AttributeIDRef::is_anonymous() const
|
|||
|
||||
inline StringRef AttributeIDRef::name() const
|
||||
{
|
||||
BLI_assert(this->is_named());
|
||||
return name_;
|
||||
}
|
||||
|
||||
|
@ -894,14 +896,4 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const
|
|||
return *anonymous_id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True if the attribute should not be removed automatically as an optimization during
|
||||
* processing or copying. Anonymous attributes can be removed when they no longer have any
|
||||
* references.
|
||||
*/
|
||||
inline bool AttributeIDRef::should_be_kept() const
|
||||
{
|
||||
return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format 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
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* This file implements some specific compute contexts for concepts in Blender.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_compute_context.hh"
|
||||
|
||||
struct bNode;
|
||||
|
@ -41,7 +43,9 @@ class NodeGroupComputeContext : public ComputeContext {
|
|||
#endif
|
||||
|
||||
public:
|
||||
NodeGroupComputeContext(const ComputeContext *parent, int32_t node_id);
|
||||
NodeGroupComputeContext(const ComputeContext *parent,
|
||||
int32_t node_id,
|
||||
const std::optional<ComputeContextHash> &cached_hash = {});
|
||||
NodeGroupComputeContext(const ComputeContext *parent, const bNode &node);
|
||||
|
||||
int32_t node_id() const
|
||||
|
|
|
@ -11,6 +11,8 @@ struct Mesh;
|
|||
|
||||
namespace blender::bke {
|
||||
|
||||
class AnonymousAttributePropagationInfo;
|
||||
|
||||
/**
|
||||
* Extrude all splines in the profile curve along the path of every spline in the curve input.
|
||||
* Transfer curve attributes to the mesh.
|
||||
|
@ -23,11 +25,13 @@ namespace blender::bke {
|
|||
*/
|
||||
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
||||
const CurvesGeometry &profile,
|
||||
bool fill_caps);
|
||||
bool fill_caps,
|
||||
const AnonymousAttributePropagationInfo &propagation_info);
|
||||
/**
|
||||
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
|
||||
* Transfer curve attributes to the mesh.
|
||||
*/
|
||||
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve);
|
||||
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
|
||||
const AnonymousAttributePropagationInfo &propagation_info);
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -287,11 +287,6 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
Span<float2> surface_uv_coords() const;
|
||||
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
|
||||
* (rather than evaluated points). The existing values of `min` and `max` are taken into account.
|
||||
|
@ -409,8 +404,10 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
|
||||
void calculate_bezier_auto_handles();
|
||||
|
||||
void remove_points(IndexMask points_to_delete);
|
||||
void remove_curves(IndexMask curves_to_delete);
|
||||
void remove_points(IndexMask points_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info = {});
|
||||
void remove_curves(IndexMask curves_to_delete,
|
||||
const AnonymousAttributePropagationInfo &propagation_info = {});
|
||||
|
||||
/**
|
||||
* Change the direction of selected curves (switch the start and end) without changing their
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct AnonymousAttributeID;
|
||||
struct BMesh;
|
||||
struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
|
@ -227,7 +226,7 @@ void *CustomData_add_layer_anonymous(struct CustomData *data,
|
|||
eCDAllocType alloctype,
|
||||
void *layer,
|
||||
int totelem,
|
||||
const struct AnonymousAttributeID *anonymous_id);
|
||||
const AnonymousAttributeIDHandle *anonymous_id);
|
||||
|
||||
/**
|
||||
* Frees the active or first data layer with the give type.
|
||||
|
@ -275,8 +274,6 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data,
|
|||
int type,
|
||||
const char *name,
|
||||
int totelem);
|
||||
void *CustomData_duplicate_referenced_layer_anonymous(
|
||||
CustomData *data, int type, const struct AnonymousAttributeID *anonymous_id, int totelem);
|
||||
bool CustomData_is_referenced_layer(struct CustomData *data, int type);
|
||||
|
||||
/**
|
||||
|
|
|
@ -261,18 +261,14 @@ class NormalFieldInput : public GeometryFieldInput {
|
|||
|
||||
class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
||||
private:
|
||||
/**
|
||||
* A strong reference is required to make sure that the referenced attribute is not removed
|
||||
* automatically.
|
||||
*/
|
||||
StrongAnonymousAttributeID anonymous_id_;
|
||||
AutoAnonymousAttributeID anonymous_id_;
|
||||
std::string producer_name_;
|
||||
|
||||
public:
|
||||
AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id,
|
||||
AnonymousAttributeFieldInput(AutoAnonymousAttributeID anonymous_id,
|
||||
const CPPType &type,
|
||||
std::string producer_name)
|
||||
: GeometryFieldInput(type, anonymous_id.debug_name()),
|
||||
: GeometryFieldInput(type, anonymous_id->name()),
|
||||
anonymous_id_(std::move(anonymous_id)),
|
||||
producer_name_(producer_name)
|
||||
{
|
||||
|
@ -280,7 +276,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name)
|
||||
static fn::Field<T> Create(AutoAnonymousAttributeID anonymous_id, std::string producer_name)
|
||||
{
|
||||
const CPPType &type = CPPType::get<T>();
|
||||
auto field_input = std::make_shared<AnonymousAttributeFieldInput>(
|
||||
|
@ -288,6 +284,11 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
|||
return fn::Field<T>{field_input};
|
||||
}
|
||||
|
||||
const AutoAnonymousAttributeID &anonymous_id() const
|
||||
{
|
||||
return anonymous_id_;
|
||||
}
|
||||
|
||||
GVArray get_varray_for_context(const GeometryFieldContext &context,
|
||||
IndexMask mask) const override;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "BLI_user_counter.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_geometry_set.h"
|
||||
|
||||
|
@ -213,6 +213,7 @@ struct GeometrySet {
|
|||
blender::Span<GeometryComponentType> component_types,
|
||||
GeometryComponentType dst_component_type,
|
||||
bool include_instances,
|
||||
const blender::bke::AnonymousAttributePropagationInfo &propagation_info,
|
||||
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const;
|
||||
|
||||
blender::Vector<GeometryComponentType> gather_component_types(bool include_instances,
|
||||
|
|
|
@ -29,7 +29,6 @@ struct PartialUpdateUser;
|
|||
|
||||
namespace blender::bke::image {
|
||||
|
||||
|
||||
namespace partial_update {
|
||||
|
||||
/* --- image_partial_update.cc --- */
|
||||
|
|
|
@ -155,7 +155,8 @@ class Instances {
|
|||
* Remove the indices that are not contained in the mask input, and remove unused instance
|
||||
* references afterwards.
|
||||
*/
|
||||
void remove(const blender::IndexMask mask);
|
||||
void remove(const blender::IndexMask mask,
|
||||
const blender::bke::AnonymousAttributePropagationInfo &propagation_info);
|
||||
/**
|
||||
* Get an id for every instance. These can be used for e.g. motion blur.
|
||||
*/
|
||||
|
|
|
@ -39,40 +39,42 @@ void BKE_movieclip_clear_proxy_cache(struct MovieClip *clip);
|
|||
*/
|
||||
void BKE_movieclip_convert_multilayer_ibuf(struct ImBuf *ibuf);
|
||||
|
||||
struct ImBuf *BKE_movieclip_get_ibuf(struct MovieClip *clip, struct MovieClipUser *user);
|
||||
struct ImBuf *BKE_movieclip_get_ibuf(struct MovieClip *clip, const struct MovieClipUser *user);
|
||||
struct ImBuf *BKE_movieclip_get_postprocessed_ibuf(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int postprocess_flag);
|
||||
struct ImBuf *BKE_movieclip_get_stable_ibuf(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
float loc[2],
|
||||
float *scale,
|
||||
float *angle,
|
||||
int postprocess_flag);
|
||||
struct ImBuf *BKE_movieclip_get_ibuf_flag(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int flag,
|
||||
int cache_flag);
|
||||
void BKE_movieclip_get_size(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int *width,
|
||||
int *height);
|
||||
void BKE_movieclip_get_size_fl(struct MovieClip *clip, struct MovieClipUser *user, float size[2]);
|
||||
void BKE_movieclip_get_size_fl(struct MovieClip *clip,
|
||||
const struct MovieClipUser *user,
|
||||
float size[2]);
|
||||
int BKE_movieclip_get_duration(struct MovieClip *clip);
|
||||
float BKE_movieclip_get_fps(struct MovieClip *clip);
|
||||
void BKE_movieclip_get_aspect(struct MovieClip *clip, float *aspx, float *aspy);
|
||||
bool BKE_movieclip_has_frame(struct MovieClip *clip, struct MovieClipUser *user);
|
||||
bool BKE_movieclip_has_frame(struct MovieClip *clip, const struct MovieClipUser *user);
|
||||
void BKE_movieclip_user_set_frame(struct MovieClipUser *user, int framenr);
|
||||
|
||||
void BKE_movieclip_update_scopes(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
struct MovieClipScopes *scopes);
|
||||
|
||||
/**
|
||||
* Get segments of cached frames. useful for debugging cache policies.
|
||||
*/
|
||||
void BKE_movieclip_get_cache_segments(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
int *r_totseg,
|
||||
int **r_points);
|
||||
|
||||
|
@ -105,7 +107,7 @@ float BKE_movieclip_remap_scene_to_clip_frame(const struct MovieClip *clip, floa
|
|||
float BKE_movieclip_remap_clip_to_scene_frame(const struct MovieClip *clip, float framenr);
|
||||
|
||||
void BKE_movieclip_filename_for_frame(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
char *name);
|
||||
|
||||
/**
|
||||
|
@ -113,11 +115,11 @@ void BKE_movieclip_filename_for_frame(struct MovieClip *clip,
|
|||
* Used by a prefetch job which takes care of creating a local copy of the clip.
|
||||
*/
|
||||
struct ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(struct MovieClip *clip,
|
||||
struct MovieClipUser *user);
|
||||
const struct MovieClipUser *user);
|
||||
|
||||
bool BKE_movieclip_has_cached_frame(struct MovieClip *clip, struct MovieClipUser *user);
|
||||
bool BKE_movieclip_has_cached_frame(struct MovieClip *clip, const struct MovieClipUser *user);
|
||||
bool BKE_movieclip_put_frame_if_possible(struct MovieClip *clip,
|
||||
struct MovieClipUser *user,
|
||||
const struct MovieClipUser *user,
|
||||
struct ImBuf *ibuf);
|
||||
|
||||
struct GPUTexture *BKE_movieclip_get_gpu_texture(struct MovieClip *clip,
|
||||
|
|
|
@ -299,12 +299,14 @@ typedef struct bNodeType {
|
|||
* when it's not just a dummy, that is, if it actually wants to access
|
||||
* the returned disabled-hint (null-check needed!).
|
||||
*/
|
||||
bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree, const char **r_disabled_hint);
|
||||
bool (*poll)(const struct bNodeType *ntype,
|
||||
const struct bNodeTree *nodetree,
|
||||
const char **r_disabled_hint);
|
||||
/** Can this node be added to a node tree?
|
||||
* \param r_disabled_hint: See `poll()`.
|
||||
*/
|
||||
bool (*poll_instance)(struct bNode *node,
|
||||
struct bNodeTree *nodetree,
|
||||
bool (*poll_instance)(const struct bNode *node,
|
||||
const struct bNodeTree *nodetree,
|
||||
const char **r_disabled_hint);
|
||||
|
||||
/* optional handling of link insertion */
|
||||
|
@ -711,6 +713,13 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
|||
|
||||
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);
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
#endif
|
||||
|
@ -841,7 +850,6 @@ struct bNode *nodeGetActivePaintCanvas(struct bNodeTree *ntree);
|
|||
*/
|
||||
bool nodeSupportsActiveFlag(const struct bNode *node, int sub_active);
|
||||
|
||||
int nodeSocketIsHidden(const struct bNodeSocket *sock);
|
||||
void nodeSetSocketAvailability(struct bNodeTree *ntree,
|
||||
struct bNodeSocket *sock,
|
||||
bool is_available);
|
||||
|
@ -864,20 +872,6 @@ bool nodeDeclarationEnsureOnOutdatedNode(struct bNodeTree *ntree, struct bNode *
|
|||
*/
|
||||
void nodeSocketDeclarationsUpdate(struct bNode *node);
|
||||
|
||||
/**
|
||||
* Node Clipboard.
|
||||
*/
|
||||
void BKE_node_clipboard_clear(void);
|
||||
void BKE_node_clipboard_free(void);
|
||||
/**
|
||||
* Return false when one or more ID's are lost.
|
||||
*/
|
||||
bool BKE_node_clipboard_validate(void);
|
||||
void BKE_node_clipboard_add_node(struct bNode *node);
|
||||
void BKE_node_clipboard_add_link(struct bNodeLink *link);
|
||||
const struct ListBase *BKE_node_clipboard_get_nodes(void);
|
||||
const struct ListBase *BKE_node_clipboard_get_links(void);
|
||||
|
||||
/**
|
||||
* Node Instance Hash.
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "BLI_cache_mutex.hh"
|
||||
#include "BLI_multi_value_map.hh"
|
||||
#include "BLI_resource_scope.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
@ -24,6 +25,10 @@ namespace blender::nodes {
|
|||
struct FieldInferencingInterface;
|
||||
class NodeDeclaration;
|
||||
struct GeometryNodesLazyFunctionGraphInfo;
|
||||
namespace anonymous_attribute_lifetime {
|
||||
struct RelationsInNode;
|
||||
}
|
||||
namespace aal = anonymous_attribute_lifetime;
|
||||
} // namespace blender::nodes
|
||||
|
||||
namespace blender {
|
||||
|
@ -106,6 +111,8 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
|
|||
|
||||
/** Information about how inputs and outputs of the node group interact with fields. */
|
||||
std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface;
|
||||
/** Information about usage of anonymous attributes within the group. */
|
||||
std::unique_ptr<nodes::aal::RelationsInNode> anonymous_attribute_relations;
|
||||
|
||||
/**
|
||||
* For geometry nodes, a lazy function graph with some additional info is cached. This is used to
|
||||
|
@ -169,7 +176,10 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
|
|||
float locx = 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;
|
||||
|
||||
/** Only valid when #topology_cache_is_dirty is false. */
|
||||
|
@ -327,7 +337,11 @@ inline bool topology_cache_is_available(const bNodeSocket &socket)
|
|||
namespace node_field_inferencing {
|
||||
bool update_field_inferencing(const bNodeTree &tree);
|
||||
}
|
||||
|
||||
namespace anonymous_attribute_inferencing {
|
||||
Array<const nodes::aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
|
||||
ResourceScope &scope);
|
||||
bool update_anonymous_attribute_relations(bNodeTree &tree);
|
||||
} // namespace anonymous_attribute_inferencing
|
||||
} // namespace blender::bke
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -652,6 +666,11 @@ inline bool bNodeLink::is_available() const
|
|||
return this->fromsock->is_available() && this->tosock->is_available();
|
||||
}
|
||||
|
||||
inline bool bNodeLink::is_used() const
|
||||
{
|
||||
return !this->is_muted() && this->is_available();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -670,6 +689,20 @@ inline int bNodeSocket::index_in_tree() const
|
|||
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
|
||||
{
|
||||
return (this->flag & SOCK_HIDDEN) != 0;
|
||||
|
@ -680,6 +713,11 @@ inline bool bNodeSocket::is_available() const
|
|||
return (this->flag & SOCK_UNAVAIL) == 0;
|
||||
}
|
||||
|
||||
inline bool bNodeSocket::is_visible() const
|
||||
{
|
||||
return !this->is_hidden() && this->is_available();
|
||||
}
|
||||
|
||||
inline bNode &bNodeSocket::owner_node()
|
||||
{
|
||||
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||
|
|
|
@ -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
|
|
@ -88,9 +88,9 @@ typedef enum eSubdivStatsValue {
|
|||
typedef struct SubdivStats {
|
||||
union {
|
||||
struct {
|
||||
/* Time spend on creating topology refiner, which includes time
|
||||
/* Time spent on creating topology refiner, which includes time
|
||||
* spend on conversion from Blender data to OpenSubdiv data, and
|
||||
* time spend on topology orientation on OpenSubdiv C-API side. */
|
||||
* time spent on topology orientation on OpenSubdiv C-API side. */
|
||||
double topology_refiner_creation_time;
|
||||
/* Total time spent in BKE_subdiv_to_mesh(). */
|
||||
double subdiv_to_mesh_time;
|
||||
|
|
|
@ -64,7 +64,7 @@ set(SRC
|
|||
intern/anim_path.c
|
||||
intern/anim_sys.c
|
||||
intern/anim_visualization.c
|
||||
intern/anonymous_attribute.cc
|
||||
intern/anonymous_attribute_id.cc
|
||||
intern/appdir.c
|
||||
intern/armature.c
|
||||
intern/armature_deform.c
|
||||
|
@ -229,6 +229,7 @@ set(SRC
|
|||
intern/nla.c
|
||||
intern/node.cc
|
||||
intern/node_runtime.cc
|
||||
intern/node_tree_anonymous_attributes.cc
|
||||
intern/node_tree_field_inferencing.cc
|
||||
intern/node_tree_update.cc
|
||||
intern/object.cc
|
||||
|
@ -254,6 +255,7 @@ set(SRC
|
|||
intern/pbvh_uv_islands.cc
|
||||
intern/pointcache.c
|
||||
intern/pointcloud.cc
|
||||
intern/pose_backup.cc
|
||||
intern/preferences.c
|
||||
intern/report.c
|
||||
intern/rigidbody.c
|
||||
|
@ -314,8 +316,7 @@ set(SRC
|
|||
BKE_anim_path.h
|
||||
BKE_anim_visualization.h
|
||||
BKE_animsys.h
|
||||
BKE_anonymous_attribute.h
|
||||
BKE_anonymous_attribute.hh
|
||||
BKE_anonymous_attribute_id.hh
|
||||
BKE_appdir.h
|
||||
BKE_armature.h
|
||||
BKE_armature.hh
|
||||
|
@ -451,6 +452,7 @@ set(SRC
|
|||
BKE_pbvh_pixels.hh
|
||||
BKE_pointcache.h
|
||||
BKE_pointcloud.h
|
||||
BKE_pose_backup.h
|
||||
BKE_preferences.h
|
||||
BKE_report.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. */
|
||||
|
||||
/* 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);
|
||||
|
||||
BLI_addtail(&action_dst->curves, fcurve_dst);
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_anonymous_attribute.hh"
|
||||
|
||||
using namespace blender::bke;
|
||||
|
||||
/**
|
||||
* A struct that identifies an attribute. It's lifetime is managed by an atomic reference count.
|
||||
*
|
||||
* Additionally, this struct can be strongly or weakly owned. The difference is that strong
|
||||
* ownership means that attributes with this id will be kept around. Weak ownership just makes sure
|
||||
* that the struct itself stays alive, but corresponding attributes might still be removed
|
||||
* automatically.
|
||||
*/
|
||||
struct AnonymousAttributeID {
|
||||
/**
|
||||
* Total number of references to this attribute id. Once this reaches zero, the struct can be
|
||||
* freed. This includes strong and weak references.
|
||||
*/
|
||||
mutable std::atomic<int> refcount_tot = 0;
|
||||
|
||||
/**
|
||||
* Number of strong references to this attribute id. When this is zero, the corresponding
|
||||
* attributes can be removed from geometries automatically.
|
||||
*/
|
||||
mutable std::atomic<int> refcount_strong = 0;
|
||||
|
||||
/**
|
||||
* Only used to identify this struct in a debugging session.
|
||||
*/
|
||||
std::string debug_name;
|
||||
|
||||
/**
|
||||
* Unique name of the this attribute id during the current session.
|
||||
*/
|
||||
std::string internal_name;
|
||||
};
|
||||
|
||||
/** Every time this function is called, it outputs a different name. */
|
||||
static std::string get_new_internal_name()
|
||||
{
|
||||
static std::atomic<int> index = 0;
|
||||
const int next_index = index.fetch_add(1);
|
||||
return ".a_" + std::to_string(next_index);
|
||||
}
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name)
|
||||
{
|
||||
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
|
||||
anonymous_id->debug_name = debug_name;
|
||||
anonymous_id->internal_name = get_new_internal_name();
|
||||
anonymous_id->refcount_tot.store(1);
|
||||
return anonymous_id;
|
||||
}
|
||||
|
||||
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name)
|
||||
{
|
||||
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
|
||||
anonymous_id->debug_name = debug_name;
|
||||
anonymous_id->internal_name = get_new_internal_name();
|
||||
anonymous_id->refcount_tot.store(1);
|
||||
anonymous_id->refcount_strong.store(1);
|
||||
return anonymous_id;
|
||||
}
|
||||
|
||||
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->refcount_strong.load() >= 1;
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_tot.fetch_add(1);
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_tot.fetch_add(1);
|
||||
anonymous_id->refcount_strong.fetch_add(1);
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1;
|
||||
if (new_refcount == 0) {
|
||||
BLI_assert(anonymous_id->refcount_strong == 0);
|
||||
delete anonymous_id;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
anonymous_id->refcount_strong.fetch_sub(1);
|
||||
BKE_anonymous_attribute_id_decrement_weak(anonymous_id);
|
||||
}
|
||||
|
||||
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->debug_name.c_str();
|
||||
}
|
||||
|
||||
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id)
|
||||
{
|
||||
return anonymous_id->internal_name.c_str();
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
bool AnonymousAttributePropagationInfo::propagate(const AnonymousAttributeID &anonymous_id) const
|
||||
{
|
||||
if (this->propagate_all) {
|
||||
return true;
|
||||
}
|
||||
if (!this->names) {
|
||||
return false;
|
||||
}
|
||||
return this->names->contains_as(anonymous_id.name());
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
|
@ -40,13 +40,9 @@ namespace blender::bke {
|
|||
|
||||
std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (attribute_id) {
|
||||
stream << attribute_id.name();
|
||||
}
|
||||
else if (attribute_id.is_anonymous()) {
|
||||
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
|
||||
stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">";
|
||||
}
|
||||
else {
|
||||
stream << "<none>";
|
||||
}
|
||||
|
@ -153,7 +149,7 @@ eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains)
|
|||
static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer)
|
||||
{
|
||||
if (layer.anonymous_id != nullptr) {
|
||||
return layer.anonymous_id;
|
||||
return *layer.anonymous_id;
|
||||
}
|
||||
return layer.name;
|
||||
}
|
||||
|
@ -207,7 +203,7 @@ static void *add_generic_custom_data_layer(CustomData &custom_data,
|
|||
const int domain_num,
|
||||
const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
char attribute_name_c[MAX_NAME];
|
||||
attribute_id.name().copy(attribute_name_c);
|
||||
return CustomData_add_layer_named(
|
||||
|
@ -261,9 +257,6 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
|
|||
if (!attribute_id) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return layer.anonymous_id == &attribute_id.anonymous_id();
|
||||
}
|
||||
return layer.name == attribute_id.name();
|
||||
}
|
||||
|
||||
|
@ -451,14 +444,8 @@ GAttributeWriter CustomDataAttributeProvider::try_get_for_write(
|
|||
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
|
||||
continue;
|
||||
}
|
||||
if (attribute_id.is_named()) {
|
||||
CustomData_duplicate_referenced_layer_named(
|
||||
custom_data, layer.type, layer.name, element_num);
|
||||
}
|
||||
else {
|
||||
CustomData_duplicate_referenced_layer_anonymous(
|
||||
custom_data, layer.type, &attribute_id.anonymous_id(), element_num);
|
||||
}
|
||||
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, element_num);
|
||||
|
||||
const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
|
||||
if (type == nullptr) {
|
||||
continue;
|
||||
|
@ -854,7 +841,7 @@ void MutableAttributeAccessor::remove_anonymous()
|
|||
}
|
||||
|
||||
while (!anonymous_ids.is_empty()) {
|
||||
this->remove(anonymous_ids.pop_last());
|
||||
this->remove(*anonymous_ids.pop_last());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -883,12 +870,7 @@ GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef
|
|||
#ifdef DEBUG
|
||||
if (attribute) {
|
||||
auto checker = std::make_shared<FinishCallChecker>();
|
||||
if (attribute_id.is_named()) {
|
||||
checker->name = attribute_id.name();
|
||||
}
|
||||
else {
|
||||
checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
|
||||
}
|
||||
checker->name = attribute_id.name();
|
||||
checker->real_finish_fn = attribute.tag_modified_fn;
|
||||
attribute.tag_modified_fn = [checker]() {
|
||||
if (checker->real_finish_fn) {
|
||||
|
@ -968,6 +950,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
|
|||
const bke::AttributeAccessor src_attributes,
|
||||
bke::MutableAttributeAccessor dst_attributes,
|
||||
const eAttrDomainMask domain_mask,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip)
|
||||
{
|
||||
Vector<AttributeTransferData> attributes;
|
||||
|
@ -976,10 +959,10 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
|
|||
if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_named() && skip.contains(id.name())) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
if (skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -999,6 +982,7 @@ void copy_attribute_domain(const AttributeAccessor src_attributes,
|
|||
MutableAttributeAccessor dst_attributes,
|
||||
const IndexMask selection,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info,
|
||||
const Set<std::string> &skip)
|
||||
{
|
||||
src_attributes.for_all(
|
||||
|
@ -1006,10 +990,10 @@ void copy_attribute_domain(const AttributeAccessor src_attributes,
|
|||
if (meta_data.domain != domain) {
|
||||
return true;
|
||||
}
|
||||
if (id.is_named() && skip.contains(id.name())) {
|
||||
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
|
||||
return true;
|
||||
}
|
||||
if (!id.should_be_kept()) {
|
||||
if (skip.contains(id.name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -336,7 +336,7 @@ namespace attribute_accessor_functions {
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return false;
|
||||
}
|
||||
const StringRef name = attribute_id.name();
|
||||
|
@ -346,7 +346,7 @@ inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_i
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
@ -396,7 +396,7 @@ template<const ComponentAttributeProviders &providers>
|
|||
inline AttributeValidator lookup_validator(const void * /*owner*/,
|
||||
const blender::bke::AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (!attribute_id.is_named()) {
|
||||
if (attribute_id.is_anonymous()) {
|
||||
return {};
|
||||
}
|
||||
const BuiltinAttributeProvider *provider =
|
||||
|
@ -443,7 +443,7 @@ inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner,
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
@ -462,7 +462,7 @@ inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attr
|
|||
template<const ComponentAttributeProviders &providers>
|
||||
inline bool remove(void *owner, const AttributeIDRef &attribute_id)
|
||||
{
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
@ -487,7 +487,7 @@ inline bool add(void *owner,
|
|||
if (contains<providers>(owner, attribute_id)) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_id.is_named()) {
|
||||
if (!attribute_id.is_anonymous()) {
|
||||
const StringRef name = attribute_id.name();
|
||||
if (const BuiltinAttributeProvider *provider =
|
||||
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue