WIP: Functions: new local allocator for better memory reuse and performance #104630

Draft
Jacques Lucke wants to merge 44 commits from JacquesLucke/blender:local-allocator into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
137 changed files with 3923 additions and 1290 deletions
Showing only changes of commit 7ed1a1aac8 - Show all commits

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

@ -402,6 +402,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 +467,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
@ -1108,7 +1111,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" )
@ -1217,8 +1222,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 +1486,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 +1582,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 +1603,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 +1630,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 +1656,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 +1857,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 +1912,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 +2108,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 +2243,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 +2267,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 +2321,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 +2337,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 +2925,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 +2952,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 +3001,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 +3040,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
@ -3147,7 +3212,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 +3246,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 +4298,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 +4487,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
@ -4928,10 +5022,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
@ -5582,9 +5678,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

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,7 @@ 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 +113,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,7 @@ 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,6 +57,14 @@ 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;
@ -66,13 +74,15 @@ class DeviceInfo {
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 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

@ -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);
}
@ -444,7 +456,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 +469,12 @@ 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

@ -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 {
/**
* 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 {
/**
@ -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,28 +128,28 @@ 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

@ -1 +1 @@
Subproject commit 4a581c54af9b92cb670d750951b9382160f10f3e
Subproject commit 7084c4ecd97d93459d9d23fd90f81589b09be5df

@ -1 +1 @@
Subproject commit 0b0052bd53ad8249ed07dfb87705c338af698bde
Subproject commit a9d4443c244f89399ec4bcc427e05a07950528cc

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

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

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

@ -11,6 +11,8 @@ struct Mesh;
namespace blender::bke {
class AnonymousAttributePropagationInfo;
/**
* Extrude all splines in the profile curve along the path of every spline in the curve input.
* Transfer curve attributes to the mesh.
@ -23,11 +25,13 @@ namespace blender::bke {
*/
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
const CurvesGeometry &profile,
bool fill_caps);
bool fill_caps,
const AnonymousAttributePropagationInfo &propagation_info);
/**
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
* Transfer curve attributes to the mesh.
*/
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve);
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
const AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::bke

View File

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

@ -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
@ -330,7 +337,11 @@ inline bool topology_cache_is_available(const bNodeSocket &socket)
namespace node_field_inferencing {
bool update_field_inferencing(const bNodeTree &tree);
}
namespace anonymous_attribute_inferencing {
Array<const nodes::aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope);
bool update_anonymous_attribute_relations(bNodeTree &tree);
} // namespace anonymous_attribute_inferencing
} // namespace blender::bke
/* -------------------------------------------------------------------- */

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

@ -336,7 +336,7 @@ namespace attribute_accessor_functions {
template<const ComponentAttributeProviders &providers>
inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_id)
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return false;
}
const StringRef name = attribute_id.name();
@ -346,7 +346,7 @@ inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_i
template<const ComponentAttributeProviders &providers>
inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
@ -396,7 +396,7 @@ template<const ComponentAttributeProviders &providers>
inline AttributeValidator lookup_validator(const void * /*owner*/,
const blender::bke::AttributeIDRef &attribute_id)
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return {};
}
const BuiltinAttributeProvider *provider =
@ -443,7 +443,7 @@ inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner,
template<const ComponentAttributeProviders &providers>
inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
@ -462,7 +462,7 @@ inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attr
template<const ComponentAttributeProviders &providers>
inline bool remove(void *owner, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
@ -487,7 +487,7 @@ inline bool add(void *owner,
if (contains<providers>(owner, attribute_id)) {
return false;
}
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {

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

@ -331,18 +331,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) {
@ -629,7 +630,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);
@ -716,7 +718,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);
@ -753,7 +756,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;
@ -797,10 +801,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) {
@ -2460,7 +2460,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) {
@ -2957,9 +2957,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);
@ -2968,7 +2968,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;
}
@ -3147,20 +3147,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

@ -997,9 +997,11 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
void set_all(Span<float> src) override
{
for (const int64_t index : src.index_range()) {
this->set(index, src[index]);
}
threading::parallel_for(src.index_range(), 4096, [&](const IndexRange range) {
for (const int64_t i : range) {
this->set(i, src[i]);
}
});
}
void materialize(IndexMask mask, MutableSpan<float> r_span) const override
@ -1007,14 +1009,16 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
if (dverts_ == nullptr) {
return r_span.fill_indices(mask, 0.0f);
}
for (const int64_t index : mask) {
if (const MDeformWeight *weight = this->find_weight_at_index(index)) {
r_span[index] = weight->weight;
threading::parallel_for(mask.index_range(), 4096, [&](const IndexRange range) {
for (const int64_t i : mask.slice(range)) {
if (const MDeformWeight *weight = this->find_weight_at_index(i)) {
r_span[i] = weight->weight;
}
else {
r_span[i] = 0.0f;
}
}
else {
r_span[index] = 0.0f;
}
}
});
}
void materialize_to_uninitialized(IndexMask mask, MutableSpan<float> r_span) const override
@ -1051,7 +1055,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);
@ -1075,7 +1079,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);
@ -1096,7 +1100,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;
}

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

@ -839,7 +839,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;

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

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

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

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

@ -48,6 +48,7 @@
#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_unit.h"
@ -64,6 +65,7 @@
#include "ED_armature.h"
#include "ED_keyframes_keylist.h"
#include "ED_keyframing.h"
#include "ED_markers.h"
#include "ED_numinput.h"
#include "ED_screen.h"
@ -1768,18 +1770,6 @@ typedef enum ePosePropagate_Termination {
POSE_PROPAGATE_SELECTED_MARKERS,
} ePosePropagate_Termination;
/**
* Termination data needed for some modes -
* assumes only one of these entries will be needed at a time.
*/
typedef union tPosePropagate_ModeData {
/** Smart holds + before frame: frame number to stop on. */
float end_frame;
/** Selected markers: listbase for CfraElem's marking these frames. */
ListBase sel_markers;
} tPosePropagate_ModeData;
/* --------------------------------- */
/**
@ -1862,80 +1852,11 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
return endFrame;
}
/**
* Get reference value from F-Curve using RNA.
*/
static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
bool found = false;
/* Base pointer is always the `object -> id_ptr`. */
RNA_id_pointer_create(&ob->id, &id_ptr);
/* Resolve the property. */
if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
if (RNA_property_array_check(prop)) {
/* Array. */
if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
found = true;
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
*value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
break;
case PROP_INT:
*value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
break;
case PROP_FLOAT:
*value = RNA_property_float_get_index(&ptr, prop, fcu->array_index);
break;
default:
found = false;
break;
}
}
}
else {
/* Not an array. */
found = true;
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
*value = (float)RNA_property_boolean_get(&ptr, prop);
break;
case PROP_INT:
*value = (float)RNA_property_int_get(&ptr, prop);
break;
case PROP_ENUM:
*value = (float)RNA_property_enum_get(&ptr, prop);
break;
case PROP_FLOAT:
*value = RNA_property_float_get(&ptr, prop);
break;
default:
found = false;
break;
}
}
}
return found;
}
/**
* Propagate just works along each F-Curve in turn.
*/
static void pose_propagate_fcurve(
wmOperator *op, Object *ob, FCurve *fcu, float startFrame, tPosePropagate_ModeData modeData)
static void pose_propagate_fcurve(FCurve *fcu, float start_frame, const float end_frame)
{
const int mode = RNA_enum_get(op->ptr, "mode");
BezTriple *bezt;
float refVal = 0.0f;
bool keyExists;
int i;
bool first = true;
/* Skip if no keyframes to edit. */
if ((fcu->bezt == NULL) || (fcu->totvert < 2)) {
return;
@ -1944,74 +1865,32 @@ static void pose_propagate_fcurve(
/* Find the reference value from bones directly, which means that the user
* doesn't need to firstly keyframe the pose (though this doesn't mean that
* they can't either). */
if (!pose_propagate_get_refVal(ob, fcu, &refVal)) {
return;
}
float refVal = evaluate_fcurve(fcu, start_frame);
/* Find the first keyframe to start propagating from:
* - if there's a keyframe on the current frame, we probably want to save this value there too
* since it may be as of yet un-keyed
* - if starting before the starting frame, don't touch the key, as it may have had some valid
* values
* - if only doing selected keyframes, start from the first one
*/
if (mode != POSE_PROPAGATE_SELECTED_KEYS) {
const int match = BKE_fcurve_bezt_binarysearch_index(
fcu->bezt, startFrame, fcu->totvert, &keyExists);
bool keyExists;
const int match = BKE_fcurve_bezt_binarysearch_index(
fcu->bezt, start_frame, fcu->totvert, &keyExists);
if (fcu->bezt[match].vec[1][0] < startFrame) {
i = match + 1;
}
else {
i = match;
}
int i;
if (fcu->bezt[match].vec[1][0] < start_frame) {
i = match + 1;
}
else {
/* Selected - start from first keyframe. */
i = 0;
i = match;
}
BezTriple *bezt;
for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
/* Additional termination conditions based on the operator 'mode' property go here. */
if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
/* Stop if keyframe is outside the accepted range. */
if (bezt->vec[1][0] > modeData.end_frame) {
break;
}
}
else if (mode == POSE_PROPAGATE_NEXT_KEY) {
/* Stop after the first keyframe has been processed. */
if (first == false) {
break;
}
}
else if (mode == POSE_PROPAGATE_LAST_KEY) {
/* Only affect this frame if it will be the last one. */
if (i != (fcu->totvert - 1)) {
continue;
}
}
else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
/* Only allow if there's a marker on this frame. */
CfraElem *ce = NULL;
/* Stop on matching marker if there is one. */
for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) {
break;
}
}
/* Skip this keyframe if no marker. */
if (ce == NULL) {
continue;
}
}
else if (mode == POSE_PROPAGATE_SELECTED_KEYS) {
/* Only allow if this keyframe is already selected - skip otherwise. */
if (BEZT_ISSEL_ANY(bezt) == 0) {
continue;
}
/* Stop if keyframe is outside the accepted range. */
if (bezt->vec[1][0] > end_frame) {
break;
}
/* Just flatten handles, since values will now be the same either side. */
@ -2020,10 +1899,123 @@ static void pose_propagate_fcurve(
/* Select keyframe to indicate that it's been changed. */
bezt->f2 |= SELECT;
first = false;
}
}
typedef struct FrameLink {
struct FrameLink *next, *prev;
float frame;
} FrameLink;
static void propagate_curve_values(ListBase /*tPChanFCurveLink*/ *pflinks,
const float source_frame,
ListBase /*FrameLink*/ *target_frames)
{
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
FCurve *fcu = (FCurve *)ld->data;
const float current_fcu_value = evaluate_fcurve(fcu, source_frame);
LISTBASE_FOREACH (FrameLink *, target_frame, target_frames) {
insert_vert_fcurve(
fcu, target_frame->frame, current_fcu_value, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NEEDED);
}
}
}
}
static float find_next_key(ListBase *pflinks, const float start_frame)
{
float target_frame = FLT_MAX;
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
FCurve *fcu = (FCurve *)ld->data;
bool replace;
int current_frame_index = BKE_fcurve_bezt_binarysearch_index(
fcu->bezt, start_frame, fcu->totvert, &replace);
if (replace) {
const int bezt_index = min_ii(current_frame_index + 1, fcu->totvert - 1);
target_frame = min_ff(target_frame, fcu->bezt[bezt_index].vec[1][0]);
}
else {
target_frame = min_ff(target_frame, fcu->bezt[current_frame_index].vec[1][0]);
}
}
}
return target_frame;
}
static float find_last_key(ListBase *pflinks)
{
float target_frame = FLT_MIN;
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
FCurve *fcu = (FCurve *)ld->data;
target_frame = max_ff(target_frame, fcu->bezt[fcu->totvert - 1].vec[1][0]);
}
}
return target_frame;
}
static void get_selected_marker_positions(Scene *scene, ListBase /*FrameLink*/ *target_frames)
{
ListBase selected_markers = {NULL, NULL};
ED_markers_make_cfra_list(&scene->markers, &selected_markers, SELECT);
LISTBASE_FOREACH (CfraElem *, marker, &selected_markers) {
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Marker Key Link");
link->frame = marker->cfra;
BLI_addtail(target_frames, link);
}
BLI_freelistN(&selected_markers);
}
static void get_keyed_frames_in_range(ListBase *pflinks,
const float start_frame,
const float end_frame,
ListBase /*FrameLink*/ *target_frames)
{
struct AnimKeylist *keylist = ED_keylist_create();
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
FCurve *fcu = (FCurve *)ld->data;
fcurve_to_keylist(NULL, fcu, keylist, 0);
}
}
LISTBASE_FOREACH (ActKeyColumn *, column, ED_keylist_listbase(keylist)) {
if (column->cfra <= start_frame) {
continue;
}
if (column->cfra > end_frame) {
break;
}
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Marker Key Link");
link->frame = column->cfra;
BLI_addtail(target_frames, link);
}
ED_keylist_free(keylist);
}
static void get_selected_frames(ListBase *pflinks, ListBase /*FrameLink*/ *target_frames)
{
struct AnimKeylist *keylist = ED_keylist_create();
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, pflinks) {
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
FCurve *fcu = (FCurve *)ld->data;
fcurve_to_keylist(NULL, fcu, keylist, 0);
}
}
LISTBASE_FOREACH (ActKeyColumn *, column, ED_keylist_listbase(keylist)) {
if (!column->sel) {
continue;
}
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Marker Key Link");
link->frame = column->cfra;
BLI_addtail(target_frames, link);
}
ED_keylist_free(keylist);
}
/* --------------------------------- */
static int pose_propagate_exec(bContext *C, wmOperator *op)
@ -2033,9 +2025,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
View3D *v3d = CTX_wm_view3d(C);
ListBase pflinks = {NULL, NULL};
tPChanFCurveLink *pfl;
tPosePropagate_ModeData modeData;
const int mode = RNA_enum_get(op->ptr, "mode");
/* Isolate F-Curves related to the selected bones. */
@ -2049,40 +2039,73 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
/* Mode-specific data preprocessing (requiring no access to curves). */
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
/* Get a list of selected markers. */
ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
}
else {
/* Assume everything else wants endFrame. */
modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
}
const float end_frame = RNA_float_get(op->ptr, "end_frame");
const float current_frame = BKE_scene_frame_get(scene);
/* For each bone, perform the copying required. */
for (pfl = pflinks.first; pfl; pfl = pfl->next) {
LinkData *ld;
ListBase target_frames = {NULL, NULL};
/* Mode-specific data preprocessing (requiring access to all curves). */
if (mode == POSE_PROPAGATE_SMART_HOLDS) {
/* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
* from the keyframe that occurs after the current frame. */
modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)scene->r.cfra);
switch (mode) {
case POSE_PROPAGATE_NEXT_KEY: {
float target_frame = find_next_key(&pflinks, current_frame);
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Next Key Link");
link->frame = target_frame;
BLI_addtail(&target_frames, link);
propagate_curve_values(&pflinks, current_frame, &target_frames);
break;
}
/* Go through propagating pose to keyframes, curve by curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)scene->r.cfra, modeData);
case POSE_PROPAGATE_LAST_KEY: {
float target_frame = find_last_key(&pflinks);
FrameLink *link = MEM_callocN(sizeof(FrameLink), "Last Key Link");
link->frame = target_frame;
BLI_addtail(&target_frames, link);
propagate_curve_values(&pflinks, current_frame, &target_frames);
break;
}
case POSE_PROPAGATE_SELECTED_MARKERS: {
get_selected_marker_positions(scene, &target_frames);
propagate_curve_values(&pflinks, current_frame, &target_frames);
break;
}
case POSE_PROPAGATE_BEFORE_END: {
get_keyed_frames_in_range(&pflinks, current_frame, FLT_MAX, &target_frames);
propagate_curve_values(&pflinks, current_frame, &target_frames);
break;
}
case POSE_PROPAGATE_BEFORE_FRAME: {
get_keyed_frames_in_range(&pflinks, current_frame, end_frame, &target_frames);
propagate_curve_values(&pflinks, current_frame, &target_frames);
break;
}
case POSE_PROPAGATE_SELECTED_KEYS: {
get_selected_frames(&pflinks, &target_frames);
propagate_curve_values(&pflinks, current_frame, &target_frames);
break;
}
case POSE_PROPAGATE_SMART_HOLDS: {
/* For each bone, perform the copying required. */
LISTBASE_FOREACH (tPChanFCurveLink *, pfl, &pflinks) {
/* Mode-specific data preprocessing (requiring access to all curves). */
/* We store in endFrame the end frame of the "long keyframe" (i.e. a held value)
* starting from the keyframe that occurs after the current frame. */
const int smart_end_frame = pose_propagate_get_boneHoldEndFrame(pfl, current_frame);
/* Go through propagating pose to keyframes, curve by curve. */
LISTBASE_FOREACH (LinkData *, ld, &pfl->fcurves) {
pose_propagate_fcurve((FCurve *)ld->data, current_frame, smart_end_frame);
}
}
break;
}
}
BLI_freelistN(&target_frames);
/* Free temp data. */
poseAnim_mapping_free(&pflinks);
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
BLI_freelistN(&modeData.sel_markers);
}
/* Updates + notifiers. */
FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
poseAnim_mapping_refresh(C, scene, ob);

View File

@ -497,7 +497,8 @@ static bool get_closest_vertex_to_point_in_nurbs(const ViewContext *vc,
int handle_display = vc->v3d->overlay.handle_display;
if (handle_display == CURVE_HANDLE_NONE ||
(handle_display == CURVE_HANDLE_SELECTED && !BEZT_ISSEL_ANY(bezt))) {
start = 1, end = 2;
start = 1;
end = 2;
}
/* Loop over each of the 3 points of the #BezTriple and update data of closest bezt. */
@ -1169,7 +1170,8 @@ static void move_segment(ViewContext *vc, MoveSegmentData *seg_data, const wmEve
BezTriple *temp_bezt = bezt2;
bezt2 = bezt1;
bezt1 = temp_bezt;
h1 = 0, h2 = 2;
h1 = 0;
h2 = 2;
}
const float t = max_ff(min_ff(seg_data->t, 0.9f), 0.1f);

View File

@ -69,73 +69,79 @@ bool ED_space_clip_maskedit_mask_poll(struct bContext *C);
* - Mask has visible and editable splines. */
bool ED_space_clip_maskedit_mask_visible_splines_poll(struct bContext *C);
void ED_space_clip_get_size(struct SpaceClip *sc, int *width, int *height);
void ED_space_clip_get_size_fl(struct SpaceClip *sc, float size[2]);
void ED_space_clip_get_zoom(struct SpaceClip *sc,
struct ARegion *region,
void ED_space_clip_get_size(const struct SpaceClip *sc, int *width, int *height);
void ED_space_clip_get_size_fl(const struct SpaceClip *sc, float size[2]);
void ED_space_clip_get_zoom(const struct SpaceClip *sc,
const struct ARegion *region,
float *zoomx,
float *zoomy);
void ED_space_clip_get_aspect(struct SpaceClip *sc, float *aspx, float *aspy);
void ED_space_clip_get_aspect_dimension_aware(struct SpaceClip *sc, float *aspx, float *aspy);
void ED_space_clip_get_aspect(const struct SpaceClip *sc, float *aspx, float *aspy);
void ED_space_clip_get_aspect_dimension_aware(const struct SpaceClip *sc,
float *aspx,
float *aspy);
/**
* Return current frame number in clip space.
*/
int ED_space_clip_get_clip_frame_number(struct SpaceClip *sc);
int ED_space_clip_get_clip_frame_number(const struct SpaceClip *sc);
struct ImBuf *ED_space_clip_get_buffer(struct SpaceClip *sc);
struct ImBuf *ED_space_clip_get_stable_buffer(struct SpaceClip *sc,
struct ImBuf *ED_space_clip_get_buffer(const struct SpaceClip *sc);
struct ImBuf *ED_space_clip_get_stable_buffer(const struct SpaceClip *sc,
float loc[2],
float *scale,
float *angle);
bool ED_space_clip_get_position(struct SpaceClip *sc,
struct ARegion *region,
bool ED_space_clip_get_position(const struct SpaceClip *sc,
const struct ARegion *region,
int mval[2],
float fpos[2]);
/**
* Returns color in linear space, matching #ED_space_image_color_sample().
*/
bool ED_space_clip_color_sample(struct SpaceClip *sc,
struct ARegion *region,
bool ED_space_clip_color_sample(const struct SpaceClip *sc,
const struct ARegion *region,
const int mval[2],
float r_col[3]);
void ED_clip_update_frame(const struct Main *mainp, int cfra);
bool ED_clip_view_selection(const struct bContext *C, struct ARegion *region, bool fit);
bool ED_clip_view_selection(const struct bContext *C, const struct ARegion *region, bool fit);
void ED_clip_select_all(struct SpaceClip *sc, int action, bool *r_has_selection);
void ED_clip_select_all(const struct SpaceClip *sc, int action, bool *r_has_selection);
bool ED_clip_can_select(struct bContext *C);
void ED_clip_point_undistorted_pos(struct SpaceClip *sc, const float co[2], float r_co[2]);
void ED_clip_point_stable_pos(
struct SpaceClip *sc, struct ARegion *region, float x, float y, float *xr, float *yr);
void ED_clip_point_undistorted_pos(const struct SpaceClip *sc, const float co[2], float r_co[2]);
void ED_clip_point_stable_pos(const struct SpaceClip *sc,
const struct ARegion *region,
float x,
float y,
float *xr,
float *yr);
/**
* \brief the reverse of #ED_clip_point_stable_pos(), gets the marker region coords.
* better name here? view_to_track / track_to_view or so?
*/
void ED_clip_point_stable_pos__reverse(struct SpaceClip *sc,
struct ARegion *region,
void ED_clip_point_stable_pos__reverse(const struct SpaceClip *sc,
const struct ARegion *region,
const float co[2],
float r_co[2]);
/**
* Takes `event->mval`.
*/
void ED_clip_mouse_pos(struct SpaceClip *sc,
struct ARegion *region,
void ED_clip_mouse_pos(const struct SpaceClip *sc,
const struct ARegion *region,
const int mval[2],
float co[2]);
bool ED_space_clip_check_show_trackedit(struct SpaceClip *sc);
bool ED_space_clip_check_show_maskedit(struct SpaceClip *sc);
bool ED_space_clip_check_show_trackedit(const struct SpaceClip *sc);
bool ED_space_clip_check_show_maskedit(const struct SpaceClip *sc);
struct MovieClip *ED_space_clip_get_clip(struct SpaceClip *sc);
struct MovieClip *ED_space_clip_get_clip(const struct SpaceClip *sc);
void ED_space_clip_set_clip(struct bContext *C,
struct bScreen *screen,
struct SpaceClip *sc,
struct MovieClip *clip);
struct Mask *ED_space_clip_get_mask(struct SpaceClip *sc);
struct Mask *ED_space_clip_get_mask(const struct SpaceClip *sc);
void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask);
/* Locked state is used to preserve current clip editor viewport upon changes. Example usage:

View File

@ -411,6 +411,7 @@ void blend_to_neighbor_fcurve_segment(struct FCurve *fcu,
struct FCurveSegment *segment,
float factor);
void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor);
void ease_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor);
bool decimate_fcurve(struct bAnimListElem *ale, float remove_ratio, float error_sq_max);
void blend_to_default_fcurve(struct PointerRNA *id_ptr, struct FCurve *fcu, float factor);
/**

View File

@ -135,7 +135,8 @@ static uiTooltipField *text_field_add(uiTooltipData *data,
uiTooltipField *field = text_field_add_only(data);
field->format = {};
field->format.style = style;
field->format.color_id = color, field->format.is_pad = is_pad;
field->format.color_id = color;
field->format.is_pad = is_pad;
return field;
}

View File

@ -1484,5 +1484,6 @@ void ED_mesh_split_faces(Mesh *mesh)
return;
}
geometry::split_edges(*mesh, split_mask);
const bke::AnonymousAttributePropagationInfo propagation_info;
geometry::split_edges(*mesh, split_mask, propagation_info);
}

View File

@ -143,7 +143,7 @@ bool ED_space_clip_maskedit_mask_visible_splines_poll(bContext *C)
/** \name Common Editing Functions
* \{ */
void ED_space_clip_get_size(SpaceClip *sc, int *width, int *height)
void ED_space_clip_get_size(const SpaceClip *sc, int *width, int *height)
{
if (sc->clip) {
BKE_movieclip_get_size(sc->clip, &sc->user, width, height);
@ -153,7 +153,7 @@ void ED_space_clip_get_size(SpaceClip *sc, int *width, int *height)
}
}
void ED_space_clip_get_size_fl(SpaceClip *sc, float size[2])
void ED_space_clip_get_size_fl(const SpaceClip *sc, float size[2])
{
int size_i[2];
ED_space_clip_get_size(sc, &size_i[0], &size_i[1]);
@ -161,7 +161,7 @@ void ED_space_clip_get_size_fl(SpaceClip *sc, float size[2])
size[1] = size_i[1];
}
void ED_space_clip_get_zoom(SpaceClip *sc, ARegion *region, float *zoomx, float *zoomy)
void ED_space_clip_get_zoom(const SpaceClip *sc, const ARegion *region, float *zoomx, float *zoomy)
{
int width, height;
@ -173,7 +173,7 @@ void ED_space_clip_get_zoom(SpaceClip *sc, ARegion *region, float *zoomx, float
(BLI_rctf_size_y(&region->v2d.cur) * height);
}
void ED_space_clip_get_aspect(SpaceClip *sc, float *aspx, float *aspy)
void ED_space_clip_get_aspect(const SpaceClip *sc, float *aspx, float *aspy)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
@ -194,7 +194,7 @@ void ED_space_clip_get_aspect(SpaceClip *sc, float *aspx, float *aspy)
}
}
void ED_space_clip_get_aspect_dimension_aware(SpaceClip *sc, float *aspx, float *aspy)
void ED_space_clip_get_aspect_dimension_aware(const SpaceClip *sc, float *aspx, float *aspy)
{
int w, h;
@ -228,7 +228,7 @@ void ED_space_clip_get_aspect_dimension_aware(SpaceClip *sc, float *aspx, float
}
}
int ED_space_clip_get_clip_frame_number(SpaceClip *sc)
int ED_space_clip_get_clip_frame_number(const SpaceClip *sc)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
@ -236,7 +236,7 @@ int ED_space_clip_get_clip_frame_number(SpaceClip *sc)
return BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
}
ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
ImBuf *ED_space_clip_get_buffer(const SpaceClip *sc)
{
if (sc->clip) {
ImBuf *ibuf;
@ -255,7 +255,10 @@ ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
return NULL;
}
ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale, float *angle)
ImBuf *ED_space_clip_get_stable_buffer(const SpaceClip *sc,
float loc[2],
float *scale,
float *angle)
{
if (sc->clip) {
ImBuf *ibuf;
@ -275,8 +278,8 @@ ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale
return NULL;
}
bool ED_space_clip_get_position(struct SpaceClip *sc,
struct ARegion *region,
bool ED_space_clip_get_position(const SpaceClip *sc,
const ARegion *region,
int mval[2],
float fpos[2])
{
@ -292,7 +295,10 @@ bool ED_space_clip_get_position(struct SpaceClip *sc,
return true;
}
bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, const int mval[2], float r_col[3])
bool ED_space_clip_color_sample(const SpaceClip *sc,
const ARegion *region,
const int mval[2],
float r_col[3])
{
ImBuf *ibuf;
float fx, fy, co[2];
@ -355,7 +361,7 @@ void ED_clip_update_frame(const Main *mainp, int cfra)
}
}
bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit)
bool ED_clip_view_selection(const bContext *C, const ARegion *UNUSED(region), bool fit)
{
float offset_x, offset_y;
float zoom;
@ -371,7 +377,7 @@ bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit
return true;
}
void ED_clip_select_all(SpaceClip *sc, int action, bool *r_has_selection)
void ED_clip_select_all(const SpaceClip *sc, int action, bool *r_has_selection)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
@ -460,7 +466,7 @@ void ED_clip_select_all(SpaceClip *sc, int action, bool *r_has_selection)
}
}
void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[2])
void ED_clip_point_undistorted_pos(const SpaceClip *sc, const float co[2], float r_co[2])
{
copy_v2_v2(r_co, co);
@ -482,7 +488,7 @@ void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[
}
void ED_clip_point_stable_pos(
SpaceClip *sc, ARegion *region, float x, float y, float *xr, float *yr)
const SpaceClip *sc, const ARegion *region, float x, float y, float *xr, float *yr)
{
int sx, sy, width, height;
float zoomx, zoomy, pos[3], imat[4][4];
@ -515,8 +521,8 @@ void ED_clip_point_stable_pos(
}
}
void ED_clip_point_stable_pos__reverse(SpaceClip *sc,
ARegion *region,
void ED_clip_point_stable_pos__reverse(const SpaceClip *sc,
const ARegion *region,
const float co[2],
float r_co[2])
{
@ -539,12 +545,12 @@ void ED_clip_point_stable_pos__reverse(SpaceClip *sc,
r_co[1] = (pos[1] * height * zoomy) + (float)sy;
}
void ED_clip_mouse_pos(SpaceClip *sc, ARegion *region, const int mval[2], float co[2])
void ED_clip_mouse_pos(const SpaceClip *sc, const ARegion *region, const int mval[2], float co[2])
{
ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &co[0], &co[1]);
}
bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
bool ED_space_clip_check_show_trackedit(const SpaceClip *sc)
{
if (sc) {
return sc->mode == SC_MODE_TRACKING;
@ -553,7 +559,7 @@ bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
return false;
}
bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
bool ED_space_clip_check_show_maskedit(const SpaceClip *sc)
{
if (sc) {
return sc->mode == SC_MODE_MASKEDIT;
@ -568,7 +574,7 @@ bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
/** \name Clip Editing Functions
* \{ */
MovieClip *ED_space_clip_get_clip(SpaceClip *sc)
MovieClip *ED_space_clip_get_clip(const SpaceClip *sc)
{
return sc->clip;
}
@ -629,7 +635,7 @@ void ED_space_clip_set_clip(bContext *C, bScreen *screen, SpaceClip *sc, MovieCl
/** \name Masking Editing Functions
* \{ */
Mask *ED_space_clip_get_mask(SpaceClip *sc)
Mask *ED_space_clip_get_mask(const SpaceClip *sc)
{
return sc->mask_info.mask;
}

View File

@ -113,6 +113,7 @@ void GRAPH_OT_delete(struct wmOperatorType *ot);
void GRAPH_OT_clean(struct wmOperatorType *ot);
void GRAPH_OT_blend_to_neighbor(struct wmOperatorType *ot);
void GRAPH_OT_breakdown(struct wmOperatorType *ot);
void GRAPH_OT_ease(struct wmOperatorType *ot);
void GRAPH_OT_decimate(struct wmOperatorType *ot);
void GRAPH_OT_blend_to_default(struct wmOperatorType *ot);
void GRAPH_OT_sample(struct wmOperatorType *ot);

View File

@ -462,6 +462,7 @@ void graphedit_operatortypes(void)
WM_operatortype_append(GRAPH_OT_decimate);
WM_operatortype_append(GRAPH_OT_blend_to_neighbor);
WM_operatortype_append(GRAPH_OT_breakdown);
WM_operatortype_append(GRAPH_OT_ease);
WM_operatortype_append(GRAPH_OT_blend_to_default);
WM_operatortype_append(GRAPH_OT_euler_filter);
WM_operatortype_append(GRAPH_OT_delete);

View File

@ -927,3 +927,129 @@ void GRAPH_OT_blend_to_default(wmOperatorType *ot)
1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Ease Operator
* \{ */
static void ease_graph_keys(bAnimContext *ac, const float factor)
{
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
FCurve *fcu = (FCurve *)ale->key_data;
ListBase segments = find_fcurve_segments(fcu);
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
ease_fcurve_segment(fcu, segment, factor);
}
ale->update |= ANIM_UPDATE_DEFAULT;
BLI_freelistN(&segments);
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
static void ease_draw_status_header(bContext *C, tGraphSliderOp *gso)
{
char status_str[UI_MAX_DRAW_STR];
char mode_str[32];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
strcpy(mode_str, TIP_("Ease Keys"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
}
static void ease_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
ease_draw_status_header(C, gso);
/* Reset keyframes to the state at invoke. */
reset_bezts(gso);
const float factor = slider_factor_get_and_remember(op);
ease_graph_keys(&gso->ac, factor);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
static int ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
const int invoke_result = graph_slider_invoke(C, op, event);
if (invoke_result == OPERATOR_CANCELLED) {
return invoke_result;
}
tGraphSliderOp *gso = op->customdata;
gso->modal_update = ease_modal_update;
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
ease_draw_status_header(C, gso);
return invoke_result;
}
static int ease_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
/* Get editor data. */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
const float factor = RNA_float_get(op->ptr, "factor");
ease_graph_keys(&ac, factor);
/* Set notifier that keyframes have changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void GRAPH_OT_ease(wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Ease Keyframes";
ot->idname = "GRAPH_OT_ease";
ot->description = "Align keyframes on a ease-in or ease-out curve";
/* API callbacks. */
ot->invoke = ease_invoke;
ot->modal = graph_slider_modal;
ot->exec = ease_exec;
ot->poll = graphop_editable_keyframes_poll;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_float_factor(ot->srna,
"factor",
0.5f,
-FLT_MAX,
FLT_MAX,
"Curve Bend",
"Control the bend of the curve",
0.0f,
1.0f);
}
/** \} */

View File

@ -288,32 +288,27 @@ struct LinkAndPosition {
float2 multi_socket_position;
};
static void sort_multi_input_socket_links_with_drag(bNode &node,
static void sort_multi_input_socket_links_with_drag(bNodeSocket &socket,
bNodeLink &drag_link,
const float2 &cursor)
{
for (bNodeSocket *socket : node.input_sockets()) {
if (!socket->is_multi_input()) {
continue;
}
const float2 &socket_location = {socket->runtime->locx, socket->runtime->locy};
const float2 &socket_location = {socket.runtime->locx, socket.runtime->locy};
Vector<LinkAndPosition, 8> links;
for (bNodeLink *link : socket->directly_linked_links()) {
const float2 location = node_link_calculate_multi_input_position(
socket_location, link->multi_input_socket_index, link->tosock->runtime->total_inputs);
links.append({link, location});
};
Vector<LinkAndPosition, 8> links;
for (bNodeLink *link : socket.directly_linked_links()) {
const float2 location = node_link_calculate_multi_input_position(
socket_location, link->multi_input_socket_index, link->tosock->runtime->total_inputs);
links.append({link, location});
};
links.append({&drag_link, cursor});
links.append({&drag_link, cursor});
std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
return a.multi_socket_position.y < b.multi_socket_position.y;
});
std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
return a.multi_socket_position.y < b.multi_socket_position.y;
});
for (const int i : links.index_range()) {
links[i].link->multi_input_socket_index = i;
}
for (const int i : links.index_range()) {
links[i].link->multi_input_socket_index = i;
}
}
@ -972,8 +967,8 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
existing_link_connected_to_fromsock->multi_input_socket_index;
continue;
}
if (link.tosock && link.tosock->flag & SOCK_MULTI_INPUT) {
sort_multi_input_socket_links_with_drag(tnode, link, cursor);
if (tsock && tsock->is_multi_input()) {
sort_multi_input_socket_links_with_drag(*tsock, link, cursor);
}
}
}

View File

@ -202,13 +202,18 @@ static void recalcData_sequencer_image(TransInfo *t)
/* Origin. */
float origin[2];
copy_v2_v2(origin, td2d->loc);
i++, td++, td2d++;
i++;
td++;
td2d++;
/* X and Y control points used to read scale and rotation. */
float handle_x[2];
copy_v2_v2(handle_x, td2d->loc);
sub_v2_v2(handle_x, origin);
i++, td++, td2d++;
i++;
td++;
td2d++;
float handle_y[2];
copy_v2_v2(handle_y, td2d->loc);
sub_v2_v2(handle_y, origin);

View File

@ -1098,7 +1098,7 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
const EnumPropertyItem pack_margin_method[] = {
static const EnumPropertyItem pack_margin_method_items[] = {
{ED_UVPACK_MARGIN_SCALED,
"SCALED",
0,
@ -1139,8 +1139,12 @@ void UV_OT_pack_islands(wmOperatorType *ot)
/* properties */
RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", "");
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
RNA_def_enum(
ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", "");
RNA_def_enum(ot->srna,
"margin_method",
pack_margin_method_items,
ED_UVPACK_MARGIN_SCALED,
"Margin Method",
"");
RNA_def_float_factor(
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
}
@ -2077,8 +2081,12 @@ void UV_OT_unwrap(wmOperatorType *ot)
0,
"Use Subdivision Surface",
"Map UVs taking vertex position after Subdivision Surface modifier has been applied");
RNA_def_enum(
ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", "");
RNA_def_enum(ot->srna,
"margin_method",
pack_margin_method_items,
ED_UVPACK_MARGIN_SCALED,
"Margin Method",
"");
RNA_def_float_factor(
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
}
@ -2444,8 +2452,12 @@ void UV_OT_smart_project(wmOperatorType *ot)
DEG2RADF(89.0f));
RNA_def_property_float_default(prop, DEG2RADF(66.0f));
RNA_def_enum(
ot->srna, "margin_method", pack_margin_method, ED_UVPACK_MARGIN_SCALED, "Margin Method", "");
RNA_def_enum(ot->srna,
"margin_method",
pack_margin_method_items,
ED_UVPACK_MARGIN_SCALED,
"Margin Method",
"");
RNA_def_float(ot->srna,
"island_margin",
0.0f,

View File

@ -53,6 +53,10 @@ class Socket : NonCopyable, NonMovable {
* Index of the socket. E.g. 0 for the first input and the first output socket.
*/
int index_in_node_;
/**
* Index of the socket in the entire graph. Every socket has a different index.
*/
int index_in_graph_;
friend Graph;
@ -61,6 +65,7 @@ class Socket : NonCopyable, NonMovable {
bool is_output() const;
int index() const;
int index_in_graph() const;
InputSocket &as_input();
OutputSocket &as_output();
@ -179,6 +184,20 @@ class DummyDebugInfo {
virtual std::string output_name(const int i) const;
};
/**
* Just stores a string per socket in a dummy node.
*/
class SimpleDummyDebugInfo : public DummyDebugInfo {
public:
std::string name;
Vector<std::string> input_names;
Vector<std::string> output_names;
std::string node_name() const override;
std::string input_name(const int i) const override;
std::string output_name(const int i) const override;
};
/**
* A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs
* and outputs of the entire graph. It can have an arbitrary number of inputs and outputs.
@ -205,6 +224,11 @@ class Graph : NonCopyable, NonMovable {
* Contains all nodes in the graph so that it is efficient to iterate over them.
*/
Vector<Node *> nodes_;
/**
* Number of sockets in the graph. Can be used as array size when indexing using
* `Socket::index_in_graph`.
*/
int socket_num_ = 0;
public:
~Graph();
@ -213,6 +237,7 @@ class Graph : NonCopyable, NonMovable {
* Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph.
*/
Span<const Node *> nodes() const;
Span<Node *> nodes();
/**
* Add a new function node with sockets that match the passed in #LazyFunction.
@ -232,10 +257,24 @@ class Graph : NonCopyable, NonMovable {
*/
void add_link(OutputSocket &from, InputSocket &to);
/**
* If the socket is linked, remove the link.
*/
void clear_origin(InputSocket &socket);
/**
* Make sure that #Node::index_in_graph is up to date.
*/
void update_node_indices();
/**
* Make sure that #Socket::index_in_graph is up to date.
*/
void update_socket_indices();
/**
* Number of sockets in the graph.
*/
int socket_num() const;
/**
* Can be used to assert that #update_node_indices has been called.
@ -280,6 +319,11 @@ inline int Socket::index() const
return index_in_node_;
}
inline int Socket::index_in_graph() const
{
return index_in_graph_;
}
inline InputSocket &Socket::as_input()
{
BLI_assert(this->is_input());
@ -445,6 +489,16 @@ inline Span<const Node *> Graph::nodes() const
return nodes_;
}
inline Span<Node *> Graph::nodes()
{
return nodes_;
}
inline int Graph::socket_num() const
{
return socket_num_;
}
/** \} */
} // namespace blender::fn::lazy_function

View File

@ -86,6 +86,14 @@ void Graph::add_link(OutputSocket &from, InputSocket &to)
from.targets_.append(&to);
}
void Graph::clear_origin(InputSocket &socket)
{
if (socket.origin_ != nullptr) {
socket.origin_->targets_.remove_first_occurrence_and_reorder(&socket);
socket.origin_ = nullptr;
}
}
void Graph::update_node_indices()
{
for (const int i : nodes_.index_range()) {
@ -93,6 +101,20 @@ void Graph::update_node_indices()
}
}
void Graph::update_socket_indices()
{
int socket_counter = 0;
for (const int i : nodes_.index_range()) {
for (InputSocket *socket : nodes_[i]->inputs()) {
socket->index_in_graph_ = socket_counter++;
}
for (OutputSocket *socket : nodes_[i]->outputs()) {
socket->index_in_graph_ = socket_counter++;
}
}
socket_num_ = socket_counter;
}
bool Graph::node_indices_are_valid() const
{
for (const int i : nodes_.index_range()) {
@ -152,6 +174,21 @@ std::string DummyDebugInfo::output_name(const int /*i*/) const
return fallback_name;
}
std::string SimpleDummyDebugInfo::node_name() const
{
return this->name;
}
std::string SimpleDummyDebugInfo::input_name(const int i) const
{
return this->input_names[i];
}
std::string SimpleDummyDebugInfo::output_name(const int i) const
{
return this->output_names[i];
}
std::string Graph::ToDotOptions::socket_name(const Socket &socket) const
{
return socket.name();

View File

@ -1080,6 +1080,12 @@ class Executor {
bool try_enable_multi_threading()
{
#ifndef WITH_TBB
/* The non-tbb task pool has the property that it immediately executes tasks under some
* circumstances. This is not supported here because tasks might be scheduled while another
* node is in the middle of being executed on the same thread. */
return false;
#endif
if (this->use_multi_threading()) {
return true;
}

View File

@ -9,15 +9,19 @@
namespace blender::geometry {
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &counts,
bool limit_radius);
bke::CurvesGeometry fillet_curves_poly(
const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &counts,
bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info);
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
bool limit_radius);
bke::CurvesGeometry fillet_curves_bezier(
const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -4,10 +4,14 @@
#include "BLI_index_mask.hh"
#include "BKE_attribute.hh"
struct Mesh;
namespace blender::geometry {
void split_edges(Mesh &mesh, IndexMask mask);
void split_edges(Mesh &mesh,
IndexMask mask,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -19,11 +19,16 @@ namespace blender::geometry {
* intersections of more than three edges will become breaks in curves. Attributes that
* are not built-in on meshes and not curves are transferred to the result curve.
*/
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection);
bke::CurvesGeometry mesh_to_curve_convert(
const Mesh &mesh,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info);
bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
Span<int> vert_indices,
Span<int> curve_offsets,
IndexRange cyclic_curves);
bke::CurvesGeometry create_curve_from_vert_indices(
const Mesh &mesh,
Span<int> vert_indices,
Span<int> curve_offsets,
IndexRange cyclic_curves,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -2,6 +2,8 @@
#include "BLI_index_mask.hh"
#include "BKE_attribute.hh"
#pragma once
struct PointCloud;
@ -17,8 +19,10 @@ namespace blender::geometry {
* Merge selected points into other selected points within the \a merge_distance. The merged
* indices favor speed over accuracy, since the results will depend on the order of the points.
*/
PointCloud *point_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const IndexMask selection);
PointCloud *point_merge_by_distance(
const PointCloud &src_points,
const float merge_distance,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -19,6 +19,8 @@ struct RealizeInstancesOptions {
* instances. Otherwise, instance attributes are ignored.
*/
bool realize_instance_attributes = true;
bke::AnonymousAttributePropagationInfo propagation_info;
};
/**

View File

@ -4,7 +4,7 @@
#include "FN_field.hh"
#include "BKE_anonymous_attribute.hh"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_curves.hh"
namespace blender::geometry {

View File

@ -27,6 +27,7 @@ bool try_curves_conversion_in_place(IndexMask selection,
*/
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
CurveType dst_type);
CurveType dst_type,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -17,8 +17,10 @@ namespace blender::geometry {
*
* \param selection: A selection of curves to consider when subdividing.
*/
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
const VArray<int> &cuts);
bke::CurvesGeometry subdivide_curves(
const bke::CurvesGeometry &src_curves,
IndexMask selection,
const VArray<int> &cuts,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -19,6 +19,7 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
const VArray<float> &starts,
const VArray<float> &ends,
GeometryNodeCurveSampleMode mode);
GeometryNodeCurveSampleMode mode,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -372,7 +372,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
Set<std::string> attributes_to_skip{{"position", "curve_type", "surface_uv_coordinate"}};
attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
if (id.is_named() && attributes_to_skip.contains(id.name())) {
if (attributes_to_skip.contains(id.name())) {
return true;
}
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);

View File

@ -397,12 +397,14 @@ static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
});
}
static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius_input,
const VArray<int> &counts,
const bool limit_radius,
const bool use_bezier_mode)
static bke::CurvesGeometry fillet_curves(
const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius_input,
const VArray<int> &counts,
const bool limit_radius,
const bool use_bezier_mode,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert(
src_curves.curves_range());
@ -520,6 +522,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) {
duplicate_fillet_point_data(
src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span);
@ -528,7 +531,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
if (!unselected_ranges.is_empty()) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
attribute.dst.finish();
@ -538,26 +541,32 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
return dst_curves;
}
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &count,
const bool limit_radius)
bke::CurvesGeometry fillet_curves_poly(
const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &count,
const bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false);
return fillet_curves(
src_curves, curve_selection, radius, count, limit_radius, false, propagation_info);
}
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const bool limit_radius)
bke::CurvesGeometry fillet_curves_bezier(
const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
return fillet_curves(src_curves,
curve_selection,
radius,
VArray<int>::ForSingle(1, src_curves.points_num()),
limit_radius,
true);
true,
propagation_info);
}
} // namespace blender::geometry

View File

@ -3,6 +3,7 @@
#include "BLI_array_utils.hh"
#include "BLI_devirtualize_parameters.hh"
#include "BLI_index_mask.hh"
#include "BLI_user_counter.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
@ -59,35 +60,36 @@ static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
static void add_new_edges(Mesh &mesh,
const Span<MEdge> new_edges,
const Span<int> new_to_old_edges_map)
const Span<int> new_to_old_edges_map,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
/* Store a copy of the IDs locally since we will remove the existing attributes which
* can also free the names, since the API does not provide pointer stability. */
Vector<std::string> named_ids;
Vector<bke::WeakAnonymousAttributeID> anonymous_ids;
Vector<UserCounter<const bke::AnonymousAttributeID>> anonymous_ids;
for (const bke::AttributeIDRef &id : attributes.all_ids()) {
if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) {
continue;
}
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
continue;
}
if (id.is_named()) {
if (!id.is_anonymous()) {
named_ids.append(id.name());
}
else {
anonymous_ids.append(bke::WeakAnonymousAttributeID(&id.anonymous_id()));
BKE_anonymous_attribute_id_increment_weak(&id.anonymous_id());
anonymous_ids.append(&id.anonymous_id());
id.anonymous_id().user_add();
}
}
Vector<bke::AttributeIDRef> local_edge_ids;
for (const StringRef name : named_ids) {
local_edge_ids.append(name);
}
for (const bke::WeakAnonymousAttributeID &id : anonymous_ids) {
local_edge_ids.append(id.get());
for (const UserCounter<const bke::AnonymousAttributeID> &id : anonymous_ids) {
local_edge_ids.append(*id);
}
/* Build new arrays for the copied edge attributes. Unlike vertices, new edges aren't all at the
@ -348,7 +350,9 @@ static void split_edge_per_poly(const int edge_i,
edge_to_loop_map[edge_i].resize(1);
}
void split_edges(Mesh &mesh, const IndexMask mask)
void split_edges(Mesh &mesh,
const IndexMask mask,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
/* Flag vertices that need to be split. */
Array<bool> should_split_vert(mesh.totvert, false);
@ -483,7 +487,7 @@ void split_edges(Mesh &mesh, const IndexMask mask)
/* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */
add_new_vertices(mesh, new_to_old_verts_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info);
BKE_mesh_tag_edges_split(&mesh);
}

View File

@ -19,10 +19,12 @@
namespace blender::geometry {
bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
const Span<int> vert_indices,
const Span<int> curve_offsets,
const IndexRange cyclic_curves)
bke::CurvesGeometry create_curve_from_vert_indices(
const Mesh &mesh,
const Span<int> vert_indices,
const Span<int> curve_offsets,
const IndexRange cyclic_curves,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
@ -43,7 +45,7 @@ bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
continue;
}
if (!attribute_id.should_be_kept()) {
if (attribute_id.is_anonymous() && !propagation_info.propagate(attribute_id.anonymous_id())) {
continue;
}
@ -209,14 +211,17 @@ static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const In
return selected_edges;
}
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection)
bke::CurvesGeometry mesh_to_curve_convert(
const Mesh &mesh,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
Vector<std::pair<int, int>> selected_edges = get_selected_edges(mesh, selection);
const Span<MVert> verts = mesh.verts();
CurveFromEdgesOutput output = edges_to_curve_point_indices(verts, selected_edges);
return create_curve_from_vert_indices(
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves);
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves, propagation_info);
}
} // namespace blender::geometry

View File

@ -15,7 +15,8 @@ namespace blender::geometry {
PointCloud *point_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const IndexMask selection)
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const bke::AttributeAccessor src_attributes = src_points.attributes();
VArraySpan<float3> positions = src_attributes.lookup_or_default<float3>(
@ -125,7 +126,7 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points,
/* Transfer all other attributes. */
for (const bke::AttributeIDRef &id : attribute_ids) {
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
continue;
}

View File

@ -636,8 +636,11 @@ static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
in_geometry_set.gather_attributes_for_propagation(
src_component_types, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes_to_propagate);
in_geometry_set.gather_attributes_for_propagation(src_component_types,
GEO_COMPONENT_TYPE_POINT_CLOUD,
true,
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
r_create_id = attributes_to_propagate.pop_try("id").has_value();
r_create_radii = attributes_to_propagate.pop_try("radius").has_value();
@ -829,8 +832,11 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
in_geometry_set.gather_attributes_for_propagation(
src_component_types, GEO_COMPONENT_TYPE_MESH, true, attributes_to_propagate);
in_geometry_set.gather_attributes_for_propagation(src_component_types,
GEO_COMPONENT_TYPE_MESH,
true,
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("normal");
attributes_to_propagate.remove("shade_smooth");
@ -1149,8 +1155,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
in_geometry_set.gather_attributes_for_propagation(
src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate);
in_geometry_set.gather_attributes_for_propagation(src_component_types,
GEO_COMPONENT_TYPE_CURVE,
true,
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("radius");
attributes_to_propagate.remove("resolution");

View File

@ -53,7 +53,7 @@ static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length
static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id,
const std::array<int, CURVE_TYPES_NUM> &type_counts)
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return true;
}
if (ELEM(attribute_id.name(),
@ -81,7 +81,7 @@ static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attri
"handle_left",
"nurbs_weight",
}};
return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name()));
return !no_interpolation.contains(attribute_id.name());
}
/**

View File

@ -286,8 +286,10 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<
});
}
static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves,
const IndexMask selection)
static bke::CurvesGeometry convert_curves_to_bezier(
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
const VArray<int8_t> src_types = src_curves.curve_types();
@ -315,6 +317,7 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
@ -460,8 +463,10 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s
return dst_curves;
}
static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves,
const IndexMask selection)
static bke::CurvesGeometry convert_curves_to_nurbs(
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const VArray<int8_t> src_types = src_curves.curve_types();
const VArray<bool> src_cyclic = src_curves.cyclic();
@ -487,6 +492,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
@ -639,16 +645,17 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const CurveType dst_type)
const CurveType dst_type,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
switch (dst_type) {
case CURVE_TYPE_CATMULL_ROM:
case CURVE_TYPE_POLY:
return convert_curves_trivial(src_curves, selection, dst_type);
case CURVE_TYPE_BEZIER:
return convert_curves_to_bezier(src_curves, selection);
return convert_curves_to_bezier(src_curves, selection, propagation_info);
case CURVE_TYPE_NURBS:
return convert_curves_to_nurbs(src_curves, selection);
return convert_curves_to_nurbs(src_curves, selection, propagation_info);
}
BLI_assert_unreachable();
return {};

View File

@ -294,9 +294,11 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r);
}
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const VArray<int> &cuts)
bke::CurvesGeometry subdivide_curves(
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const VArray<int> &cuts,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range());
@ -338,7 +340,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
auto subdivide_catmull_rom = [&](IndexMask selection) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
subdivide_attribute_catmull_rom(src_curves,
dst_curves,
selection,
@ -352,7 +354,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
auto subdivide_poly = [&](IndexMask selection) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
attribute.dst.finish();
@ -396,6 +398,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
@ -421,7 +424,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
if (!unselected_ranges.is_empty()) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
attribute.dst.finish();

View File

@ -929,7 +929,8 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const VArray<float> &starts,
const VArray<float> &ends,
const GeometryNodeCurveSampleMode mode)
const GeometryNodeCurveSampleMode mode,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
BLI_assert(selection.size() > 0);
BLI_assert(selection.last() <= src_curves.curves_num());
@ -991,14 +992,19 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
transfer_curve_skip.remove("nurbs_order");
transfer_curve_skip.remove("knots_mode");
}
bke::copy_attribute_domain(
src_attributes, dst_attributes, selection, ATTR_DOMAIN_CURVE, transfer_curve_skip);
bke::copy_attribute_domain(src_attributes,
dst_attributes,
selection,
ATTR_DOMAIN_CURVE,
propagation_info,
transfer_curve_skip);
/* Fetch custom point domain attributes for transfer (copy). */
Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer(
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_left",
"handle_right",
@ -1063,8 +1069,12 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
/* Copy unselected */
if (!inverse_selection.is_empty()) {
transfer_curve_skip.remove("cyclic");
bke::copy_attribute_domain(
src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE, transfer_curve_skip);
bke::copy_attribute_domain(src_attributes,
dst_attributes,
inverse_selection,
ATTR_DOMAIN_CURVE,
propagation_info,
transfer_curve_skip);
/* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */
dst_curves.cyclic_for_write().fill_indices(selection, false);
@ -1075,8 +1085,11 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
}
/* Copy point domain. */
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, copy_point_skip)) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
copy_point_skip)) {
bke::curves::copy_point_data(
src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span);
attribute.dst.finish();

View File

@ -2450,9 +2450,10 @@ static void lineart_object_load_single_instance(LineartData *ld,
}
if (ob->type == OB_MESH) {
use_mesh = BKE_object_get_evaluated_mesh(ob);
if (use_mesh->edit_mesh) {
if ((!use_mesh) || use_mesh->edit_mesh) {
/* If the object is being edited, then the mesh is not evaluated fully into the final
* result, do not load them. */
* result, do not load them. This could be caused by incorrect evaluation order due to
* the way line art uses depsgraph.See T102612 for explaination of this workaround. */
return;
}
}
@ -2460,7 +2461,7 @@ static void lineart_object_load_single_instance(LineartData *ld,
use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true);
}
/* In case we still can not get any mesh geometry data from the object */
/* In case we still can not get any mesh geometry data from the object, same as above. */
if (!use_mesh) {
return;
}

View File

@ -603,9 +603,15 @@ static void lineart_shadow_edge_cut(LineartData *ld,
for (seg = cut_start_after; seg != cut_end_before; seg = nes) {
nes = seg->next;
s1_fb_co_1 = seg->fbc2, s1_fb_co_2 = nes->fbc1;
s1_gloc_1 = seg->g2, s1_gloc_2 = nes->g1;
seg_1 = seg, seg_2 = nes;
s1_fb_co_1 = seg->fbc2;
s1_fb_co_2 = nes->fbc1;
s1_gloc_1 = seg->g2;
s1_gloc_2 = nes->g1;
seg_1 = seg;
seg_2 = nes;
if (seg == cut_start_after) {
lineart_shadow_segment_slice_get(seg->fbc2,
nes->fbc1,
@ -616,7 +622,9 @@ static void lineart_shadow_edge_cut(LineartData *ld,
nes->ratio,
m_fbc1,
m_g1);
s1_fb_co_1 = m_fbc1, s1_gloc_1 = m_g1;
s1_fb_co_1 = m_fbc1;
s1_gloc_1 = m_g1;
seg_1 = new_seg_1;
if (cut_start_after != new_seg_1) {
BLI_insertlinkafter(&e->shadow_segments, cut_start_after, new_seg_1);
@ -634,7 +642,9 @@ static void lineart_shadow_edge_cut(LineartData *ld,
nes->ratio,
m_fbc2,
m_g2);
s1_fb_co_2 = m_fbc2, s1_gloc_2 = m_g2;
s1_fb_co_2 = m_fbc2;
s1_gloc_2 = m_g2;
seg_2 = new_seg_2;
if (cut_end_before != new_seg_2) {
BLI_insertlinkbefore(&e->shadow_segments, cut_end_before, new_seg_2);
@ -821,13 +831,15 @@ static bool lineart_shadow_cast_onto_triangle(LineartData *ld,
interp_v3_v3v3_db(t_gpos1, gpos1, gpos2, gat1);
interp_v3_v3v3_db(t_fbc1, fbc1, fbc2, rat1);
t_fbc1[3] = interpd(fbc2[3], fbc1[3], gat1);
at1 = 0, trimmed1 = true;
at1 = 0;
trimmed1 = true;
}
if (at2 > 1) {
interp_v3_v3v3_db(t_gpos2, gpos1, gpos2, gat2);
interp_v3_v3v3_db(t_fbc2, fbc1, fbc2, rat2);
t_fbc2[3] = interpd(fbc2[3], fbc1[3], gat2);
at2 = 1, trimmed2 = true;
at2 = 1;
trimmed2 = true;
}
}
if (trimmed1) {

View File

@ -141,7 +141,9 @@ GPU_INLINE void *GPU_vertbuf_raw_step(GPUVertBufRaw *a)
{
unsigned char *data = a->data;
a->data += a->stride;
#ifdef DEBUG
BLI_assert(data < a->_data_end);
#endif
return (void *)data;
}

View File

@ -1236,6 +1236,11 @@ mat3 MAT3x3(
{
return mat3(vec3(a1, a2, a3), vec3(b1, b2, b3), vec3(c1, c2, c3));
}
mat3 MAT3x3(
vec3 a, float b1, float b2, float b3, float c1, float c2, float c3)
{
return mat3(a, vec3(b1, b2, b3), vec3(c1, c2, c3));
}
mat3 MAT3x3(float f)
{
return mat3(f);

View File

@ -15,7 +15,15 @@
extern "C" {
#endif
struct AnonymousAttributeID;
/** Workaround to forward-declare C++ type in C header. */
#ifdef __cplusplus
namespace blender::bke {
class AnonymousAttributeID;
} // namespace blender::bke
using AnonymousAttributeIDHandle = blender::bke::AnonymousAttributeID;
#else
typedef struct AnonymousAttributeIDHandle AnonymousAttributeIDHandle;
#endif
/** Descriptor and storage for a custom data layer. */
typedef struct CustomDataLayer {
@ -40,12 +48,10 @@ typedef struct CustomDataLayer {
/** Layer data. */
void *data;
/**
* Run-time identifier for this layer. If no one has a strong reference to this id anymore,
* the layer can be removed. The custom data layer only has a weak reference to the id, because
* otherwise there will always be a strong reference and the attribute can't be removed
* automatically.
* Run-time identifier for this layer. Can be used to retrieve information about where this
* attribute was created.
*/
const struct AnonymousAttributeID *anonymous_id;
const AnonymousAttributeIDHandle *anonymous_id;
} CustomDataLayer;
#define MAX_CUSTOMDATA_LAYER_NAME 64

View File

@ -65,8 +65,7 @@ typedef struct MovieClip {
/** Sequence or movie. */
int source;
/** Last accessed frame number. */
int lastframe;
int _pad;
/** Size of last accessed frame. */
int lastsize[2];

View File

@ -101,6 +101,7 @@ DEF_ENUM(rna_enum_operator_type_flag_items)
DEF_ENUM(rna_enum_operator_return_items)
DEF_ENUM(rna_enum_operator_property_tags)
DEF_ENUM(rna_enum_brush_automasking_flag_items)
DEF_ENUM(rna_enum_brush_sculpt_tool_items)
DEF_ENUM(rna_enum_brush_uv_sculpt_tool_items)
DEF_ENUM(rna_enum_brush_vertex_tool_items)

View File

@ -94,7 +94,7 @@ static const EnumPropertyItem rna_enum_brush_texture_slot_map_texture_mode_items
/* clang-format off */
/* Note: we don't actually turn these into a single enum bit-mask property,
* instead we construct individual boolean properties. */
const EnumPropertyItem RNA_automasking_flags[] = {
const EnumPropertyItem rna_enum_brush_automasking_flag_items[] = {
{BRUSH_AUTOMASKING_TOPOLOGY, "use_automasking_topology", 0,"Topology", "Affect only vertices connected to the active vertex under the brush"},
{BRUSH_AUTOMASKING_FACE_SETS, "use_automasking_face_sets", 0,"Face Sets", "Affect only vertices that share Face Sets with the active vertex"},
{BRUSH_AUTOMASKING_BOUNDARY_EDGES, "use_automasking_boundary_edges", 0,"Mesh Boundary Auto-Masking", "Do not affect non manifold boundary edges"},
@ -3204,7 +3204,7 @@ static void rna_def_brush(BlenderRNA *brna)
"When locked keep using the plane origin of surface where stroke was initiated");
RNA_def_property_update(prop, 0, "rna_Brush_update");
const EnumPropertyItem *entry = RNA_automasking_flags;
const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items;
do {
prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", entry->value);

View File

@ -476,7 +476,7 @@ const EnumPropertyItem rna_enum_bake_save_mode_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_bake_view_from_items[] = {
static const EnumPropertyItem rna_enum_bake_view_from_items[] = {
{R_BAKE_VIEW_FROM_ABOVE_SURFACE,
"ABOVE_SURFACE",
0,
@ -7378,12 +7378,14 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
RNA_def_property_range(prop, 0, INT_MAX);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
RNA_def_property_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "taa_render_samples", PROP_INT, PROP_NONE);
RNA_def_property_ui_text(prop, "Render Samples", "Number of samples per pixel for rendering");
RNA_def_property_range(prop, 1, INT_MAX);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
RNA_def_property_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_taa_reprojection", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_TAA_REPROJECTION);
@ -7393,6 +7395,7 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
"(can leave some ghosting)");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
RNA_def_property_flag(prop, PROP_ANIMATABLE);
/* Screen Space Subsurface Scattering */
prop = RNA_def_property(srna, "sss_samples", PROP_INT, PROP_NONE);

View File

@ -32,8 +32,6 @@
#include "WM_api.h"
#include "WM_types.h"
extern const EnumPropertyItem RNA_automasking_flags[];
const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[] = {
{PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb hairs"},
{PE_BRUSH_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth hairs"},
@ -864,7 +862,7 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update");
const EnumPropertyItem *entry = RNA_automasking_flags;
const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items;
do {
prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", entry->value);

View File

@ -140,7 +140,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
static const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
{GPU_BACKEND_OPENGL, "OPENGL", 0, "OpenGL", "Use OpenGL backend"},
{GPU_BACKEND_METAL, "METAL", 0, "Metal", "Use Metal backend"},
{GPU_BACKEND_VULKAN, "VULKAN", 0, "Vulkan", "Use Vulkan backend"},

View File

@ -884,12 +884,12 @@ static void find_side_effect_nodes_for_viewer_path(
/* Not only mark the viewer node as having side effects, but also all group nodes it is contained
* in. */
r_side_effect_nodes.add(compute_context_builder.hash(),
&find_viewer_lf_node(*found_viewer_node));
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
&find_viewer_lf_node(*found_viewer_node));
compute_context_builder.pop();
while (!compute_context_builder.is_empty()) {
r_side_effect_nodes.add(compute_context_builder.hash(),
&find_group_lf_node(*group_node_stack.pop()));
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
&find_group_lf_node(*group_node_stack.pop()));
compute_context_builder.pop();
}
}
@ -1124,12 +1124,11 @@ static GeometrySet compute_geometry(
{
const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
Span<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
Vector<const lf::InputSocket *> graph_outputs;
for (const bNodeSocket *bsocket : output_node.input_sockets().drop_back(1)) {
const lf::InputSocket &socket = mapping.dummy_socket_map.lookup(bsocket)->as_input();
graph_outputs.append(&socket);
}
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
graph_inputs.extend(mapping.group_output_used_sockets);
graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(),
mapping.attribute_set_by_geometry_output.values().end());
Vector<const lf::InputSocket *> graph_outputs = mapping.standard_group_output_sockets;
Array<GMutablePointer> param_inputs(graph_inputs.size());
Array<GMutablePointer> param_outputs(graph_outputs.size());
@ -1171,21 +1170,36 @@ static GeometrySet compute_geometry(
Vector<GMutablePointer> inputs_to_destruct;
int input_index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, interface_socket, &btree.inputs, input_index) {
if (interface_socket->type == SOCK_GEOMETRY && input_index == 0) {
int input_index = -1;
for (const int i : btree.interface_inputs().index_range()) {
input_index++;
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
param_inputs[input_index] = &input_geometry_set;
continue;
}
const CPPType *type = interface_socket->typeinfo->geometry_nodes_cpp_type;
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
BLI_assert(type != nullptr);
void *value = allocator.allocate(type->size(), type->alignment());
initialize_group_input(*nmd, *interface_socket, input_index, value);
initialize_group_input(*nmd, interface_socket, i, value);
param_inputs[input_index] = {type, value};
inputs_to_destruct.append({type, value});
}
Array<bool> output_used_inputs(btree.interface_outputs().size(), true);
for (const int i : btree.interface_outputs().index_range()) {
input_index++;
param_inputs[input_index] = &output_used_inputs[i];
}
Array<blender::bke::AnonymousAttributeSet> attributes_to_propagate(
mapping.attribute_set_by_geometry_output.size());
for (const int i : attributes_to_propagate.index_range()) {
input_index++;
param_inputs[input_index] = &attributes_to_propagate[i];
}
for (const int i : graph_outputs.index_range()) {
const lf::InputSocket &socket = *graph_outputs[i];
const CPPType &type = socket.type();

View File

@ -19,6 +19,8 @@ struct ModifierData;
namespace blender::nodes {
using bke::AnonymousAttributeFieldInput;
using bke::AnonymousAttributeID;
using bke::AnonymousAttributePropagationInfo;
using bke::AttributeAccessor;
using bke::AttributeFieldInput;
using bke::AttributeIDRef;
@ -26,13 +28,12 @@ using bke::AttributeKind;
using bke::AttributeMetaData;
using bke::AttributeReader;
using bke::AttributeWriter;
using bke::AutoAnonymousAttributeID;
using bke::GAttributeReader;
using bke::GAttributeWriter;
using bke::GSpanAttributeWriter;
using bke::MutableAttributeAccessor;
using bke::SpanAttributeWriter;
using bke::StrongAnonymousAttributeID;
using bke::WeakAnonymousAttributeID;
using fn::Field;
using fn::FieldContext;
using fn::FieldEvaluator;
@ -43,15 +44,38 @@ using fn::ValueOrField;
using geo_eval_log::NamedAttributeUsage;
using geo_eval_log::NodeWarningType;
/**
* An anonymous attribute created by a node.
*/
class NodeAnonymousAttributeID : public AnonymousAttributeID {
std::string long_name_;
public:
NodeAnonymousAttributeID(const Object &object,
const ComputeContext &compute_context,
const bNode &bnode,
const StringRef identifier);
};
class GeoNodeExecParams {
private:
const bNode &node_;
lf::Params &params_;
const lf::Context &lf_context_;
const Map<StringRef, int> &lf_input_for_output_bsocket_usage_;
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output_;
public:
GeoNodeExecParams(const bNode &node, lf::Params &params, const lf::Context &lf_context)
: node_(node), params_(params), lf_context_(lf_context)
GeoNodeExecParams(const bNode &node,
lf::Params &params,
const lf::Context &lf_context,
const Map<StringRef, int> &lf_input_for_output_bsocket_usage,
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output)
: node_(node),
params_(params),
lf_context_(lf_context),
lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage),
lf_input_for_attribute_propagation_to_output_(lf_input_for_attribute_propagation_to_output)
{
}
@ -245,6 +269,49 @@ class GeoNodeExecParams {
void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
/**
* Return true when the anonymous attribute referenced by the given output should be created.
*/
bool anonymous_attribute_output_is_required(const StringRef output_identifier)
{
const int lf_index = lf_input_for_output_bsocket_usage_.lookup(output_identifier);
return params_.get_input<bool>(lf_index);
}
/**
* Return a new anonymous attribute id for the given output. None is returned if the anonymous
* attribute is not needed.
*/
AutoAnonymousAttributeID get_output_anonymous_attribute_id_if_needed(
const StringRef output_identifier)
{
if (!this->anonymous_attribute_output_is_required(output_identifier)) {
return {};
}
const GeoNodesLFUserData &user_data = *this->user_data();
const ComputeContext &compute_context = *user_data.compute_context;
return MEM_new<NodeAnonymousAttributeID>(__func__,
*user_data.modifier_data->self_object,
compute_context,
node_,
output_identifier);
}
/**
* Get information about which anonymous attributes should be propagated to the given output.
*/
AnonymousAttributePropagationInfo get_output_propagation_info(
const StringRef output_identifier) const
{
const int lf_index = lf_input_for_attribute_propagation_to_output_.lookup(output_identifier);
const bke::AnonymousAttributeSet &set = params_.get_input<bke::AnonymousAttributeSet>(
lf_index);
AnonymousAttributePropagationInfo info;
info.names = set.names;
info.propagate_all = false;
return info;
}
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;

View File

@ -78,6 +78,28 @@ struct GeoNodesLFUserData : public lf::UserData {
bool log_socket_values = true;
};
/**
* In the general case, this is #DynamicSocket. That means that to determine if a node group will
* use a particular input, it has to be partially executed.
*
* In other cases, it's not necessary to look into the node group to determine if an input is
* necessary.
*/
enum class InputUsageHintType {
/** The input socket is never used. */
Never,
/** The input socket is used when a subset of the outputs is used. */
DependsOnOutput,
/** Can't determine statically if the input is used, check the corresponding output socket. */
DynamicSocket,
};
struct InputUsageHint {
InputUsageHintType type = InputUsageHintType::DependsOnOutput;
/** Used in depends-on-output mode. */
Vector<int> output_dependencies;
};
/**
* Contains the mapping between the #bNodeTree and the corresponding lazy-function graph.
* This is *not* a one-to-one mapping.
@ -91,7 +113,32 @@ struct GeometryNodeLazyFunctionGraphMapping {
* The inputs sockets in the graph. Multiple group input nodes are combined into one in the
* lazy-function graph.
*/
Vector<lf::OutputSocket *> group_input_sockets;
Vector<const lf::OutputSocket *> group_input_sockets;
/**
* Dummy output sockets that correspond to the active group output node. If there is no such
* node, defaulted fallback outputs are created.
*/
Vector<const lf::InputSocket *> standard_group_output_sockets;
/**
* Dummy boolean sockets that have to be passed in from the outside and indicate whether a
* specific output will be used.
*/
Vector<const lf::OutputSocket *> group_output_used_sockets;
/**
* Dummy boolean sockets that can be used as group output that indicate whether a specific input
* will be used (this may depend on the used outputs as well as other inputs).
*/
Vector<const lf::InputSocket *> group_input_usage_sockets;
/**
* This is an optimization to avoid partially evaluating a node group just to figure out which
* inputs are needed.
*/
Vector<InputUsageHint> group_input_usage_hints;
/**
* If the node group propagates attributes from an input geometry to the output, it has to know
* which attributes should be propagated and which can be removed (for optimization purposes).
*/
Map<int, const lf::OutputSocket *> attribute_set_by_geometry_output;
/**
* A mapping used for logging intermediate values.
*/

View File

@ -50,10 +50,10 @@ Mesh *create_grid_mesh(
int verts_x, int verts_y, float size_x, float size_y, const AttributeIDRef &uv_map_id);
struct ConeAttributeOutputs {
StrongAnonymousAttributeID top_id;
StrongAnonymousAttributeID bottom_id;
StrongAnonymousAttributeID side_id;
StrongAnonymousAttributeID uv_map_id;
AutoAnonymousAttributeID top_id;
AutoAnonymousAttributeID bottom_id;
AutoAnonymousAttributeID side_id;
AutoAnonymousAttributeID uv_map_id;
};
Mesh *create_cylinder_or_cone_mesh(float radius_top,
@ -81,6 +81,7 @@ void separate_geometry(GeometrySet &geometry_set,
eAttrDomain domain,
GeometryNodeDeleteGeometryMode mode,
const Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info,
bool &r_is_error);
void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,

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