Cycles: new Microfacet-based Hair BSDF with elliptical cross-section support #105600

Merged
Weizhen Huang merged 114 commits from weizhen/blender:microfacet_hair into main 2023-08-18 12:46:20 +02:00
532 changed files with 10804 additions and 5763 deletions
Showing only changes of commit b47fc535f1 - Show all commits

View File

@ -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.

View File

@ -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})

View File

@ -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"

View File

@ -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
)

View File

@ -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)

View File

@ -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()

View File

@ -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.

View File

@ -49,7 +49,7 @@ update-code:
#
buildbot:
gcc:
version: '9.0.0'
version: '11.0.0'
cuda10:
version: '10.1.243'
cuda11:

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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
/* --------------------------------------------------------------------

View File

@ -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;

View File

@ -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");

View File

@ -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()

View File

@ -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. */

View File

@ -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 */

View File

@ -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();
}
}

View File

@ -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 */

View File

@ -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*/

View File

@ -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_++];
}
}
}
}

View File

@ -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>());

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -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. */

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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, &params);
}
}
@ -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, &params);
@ -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],

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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'}

View File

@ -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)

View File

@ -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", ""),

View File

@ -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:

View File

@ -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':

View File

@ -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):

View File

@ -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'

View File

@ -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, "")

View File

@ -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'

View File

@ -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)

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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"):

View File

@ -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

View File

@ -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):

View File

@ -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="")

View File

@ -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

View File

@ -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)

View File

@ -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: [

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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 = (

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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);
/**

View File

@ -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;

View File

@ -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,

View File

@ -29,7 +29,6 @@ struct PartialUpdateUser;
namespace blender::bke::image {
namespace partial_update {
/* --- image_partial_update.cc --- */

View File

@ -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.
*/

View File

@ -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,

View File

@ -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.
*/

View File

@ -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));

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}

View File

@ -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