Merge branch 'master' into refactor-mesh-position-generic

This commit is contained in:
Hans Goudey 2023-01-06 09:44:49 -05:00
commit c525fcb9a3
229 changed files with 6017 additions and 2321 deletions

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

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.
@ -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"
@ -466,7 +476,9 @@ 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
@ -560,6 +572,14 @@ ALEMBIC_FORCE_BUILD=false
ALEMBIC_FORCE_REBUILD=false
ALEMBIC_SKIP=false
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"
@ -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" )
@ -1165,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
@ -1217,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:
@ -1479,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
}
@ -1567,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"
}
# ----------------------------------------------------------------------------
@ -1585,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
}
@ -1610,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
@ -1636,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
@ -1835,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.
@ -1890,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
@ -2082,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
}
@ -2215,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
}
@ -2237,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.
@ -2291,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"
@ -2306,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"
@ -2878,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() {
@ -2900,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.
@ -2949,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
@ -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

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

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

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

View File

@ -22,7 +22,7 @@ class MetalDevice;
thread_mutex MetalDevice::existing_devices_mutex;
std::map<int, MetalDevice *> MetalDevice::active_device_ids;
/* Thread-safe device access for async work. Calling code must pass an appropriatelty scoped lock
/* 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)
{
@ -110,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;
}
}
@ -126,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);
}
@ -365,7 +377,7 @@ bool MetalDevice::load_kernels(const uint _kernel_features)
/* Only request generic kernels if they aren't cached in memory. */
if (make_source_and_check_if_compile_needed(PSO_GENERIC)) {
/* If needed, load them asynchronously in order to responsively message progess to the user. */
/* 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);
@ -389,8 +401,7 @@ bool MetalDevice::make_source_and_check_if_compile_needed(MetalPipelineType pso_
void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
{
/* Thread-safe front-end compilation. Typically the MSL->AIR compilation can take a few seconds,
* so we avoid blocking device teardown if the user cancels a render immediately.
*/
* so we avoid blocking device tear-down if the user cancels a render immediately. */
id<MTLDevice> mtlDevice;
string source;
@ -444,7 +455,7 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
source);
}
const double starttime = time_dt();
double starttime = time_dt();
NSError *error = NULL;
id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str())
@ -457,6 +468,13 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
[options release];
bool blocking_pso_build = (getenv("CYCLES_METAL_PROFILING") ||
MetalDeviceKernels::is_benchmark_warmup());
if (blocking_pso_build) {
MetalDeviceKernels::wait_for_all();
starttime = 0.0;
}
/* Save the compiled MTLLibrary and trigger the AIR->PSO builds (if the MetalDevice still
* exists). */
{
@ -464,6 +482,8 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
if (MetalDevice *instance = get_device_by_ID(device_id, lock)) {
if (mtlLibrary) {
instance->mtlLibrary[pso_type] = mtlLibrary;
starttime = time_dt();
MetalDeviceKernels::load(instance, pso_type);
}
else {
@ -472,6 +492,14 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type)
}
}
}
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()
@ -832,10 +860,8 @@ void MetalDevice::optimize_for_scene(Scene *scene)
}
/* Block during benchmark warm-up to ensure kernels are cached prior to the observed run. */
for (int i = 0; i < *_NSGetArgc(); i++) {
if (!strcmp((*_NSGetArgv())[i], "--warm-up")) {
specialize_in_background = false;
}
if (MetalDeviceKernels::is_benchmark_warmup()) {
specialize_in_background = false;
}
if (specialize_in_background) {

View File

@ -101,6 +101,8 @@ int get_loaded_kernel_count(MetalDevice const *device, MetalPipelineType pso_typ
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

@ -116,19 +116,29 @@ struct ShaderCache {
};
bool ShaderCache::running = true;
std::mutex g_shaderCacheMutex;
std::map<id<MTLDevice>, unique_ptr<ShaderCache>> g_shaderCache;
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()
@ -145,7 +155,7 @@ ShaderCache::~ShaderCache()
num_incomplete = int(incomplete_requests);
}
if (num_incomplete) {
if (num_incomplete && !MetalDeviceKernels::is_benchmark_warmup()) {
metal_printf("ShaderCache still busy (incomplete_requests = %d). Terminating...\n",
num_incomplete);
std::terminate();
@ -313,17 +323,15 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup;
if (occupancy_tuning[device_kernel].threads_per_threadgroup) {
pipeline->threads_per_threadgroup =
occupancy_tuning[device_kernel].threads_per_threadgroup;
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 */
pipeline->use_metalrt = device->use_metalrt;
pipeline->metalrt_features = device->use_metalrt ?
(device->kernel_features & METALRT_FEATURE_MASK) :
0;
(device->kernel_features & METALRT_FEATURE_MASK) :
0;
{
thread_scoped_lock lock(cache_mutex);
@ -334,12 +342,6 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
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;
@ -351,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));
}
}
@ -742,8 +753,7 @@ void MetalKernelPipeline::compile()
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);
num_threads_per_block = std::max(num_threads_per_block, (int)pipeline.threadExecutionWidth);
}
if (@available(macOS 11.0, *)) {
@ -805,28 +815,26 @@ 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);
}
if (getenv("CYCLES_METAL_PROFILING")) {
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;
}
void MetalDeviceKernels::wait_for_all()
{
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). */
thread_scoped_lock lock(g_shaderCacheMutex);
for (auto &it : g_shaderCache) {
if (it.second->incomplete_specialization_requests > 0) {
for (int i = 0; i < g_shaderCacheCount; i++) {
if (g_shaderCache[i].second->incomplete_specialization_requests > 0) {
return true;
}
}
@ -857,6 +865,19 @@ const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevi
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_) {
@ -855,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_) {
@ -897,9 +896,7 @@ id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder()
return mtlBlitEncoder_;
}
if (mtlComputeEncoder_) {
close_compute_encoder();
}
close_compute_encoder();
if (!mtlCommandBuffer_) {
mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer];
@ -913,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

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

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

@ -4,6 +4,7 @@
#include <atomic>
#include <cassert>
#include <iostream>
#include <memory>
#include <mutex>
#include <vector>
@ -14,10 +15,18 @@
namespace {
struct Local;
struct Global;
/**
* This is stored per thread. Align to cache line size to avoid false sharing.
*/
struct alignas(64) Local {
struct alignas(128) 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;
/**
@ -49,7 +58,8 @@ struct alignas(64) Local {
};
/**
* This is a singleton that stores global data.
* 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 {
/**
@ -71,7 +81,7 @@ struct Global {
* To solve this, the memory counts are added to these global counters when the thread
* exists. The global counters are also used when the entire process starts to exit, because the
* #Local data of the main thread is already destructed when the leak detection happens (during
* destruction of static variables which happens after destruction of threadlocals).
* destruction of static variables which happens after destruction of thread-locals).
*/
std::atomic<int64_t> mem_in_use_outside_locals = 0;
/**
@ -98,10 +108,15 @@ static std::atomic<bool> use_local_counters = true;
*/
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()
{
static Global global;
return global;
return *get_global_ptr();
}
static Local &get_local_data()
@ -113,32 +128,32 @@ static Local &get_local_data()
Local::Local()
{
Global &global = get_global();
std::lock_guard lock{global.locals_mutex};
this->global = get_global_ptr();
if (global.locals.empty()) {
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. */
global.locals.push_back(this);
this->global->locals.push_back(this);
}
Local::~Local()
{
Global &global = get_global();
std::lock_guard lock{global.locals_mutex};
std::lock_guard lock{this->global->locals_mutex};
/* Unregister self from the global list. */
global.locals.erase(std::find(global.locals.begin(), global.locals.end(), this));
this->global->locals.erase(
std::find(this->global->locals.begin(), this->global->locals.end(), this));
/* Don't forget the memory counts stored locally. */
global.blocks_num_outside_locals.fetch_add(this->blocks_num, std::memory_order_relaxed);
global.mem_in_use_outside_locals.fetch_add(this->mem_in_use, std::memory_order_relaxed);
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
* threadlocals after they have been destructed. */
* 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. */
@ -164,7 +179,7 @@ static void update_global_peak()
void memory_usage_init()
{
/* Makes sure that the static and threadlocal variables on the main thread are initialized. */
/* Makes sure that the static and thread-local variables on the main thread are initialized. */
get_local_data();
}

@ -1 +1 @@
Subproject commit 96143b1a8b037ea3c81f065f557025db9fe1ace3
Subproject commit bdcfdd47ec3451822b21d1cff2ea2db751093c9a

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

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

@ -66,7 +66,7 @@ class MotionPathButtonsPanel:
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(op_category + ".paths_clear", text="", icon='X').only_selected = False

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

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

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

@ -550,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':
@ -661,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):
@ -961,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
@ -2324,7 +2324,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
({"property": "use_sculpt_tools_tilt"}, "T82877"),
({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")),
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
({"property": "use_realtime_compositor"}, "T99210"),
),
)

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)
@ -889,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
@ -2290,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'
@ -3291,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
@ -3352,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()
@ -3367,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()
@ -3389,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):
@ -3431,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'
@ -3465,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'
@ -3660,10 +3660,6 @@ class VIEW3D_MT_pose_propagate(Menu):
def draw(self, _context):
layout = self.layout
layout.operator("pose.propagate").mode = 'WHILE_HELD'
layout.separator()
layout.operator("pose.propagate", text="To Next Keyframe").mode = 'NEXT_KEY'
layout.operator("pose.propagate", text="To Last Keyframe (Make Cyclic)").mode = 'LAST_KEY'
@ -5500,23 +5496,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
@ -5565,16 +5561,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):
@ -6216,8 +6212,7 @@ class VIEW3D_PT_shading_compositor(Panel):
@classmethod
def poll(cls, context):
return (context.space_data.shading.type in {'MATERIAL', 'RENDERED'} and
context.preferences.experimental.use_realtime_compositor)
return context.space_data.shading.type in {'MATERIAL', 'RENDERED'}
def draw(self, context):
shading = context.space_data.shading
@ -6974,9 +6969,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

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

@ -2,7 +2,6 @@
#pragma once
struct CurvesGeometry;
struct Mesh;
/** \file
@ -11,6 +10,9 @@ struct Mesh;
namespace blender::bke {
struct CurvesGeometry;
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

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

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

@ -410,8 +410,6 @@ bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct
* \note This is by far the biggest operation (the more time-consuming) of the three so far,
* since it has to go over all properties in depth (all overridable ones at least).
* Generating differential values and applying overrides are much cheaper.
*
* \return true if any library operation was created.
*/
void BKE_lib_override_library_operations_create(struct Main *bmain,
struct ID *local,
@ -425,6 +423,29 @@ void BKE_lib_override_library_main_operations_create(struct Main *bmain,
bool force_auto,
int *r_report_flags);
/**
* Restore forbidden modified override properties to the values of their matching properties in the
* linked reference ID.
*
* \param r_report_flags #eRNAOverrideMatchResult flags giving info about the result of this call.
*
* \note Typically used as part of BKE_lib_override_library_main_operations_create process, since
* modifying RNA properties from non-main threads is not safe.
*/
void BKE_lib_override_library_operations_restore(struct Main *bmain,
struct ID *local,
int *r_report_flags);
/**
* Restore forbidden modified override properties to the values of their matching properties in the
* linked reference ID, for all liboverride IDs tagged as needing such process in given `bmain`.
*
* \param r_report_flags #eRNAOverrideMatchResult flags giving info about the result of this call.
*
* \note Typically used as part of BKE_lib_override_library_main_operations_create process, since
* modifying RNA properties from non-main threads is not safe.
*/
void BKE_lib_override_library_main_operations_restore(struct Main *bmain, int *r_report_flags);
/**
* Reset all overrides in given \a id_root, while preserving ID relations.
*

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

@ -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
@ -162,13 +169,6 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
/** #eNodeTreeChangedFlag. */
uint32_t changed_flag = 0;
/**
* The location of the sockets, in the view-space of the node editor.
* \note Only calculated when drawing.
*/
float locx = 0;
float locy = 0;
/**
* Runtime-only cache of the number of input links, for multi-input sockets,
* including dragged node links that aren't actually in the tree.
@ -330,7 +330,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
/* -------------------------------------------------------------------- */

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

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

@ -335,7 +335,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();
@ -345,7 +345,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)) {
@ -395,7 +395,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 =
@ -442,7 +442,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)) {
@ -461,7 +461,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)) {
@ -486,7 +486,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)) {

View File

@ -1115,8 +1115,15 @@ static void cloth_selfcollision(void *__restrict userdata,
float epsilon = clmd->coll_parms->selfepsilon;
float pa[3], pb[3], vect[3];
tri_a = &clmd->clothObject->tri[data->overlap[index].indexA];
tri_b = &clmd->clothObject->tri[data->overlap[index].indexB];
/* Collision math is currently not symmetric, so ensure a stable order for each pair. */
int indexA = data->overlap[index].indexA, indexB = data->overlap[index].indexB;
if (indexA > indexB) {
SWAP(int, indexA, indexB);
}
tri_a = &clmd->clothObject->tri[indexA];
tri_b = &clmd->clothObject->tri[indexB];
BLI_assert(cloth_bvh_selfcollision_is_active(clmd, clmd->clothObject, tri_a, tri_b));
@ -1521,8 +1528,9 @@ static bool cloth_bvh_obj_overlap_cb(void *userdata,
static bool cloth_bvh_self_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
{
/* No need for equal combinations (eg. (0,1) & (1,0)). */
if (index_a < index_b) {
/* This shouldn't happen, but just in case. Note that equal combinations
* (eg. (0,1) & (1,0)) would be filtered out by BLI_bvhtree_overlap_self. */
if (index_a != index_b) {
ClothModifierData *clmd = (ClothModifierData *)userdata;
struct Cloth *clothObject = clmd->clothObject;
const MVertTri *tri_a, *tri_b;
@ -1599,8 +1607,8 @@ int cloth_bvh_collision(
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
bvhtree_update_from_cloth(clmd, false, true);
overlap_self = BLI_bvhtree_overlap(
cloth->bvhselftree, cloth->bvhselftree, &coll_count_self, cloth_bvh_self_overlap_cb, clmd);
overlap_self = BLI_bvhtree_overlap_self(
cloth->bvhselftree, &coll_count_self, cloth_bvh_self_overlap_cb, clmd);
}
do {

View File

@ -28,6 +28,8 @@ BLI_CPP_TYPE_MAKE(Material *, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(MStringProperty, CPPTypeFlags::None);
BLI_CPP_TYPE_MAKE(blender::bke::AnonymousAttributeSet, CPPTypeFlags::None);
void BKE_cpp_types_init()
{
blender::register_cpp_types();
@ -45,4 +47,6 @@ void BKE_cpp_types_init()
BLI_CPP_TYPE_REGISTER(Material *);
BLI_CPP_TYPE_REGISTER(MStringProperty);
BLI_CPP_TYPE_REGISTER(blender::bke::AnonymousAttributeSet);
}

View File

@ -328,18 +328,19 @@ static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_a
static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes,
const AttributeAccessor &mesh_attributes,
const AttributeIDRef &id,
const AttributeMetaData &meta_data)
const AttributeMetaData &meta_data,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* The position attribute has special non-generic evaluation. */
if (id.is_named() && id.name() == "position") {
if (id.name() == "position") {
return false;
}
/* Don't propagate built-in curves attributes that are not built-in on meshes. */
if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) {
return false;
}
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return false;
}
if (meta_data.data_type == CD_PROP_STRING) {
@ -626,7 +627,8 @@ static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offset
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
const CurvesGeometry &profile,
const bool fill_caps)
const bool fill_caps,
const AnonymousAttributePropagationInfo &propagation_info)
{
const CurvesInfo curves_info = get_curves_info(main, profile);
@ -713,7 +715,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write();
main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id, meta_data)) {
if (!should_add_attribute_to_mesh(
main_attributes, mesh_attributes, id, meta_data, propagation_info)) {
return true;
}
main_attributes_set.add_new(id);
@ -750,7 +753,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
if (main_attributes.contains(id)) {
return true;
}
if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id, meta_data)) {
if (!should_add_attribute_to_mesh(
profile_attributes, mesh_attributes, id, meta_data, propagation_info)) {
return true;
}
const eAttrDomain src_domain = meta_data.domain;
@ -794,10 +798,11 @@ static CurvesGeometry get_curve_single_vert()
return curves;
}
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve)
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
const AnonymousAttributePropagationInfo &propagation_info)
{
static const CurvesGeometry vert_curve = get_curve_single_vert();
return curve_to_mesh_sweep(curve, vert_curve, false);
return curve_to_mesh_sweep(curve, vert_curve, false, propagation_info);
}
} // namespace blender::bke

View File

@ -1047,8 +1047,10 @@ static Array<int> build_point_to_curve_map(const CurvesGeometry &curves)
return point_to_curve_map;
}
static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
const IndexMask points_to_delete)
static CurvesGeometry copy_with_removed_points(
const CurvesGeometry &curves,
const IndexMask points_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* Use a map from points to curves to facilitate using an #IndexMask input. */
const Array<int> point_to_curve_map = build_point_to_curve_map(curves);
@ -1093,9 +1095,15 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
CurvesGeometry new_curves{new_point_count, new_curve_count};
Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_POINT,
propagation_info);
Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_CURVE,
propagation_info);
threading::parallel_invoke(
256 < new_point_count * new_curve_count,
@ -1144,7 +1152,8 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
return new_curves;
}
void CurvesGeometry::remove_points(const IndexMask points_to_delete)
void CurvesGeometry::remove_points(const IndexMask points_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (points_to_delete.is_empty()) {
return;
@ -1152,11 +1161,13 @@ void CurvesGeometry::remove_points(const IndexMask points_to_delete)
if (points_to_delete.size() == this->points_num()) {
*this = {};
}
*this = copy_with_removed_points(*this, points_to_delete);
*this = copy_with_removed_points(*this, points_to_delete, propagation_info);
}
static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
const IndexMask curves_to_delete)
static CurvesGeometry copy_with_removed_curves(
const CurvesGeometry &curves,
const IndexMask curves_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Span<int> old_offsets = curves.offsets();
const Vector<IndexRange> old_curve_ranges = curves_to_delete.extract_ranges_invert(
@ -1178,9 +1189,15 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
CurvesGeometry new_curves{new_tot_points, new_tot_curves};
Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_POINT,
propagation_info);
Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_CURVE,
propagation_info);
threading::parallel_invoke(
256 < new_tot_points * new_tot_curves,
@ -1251,7 +1268,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
return new_curves;
}
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (curves_to_delete.is_empty()) {
return;
@ -1260,7 +1278,7 @@ void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
*this = {};
return;
}
*this = copy_with_removed_curves(*this, curves_to_delete);
*this = copy_with_removed_curves(*this, curves_to_delete, propagation_info);
}
template<typename T>
@ -1315,7 +1333,7 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
if (meta_data.data_type == CD_PROP_STRING) {
return true;
}
if (id.is_named() && bezier_handle_names.contains(id.name())) {
if (bezier_handle_names.contains(id.name())) {
return true;
}

View File

@ -39,7 +39,7 @@
#include "BLT_translation.h"
#include "BKE_anonymous_attribute.h"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_customdata.h"
#include "BKE_customdata_file.h"
#include "BKE_deform.h"
@ -2356,7 +2356,7 @@ bool CustomData_merge(const CustomData *source,
layer->anonymous_id = nullptr;
}
else {
BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id);
layer->anonymous_id->user_add();
}
}
if (alloctype == CD_ASSIGN) {
@ -2461,7 +2461,7 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to
const LayerTypeInfo *typeInfo;
if (layer->anonymous_id != nullptr) {
BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id);
layer->anonymous_id->user_remove();
layer->anonymous_id = nullptr;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
@ -2958,9 +2958,9 @@ void *CustomData_add_layer_anonymous(CustomData *data,
const eCDAllocType alloctype,
void *layerdata,
const int totelem,
const AnonymousAttributeID *anonymous_id)
const AnonymousAttributeIDHandle *anonymous_id)
{
const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id);
const char *name = anonymous_id->name().c_str();
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, layerdata, totelem, name);
CustomData_update_typemap(data);
@ -2969,7 +2969,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
return nullptr;
}
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
anonymous_id->user_add();
layer->anonymous_id = anonymous_id;
return layer->data;
}
@ -3148,20 +3148,6 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data,
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
}
void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data,
const int /*type*/,
const AnonymousAttributeID *anonymous_id,
const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].anonymous_id == anonymous_id) {
return customData_duplicate_referenced_layer_index(data, i, totelem);
}
}
BLI_assert_unreachable();
return nullptr;
}
void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {

View File

@ -1045,7 +1045,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
GAttributeReader try_get_for_read(const void *owner,
const AttributeIDRef &attribute_id) const final
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return {};
}
const Mesh *mesh = static_cast<const Mesh *>(owner);
@ -1069,7 +1069,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return {};
}
Mesh *mesh = static_cast<Mesh *>(owner);
@ -1090,7 +1090,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return false;
}
Mesh *mesh = static_cast<Mesh *>(owner);

View File

@ -332,7 +332,7 @@ GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryField
const IndexMask /*mask*/) const
{
const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
return context.attributes()->lookup(anonymous_id_.get(), context.domain(), data_type);
return context.attributes()->lookup(*anonymous_id_, context.domain(), data_type);
}
std::string AnonymousAttributeFieldInput::socket_inspection_name() const
@ -363,8 +363,7 @@ std::optional<eAttrDomain> AnonymousAttributeFieldInput::preferred_domain(
if (!attributes.has_value()) {
return std::nullopt;
}
const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(
anonymous_id_.get());
const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(*anonymous_id_);
if (!meta_data.has_value()) {
return std::nullopt;
}
@ -434,8 +433,10 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
GMutableSpan{type, buffer, domain_size});
evaluator.evaluate();
if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) {
if (attribute.domain == domain && attribute.varray.type() == type) {
const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(attribute_id);
if (meta_data && meta_data->domain == domain && meta_data->data_type == data_type) {
if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) {
attribute.varray.set_all(buffer);
attribute.finish();
type.destruct_n(buffer, domain_size);
@ -443,6 +444,7 @@ bool try_capture_field_on_geometry(GeometryComponent &component,
return true;
}
}
attributes.remove(attribute_id);
if (attributes.add(attribute_id, domain, data_type, bke::AttributeInitMoveArray{buffer})) {
return true;

View File

@ -581,6 +581,7 @@ void GeometrySet::gather_attributes_for_propagation(
const Span<GeometryComponentType> component_types,
const GeometryComponentType dst_component_type,
bool include_instances,
const blender::bke::AnonymousAttributePropagationInfo &propagation_info,
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const
{
using namespace blender;
@ -605,7 +606,8 @@ void GeometrySet::gather_attributes_for_propagation(
/* Propagating string attributes is not supported yet. */
return;
}
if (!attribute_id.should_be_kept()) {
if (attribute_id.is_anonymous() &&
!propagation_info.propagate(attribute_id.anonymous_id())) {
return;
}

View File

@ -105,7 +105,8 @@ blender::Span<InstanceReference> Instances::references() const
return references_;
}
void Instances::remove(const IndexMask mask)
void Instances::remove(const IndexMask mask,
const AnonymousAttributePropagationInfo &propagation_info)
{
using namespace blender;
if (mask.is_range() && mask.as_range().start() == 0) {
@ -132,7 +133,7 @@ void Instances::remove(const IndexMask mask)
src_attributes.foreach_attribute(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) {
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}

View File

@ -3297,7 +3297,10 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
return true;
}
void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_report_flags)
static void lib_override_library_operations_create(Main *bmain,
ID *local,
const eRNAOverrideMatch override_match_flags,
eRNAOverrideMatchResult *r_report_flags)
{
BLI_assert(!ID_IS_LINKED(local));
BLI_assert(local->override_library != nullptr);
@ -3330,19 +3333,24 @@ void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_r
RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference);
eRNAOverrideMatchResult local_report_flags = RNA_OVERRIDE_MATCH_RESULT_INIT;
RNA_struct_override_matches(
bmain,
&rnaptr_local,
&rnaptr_reference,
nullptr,
0,
local->override_library,
(eRNAOverrideMatch)(RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE),
&local_report_flags);
RNA_struct_override_matches(bmain,
&rnaptr_local,
&rnaptr_reference,
nullptr,
0,
local->override_library,
override_match_flags,
&local_report_flags);
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
CLOG_INFO(&LOG, 2, "We did restore some properties of %s from its reference", local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED) {
CLOG_INFO(&LOG,
2,
"We did tag some properties of %s for restoration from its reference",
local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
CLOG_INFO(&LOG, 2, "We did generate library override rules for %s", local->name);
}
@ -3351,10 +3359,50 @@ void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_r
}
if (r_report_flags != nullptr) {
*r_report_flags |= local_report_flags;
*r_report_flags = static_cast<eRNAOverrideMatchResult>(*r_report_flags | local_report_flags);
}
}
}
void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_report_flags)
{
lib_override_library_operations_create(
bmain,
local,
static_cast<eRNAOverrideMatch>(RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE),
reinterpret_cast<eRNAOverrideMatchResult *>(r_report_flags));
}
void BKE_lib_override_library_operations_restore(Main *bmain, ID *local, int *r_report_flags)
{
if (!ID_IS_OVERRIDE_LIBRARY_REAL(local) || (local->override_library->runtime->tag &
IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RESTORE) == 0) {
return;
}
PointerRNA rnaptr_src, rnaptr_dst;
RNA_id_pointer_create(local, &rnaptr_dst);
RNA_id_pointer_create(local->override_library->reference, &rnaptr_src);
RNA_struct_override_apply(
bmain,
&rnaptr_dst,
&rnaptr_src,
nullptr,
local->override_library,
static_cast<eRNAOverrideApplyFlag>(RNA_OVERRIDE_APPLY_FLAG_SKIP_RESYNC_CHECK |
RNA_OVERRIDE_APPLY_FLAG_RESTORE_ONLY));
LISTBASE_FOREACH_MUTABLE (
IDOverrideLibraryProperty *, op, &local->override_library->properties) {
if (op->tag & IDOVERRIDE_LIBRARY_PROPERTY_TAG_NEEDS_RETORE) {
BKE_lib_override_library_property_delete(local->override_library, op);
}
}
local->override_library->runtime->tag &= ~IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RESTORE;
if (r_report_flags != nullptr) {
*r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_RESTORED;
}
}
struct LibOverrideOpCreateData {
Main *bmain;
@ -3368,8 +3416,12 @@ static void lib_override_library_operations_create_cb(TaskPool *__restrict pool,
ID *id = static_cast<ID *>(taskdata);
eRNAOverrideMatchResult report_flags = RNA_OVERRIDE_MATCH_RESULT_INIT;
BKE_lib_override_library_operations_create(
create_data->bmain, id, reinterpret_cast<int *>(&report_flags));
lib_override_library_operations_create(
create_data->bmain,
id,
static_cast<eRNAOverrideMatch>(RNA_OVERRIDE_COMPARE_CREATE |
RNA_OVERRIDE_COMPARE_TAG_FOR_RESTORE),
&report_flags);
atomic_fetch_and_or_uint32(reinterpret_cast<uint32_t *>(&create_data->report_flags),
report_flags);
}
@ -3443,6 +3495,13 @@ void BKE_lib_override_library_main_operations_create(Main *bmain,
BLI_task_pool_free(task_pool);
if (create_pool_data.report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED) {
BKE_lib_override_library_main_operations_restore(
bmain, reinterpret_cast<int *>(&create_pool_data.report_flags));
create_pool_data.report_flags = static_cast<eRNAOverrideMatchResult>(
(create_pool_data.report_flags & ~RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED));
}
if (r_report_flags != nullptr) {
*r_report_flags |= create_pool_data.report_flags;
}
@ -3456,6 +3515,28 @@ void BKE_lib_override_library_main_operations_create(Main *bmain,
#endif
}
void BKE_lib_override_library_main_operations_restore(Main *bmain, int *r_report_flags)
{
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!(!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
(id->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RESTORE) !=
0)) {
continue;
}
/* Only restore overrides if we do have the real reference data available, and not some empty
* 'placeholder' for missing data (broken links). */
if (id->override_library->reference->tag & LIB_TAG_MISSING) {
continue;
}
BKE_lib_override_library_operations_restore(bmain, id, r_report_flags);
}
FOREACH_MAIN_ID_END;
}
static bool lib_override_library_id_reset_do(Main *bmain,
ID *id_root,
const bool do_reset_system_override)

View File

@ -822,7 +822,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
return BKE_mesh_copy_for_eval(mesh, false);
}
if (const Curves *curves = get_evaluated_curves_from_object(evaluated_object)) {
return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry));
const blender::bke::AnonymousAttributePropagationInfo propagation_info;
return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry),
propagation_info);
}
return nullptr;
}

View File

@ -884,7 +884,7 @@ static ImBuf *get_imbuf_cache(MovieClip *clip, const MovieClipUser *user, int fl
return NULL;
}
static bool has_imbuf_cache(MovieClip *clip, MovieClipUser *user, int flag)
static bool has_imbuf_cache(MovieClip *clip, const MovieClipUser *user, int flag)
{
if (clip->cache) {
MovieClipImBufCacheKey key;
@ -1344,7 +1344,6 @@ static ImBuf *movieclip_get_postprocessed_ibuf(
}
if (ibuf) {
clip->lastframe = framenr;
real_ibuf_size(clip, user, ibuf, &clip->lastsize[0], &clip->lastsize[1]);
/* Post-process frame and put to cache if needed. */
@ -1373,25 +1372,31 @@ static ImBuf *movieclip_get_postprocessed_ibuf(
return ibuf;
}
ImBuf *BKE_movieclip_get_ibuf(MovieClip *clip, MovieClipUser *user)
ImBuf *BKE_movieclip_get_ibuf(MovieClip *clip, const MovieClipUser *user)
{
return BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, 0);
}
ImBuf *BKE_movieclip_get_ibuf_flag(MovieClip *clip, MovieClipUser *user, int flag, int cache_flag)
ImBuf *BKE_movieclip_get_ibuf_flag(MovieClip *clip,
const MovieClipUser *user,
const int flag,
const int cache_flag)
{
return movieclip_get_postprocessed_ibuf(clip, user, flag, 0, cache_flag);
}
ImBuf *BKE_movieclip_get_postprocessed_ibuf(MovieClip *clip,
MovieClipUser *user,
int postprocess_flag)
const MovieClipUser *user,
const int postprocess_flag)
{
return movieclip_get_postprocessed_ibuf(clip, user, clip->flag, postprocess_flag, 0);
}
static ImBuf *get_stable_cached_frame(
MovieClip *clip, MovieClipUser *user, ImBuf *reference_ibuf, int framenr, int postprocess_flag)
static ImBuf *get_stable_cached_frame(MovieClip *clip,
const MovieClipUser *user,
ImBuf *reference_ibuf,
const int framenr,
const int postprocess_flag)
{
MovieClipCache *cache = clip->cache;
MovieTracking *tracking = &clip->tracking;
@ -1449,8 +1454,11 @@ static ImBuf *get_stable_cached_frame(
return stableibuf;
}
static ImBuf *put_stabilized_frame_to_cache(
MovieClip *clip, MovieClipUser *user, ImBuf *ibuf, int framenr, int postprocess_flag)
static ImBuf *put_stabilized_frame_to_cache(MovieClip *clip,
const MovieClipUser *user,
ImBuf *ibuf,
const int framenr,
const int postprocess_flag)
{
MovieClipCache *cache = clip->cache;
MovieTracking *tracking = &clip->tracking;
@ -1492,11 +1500,11 @@ static ImBuf *put_stabilized_frame_to_cache(
}
ImBuf *BKE_movieclip_get_stable_ibuf(MovieClip *clip,
MovieClipUser *user,
const MovieClipUser *user,
float loc[2],
float *scale,
float *angle,
int postprocess_flag)
const int postprocess_flag)
{
ImBuf *ibuf, *stableibuf = NULL;
int framenr = user->framenr;
@ -1552,7 +1560,7 @@ ImBuf *BKE_movieclip_get_stable_ibuf(MovieClip *clip,
return ibuf;
}
bool BKE_movieclip_has_frame(MovieClip *clip, MovieClipUser *user)
bool BKE_movieclip_has_frame(MovieClip *clip, const MovieClipUser *user)
{
ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, user);
@ -1564,19 +1572,9 @@ bool BKE_movieclip_has_frame(MovieClip *clip, MovieClipUser *user)
return false;
}
void BKE_movieclip_get_size(MovieClip *clip, MovieClipUser *user, int *width, int *height)
void BKE_movieclip_get_size(MovieClip *clip, const MovieClipUser *user, int *width, int *height)
{
#if 0
/* originally was needed to support image sequences with different image dimensions,
* which might be useful for such things as reconstruction of unordered image sequence,
* or painting/rotoscoping of non-equal-sized images, but this ended up in unneeded
* cache lookups and even unwanted non-proxied files loading when doing mask parenting,
* so let's disable this for now and assume image sequence consists of images with
* equal sizes (sergey)
* TODO(sergey): Support reading sequences of different resolution.
*/
if (user->framenr == clip->lastframe) {
#endif
/* TODO(sergey): Support reading sequences of different resolution. */
if (clip->lastsize[0] != 0 && clip->lastsize[1] != 0) {
*width = clip->lastsize[0];
*height = clip->lastsize[1];
@ -1597,7 +1595,7 @@ void BKE_movieclip_get_size(MovieClip *clip, MovieClipUser *user, int *width, in
}
}
}
void BKE_movieclip_get_size_fl(MovieClip *clip, MovieClipUser *user, float size[2])
void BKE_movieclip_get_size_fl(MovieClip *clip, const MovieClipUser *user, float size[2])
{
int width, height;
BKE_movieclip_get_size(clip, user, &width, &height);
@ -1641,7 +1639,7 @@ void BKE_movieclip_get_aspect(MovieClip *clip, float *aspx, float *aspy)
}
void BKE_movieclip_get_cache_segments(MovieClip *clip,
MovieClipUser *user,
const MovieClipUser *user,
int *r_totseg,
int **r_points)
{
@ -1728,7 +1726,9 @@ void BKE_movieclip_reload(Main *bmain, MovieClip *clip)
BKE_ntree_update_tag_id_changed(bmain, &clip->id);
}
void BKE_movieclip_update_scopes(MovieClip *clip, MovieClipUser *user, MovieClipScopes *scopes)
void BKE_movieclip_update_scopes(MovieClip *clip,
const MovieClipUser *user,
MovieClipScopes *scopes)
{
if (scopes->ok) {
return;
@ -1945,17 +1945,17 @@ bool BKE_movieclip_proxy_enabled(MovieClip *clip)
return clip->flag & MCLIP_USE_PROXY;
}
float BKE_movieclip_remap_scene_to_clip_frame(const MovieClip *clip, float framenr)
float BKE_movieclip_remap_scene_to_clip_frame(const MovieClip *clip, const float framenr)
{
return framenr - (float)clip->start_frame + 1.0f;
}
float BKE_movieclip_remap_clip_to_scene_frame(const MovieClip *clip, float framenr)
float BKE_movieclip_remap_clip_to_scene_frame(const MovieClip *clip, const float framenr)
{
return framenr + (float)clip->start_frame - 1.0f;
}
void BKE_movieclip_filename_for_frame(MovieClip *clip, MovieClipUser *user, char *name)
void BKE_movieclip_filename_for_frame(MovieClip *clip, const MovieClipUser *user, char *name)
{
if (clip->source == MCLIP_SRC_SEQUENCE) {
int use_proxy;
@ -1977,7 +1977,7 @@ void BKE_movieclip_filename_for_frame(MovieClip *clip, MovieClipUser *user, char
}
}
ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(MovieClip *clip, MovieClipUser *user)
ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(MovieClip *clip, const MovieClipUser *user)
{
ImBuf *ibuf = NULL;
@ -1988,7 +1988,7 @@ ImBuf *BKE_movieclip_anim_ibuf_for_frame_no_lock(MovieClip *clip, MovieClipUser
return ibuf;
}
bool BKE_movieclip_has_cached_frame(MovieClip *clip, MovieClipUser *user)
bool BKE_movieclip_has_cached_frame(MovieClip *clip, const MovieClipUser *user)
{
bool has_frame = false;
@ -1999,7 +1999,7 @@ bool BKE_movieclip_has_cached_frame(MovieClip *clip, MovieClipUser *user)
return has_frame;
}
bool BKE_movieclip_put_frame_if_possible(MovieClip *clip, MovieClipUser *user, ImBuf *ibuf)
bool BKE_movieclip_put_frame_if_possible(MovieClip *clip, const MovieClipUser *user, ImBuf *ibuf)
{
bool result;

View File

@ -207,6 +207,11 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons
dst_runtime.field_inferencing_interface = std::make_unique<FieldInferencingInterface>(
*ntree_src->runtime->field_inferencing_interface);
}
if (ntree_src->runtime->anonymous_attribute_relations) {
dst_runtime.anonymous_attribute_relations =
std::make_unique<blender::nodes::anonymous_attribute_lifetime::RelationsInNode>(
*ntree_src->runtime->anonymous_attribute_relations);
}
if (flag & LIB_ID_COPY_NO_PREVIEW) {
ntree_dst->preview = nullptr;
@ -3556,16 +3561,16 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
{
const bool was_available = (sock->flag & SOCK_UNAVAIL) == 0;
if (is_available != was_available) {
BKE_ntree_update_tag_socket_availability(ntree, sock);
if (is_available == was_available) {
return;
}
if (is_available) {
sock->flag &= ~SOCK_UNAVAIL;
}
else {
sock->flag |= SOCK_UNAVAIL;
}
BKE_ntree_update_tag_socket_availability(ntree, sock);
}
int nodeSocketLinkLimit(const bNodeSocket *sock)

View File

@ -0,0 +1,452 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "NOD_node_declaration.hh"
#include "BKE_node_runtime.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_resource_scope.hh"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_timeit.hh"
namespace blender::bke::anonymous_attribute_inferencing {
namespace aal = nodes::aal;
using nodes::NodeDeclaration;
static const aal::RelationsInNode &get_relations_in_node(const bNode &node, ResourceScope &scope)
{
if (node.is_group()) {
if (const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id)) {
BLI_assert(group->runtime->anonymous_attribute_relations);
return *group->runtime->anonymous_attribute_relations;
}
}
if (const NodeDeclaration *node_decl = node.declaration()) {
if (const aal::RelationsInNode *relations = node_decl->anonymous_attribute_relations()) {
return *relations;
}
}
return scope.construct<aal::RelationsInNode>();
}
Array<const aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope)
{
const Span<const bNode *> nodes = tree.all_nodes();
Array<const aal::RelationsInNode *> relations_by_node(nodes.size());
for (const int i : nodes.index_range()) {
relations_by_node[i] = &get_relations_in_node(*nodes[i], scope);
}
return relations_by_node;
}
static bool socket_is_field(const bNodeSocket &socket)
{
return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND;
}
/**
* Start at a group output socket and find all linked group inputs.
*/
static Vector<int> find_linked_group_inputs(
const bNodeTree &tree,
const bNodeSocket &group_output_socket,
const FunctionRef<Vector<int>(const bNodeSocket &)> get_linked_node_inputs)
{
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> input_indices;
found_sockets.add_new(&group_output_socket);
sockets_to_check.push(&group_output_socket);
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
for (const int input_index : get_linked_node_inputs(socket)) {
const bNodeSocket &input_socket = node.input_socket(input_index);
if (input_socket.is_available()) {
if (found_sockets.add(&input_socket)) {
sockets_to_check.push(&input_socket);
}
}
}
}
}
for (const bNode *node : tree.group_input_nodes()) {
for (const bNodeSocket *socket : node->output_sockets()) {
if (found_sockets.contains(socket)) {
input_indices.append_non_duplicates(socket->index());
}
}
}
return input_indices;
}
static void infer_propagate_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (group_output_socket->type != SOCK_GEOMETRY) {
continue;
}
const Vector<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> indices;
for (const aal::PropagateRelation &relation :
relations_by_node[output_socket.owner_node().index()]->propagate_relations) {
if (relation.to_geometry_output == output_socket.index()) {
indices.append(relation.from_geometry_input);
}
}
return indices;
});
for (const int input_index : input_indices) {
aal::PropagateRelation relation;
relation.from_geometry_input = input_index;
relation.to_geometry_output = group_output_socket->index();
r_relations.propagate_relations.append(relation);
}
}
}
static void infer_reference_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (!socket_is_field(*group_output_socket)) {
continue;
}
const Vector<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> indices;
for (const aal::ReferenceRelation &relation :
relations_by_node[output_socket.owner_node().index()]->reference_relations) {
if (relation.to_field_output == output_socket.index()) {
indices.append(relation.from_field_input);
}
}
return indices;
});
for (const int input_index : input_indices) {
if (tree.runtime->field_inferencing_interface->inputs[input_index] !=
nodes::InputSocketFieldType::None) {
aal::ReferenceRelation relation;
relation.from_field_input = input_index;
relation.to_field_output = group_output_socket->index();
r_relations.reference_relations.append(relation);
}
}
}
}
/**
* Find group output geometries that contain anonymous attributes referenced by the field.
* If `nullopt` is returned, the field does not depend on any anonymous attribute created in this
* node tree.
*/
static std::optional<Vector<int>> find_available_on_outputs(
const bNodeSocket &initial_group_output_socket,
const bNode &group_output_node,
const Span<const aal::RelationsInNode *> relations_by_node)
{
Set<const bNodeSocket *> geometry_sockets;
{
/* Find the nodes that added anonymous attributes to the field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
found_sockets.add_new(&initial_group_output_socket);
sockets_to_check.push(&initial_group_output_socket);
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::AvailableRelation &relation : relations.available_relations) {
if (socket.index() == relation.field_output) {
const bNodeSocket &geometry_output = node.output_socket(relation.geometry_output);
if (geometry_output.is_available()) {
geometry_sockets.add(&geometry_output);
}
}
}
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
if (socket.index() == relation.to_field_output) {
const bNodeSocket &field_input = node.input_socket(relation.from_field_input);
if (field_input.is_available()) {
if (found_sockets.add(&field_input)) {
sockets_to_check.push(&field_input);
}
}
}
}
}
}
}
if (geometry_sockets.is_empty()) {
/* The field does not depend on any anonymous attribute created within this node tree. */
return std::nullopt;
}
/* Find the group output geometries that contain the anonymous attribute referenced by the field
* output. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
for (const bNodeSocket *socket : geometry_sockets) {
found_sockets.add_new(socket);
sockets_to_check.push(socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
if (socket.index() == relation.from_geometry_input) {
const bNodeSocket &output_socket = node.output_socket(relation.to_geometry_output);
if (output_socket.is_available()) {
if (found_sockets.add(&output_socket)) {
sockets_to_check.push(&output_socket);
}
}
}
}
}
else {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &to_socket = *link->tosock;
if (found_sockets.add(&to_socket)) {
sockets_to_check.push(&to_socket);
}
}
}
}
}
Vector<int> output_indices;
for (const bNodeSocket *socket : group_output_node.input_sockets().drop_back(1)) {
if (found_sockets.contains(socket)) {
output_indices.append(socket->index());
}
}
return output_indices;
}
static void infer_available_relations(const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (!socket_is_field(*group_output_socket)) {
continue;
}
const std::optional<Vector<int>> output_indices = find_available_on_outputs(
*group_output_socket, group_output_node, relations_by_node);
if (output_indices.has_value()) {
if (output_indices->is_empty()) {
r_relations.available_on_none.append(group_output_socket->index());
}
else {
for (const int output_index : *output_indices) {
aal::AvailableRelation relation;
relation.field_output = group_output_socket->index();
relation.geometry_output = output_index;
r_relations.available_relations.append(relation);
}
}
}
}
}
/**
* Returns a list of all the geometry inputs that the field input may be evaluated on.
*/
static Vector<int> find_eval_on_inputs(const bNodeTree &tree,
const int field_input_index,
const Span<const aal::RelationsInNode *> relations_by_node)
{
const Span<const bNode *> group_input_nodes = tree.group_input_nodes();
Set<const bNodeSocket *> geometry_sockets;
{
/* Find all the nodes that evaluate the input field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
for (const bNode *node : group_input_nodes) {
const bNodeSocket &socket = node->output_socket(field_input_index);
found_sockets.add_new(&socket);
sockets_to_check.push(&socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::EvalRelation &relation : relations.eval_relations) {
if (socket.index() == relation.field_input) {
const bNodeSocket &geometry_input = node.input_socket(relation.geometry_input);
if (geometry_input.is_available()) {
geometry_sockets.add(&geometry_input);
}
}
}
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
if (socket.index() == relation.from_field_input) {
const bNodeSocket &field_output = node.output_socket(relation.to_field_output);
if (field_output.is_available()) {
if (found_sockets.add(&field_output)) {
sockets_to_check.push(&field_output);
}
}
}
}
}
else {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &to_socket = *link->tosock;
if (found_sockets.add(&to_socket)) {
sockets_to_check.push(&to_socket);
}
}
}
}
}
}
if (geometry_sockets.is_empty()) {
return {};
}
/* Find the group input geometries whose attributes are propagated to the nodes that evaluate the
* field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> geometry_input_indices;
for (const bNodeSocket *socket : geometry_sockets) {
found_sockets.add_new(socket);
sockets_to_check.push(socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
if (node.is_group_input()) {
geometry_input_indices.append_non_duplicates(socket.index());
}
else {
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
if (socket.index() == relation.to_geometry_output) {
const bNodeSocket &input_socket = node.input_socket(relation.from_geometry_input);
if (input_socket.is_available()) {
if (found_sockets.add(&input_socket)) {
sockets_to_check.push(&input_socket);
}
}
}
}
}
}
}
return geometry_input_indices;
}
static void infer_eval_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
aal::RelationsInNode &r_relations)
{
for (const int input_index : tree.interface_inputs().index_range()) {
if (tree.runtime->field_inferencing_interface->inputs[input_index] ==
nodes::InputSocketFieldType::None) {
continue;
}
const Vector<int> geometry_input_indices = find_eval_on_inputs(
tree, input_index, relations_by_node);
for (const int geometry_input : geometry_input_indices) {
aal::EvalRelation relation;
relation.field_input = input_index;
relation.geometry_input = geometry_input;
r_relations.eval_relations.append(std::move(relation));
}
}
}
bool update_anonymous_attribute_relations(bNodeTree &tree)
{
tree.ensure_topology_cache();
ResourceScope scope;
Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
std::unique_ptr<aal::RelationsInNode> new_relations = std::make_unique<aal::RelationsInNode>();
if (!tree.has_available_link_cycle()) {
if (const bNode *group_output_node = tree.group_output_node()) {
infer_propagate_relations(tree, relations_by_node, *group_output_node, *new_relations);
infer_reference_relations(tree, relations_by_node, *group_output_node, *new_relations);
infer_available_relations(relations_by_node, *group_output_node, *new_relations);
}
infer_eval_relations(tree, relations_by_node, *new_relations);
}
const bool group_interface_changed = !tree.runtime->anonymous_attribute_relations ||
*tree.runtime->anonymous_attribute_relations !=
*new_relations;
tree.runtime->anonymous_attribute_relations = std::move(new_relations);
return group_interface_changed;
}
} // namespace blender::bke::anonymous_attribute_inferencing

View File

@ -473,6 +473,9 @@ class NodeTreeMainUpdater {
if (node_field_inferencing::update_field_inferencing(ntree)) {
result.interface_changed = true;
}
if (anonymous_attribute_inferencing::update_anonymous_attribute_relations(ntree)) {
result.interface_changed = true;
}
}
result.output_changed = this->check_if_output_changed(ntree);

View File

@ -73,9 +73,11 @@ typedef struct BVHTreeRayHit {
} BVHTreeRayHit;
enum {
/* Use a priority queue to process nodes in the optimal order (for slow callbacks) */
BVH_OVERLAP_USE_THREADING = (1 << 0),
BVH_OVERLAP_RETURN_PAIRS = (1 << 1),
/* Use a specialized self-overlap traversal to only test and output every
* pair once, rather than twice in different order as usual. */
BVH_OVERLAP_SELF = (1 << 2),
};
enum {
/* Use a priority queue to process nodes in the optimal order (for slow callbacks) */
@ -163,6 +165,9 @@ bool BLI_bvhtree_update_node(
BVHTree *tree, int index, const float co[3], const float co_moving[3], int numpoints);
/**
* Call #BLI_bvhtree_update_node() first for every node/point/triangle.
*
* Note that this does not rebalance the tree, so if the shape of the mesh changes
* too much, operations on the tree may become suboptimal.
*/
void BLI_bvhtree_update_tree(BVHTree *tree);
@ -191,6 +196,12 @@ BVHTreeOverlap *BLI_bvhtree_overlap(const BVHTree *tree1,
unsigned int *r_overlap_num,
BVHTree_OverlapCallback callback,
void *userdata);
/** Compute overlaps of the tree with itself. This is faster than BLI_bvhtree_overlap
* because it only tests and returns each symmetrical pair once. */
BVHTreeOverlap *BLI_bvhtree_overlap_self(const BVHTree *tree,
unsigned int *r_overlap_num,
BVHTree_OverlapCallback callback,
void *userdata);
int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_num);

View File

@ -1164,8 +1164,8 @@ void box_minmax_bounds_m4(float min[3], float max[3], float boundbox[2][3], floa
/** \name Mapping
* \{ */
void map_to_tube(float *r_u, float *r_v, float x, float y, float z);
void map_to_sphere(float *r_u, float *r_v, float x, float y, float z);
bool map_to_tube(float *r_u, float *r_v, float x, float y, float z);
bool map_to_sphere(float *r_u, float *r_v, float x, float y, float z);
void map_to_plane_v2_v3v3(float r_co[2], const float co[3], const float no[3]);
void map_to_plane_axis_angle_v2_v3v3fl(float r_co[2],
const float co[3],

View File

@ -95,6 +95,7 @@ BLI_STATIC_ASSERT((sizeof(void *) == 8 && sizeof(BVHTree) <= 48) ||
typedef struct BVHOverlapData_Shared {
const BVHTree *tree1, *tree2;
axis_t start_axis, stop_axis;
bool use_self;
/* use for callbacks */
BVHTree_OverlapCallback callback;
@ -1231,6 +1232,62 @@ static bool tree_overlap_traverse_num(BVHOverlapData_Thread *data_thread,
return false;
}
/** Calls the appropriate recursive overlap traversal function. */
static void tree_overlap_invoke_traverse(BVHOverlapData_Thread *data,
const BVHNode *node1,
const BVHNode *node2)
{
if (data->max_interactions) {
tree_overlap_traverse_num(data, node1, node2);
}
else if (data->shared->callback) {
tree_overlap_traverse_cb(data, node1, node2);
}
else {
tree_overlap_traverse(data, node1, node2);
}
}
/** Self-overlap traversal with callback. */
static void tree_overlap_traverse_self_cb(BVHOverlapData_Thread *data_thread, const BVHNode *node)
{
for (int i = 0; i < node->node_num; i++) {
/* Recursively compute self-overlap within each child. */
tree_overlap_traverse_self_cb(data_thread, node->children[i]);
/* Compute overlap of pairs of children, testing each one only once (assume symmetry). */
for (int j = i + 1; j < node->node_num; j++) {
tree_overlap_traverse_cb(data_thread, node->children[i], node->children[j]);
}
}
}
/** Self-overlap traversal without callback. */
static void tree_overlap_traverse_self(BVHOverlapData_Thread *data_thread, const BVHNode *node)
{
for (int i = 0; i < node->node_num; i++) {
/* Recursively compute self-overlap within each child. */
tree_overlap_traverse_self(data_thread, node->children[i]);
/* Compute overlap of pairs of children, testing each one only once (assume symmetry). */
for (int j = i + 1; j < node->node_num; j++) {
tree_overlap_traverse(data_thread, node->children[i], node->children[j]);
}
}
}
/** Calls the appropriate recursive self-overlap traversal. */
static void tree_overlap_invoke_traverse_self(BVHOverlapData_Thread *data_thread,
const BVHNode *node)
{
if (data_thread->shared->callback) {
tree_overlap_traverse_self_cb(data_thread, node);
}
else {
tree_overlap_traverse_self(data_thread, node);
}
}
int BLI_bvhtree_overlap_thread_num(const BVHTree *tree)
{
return (int)MIN2(tree->tree_type, tree->nodes[tree->leaf_num]->node_num);
@ -1243,20 +1300,20 @@ static void bvhtree_overlap_task_cb(void *__restrict userdata,
BVHOverlapData_Thread *data = &((BVHOverlapData_Thread *)userdata)[j];
BVHOverlapData_Shared *data_shared = data->shared;
if (data->max_interactions) {
tree_overlap_traverse_num(data,
data_shared->tree1->nodes[data_shared->tree1->leaf_num]->children[j],
data_shared->tree2->nodes[data_shared->tree2->leaf_num]);
}
else if (data_shared->callback) {
tree_overlap_traverse_cb(data,
data_shared->tree1->nodes[data_shared->tree1->leaf_num]->children[j],
data_shared->tree2->nodes[data_shared->tree2->leaf_num]);
const BVHNode *root1 = data_shared->tree1->nodes[data_shared->tree1->leaf_num];
if (data_shared->use_self) {
/* This code matches one outer loop iteration within traverse_self. */
tree_overlap_invoke_traverse_self(data, root1->children[j]);
for (int k = j + 1; k < root1->node_num; k++) {
tree_overlap_invoke_traverse(data, root1->children[j], root1->children[k]);
}
}
else {
tree_overlap_traverse(data,
data_shared->tree1->nodes[data_shared->tree1->leaf_num]->children[j],
data_shared->tree2->nodes[data_shared->tree2->leaf_num]);
const BVHNode *root2 = data_shared->tree2->nodes[data_shared->tree2->leaf_num];
tree_overlap_invoke_traverse(data, root1->children[j], root2);
}
}
@ -1273,9 +1330,12 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
bool overlap_pairs = (flag & BVH_OVERLAP_RETURN_PAIRS) != 0;
bool use_threading = (flag & BVH_OVERLAP_USE_THREADING) != 0 &&
(tree1->leaf_num > KDOPBVH_THREAD_LEAF_THRESHOLD);
bool use_self = (flag & BVH_OVERLAP_SELF) != 0;
/* 'RETURN_PAIRS' was not implemented without 'max_interactions'. */
BLI_assert(overlap_pairs || max_interactions);
/* Self-overlap does not support max interactions (it's not symmetrical). */
BLI_assert(!use_self || (tree1 == tree2 && !max_interactions));
const int root_node_len = BLI_bvhtree_overlap_thread_num(tree1);
const int thread_num = use_threading ? root_node_len : 1;
@ -1293,6 +1353,10 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
return NULL;
}
if (UNLIKELY(use_self && tree1 != tree2)) {
use_self = false;
}
const BVHNode *root1 = tree1->nodes[tree1->leaf_num];
const BVHNode *root2 = tree2->nodes[tree2->leaf_num];
@ -1308,6 +1372,7 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
data_shared.tree2 = tree2;
data_shared.start_axis = start_axis;
data_shared.stop_axis = stop_axis;
data_shared.use_self = use_self;
/* can be NULL */
data_shared.callback = callback;
@ -1317,7 +1382,7 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
/* init BVHOverlapData_Thread */
data[j].shared = &data_shared;
data[j].overlap = overlap_pairs ? BLI_stack_new(sizeof(BVHTreeOverlap), __func__) : NULL;
data[j].max_interactions = max_interactions;
data[j].max_interactions = use_self ? 0 : max_interactions;
/* for callback */
data[j].thread = j;
@ -1329,16 +1394,11 @@ BVHTreeOverlap *BLI_bvhtree_overlap_ex(
settings.min_iter_per_thread = 1;
BLI_task_parallel_range(0, root_node_len, data, bvhtree_overlap_task_cb, &settings);
}
else if (use_self) {
tree_overlap_invoke_traverse_self(data, root1);
}
else {
if (max_interactions) {
tree_overlap_traverse_num(data, root1, root2);
}
else if (callback) {
tree_overlap_traverse_cb(data, root1, root2);
}
else {
tree_overlap_traverse(data, root1, root2);
}
tree_overlap_invoke_traverse(data, root1, root2);
}
if (overlap_pairs) {
@ -1377,6 +1437,23 @@ BVHTreeOverlap *BLI_bvhtree_overlap(
BVH_OVERLAP_USE_THREADING | BVH_OVERLAP_RETURN_PAIRS);
}
BVHTreeOverlap *BLI_bvhtree_overlap_self(
const BVHTree *tree,
uint *r_overlap_num,
/* optional callback to test the overlap before adding (must be thread-safe!) */
BVHTree_OverlapCallback callback,
void *userdata)
{
return BLI_bvhtree_overlap_ex(tree,
tree,
r_overlap_num,
callback,
userdata,
0,
BVH_OVERLAP_USE_THREADING | BVH_OVERLAP_RETURN_PAIRS |
BVH_OVERLAP_SELF);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -4914,39 +4914,59 @@ void box_minmax_bounds_m4(float min[3], float max[3], float boundbox[2][3], floa
/********************************** Mapping **********************************/
void map_to_tube(float *r_u, float *r_v, const float x, const float y, const float z)
static float snap_coordinate(float u)
{
float len;
*r_v = (z + 1.0f) / 2.0f;
len = sqrtf(x * x + y * y);
if (len > 0.0f) {
*r_u = (1.0f - (atan2f(x / len, y / len) / (float)M_PI)) / 2.0f;
/* Adjust a coordinate value `u` to obtain a value inside the (closed) unit interval.
* i.e. 0.0 <= snap_coordinate(u) <= 1.0.
* Due to round-off errors, it is possible that the value of `u` may be close to the boundary of
* the unit interval, but not exactly on it. In order to handle these cases, `snap_coordinate`
* checks whether `u` is within `epsilon` of the boundary, and if so, it snaps the return value
* to the boundary. */
if (u < 0.0f) {
u += 1.0f; /* Get back into the unit interval. */
}
else {
*r_v = *r_u = 0.0f; /* to avoid un-initialized variables */
BLI_assert(0.0f <= u);
BLI_assert(u <= 1.0f);
const float epsilon = 0.25f / 65536.0f; /* i.e. Quarter of a texel on a 65536 x 65536 texture. */
if (u < epsilon) {
return 0.0f; /* `u` is close to 0, just return 0. */
}
if (1.0f - epsilon < u) {
return 1.0f; /* `u` is close to 1, just return 1. */
}
return u;
}
void map_to_sphere(float *r_u, float *r_v, const float x, const float y, const float z)
bool map_to_tube(float *r_u, float *r_v, const float x, const float y, const float z)
{
float len;
len = sqrtf(x * x + y * y + z * z);
if (len > 0.0f) {
if (UNLIKELY(x == 0.0f && y == 0.0f)) {
*r_u = 0.0f; /* Otherwise domain error. */
}
else {
*r_u = (1.0f - atan2f(x, y) / (float)M_PI) / 2.0f;
}
*r_v = 1.0f - saacos(z / len) / (float)M_PI;
bool regular = true;
if (x * x + y * y < 1e-6f * 1e-6f) {
regular = false; /* We're too close to the cylinder's axis. */
*r_u = 0.5f;
}
else {
*r_v = *r_u = 0.0f; /* to avoid un-initialized variables */
/* The "Regular" case, just compute the coordinate. */
*r_u = snap_coordinate(atan2f(x, -y) / (float)(2.0f * M_PI));
}
*r_v = (z + 1.0f) / 2.0f;
return regular;
}
bool map_to_sphere(float *r_u, float *r_v, const float x, const float y, const float z)
{
bool regular = true;
const float epsilon = 0.25f / 65536.0f; /* i.e. Quarter of a texel on a 65536 x 65536 texture. */
const float len_xy = sqrtf(x * x + y * y);
if (len_xy <= fabsf(z) * epsilon) {
regular = false; /* We're on the line that runs through the north and south poles. */
*r_u = 0.5f;
}
else {
/* The "Regular" case, just compute the coordinate. */
*r_u = snap_coordinate(atan2f(x, -y) / (float)(2.0f * M_PI));
}
*r_v = snap_coordinate(atan2f(len_xy, -z) / (float)M_PI);
return regular;
}
void map_to_plane_v2_v3v3(float r_co[2], const float co[3], const float no[3])

View File

@ -1604,7 +1604,7 @@ static void blo_cache_storage_entry_register(
/** Restore a cache data entry from old ID into new one, when reading some undo memfile. */
static void blo_cache_storage_entry_restore_in_new(
ID * /*id*/, const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v)
ID *id, const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v)
{
BLOCacheStorage *cache_storage = static_cast<BLOCacheStorage *>(cache_storage_v);
@ -1618,6 +1618,15 @@ static void blo_cache_storage_entry_restore_in_new(
return;
}
/* Assume that when ID source is tagged as changed, its caches need to be cleared.
* NOTE: This is mainly a work-around for some IDs, like Image, which use a non-depsgraph-handled
* process for part of their updates.
*/
if (id->recalc & ID_RECALC_SOURCE) {
*cache_p = nullptr;
return;
}
BLOCacheStorageValue *storage_value = static_cast<BLOCacheStorageValue *>(
BLI_ghash_lookup(cache_storage->cache_map, key));
if (storage_value == nullptr) {

View File

@ -116,15 +116,16 @@ set(GLSL_SRC
shaders/compositor_normalize.glsl
shaders/compositor_parallel_reduction.glsl
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_pass.glsl
shaders/compositor_realize_on_domain.glsl
shaders/compositor_screen_lens_distortion.glsl
shaders/compositor_set_alpha.glsl
shaders/compositor_split_viewer.glsl
shaders/compositor_symmetric_blur.glsl
shaders/compositor_symmetric_blur_variable_size.glsl
shaders/compositor_symmetric_separable_blur.glsl
shaders/compositor_tone_map_photoreceptor.glsl
shaders/compositor_tone_map_simple.glsl
shaders/compositor_write_output.glsl
shaders/library/gpu_shader_compositor_alpha_over.glsl
shaders/library/gpu_shader_compositor_blur_common.glsl
@ -204,15 +205,16 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_normalize_info.hh
shaders/infos/compositor_parallel_reduction_info.hh
shaders/infos/compositor_projector_lens_distortion_info.hh
shaders/infos/compositor_read_pass_info.hh
shaders/infos/compositor_realize_on_domain_info.hh
shaders/infos/compositor_screen_lens_distortion_info.hh
shaders/infos/compositor_set_alpha_info.hh
shaders/infos/compositor_split_viewer_info.hh
shaders/infos/compositor_symmetric_blur_info.hh
shaders/infos/compositor_symmetric_blur_variable_size_info.hh
shaders/infos/compositor_symmetric_separable_blur_info.hh
shaders/infos/compositor_tone_map_photoreceptor_info.hh
shaders/infos/compositor_tone_map_simple_info.hh
shaders/infos/compositor_write_output_info.hh
)
set(SHADER_CREATE_INFOS_CONTENT "")

View File

@ -6,6 +6,7 @@
#include "BLI_string_ref.hh"
#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
#include "GPU_texture.h"
@ -41,8 +42,17 @@ class Context {
/* Get the active compositing scene. */
virtual const Scene *get_scene() const = 0;
/* Get the dimensions of the output. */
virtual int2 get_output_size() = 0;
/* Get the width and height of the render passes and of the output texture returned by the
* get_input_texture and get_output_texture methods respectively. */
virtual int2 get_render_size() const = 0;
/* Get the rectangular region representing the area of the input that the compositor will operate
* on. Conversely, the compositor will only update the region of the output that corresponds to
* the compositing region. In the base case, the compositing region covers the entirety of the
* render region with a lower bound of zero and an upper bound of the render size returned by the
* get_render_size method. In other cases, the compositing region might be a subset of the render
* region. */
virtual rcti get_compositing_region() const = 0;
/* Get the texture representing the output where the result of the compositor should be
* written. This should be called by output nodes to get their target texture. */
@ -60,6 +70,9 @@ class Context {
* appropriate place, which can be directly in the UI or just logged to the output stream. */
virtual void set_info_message(StringRef message) const = 0;
/* Get the size of the compositing region. See get_compositing_region(). */
int2 get_compositing_region_size() const;
/* Get the current frame number of the active scene. */
int get_frame_number() const;

View File

@ -143,9 +143,9 @@ class Domain {
public:
/* A size only constructor that sets the transformation to identity. */
Domain(int2 size);
Domain(const int2 &size);
Domain(int2 size, float3x3 transformation);
Domain(const int2 &size, const float3x3 &transformation);
/* Transform the domain by the given transformation. This effectively pre-multiply the given
* transformation by the current transformation of the domain. */

View File

@ -1,5 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_rect.h"
#include "DNA_vec_types.h"
#include "COM_context.hh"
#include "COM_static_cache_manager.hh"
#include "COM_static_shader_manager.hh"
@ -11,6 +15,12 @@ Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool)
{
}
int2 Context::get_compositing_region_size() const
{
const rcti compositing_region = get_compositing_region();
return int2(BLI_rcti_size_x(&compositing_region), BLI_rcti_size_y(&compositing_region));
}
int Context::get_frame_number() const
{
return get_scene()->r.cfra;

View File

@ -7,11 +7,12 @@
namespace blender::realtime_compositor {
Domain::Domain(int2 size) : size(size), transformation(float3x3::identity())
Domain::Domain(const int2 &size) : size(size), transformation(float3x3::identity())
{
}
Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation)
Domain::Domain(const int2 &size, const float3x3 &transformation)
: size(size), transformation(transformation)
{
}

View File

@ -0,0 +1,8 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec4 pass_color = texture_load(input_tx, texel + compositing_region_lower_bound);
imageStore(output_img, texel, READ_EXPRESSION(pass_color));
}

View File

@ -1,8 +0,0 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x);
imageStore(output_img, texel, color);
}

View File

@ -10,5 +10,5 @@ void main()
#endif
vec4 color = condition ? texture_load(first_image_tx, texel) :
texture_load(second_image_tx, texel);
imageStore(output_img, texel, color);
imageStore(output_img, texel + compositing_region_lower_bound, color);
}

View File

@ -0,0 +1,18 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec4 input_color = texture_load(input_tx, texel);
#if defined(DIRECT_OUTPUT)
vec4 output_color = input_color;
#elif defined(OPAQUE_OUTPUT)
vec4 output_color = vec4(input_color.rgb, 1.0);
#elif defined(ALPHA_OUTPUT)
float alpha = texture_load(alpha_tx, texel).x;
vec4 output_color = vec4(input_color.rgb, alpha);
#endif
imageStore(output_img, texel + compositing_region_lower_bound, output_color);
}

View File

@ -61,9 +61,3 @@ GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float)
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque)
.additional_info("compositor_convert_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)")
.do_static_compilation(true);

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_read_pass_shared)
.local_group_size(16, 16)
.push_constant(Type::IVEC2, "compositing_region_lower_bound")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.compute_source("compositor_read_pass.glsl");
GPU_SHADER_CREATE_INFO(compositor_read_pass)
.additional_info("compositor_read_pass_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("READ_EXPRESSION(pass_color)", "pass_color")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_read_pass_alpha)
.additional_info("compositor_read_pass_shared")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("READ_EXPRESSION(pass_color)", "vec4(pass_color.a, vec3(0.0))")
.do_static_compilation(true);

View File

@ -1,11 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_set_alpha)
.local_group_size(16, 16)
.sampler(0, ImageType::FLOAT_2D, "image_tx")
.sampler(1, ImageType::FLOAT_2D, "alpha_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_set_alpha.glsl")
.do_static_compilation(true);

View File

@ -6,6 +6,7 @@ GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared)
.local_group_size(16, 16)
.push_constant(Type::FLOAT, "split_ratio")
.push_constant(Type::IVEC2, "view_size")
.push_constant(Type::IVEC2, "compositing_region_lower_bound")
.sampler(0, ImageType::FLOAT_2D, "first_image_tx")
.sampler(1, ImageType::FLOAT_2D, "second_image_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_write_output_shared)
.local_group_size(16, 16)
.push_constant(Type::IVEC2, "compositing_region_lower_bound")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_write_output.glsl");
GPU_SHADER_CREATE_INFO(compositor_write_output)
.additional_info("compositor_write_output_shared")
.define("DIRECT_OUTPUT")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_write_output_opaque)
.additional_info("compositor_write_output_shared")
.define("OPAQUE_OUTPUT")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_write_output_alpha)
.additional_info("compositor_write_output_shared")
.sampler(1, ImageType::FLOAT_2D, "alpha_tx")
.define("ALPHA_OUTPUT")
.do_static_compilation(true);

View File

@ -2,16 +2,23 @@
#include "BLI_listbase.h"
#include "BLI_math_vec_types.hh"
#include "BLI_rect.h"
#include "BLI_string_ref.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_ID_enums.h"
#include "DNA_camera_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
#include "DNA_view3d_types.h"
#include "DEG_depsgraph_query.h"
#include "ED_view3d.h"
#include "DRW_render.h"
#include "IMB_colormanagement.h"
@ -50,11 +57,68 @@ class Context : public realtime_compositor::Context {
return DRW_context_state_get()->scene;
}
int2 get_output_size() override
int2 get_render_size() const override
{
return int2(float2(DRW_viewport_size_get()));
}
/* Returns true if the viewport is in camera view and has an opaque passepartout, that is, the
* area outside of the camera border is not visible. */
bool is_opaque_camera_view() const
{
/* Check if the viewport is in camera view. */
if (DRW_context_state_get()->rv3d->persp != RV3D_CAMOB) {
return false;
}
/* Check if the camera object that is currently in view is an actual camera. It is possible for
* a non camera object to be used as a camera, in which case, there will be no passepartout or
* any other camera setting, so those pseudo cameras can be ignored. */
Object *camera_object = DRW_context_state_get()->v3d->camera;
if (camera_object->type != OB_CAMERA) {
return false;
}
/* Check if the camera has passepartout active and is totally opaque. */
Camera *cam = static_cast<Camera *>(camera_object->data);
if (!(cam->flag & CAM_SHOWPASSEPARTOUT) || cam->passepartalpha != 1.0f) {
return false;
}
return true;
}
rcti get_compositing_region() const override
{
const int2 viewport_size = int2(float2(DRW_viewport_size_get()));
const rcti render_region = rcti{0, viewport_size.x, 0, viewport_size.y};
/* If the camera view is not opaque, that means the content outside of the camera region is
* visible to some extent, so it would make sense to include them in the compositing region.
* Otherwise, we limit the compositing region to the visible camera region because anything
* outside of the camera region will not be visible anyways. */
if (!is_opaque_camera_view()) {
return render_region;
}
rctf camera_border;
ED_view3d_calc_camera_border(DRW_context_state_get()->scene,
DRW_context_state_get()->depsgraph,
DRW_context_state_get()->region,
DRW_context_state_get()->v3d,
DRW_context_state_get()->rv3d,
&camera_border,
false);
rcti camera_region;
BLI_rcti_rctf_copy_floor(&camera_region, &camera_border);
rcti visible_camera_region;
BLI_rcti_isect(&render_region, &camera_region, &visible_camera_region);
return visible_camera_region;
}
GPUTexture *get_output_texture() override
{
return DRW_viewport_texture_list_get()->color;
@ -83,36 +147,36 @@ class Engine {
TexturePool texture_pool_;
Context context_;
realtime_compositor::Evaluator evaluator_;
/* Stores the viewport size at the time the last compositor evaluation happened. See the
* update_viewport_size method for more information. */
int2 last_viewport_size_;
/* Stores the compositing region size at the time the last compositor evaluation happened. See
* the update_compositing_region_size method for more information. */
int2 last_compositing_region_size_;
public:
Engine(char *info_message)
: context_(texture_pool_, info_message),
evaluator_(context_),
last_viewport_size_(context_.get_output_size())
last_compositing_region_size_(context_.get_compositing_region_size())
{
}
/* Update the viewport size and evaluate the compositor. */
/* Update the compositing region size and evaluate the compositor. */
void draw()
{
update_viewport_size();
update_compositing_region_size();
evaluator_.evaluate();
}
/* If the size of the viewport changed from the last time the compositor was evaluated, update
* the viewport size and reset the evaluator. That's because the evaluator compiles the node tree
* in a manner that is specifically optimized for the size of the viewport. This should be called
* before evaluating the compositor. */
void update_viewport_size()
/* If the size of the compositing region changed from the last time the compositor was evaluated,
* update the last compositor region size and reset the evaluator. That's because the evaluator
* compiles the node tree in a manner that is specifically optimized for the size of the
* compositing region. This should be called before evaluating the compositor. */
void update_compositing_region_size()
{
if (last_viewport_size_ == context_.get_output_size()) {
if (last_compositing_region_size_ == context_.get_compositing_region_size()) {
return;
}
last_viewport_size_ = context_.get_output_size();
last_compositing_region_size_ = context_.get_compositing_region_size();
evaluator_.reset();
}

View File

@ -1244,10 +1244,6 @@ static void drw_engines_enable_editors(void)
static bool is_compositor_enabled(void)
{
if (!U.experimental.use_realtime_compositor) {
return false;
}
if (DST.draw_ctx.v3d->shading.use_compositor == V3D_SHADING_USE_COMPOSITOR_DISABLED) {
return false;
}

View File

@ -347,7 +347,7 @@ static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
data.mlooptri = mr->mlooptri;
data.epsilon = BLI_bvhtree_get_epsilon(tree);
BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
BVHTreeOverlap *overlap = BLI_bvhtree_overlap_self(tree, &overlap_len, bvh_overlap_cb, &data);
if (overlap) {
for (int i = 0; i < overlap_len; i++) {
const MPoly *f_hit_pair[2] = {

View File

@ -2845,12 +2845,15 @@ static bool rename_anim_channels(bAnimContext *ac, int channel_index)
int filter;
bool success = false;
/* get the channel that was clicked on */
/* filter channels */
/* Filter relevant channels (note that grease-pencil/annotations are not displayed in Graph
* Editor). */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
if (ELEM(ac->datatype, ANIMCONT_FCURVES, ANIMCONT_NLA)) {
filter |= ANIMFILTER_FCURVESONLY;
}
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* get channel from index */
/* Get channel that was clicked on from index. */
ale = BLI_findlink(&anim_data, channel_index);
if (ale == NULL) {
/* channel not found */

View File

@ -377,6 +377,40 @@ void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor
/* ---------------- */
void ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
{
const BezTriple left_key = fcurve_segment_start_get(fcu, segment->start_index);
const float left_x = left_key.vec[1][0];
const float left_y = left_key.vec[1][1];
const BezTriple right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
const float key_x_range = right_key.vec[1][0] - left_x;
const float key_y_range = right_key.vec[1][1] - left_y;
/* In order to have a curve that favors the right key, the curve needs to be mirrored in x and y.
* Having an exponent that is a fraction of 1 would produce a similar but inferior result. */
const bool inverted = factor > 0.5;
const float exponent = 1 + fabs(factor * 2 - 1) * 4;
for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
/* For easy calculation of the curve, the values are normalized. */
const float normalized_x = (fcu->bezt[i].vec[1][0] - left_x) / key_x_range;
float normalized_y = 0;
if (inverted) {
normalized_y = 1 - pow(1 - normalized_x, exponent);
}
else {
normalized_y = pow(normalized_x, exponent);
}
fcu->bezt[i].vec[1][1] = left_y + normalized_y * key_y_range;
}
}
/* ---------------- */
void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
{
BezTriple left_bezt = fcurve_segment_start_get(fcu, segment->start_index);

View File

@ -66,9 +66,11 @@ typedef struct PoseBlendData {
/* For temp-loading the Action from the pose library. */
AssetTempIDConsumer *temp_id_consumer;
/* Blend factor, interval [-1, 1] for interpolating between current and given pose.
* Positive factors will blend in `act`, whereas negative factors will blend in `act_flipped`. */
/* Blend factor for interpolating between current and given pose.
* 1.0 means "100% pose asset". Negative values and values > 1.0 will be used as-is, and can
* cause interesting effects. */
float blend_factor;
bool is_flipped;
struct PoseBackup *pose_backup;
Object *ob; /* Object to work on. */
@ -85,11 +87,11 @@ typedef struct PoseBlendData {
} PoseBlendData;
/** Return the bAction that should be blended.
* This is either pbd->act or pbd->act_flipped, depending on the sign of the blend factor.
* This is either pbd->act or pbd->act_flipped, depending on is_flipped.
*/
static bAction *poselib_action_to_blend(PoseBlendData *pbd)
{
return (pbd->blend_factor >= 0) ? pbd->act : pbd->act_flipped;
return pbd->is_flipped ? pbd->act_flipped : pbd->act;
}
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
@ -177,27 +179,32 @@ static void poselib_blend_apply(bContext *C, wmOperator *op)
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f);
bAction *to_blend = poselib_action_to_blend(pbd);
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, fabs(pbd->blend_factor));
BKE_pose_apply_action_blend(pbd->ob, to_blend, &anim_eval_context, pbd->blend_factor);
}
/* ---------------------------- */
static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor)
{
const bool sign_changed = signf(new_factor) != signf(pbd->blend_factor);
if (sign_changed) {
/* The zero point was crossed, meaning that the pose will be flipped. This means the pose
* backup has to change, as it only contains the bones for one side. */
BKE_pose_backup_restore(pbd->pose_backup);
BKE_pose_backup_free(pbd->pose_backup);
pbd->blend_factor = new_factor;
pbd->needs_redraw = true;
}
static void poselib_set_flipped(PoseBlendData *pbd, const bool new_flipped)
{
if (pbd->is_flipped == new_flipped) {
return;
}
pbd->blend_factor = CLAMPIS(new_factor, -1.0f, 1.0f);
/* The pose will toggle between flipped and normal. This means the pose
* backup has to change, as it only contains the bones for one side. */
BKE_pose_backup_restore(pbd->pose_backup);
BKE_pose_backup_free(pbd->pose_backup);
pbd->is_flipped = new_flipped;
pbd->needs_redraw = true;
if (sign_changed) {
poselib_backup_posecopy(pbd);
}
poselib_backup_posecopy(pbd);
}
/* Return operator return value. */
@ -220,6 +227,9 @@ static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const
return OPERATOR_RUNNING_MODAL;
}
/* Ctrl manages the 'flipped' state. */
poselib_set_flipped(pbd, event->modifier & KM_CTRL);
/* only accept 'press' event, and ignore 'release', so that we don't get double actions */
if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
return OPERATOR_RUNNING_MODAL;
@ -318,14 +328,12 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
return false;
}
/* Passing `flipped=True` is the same as flipping the sign of the blend factor. */
const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped");
const float multiply_factor = apply_flipped ? -1.0f : 1.0f;
pbd->blend_factor = multiply_factor * RNA_float_get(op->ptr, "blend_factor");
pbd->is_flipped = RNA_boolean_get(op->ptr, "flipped");
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
/* Only construct the flipped pose if there is a chance it's actually needed. */
const bool is_interactive = (event != NULL);
if (is_interactive || pbd->blend_factor < 0) {
if (is_interactive || pbd->is_flipped) {
pbd->act_flipped = flip_pose(C, ob, pbd->act);
}
@ -351,7 +359,8 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
pbd->slider = ED_slider_create(C);
ED_slider_init(pbd->slider, event);
ED_slider_factor_set(pbd->slider, pbd->blend_factor);
ED_slider_allow_overshoot_set(pbd->slider, false);
ED_slider_allow_overshoot_set(pbd->slider, true);
ED_slider_allow_increments_set(pbd->slider, false);
ED_slider_is_bidirectional_set(pbd->slider, true);
}
@ -394,8 +403,8 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
poselib_keytag_pose(C, scene, pbd);
/* Ensure the redo panel has the actually-used value, instead of the initial value. */
RNA_float_set(op->ptr, "blend_factor", fabs(pbd->blend_factor));
RNA_boolean_set(op->ptr, "flipped", pbd->blend_factor < 0);
RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor);
RNA_boolean_set(op->ptr, "flipped", pbd->is_flipped);
break;
}
@ -485,7 +494,11 @@ static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event
strcpy(tab_string, TIP_("[Tab] - Show blended pose"));
}
BLI_snprintf(status_string, sizeof(status_string), "%s | %s", tab_string, slider_string);
BLI_snprintf(status_string,
sizeof(status_string),
"%s | %s | [Ctrl] - Flip Pose",
tab_string,
slider_string);
ED_workspace_status_text(C, status_string);
poselib_blend_apply(C, op);
@ -568,20 +581,18 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
RNA_def_float_factor(ot->srna,
"blend_factor",
1.0f,
-1.0f,
1.0f,
-FLT_MAX,
FLT_MAX,
"Blend Factor",
"Amount that the pose is applied on top of the existing poses. A negative "
"value will apply the pose flipped over the X-axis",
"value will subtract the pose instead of adding it",
-1.0f,
1.0f);
prop = RNA_def_boolean(
ot->srna,
"flipped",
false,
"Apply Flipped",
"When enabled, applies the pose flipped over the X-axis. This is the same as "
"passing a negative `blend_factor`");
prop = RNA_def_boolean(ot->srna,
"flipped",
false,
"Apply Flipped",
"When enabled, applies the pose flipped over the X-axis");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@ -608,11 +619,11 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
prop = RNA_def_float_factor(ot->srna,
"blend_factor",
0.0f,
-1.0f,
1.0f,
-FLT_MAX,
FLT_MAX,
"Blend Factor",
"Amount that the pose is applied on top of the existing poses. A "
"negative value will apply the pose flipped over the X-axis",
"negative value will subtract the pose instead of adding it",
-1.0f,
1.0f);
/* Blending should always start at 0%, and not at whatever percentage was last used. This RNA
@ -624,8 +635,7 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
"flipped",
false,
"Apply Flipped",
"When enabled, applies the pose flipped over the X-axis. This is the "
"same as passing a negative `blend_factor`");
"When enabled, applies the pose flipped over the X-axis");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna,

Some files were not shown because too many files have changed in this diff Show More