WIP: Rewrite asset browser as separate editor #104978

Closed
Julian Eisel wants to merge 68 commits from asset-browser-grid-view into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
728 changed files with 14402 additions and 7915 deletions
Showing only changes of commit 6fc388743d - Show all commits

View File

@ -300,6 +300,9 @@ option(WITH_USD "Enable Universal Scene Description (USD) Suppor
# 3D format support
# Disable opencollada when we don't have precompiled libs
option(WITH_OPENCOLLADA "Enable OpenCollada Support (http://www.opencollada.org)" ON)
option(WITH_IO_WAVEFRONT_OBJ "Enable Wavefront-OBJ 3D file format support (*.obj)" ON)
option(WITH_IO_STL "Enable STL 3D file format support (*.stl)" ON)
option(WITH_IO_GPENCIL "Enable grease-pencil file format IO (*.svg, *.pdf)" ON)
# Sound output
option(WITH_SDL "Enable SDL for sound" ON)

View File

@ -36,19 +36,19 @@ getopt \
-o s:i:t:h \
--long source:,install:,tmp:,info:,threads:,help,show-deps,no-sudo,no-build,no-confirm,\
with-all,with-opencollada,with-jack,with-pulseaudio,with-embree,with-oidn,with-nanovdb,\
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,ver-xr-openxr:,\
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,ver-xr-openxr:,ver-level-zero:\
force-all,force-python,force-boost,force-tbb,\
force-ocio,force-imath,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
force-ffmpeg,force-opencollada,force-alembic,force-embree,force-oidn,force-usd,\
force-xr-openxr,\
force-xr-openxr,force-level-zero,\
build-all,build-python,build-boost,build-tbb,\
build-ocio,build-imath,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\
build-ffmpeg,build-opencollada,build-alembic,build-embree,build-oidn,build-usd,\
build-xr-openxr,\
build-xr-openxr,build-level-zero,\
skip-python,skip-boost,skip-tbb,\
skip-ocio,skip-imath,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\
skip-ffmpeg,skip-opencollada,skip-alembic,skip-embree,skip-oidn,skip-usd,\
skip-xr-openxr \
skip-xr-openxr,skip-level-zero \
-- "$@" \
)
@ -165,6 +165,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--ver-xr-openxr=<ver>
Force version of OpenXR-SDK.
--ver-level-zero=<ver>
Force version of OneAPI Level Zero library.
Note about the --ver-foo options:
It may not always work as expected (some libs are actually checked out from a git rev...), yet it might help
to fix some build issues (like LLVM mismatch with the version used by your graphic system).
@ -226,6 +229,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--build-xr-openxr
Force the build of OpenXR-SDK.
--build-level-zero=<ver>
Force the build of OneAPI Level Zero library.
Note about the --build-foo options:
* They force the script to prefer building dependencies rather than using available packages.
This may make things simpler and allow working around some distribution bugs, but on the other hand it will
@ -293,6 +299,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--force-xr-openxr
Force the rebuild of OpenXR-SDK.
--force-level-zero=<ver>
Force the rebuild of OneAPI Level Zero library.
Note about the --force-foo options:
* They obviously only have an effect if those libraries are built by this script
(i.e. if there is no available and satisfactory package)!
@ -351,7 +360,10 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
Unconditionally skip Universal Scene Description installation/building.
--skip-xr-openxr
Unconditionally skip OpenXR-SDK installation/building.\""
Unconditionally skip OpenXR-SDK installation/building.
--skip-level-zero=<ver>
Unconditionally skip OneAPI Level Zero installation/building.\""
# ----------------------------------------------------------------------------
# Main Vars
@ -573,14 +585,13 @@ OIDN_SKIP=false
ISPC_VERSION="1.17.0"
FFMPEG_VERSION="4.4"
FFMPEG_VERSION_SHORT="4.4"
FFMPEG_VERSION_MIN="3.0"
FFMPEG_VERSION_MEX="5.0"
FFMPEG_FORCE_BUILD=false
FFMPEG_FORCE_REBUILD=false
FFMPEG_SKIP=false
_ffmpeg_list_sep=";"
LEVEL_ZERO_VERSION="1.7.15"
LEVEL_ZERO_VERSION_SHORT="1.7"
LEVEL_ZERO_VERSION_MIN="1.7"
LEVEL_ZERO_VERSION_MEX="2.0"
LEVEL_ZERO_FORCE_BUILD=false
LEVEL_ZERO_FORCE_REBUILD=false
LEVEL_ZERO_SKIP=false
XR_OPENXR_VERSION="1.0.22"
XR_OPENXR_VERSION_SHORT="1.0"
@ -590,6 +601,15 @@ XR_OPENXR_FORCE_BUILD=false
XR_OPENXR_FORCE_REBUILD=false
XR_OPENXR_SKIP=false
FFMPEG_VERSION="5.0"
FFMPEG_VERSION_SHORT="5.0"
FFMPEG_VERSION_MIN="4.0"
FFMPEG_VERSION_MEX="6.0"
FFMPEG_FORCE_BUILD=false
FFMPEG_FORCE_REBUILD=false
FFMPEG_SKIP=false
_ffmpeg_list_sep=";"
# FFMPEG optional libs.
VORBIS_USE=false
VORBIS_DEV=""
@ -781,6 +801,12 @@ while true; do
XR_OPENXR_VERSION_SHORT=$XR_OPENXR_VERSION
shift; shift; continue
;;
--ver-level-zero)
LEVEL_ZERO_VERSION="$2"
LEVEL_ZERO_VERSION_MIN=$LEVEL_ZERO_VERSION
LEVEL_ZERO_VERSION_SHORT=$LEVEL_ZERO_VERSION
shift; shift; continue
;;
--build-all)
PYTHON_FORCE_BUILD=true
BOOST_FORCE_BUILD=true
@ -800,6 +826,7 @@ while true; do
ALEMBIC_FORCE_BUILD=true
USD_FORCE_BUILD=true
XR_OPENXR_FORCE_BUILD=true
LEVEL_ZERO_FORCE_BUILD=true
shift; continue
;;
--build-python)
@ -857,6 +884,9 @@ while true; do
--build-xr-openxr)
XR_OPENXR_FORCE_BUILD=true; shift; continue
;;
--build-level-zero)
LEVEL_ZERO_FORCE_BUILD=true; shift; continue
;;
--force-all)
PYTHON_FORCE_REBUILD=true
BOOST_FORCE_REBUILD=true
@ -876,6 +906,7 @@ while true; do
ALEMBIC_FORCE_REBUILD=true
USD_FORCE_REBUILD=true
XR_OPENXR_FORCE_REBUILD=true
LEVEL_ZERO_FORCE_REBUILD=true
shift; continue
;;
--force-python)
@ -933,6 +964,9 @@ while true; do
--force-xr-openxr)
XR_OPENXR_FORCE_REBUILD=true; shift; continue
;;
--force-level-zero)
LEVEL_ZERO_FORCE_REBUILD=true; shift; continue
;;
--skip-python)
PYTHON_SKIP=true; shift; continue
;;
@ -987,6 +1021,9 @@ while true; do
--skip-xr-openxr)
XR_OPENXR_SKIP=true; shift; continue
;;
--skip-level-zero)
LEVEL_ZERO_SKIP=true; shift; continue
;;
--)
# no more arguments to parse
break
@ -1128,14 +1165,16 @@ OIDN_SOURCE=( "https://github.com/OpenImageDenoise/oidn/releases/download/v${OID
ISPC_BINARY=( "https://github.com/ispc/ispc/releases/download/v${ISPC_VERSION}/ispc-v${ISPC_VERSION}-linux.tar.gz" )
FFMPEG_SOURCE=( "http://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2" )
XR_OPENXR_USE_REPO=false
XR_OPENXR_SOURCE=("https://github.com/KhronosGroup/OpenXR-SDK/archive/release-${XR_OPENXR_VERSION}.tar.gz")
XR_OPENXR_SOURCE_REPO=("https://github.com/KhronosGroup/OpenXR-SDK.git")
XR_OPENXR_REPO_UID="458984d7f59d1ae6dc1b597d94b02e4f7132eaba"
XR_OPENXR_REPO_BRANCH="master"
LEVEL_ZERO_SOURCE=("https://github.com/oneapi-src/level-zero/archive/refs/tags/v${LEVEL_ZERO_VERSION}.tar.gz")
FFMPEG_SOURCE=( "http://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2" )
# C++11 is required now
CXXFLAGS_BACK=$CXXFLAGS
CXXFLAGS="$CXXFLAGS -std=c++11"
@ -1187,7 +1226,8 @@ You may also want to build them yourself (optional ones are [between brackets]):
* [OpenImageDenoise $OIDN_VERSION] (from $OIDN_SOURCE).
* [Alembic $ALEMBIC_VERSION] (from $ALEMBIC_SOURCE).
* [Universal Scene Description $USD_VERSION] (from $USD_SOURCE).
* [OpenXR-SDK $XR_OPENXR_VERSION] (from $XR_OPENXR_SOURCE).\""
* [OpenXR-SDK $XR_OPENXR_VERSION] (from $XR_OPENXR_SOURCE).
* [OneAPI Level Zero $LEVEL_ZERO_VERSION] (from $LEVEL_ZERO_SOURCE).\""
if [ "$DO_SHOW_DEPS" = true ]; then
PRINT ""
@ -3822,6 +3862,103 @@ compile_XR_OpenXR_SDK() {
}
# ----------------------------------------------------------------------------
# Build OneAPI Level Zero library.
_init_level_zero() {
_src=$SRC/level-zero-$LEVEL_ZERO_VERSION
_git=false
_inst=$INST/level-zero-$LEVEL_ZERO_VERSION_SHORT
_inst_shortcut=$INST/level-zero
}
_update_deps_level_zero() {
:
}
clean_Level_Zero() {
_init_level_zero
if [ -d $_inst ]; then
# Force rebuilding the dependencies if needed.
_update_deps_level_zero false true
fi
_clean
}
compile_Level_Zero() {
if [ "$NO_BUILD" = true ]; then
WARNING "--no-build enabled, Level Zero will not be compiled!"
return
fi
# To be changed each time we make edits that would modify the compiled result!
level_zero_magic=1
_init_level_zero
# Force having own builds for the dependencies.
_update_deps_level_zero true false
# Clean install if needed!
magic_compile_check level-zero-$LEVEL_ZERO_VERSION $level_zero_magic
if [ $? -eq 1 -o "$LEVEL_ZERO_FORCE_REBUILD" = true ]; then
clean_Level_Zero
fi
if [ ! -d $_inst ]; then
INFO "Building Level-Zero-$LEVEL_ZERO_VERSION"
# Force rebuilding the dependencies.
_update_deps_level_zero true true
prepare_inst
if [ ! -d $_src ]; then
mkdir -p $SRC
download LEVEL_ZERO_SOURCE[@] "$_src.tar.gz"
INFO "Unpacking Level-Zero-$LEVEL_ZERO_VERSION"
tar -C $SRC -xf $_src.tar.gz
fi
cd $_src
# Always refresh the whole build!
if [ -d build ]; then
rm -rf build
fi
mkdir build
cd build
# Keep flags in sync with LEVEL_ZERO_EXTRA_ARGS in level-zero.cmake!
cmake_d="-D CMAKE_BUILD_TYPE=Release"
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake $cmake_d ..
make -j$THREADS && make install
make clean
if [ ! -d $_inst ]; then
ERROR "Level-Zero-$LEVEL_ZERO_VERSION failed to compile, exiting"
exit 1
fi
magic_compile_set level-zero-$LEVEL_ZERO_VERSION $level_zero_magic
cd $CWD
INFO "Done compiling Level-Zero-$LEVEL_ZERO_VERSION!"
else
INFO "Own Level-Zero-$LEVEL_ZERO_VERSION is up to date, nothing to do!"
INFO "If you want to force rebuild of this lib, use the --force-level-zero option."
fi
if [ -d $_inst ]; then
_create_inst_shortcut
fi
run_ldconfig "level-zero"
}
# ----------------------------------------------------------------------------
# Install on DEB-like
@ -4458,6 +4595,18 @@ install_DEB() {
PRINT ""
compile_XR_OpenXR_SDK
fi
PRINT ""
if [ "$LEVEL_ZERO_SKIP" = true ]; then
WARNING "Skipping Level Zero installation, as requested..."
elif [ "$LEVEL_ZERO_FORCE_BUILD" = true ]; then
INFO "Forced Level Zero building, as requested..."
compile_Level_Zero
else
# No package currently!
PRINT ""
compile_Level_Zero
fi
}
@ -5144,6 +5293,18 @@ install_RPM() {
# No package currently!
compile_XR_OpenXR_SDK
fi
PRINT ""
if [ "$LEVEL_ZERO_SKIP" = true ]; then
WARNING "Skipping Level Zero installation, as requested..."
elif [ "$LEVEL_ZERO_FORCE_BUILD" = true ]; then
INFO "Forced Level Zero building, as requested..."
compile_Level_Zero
else
# No package currently!
PRINT ""
compile_Level_Zero
fi
}
@ -5721,6 +5882,18 @@ install_ARCH() {
# No package currently!
compile_XR_OpenXR_SDK
fi
PRINT ""
if [ "$LEVEL_ZERO_SKIP" = true ]; then
WARNING "Skipping Level Zero installation, as requested..."
elif [ "$LEVEL_ZERO_FORCE_BUILD" = true ]; then
INFO "Forced Level Zero building, as requested..."
compile_Level_Zero
else
# No package currently!
PRINT ""
compile_Level_Zero
fi
}
@ -5895,6 +6068,14 @@ install_OTHER() {
INFO "Forced OpenXR-SDK building, as requested..."
compile_XR_OpenXR_SDK
fi
PRINT ""
if [ "$LEVEL_ZERO_SKIP" = true ]; then
WARNING "Skipping Level Zero installation, as requested..."
elif [ "$LEVEL_ZERO_FORCE_BUILD" = true ]; then
INFO "Forced Level Zero building, as requested..."
compile_Level_Zero
fi
}
# ----------------------------------------------------------------------------
@ -6137,6 +6318,18 @@ print_info() {
fi
fi
# Not yet available in Blender.
#~ if [ "$LEVEL_ZERO_SKIP" = false ]; then
#~ _1="-D WITH_LEVEL_ZERO=ON"
#~ PRINT " $_1"
#~ _buildargs="$_buildargs $_1"
#~ if [ -d $INST/level-zero ]; then
#~ _1="-D LEVEL_ZERO_ROOT_DIR=$INST/level-zero"
#~ PRINT " $_1"
#~ _buildargs="$_buildargs $_1"
#~ fi
#~ fi
PRINT ""
PRINT "Or even simpler, just run (in your blender-source dir):"
PRINT " make -j$THREADS BUILD_CMAKE_ARGS=\"$_buildargs\""

View File

@ -48,10 +48,13 @@ if "%4" == "nobuild" set dobuild=0
REM If Python is be available certain deps may try to
REM to use this over the version we build, to prevent that
REM make sure python is NOT in the path
for %%X in (python.exe) do (set PYTHON=%%~$PATH:X)
if EXIST "%PYTHON%" (
echo PYTHON found at %PYTHON% dependencies cannot be build with python available in the path
REM make sure pythonw is NOT in the path. We look for pythonw.exe
REM since windows apparently ships a python.exe that just opens up
REM the windows store but does not ship any actual python files that
REM could cause issues.
for %%X in (pythonw.exe) do (set PYTHONW=%%~$PATH:X)
if EXIST "%PYTHONW%" (
echo PYTHON found at %PYTHONW% dependencies cannot be build with python available in the path
goto exit
)

View File

@ -37,6 +37,9 @@ set(WITH_IMAGE_TIFF OFF CACHE BOOL "" FORCE)
set(WITH_IMAGE_WEBP OFF CACHE BOOL "" FORCE)
set(WITH_INPUT_NDOF OFF CACHE BOOL "" FORCE)
set(WITH_INTERNATIONAL OFF CACHE BOOL "" FORCE)
set(WITH_IO_STL OFF CACHE BOOL "" FORCE)
set(WITH_IO_WAVEFRONT_OBJ OFF CACHE BOOL "" FORCE)
set(WITH_IO_GPENCIL OFF CACHE BOOL "" FORCE)
set(WITH_JACK OFF CACHE BOOL "" FORCE)
set(WITH_LIBMV OFF CACHE BOOL "" FORCE)
set(WITH_LLVM OFF CACHE BOOL "" FORCE)

View File

@ -1,12 +1,12 @@
sphinx==4.1.1
sphinx==5.0.1
# Sphinx dependencies that are important
Jinja2==3.0.1
Pygments==2.10.0
Jinja2==3.1.2
Pygments==2.12.0
docutils==0.17.1
snowballstemmer==2.1.0
babel==2.9.1
requests==2.26.0
snowballstemmer==2.2.0
babel==2.10.1
requests==2.27.1
# Only needed to match the theme used for the official documentation.
# Without this theme, the default theme will be used.

View File

@ -754,8 +754,6 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel):
layout.use_property_split = True
layout.use_property_decorate = False
with_freestyle = bpy.app.build_options.freestyle
scene = context.scene
rd = scene.render
view_layer = context.view_layer

View File

@ -7,6 +7,8 @@
#include "blender/session.h"
#include "blender/util.h"
#include "util/half.h"
CCL_NAMESPACE_BEGIN
/* Packed Images */
@ -26,10 +28,33 @@ BlenderImageLoader::BlenderImageLoader(BL::Image b_image,
bool BlenderImageLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
{
metadata.width = b_image.size()[0];
metadata.height = b_image.size()[1];
if (b_image.source() != BL::Image::source_TILED) {
/* Image sequence might have different dimensions, and hence needs to be handled in a special
* manner.
* NOTE: Currently the sequences are not handled by this image loader. */
assert(b_image.source() != BL::Image::source_SEQUENCE);
metadata.width = b_image.size()[0];
metadata.height = b_image.size()[1];
metadata.channels = b_image.channels();
}
else {
/* Different UDIM tiles might have different resolutions, so get resolution from the actual
* tile. */
BL::UDIMTile b_udim_tile = b_image.tiles.get(tile_number);
if (b_udim_tile) {
metadata.width = b_udim_tile.size()[0];
metadata.height = b_udim_tile.size()[1];
metadata.channels = b_udim_tile.channels();
}
else {
metadata.width = 0;
metadata.height = 0;
metadata.channels = 0;
}
}
metadata.depth = 1;
metadata.channels = b_image.channels();
if (b_image.is_float()) {
if (metadata.channels == 1) {
@ -62,79 +87,133 @@ bool BlenderImageLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaDat
}
bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata,
void *pixels,
const size_t pixels_size,
void *out_pixels,
const size_t out_pixels_size,
const bool associate_alpha)
{
const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
const int channels = metadata.channels;
if (b_image.is_float()) {
/* image data */
float *image_pixels;
image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile_number);
if (metadata.type == IMAGE_DATA_TYPE_FLOAT || metadata.type == IMAGE_DATA_TYPE_FLOAT4) {
/* Float. */
float *in_pixels = image_get_float_pixels_for_frame(b_image, frame, tile_number);
if (image_pixels && num_pixels * channels == pixels_size) {
memcpy(pixels, image_pixels, pixels_size * sizeof(float));
if (in_pixels && num_pixels * channels == out_pixels_size) {
/* Straight copy pixel data. */
memcpy(out_pixels, in_pixels, out_pixels_size * sizeof(float));
}
else {
/* Missing or invalid pixel data. */
if (channels == 1) {
memset(pixels, 0, num_pixels * sizeof(float));
memset(out_pixels, 0, num_pixels * sizeof(float));
}
else {
const size_t num_pixels_safe = pixels_size / channels;
float *fp = (float *)pixels;
for (int i = 0; i < num_pixels_safe; i++, fp += channels) {
fp[0] = 1.0f;
fp[1] = 0.0f;
fp[2] = 1.0f;
const size_t num_pixels_safe = out_pixels_size / channels;
float *out_pixel = (float *)out_pixels;
for (int i = 0; i < num_pixels_safe; i++, out_pixel += channels) {
out_pixel[0] = 1.0f;
out_pixel[1] = 0.0f;
out_pixel[2] = 1.0f;
if (channels == 4) {
fp[3] = 1.0f;
out_pixel[3] = 1.0f;
}
}
}
}
if (image_pixels) {
MEM_freeN(image_pixels);
if (in_pixels) {
MEM_freeN(in_pixels);
}
}
else if (metadata.type == IMAGE_DATA_TYPE_HALF || metadata.type == IMAGE_DATA_TYPE_HALF4) {
/* Half float. Blender does not have a half type, but in some cases
* we up-sample byte to half to avoid precision loss for colorspace
* conversion. */
unsigned char *in_pixels = image_get_pixels_for_frame(b_image, frame, tile_number);
if (in_pixels && num_pixels * channels == out_pixels_size) {
/* Convert uchar to half. */
const uchar *in_pixel = in_pixels;
half *out_pixel = (half *)out_pixels;
if (associate_alpha && channels == 4) {
for (size_t i = 0; i < num_pixels; i++, in_pixel += 4, out_pixel += 4) {
const float alpha = util_image_cast_to_float(in_pixel[3]);
out_pixel[0] = float_to_half_image(util_image_cast_to_float(in_pixel[0]) * alpha);
out_pixel[1] = float_to_half_image(util_image_cast_to_float(in_pixel[1]) * alpha);
out_pixel[2] = float_to_half_image(util_image_cast_to_float(in_pixel[2]) * alpha);
out_pixel[3] = float_to_half_image(alpha);
}
}
else {
for (size_t i = 0; i < num_pixels; i++) {
for (int c = 0; c < channels; c++, in_pixel++, out_pixel++) {
*out_pixel = float_to_half_image(util_image_cast_to_float(*in_pixel));
}
}
}
}
else {
/* Missing or invalid pixel data. */
if (channels == 1) {
memset(out_pixels, 0, num_pixels * sizeof(half));
}
else {
const size_t num_pixels_safe = out_pixels_size / channels;
half *out_pixel = (half *)out_pixels;
for (int i = 0; i < num_pixels_safe; i++, out_pixel += channels) {
out_pixel[0] = float_to_half_image(1.0f);
out_pixel[1] = float_to_half_image(0.0f);
out_pixel[2] = float_to_half_image(1.0f);
if (channels == 4) {
out_pixel[3] = float_to_half_image(1.0f);
}
}
}
}
if (in_pixels) {
MEM_freeN(in_pixels);
}
}
else {
unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile_number);
/* Byte. */
unsigned char *in_pixels = image_get_pixels_for_frame(b_image, frame, tile_number);
if (image_pixels && num_pixels * channels == pixels_size) {
memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char));
if (in_pixels && num_pixels * channels == out_pixels_size) {
/* Straight copy pixel data. */
memcpy(out_pixels, in_pixels, out_pixels_size * sizeof(unsigned char));
if (associate_alpha && channels == 4) {
/* Premultiply, byte images are always straight for Blender. */
unsigned char *out_pixel = (unsigned char *)out_pixels;
for (size_t i = 0; i < num_pixels; i++, out_pixel += 4) {
out_pixel[0] = (out_pixel[0] * out_pixel[3]) / 255;
out_pixel[1] = (out_pixel[1] * out_pixel[3]) / 255;
out_pixel[2] = (out_pixel[2] * out_pixel[3]) / 255;
}
}
}
else {
/* Missing or invalid pixel data. */
if (channels == 1) {
memset(pixels, 0, pixels_size * sizeof(unsigned char));
memset(out_pixels, 0, out_pixels_size * sizeof(unsigned char));
}
else {
const size_t num_pixels_safe = pixels_size / channels;
unsigned char *cp = (unsigned char *)pixels;
for (size_t i = 0; i < num_pixels_safe; i++, cp += channels) {
cp[0] = 255;
cp[1] = 0;
cp[2] = 255;
const size_t num_pixels_safe = out_pixels_size / channels;
unsigned char *out_pixel = (unsigned char *)out_pixels;
for (size_t i = 0; i < num_pixels_safe; i++, out_pixel += channels) {
out_pixel[0] = 255;
out_pixel[1] = 0;
out_pixel[2] = 255;
if (channels == 4) {
cp[3] = 255;
out_pixel[3] = 255;
}
}
}
}
if (image_pixels) {
MEM_freeN(image_pixels);
}
if (associate_alpha) {
/* Premultiply, byte images are always straight for Blender. */
unsigned char *cp = (unsigned char *)pixels;
for (size_t i = 0; i < num_pixels; i++, cp += channels) {
cp[0] = (cp[0] * cp[3]) / 255;
cp[1] = (cp[1] * cp[3]) / 255;
cp[2] = (cp[2] * cp[3]) / 255;
}
if (in_pixels) {
MEM_freeN(in_pixels);
}
}

View File

@ -11,6 +11,7 @@
# include "util/progress.h"
# include "device/metal/bvh.h"
# include "device/metal/util.h"
CCL_NAMESPACE_BEGIN
@ -18,6 +19,7 @@ CCL_NAMESPACE_BEGIN
{ \
string str = string_printf(__VA_ARGS__); \
progress.set_substatus(str); \
metal_printf("%s\n", str.c_str()); \
}
BVHMetal::BVHMetal(const BVHParams &params_,

View File

@ -31,6 +31,8 @@ class MetalDevice : public Device {
string source[PSO_NUM];
string source_md5[PSO_NUM];
bool capture_enabled = false;
KernelParamsMetal launch_params = {0};
/* MetalRT members ----------------------------------*/

View File

@ -86,6 +86,10 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
use_metalrt = (atoi(metalrt) != 0);
}
if (getenv("CYCLES_DEBUG_METAL_CAPTURE_KERNEL")) {
capture_enabled = true;
}
MTLArgumentDescriptor *arg_desc_params = [[MTLArgumentDescriptor alloc] init];
arg_desc_params.dataType = MTLDataTypePointer;
arg_desc_params.access = MTLArgumentAccessReadOnly;
@ -394,7 +398,7 @@ MetalDevice::MetalMem *MetalDevice::generic_alloc(device_memory &mem)
}
if (size > 0) {
if (mem.type == MEM_DEVICE_ONLY) {
if (mem.type == MEM_DEVICE_ONLY && !capture_enabled) {
options = MTLResourceStorageModePrivate;
}

View File

@ -12,8 +12,6 @@
# include "device/metal/util.h"
# include "kernel/device/metal/globals.h"
# define metal_printf VLOG(4) << string_printf
CCL_NAMESPACE_BEGIN
class MetalDevice;
@ -40,43 +38,82 @@ class MetalDeviceQueue : public DeviceQueue {
virtual void copy_from_device(device_memory &mem) override;
protected:
void setup_capture();
void update_capture(DeviceKernel kernel);
void begin_capture();
void end_capture();
void prepare_resources(DeviceKernel kernel);
id<MTLComputeCommandEncoder> get_compute_encoder(DeviceKernel kernel);
id<MTLBlitCommandEncoder> get_blit_encoder();
MetalDevice *metal_device;
MetalBufferPool temp_buffer_pool;
MetalDevice *metal_device_;
MetalBufferPool temp_buffer_pool_;
API_AVAILABLE(macos(11.0), ios(14.0))
MTLCommandBufferDescriptor *command_buffer_desc = nullptr;
id<MTLDevice> mtlDevice = nil;
id<MTLCommandQueue> mtlCommandQueue = nil;
id<MTLCommandBuffer> mtlCommandBuffer = nil;
id<MTLComputeCommandEncoder> mtlComputeEncoder = nil;
id<MTLBlitCommandEncoder> mtlBlitEncoder = nil;
MTLCommandBufferDescriptor *command_buffer_desc_ = nullptr;
id<MTLDevice> mtlDevice_ = nil;
id<MTLCommandQueue> mtlCommandQueue_ = nil;
id<MTLCommandBuffer> mtlCommandBuffer_ = nil;
id<MTLComputeCommandEncoder> mtlComputeEncoder_ = nil;
id<MTLBlitCommandEncoder> mtlBlitEncoder_ = nil;
API_AVAILABLE(macos(10.14), ios(14.0))
id<MTLSharedEvent> shared_event = nil;
id<MTLSharedEvent> shared_event_ = nil;
API_AVAILABLE(macos(10.14), ios(14.0))
MTLSharedEventListener *shared_event_listener = nil;
MTLSharedEventListener *shared_event_listener_ = nil;
dispatch_queue_t event_queue;
dispatch_semaphore_t wait_semaphore;
dispatch_queue_t event_queue_;
dispatch_semaphore_t wait_semaphore_;
struct CopyBack {
void *host_pointer;
void *gpu_mem;
uint64_t size;
};
std::vector<CopyBack> copy_back_mem;
std::vector<CopyBack> copy_back_mem_;
uint64_t shared_event_id;
uint64_t command_buffers_submitted = 0;
uint64_t command_buffers_completed = 0;
Stats &stats;
uint64_t shared_event_id_;
uint64_t command_buffers_submitted_ = 0;
uint64_t command_buffers_completed_ = 0;
Stats &stats_;
void close_compute_encoder();
void close_blit_encoder();
bool verbose_tracing_ = false;
bool label_command_encoders_ = false;
/* Per-kernel profiling (see CYCLES_METAL_PROFILING). */
struct TimingData {
DeviceKernel kernel;
int work_size;
uint64_t timing_id;
};
std::vector<TimingData> command_encoder_labels_;
API_AVAILABLE(macos(10.14), ios(14.0))
id<MTLSharedEvent> timing_shared_event_ = nil;
uint64_t timing_shared_event_id_;
uint64_t command_buffer_start_timing_id_;
struct TimingStats {
double total_time = 0.0;
uint64_t total_work_size = 0;
uint64_t num_dispatches = 0;
};
TimingStats timing_stats_[DEVICE_KERNEL_NUM];
double last_completion_time_ = 0.0;
/* .gputrace capture (see CYCLES_DEBUG_METAL_CAPTURE_...). */
id<MTLCaptureScope> mtlCaptureScope_ = nil;
DeviceKernel capture_kernel_;
int capture_dispatch_counter_ = 0;
bool capture_samples_ = false;
int capture_reset_counter_ = 0;
bool is_capturing_ = false;
bool is_capturing_to_disk_ = false;
bool has_captured_to_disk_ = false;
};
CCL_NAMESPACE_END

View File

@ -17,46 +17,250 @@ CCL_NAMESPACE_BEGIN
/* MetalDeviceQueue */
MetalDeviceQueue::MetalDeviceQueue(MetalDevice *device)
: DeviceQueue(device), metal_device(device), stats(device->stats)
: DeviceQueue(device), metal_device_(device), stats_(device->stats)
{
if (@available(macos 11.0, *)) {
command_buffer_desc = [[MTLCommandBufferDescriptor alloc] init];
command_buffer_desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus;
command_buffer_desc_ = [[MTLCommandBufferDescriptor alloc] init];
command_buffer_desc_.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus;
}
mtlDevice = device->mtlDevice;
mtlCommandQueue = [mtlDevice newCommandQueue];
mtlDevice_ = device->mtlDevice;
mtlCommandQueue_ = [mtlDevice_ newCommandQueue];
if (@available(macos 10.14, *)) {
shared_event = [mtlDevice newSharedEvent];
shared_event_id = 1;
shared_event_ = [mtlDevice_ newSharedEvent];
shared_event_id_ = 1;
/* Shareable event listener */
event_queue = dispatch_queue_create("com.cycles.metal.event_queue", NULL);
shared_event_listener = [[MTLSharedEventListener alloc] initWithDispatchQueue:event_queue];
event_queue_ = dispatch_queue_create("com.cycles.metal.event_queue", NULL);
shared_event_listener_ = [[MTLSharedEventListener alloc] initWithDispatchQueue:event_queue_];
}
wait_semaphore = dispatch_semaphore_create(0);
wait_semaphore_ = dispatch_semaphore_create(0);
if (@available(macos 10.14, *)) {
if (getenv("CYCLES_METAL_PROFILING")) {
/* Enable per-kernel timing breakdown (shown at end of render). */
timing_shared_event_ = [mtlDevice_ newSharedEvent];
label_command_encoders_ = true;
}
if (getenv("CYCLES_METAL_DEBUG")) {
/* Enable very verbose tracing (shows every dispatch). */
verbose_tracing_ = true;
label_command_encoders_ = true;
}
timing_shared_event_id_ = 1;
}
setup_capture();
}
void MetalDeviceQueue::setup_capture()
{
capture_kernel_ = DeviceKernel(-1);
if (auto capture_kernel_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_KERNEL")) {
/* CYCLES_DEBUG_METAL_CAPTURE_KERNEL captures a single dispatch of the specified kernel. */
capture_kernel_ = DeviceKernel(atoi(capture_kernel_str));
printf("Capture kernel: %d = %s\n", capture_kernel_, device_kernel_as_string(capture_kernel_));
capture_dispatch_counter_ = 0;
if (auto capture_dispatch_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_DISPATCH")) {
capture_dispatch_counter_ = atoi(capture_dispatch_str);
printf("Capture dispatch number %d\n", capture_dispatch_counter_);
}
}
else if (auto capture_samples_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_SAMPLES")) {
/* CYCLES_DEBUG_METAL_CAPTURE_SAMPLES captures a block of dispatches from reset#(N) to
* reset#(N+1). */
capture_samples_ = true;
capture_reset_counter_ = atoi(capture_samples_str);
capture_dispatch_counter_ = INT_MAX;
if (auto capture_limit_str = getenv("CYCLES_DEBUG_METAL_CAPTURE_LIMIT")) {
/* CYCLES_DEBUG_METAL_CAPTURE_LIMIT sets the maximum number of dispatches to capture. */
capture_dispatch_counter_ = atoi(capture_limit_str);
}
printf("Capturing sample block %d (dispatch limit: %d)\n",
capture_reset_counter_,
capture_dispatch_counter_);
}
else {
/* No capturing requested. */
return;
}
/* Enable .gputrace capture for the specified DeviceKernel. */
MTLCaptureManager *captureManager = [MTLCaptureManager sharedCaptureManager];
mtlCaptureScope_ = [captureManager newCaptureScopeWithDevice:mtlDevice_];
mtlCaptureScope_.label = [NSString stringWithFormat:@"Cycles kernel dispatch"];
[captureManager setDefaultCaptureScope:mtlCaptureScope_];
label_command_encoders_ = true;
if (auto capture_url = getenv("CYCLES_DEBUG_METAL_CAPTURE_URL")) {
if (@available(macos 10.15, *)) {
if ([captureManager supportsDestination:MTLCaptureDestinationGPUTraceDocument]) {
MTLCaptureDescriptor *captureDescriptor = [[MTLCaptureDescriptor alloc] init];
captureDescriptor.captureObject = mtlCaptureScope_;
captureDescriptor.destination = MTLCaptureDestinationGPUTraceDocument;
captureDescriptor.outputURL = [NSURL fileURLWithPath:@(capture_url)];
NSError *error;
if (![captureManager startCaptureWithDescriptor:captureDescriptor error:&error]) {
NSString *err = [error localizedDescription];
printf("Start capture failed: %s\n", [err UTF8String]);
}
else {
printf("Capture started (URL: %s)\n", capture_url);
is_capturing_to_disk_ = true;
}
}
else {
printf("Capture to file is not supported\n");
}
}
}
}
void MetalDeviceQueue::update_capture(DeviceKernel kernel)
{
/* Handle capture end triggers. */
if (is_capturing_) {
capture_dispatch_counter_ -= 1;
if (capture_dispatch_counter_ <= 0 || kernel == DEVICE_KERNEL_INTEGRATOR_RESET) {
/* End capture if we've hit the dispatch limit or we hit a "reset". */
end_capture();
}
return;
}
if (capture_dispatch_counter_ < 0) {
/* We finished capturing. */
return;
}
/* Handle single-capture start trigger. */
if (kernel == capture_kernel_) {
/* Start capturing when the we hit the Nth dispatch of the specified kernel. */
if (capture_dispatch_counter_ == 0) {
begin_capture();
}
capture_dispatch_counter_ -= 1;
return;
}
/* Handle multi-capture start trigger. */
if (capture_samples_) {
/* Start capturing when the reset countdown is at 0. */
if (capture_reset_counter_ == 0) {
begin_capture();
}
if (kernel == DEVICE_KERNEL_INTEGRATOR_RESET) {
capture_reset_counter_ -= 1;
}
return;
}
}
void MetalDeviceQueue::begin_capture()
{
/* Start gputrace capture. */
if (mtlCommandBuffer_) {
synchronize();
}
[mtlCaptureScope_ beginScope];
printf("[mtlCaptureScope_ beginScope]\n");
is_capturing_ = true;
}
void MetalDeviceQueue::end_capture()
{
[mtlCaptureScope_ endScope];
is_capturing_ = false;
printf("[mtlCaptureScope_ endScope]\n");
if (is_capturing_to_disk_) {
if (@available(macos 10.15, *)) {
[[MTLCaptureManager sharedCaptureManager] stopCapture];
has_captured_to_disk_ = true;
is_capturing_to_disk_ = false;
is_capturing_ = false;
printf("Capture stopped\n");
}
}
}
MetalDeviceQueue::~MetalDeviceQueue()
{
/* Tidying up here isn't really practical - we should expect and require the work
* queue to be empty here. */
assert(mtlCommandBuffer == nil);
assert(command_buffers_submitted == command_buffers_completed);
assert(mtlCommandBuffer_ == nil);
assert(command_buffers_submitted_ == command_buffers_completed_);
if (@available(macos 10.14, *)) {
[shared_event_listener release];
[shared_event release];
[shared_event_listener_ release];
[shared_event_ release];
}
if (@available(macos 11.0, *)) {
[command_buffer_desc release];
[command_buffer_desc_ release];
}
if (mtlCommandQueue) {
[mtlCommandQueue release];
mtlCommandQueue = nil;
if (mtlCommandQueue_) {
[mtlCommandQueue_ release];
mtlCommandQueue_ = nil;
}
if (mtlCaptureScope_) {
[mtlCaptureScope_ release];
}
double total_time = 0.0;
/* Show per-kernel timings, if gathered (see CYCLES_METAL_PROFILING). */
int64_t num_dispatches = 0;
for (auto &stat : timing_stats_) {
total_time += stat.total_time;
num_dispatches += stat.num_dispatches;
}
if (num_dispatches) {
printf("\nMetal dispatch stats:\n\n");
auto header = string_printf("%-40s %16s %12s %12s %7s %7s",
"Kernel name",
"Total threads",
"Dispatches",
"Avg. T/D",
"Time",
"Time%");
auto divider = string(header.length(), '-');
printf("%s\n%s\n%s\n", divider.c_str(), header.c_str(), divider.c_str());
for (size_t i = 0; i < DEVICE_KERNEL_NUM; i++) {
auto &stat = timing_stats_[i];
if (stat.num_dispatches > 0) {
printf("%-40s %16s %12s %12s %6.2fs %6.2f%%\n",
device_kernel_as_string(DeviceKernel(i)),
string_human_readable_number(stat.total_work_size).c_str(),
string_human_readable_number(stat.num_dispatches).c_str(),
string_human_readable_number(stat.total_work_size / stat.num_dispatches).c_str(),
stat.total_time,
stat.total_time * 100.0 / total_time);
}
}
printf("%s\n", divider.c_str());
printf("%-40s %16s %12s %12s %6.2fs %6.2f%%\n",
"",
"",
string_human_readable_number(num_dispatches).c_str(),
"",
total_time,
100.0);
printf("%s\n\n", divider.c_str());
}
}
@ -66,10 +270,10 @@ int MetalDeviceQueue::num_concurrent_states(const size_t /*state_size*/) const
/* TODO: compute automatically. */
/* TODO: must have at least num_threads_per_block. */
int result = 1048576;
if (metal_device->device_vendor == METAL_GPU_AMD) {
if (metal_device_->device_vendor == METAL_GPU_AMD) {
result *= 2;
}
else if (metal_device->device_vendor == METAL_GPU_APPLE) {
else if (metal_device_->device_vendor == METAL_GPU_APPLE) {
result *= 4;
}
return result;
@ -80,10 +284,10 @@ int MetalDeviceQueue::num_concurrent_busy_states() const
/* METAL_WIP */
/* TODO: compute automatically. */
int result = 65536;
if (metal_device->device_vendor == METAL_GPU_AMD) {
if (metal_device_->device_vendor == METAL_GPU_AMD) {
result *= 2;
}
else if (metal_device->device_vendor == METAL_GPU_APPLE) {
else if (metal_device_->device_vendor == METAL_GPU_APPLE) {
result *= 4;
}
return result;
@ -92,7 +296,7 @@ int MetalDeviceQueue::num_concurrent_busy_states() const
void MetalDeviceQueue::init_execution()
{
/* Synchronize all textures and memory copies before executing task. */
metal_device->load_texture_info();
metal_device_->load_texture_info();
synchronize();
}
@ -101,7 +305,9 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
const int work_size,
DeviceKernelArguments const &args)
{
if (metal_device->have_error()) {
update_capture(kernel);
if (metal_device_->have_error()) {
return false;
}
@ -110,6 +316,12 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
id<MTLComputeCommandEncoder> mtlComputeCommandEncoder = get_compute_encoder(kernel);
if (@available(macos 10.14, *)) {
if (timing_shared_event_) {
command_encoder_labels_.push_back({kernel, work_size, timing_shared_event_id_});
}
}
/* Determine size requirement for argument buffer. */
size_t arg_buffer_length = 0;
for (size_t i = 0; i < args.count; i++) {
@ -126,8 +338,8 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
/* Metal ancillary bindless pointers. */
size_t metal_offsets = arg_buffer_length;
arg_buffer_length += metal_device->mtlAncillaryArgEncoder.encodedLength;
arg_buffer_length = round_up(arg_buffer_length, metal_device->mtlAncillaryArgEncoder.alignment);
arg_buffer_length += metal_device_->mtlAncillaryArgEncoder.encodedLength;
arg_buffer_length = round_up(arg_buffer_length, metal_device_->mtlAncillaryArgEncoder.alignment);
/* Temporary buffer used to prepare arg_buffer */
uint8_t *init_arg_buffer = (uint8_t *)alloca(arg_buffer_length);
@ -150,19 +362,23 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
sizeof(IntegratorStateGPU);
size_t plain_old_launch_data_size = sizeof(KernelParamsMetal) - plain_old_launch_data_offset;
memcpy(init_arg_buffer + globals_offsets + plain_old_launch_data_offset,
(uint8_t *)&metal_device->launch_params + plain_old_launch_data_offset,
(uint8_t *)&metal_device_->launch_params + plain_old_launch_data_offset,
plain_old_launch_data_size);
/* Allocate an argument buffer. */
MTLResourceOptions arg_buffer_options = MTLResourceStorageModeManaged;
if (@available(macOS 11.0, *)) {
if ([mtlDevice hasUnifiedMemory]) {
if ([mtlDevice_ hasUnifiedMemory]) {
arg_buffer_options = MTLResourceStorageModeShared;
}
}
id<MTLBuffer> arg_buffer = temp_buffer_pool.get_buffer(
mtlDevice, mtlCommandBuffer, arg_buffer_length, arg_buffer_options, init_arg_buffer, stats);
id<MTLBuffer> arg_buffer = temp_buffer_pool_.get_buffer(mtlDevice_,
mtlCommandBuffer_,
arg_buffer_length,
arg_buffer_options,
init_arg_buffer,
stats_);
/* Encode the pointer "enqueue" arguments */
bytes_written = 0;
@ -170,16 +386,16 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
size_t size_in_bytes = args.sizes[i];
bytes_written = round_up(bytes_written, size_in_bytes);
if (args.types[i] == DeviceKernelArguments::POINTER) {
[metal_device->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer
offset:bytes_written];
[metal_device_->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer
offset:bytes_written];
if (MetalDevice::MetalMem *mmem = *(MetalDevice::MetalMem **)args.values[i]) {
[mtlComputeCommandEncoder useResource:mmem->mtlBuffer
usage:MTLResourceUsageRead | MTLResourceUsageWrite];
[metal_device->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer offset:0 atIndex:0];
[metal_device_->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer offset:0 atIndex:0];
}
else {
if (@available(macos 12.0, *)) {
[metal_device->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:0];
[metal_device_->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:0];
}
}
}
@ -187,49 +403,58 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
}
/* Encode KernelParamsMetal buffers */
[metal_device->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer offset:globals_offsets];
[metal_device_->mtlBufferKernelParamsEncoder setArgumentBuffer:arg_buffer
offset:globals_offsets];
if (label_command_encoders_) {
/* Add human-readable labels if we're doing any form of debugging / profiling. */
mtlComputeCommandEncoder.label = [[NSString alloc]
initWithFormat:@"Metal queue launch %s, work_size %d",
device_kernel_as_string(kernel),
work_size];
}
/* this relies on IntegratorStateGPU layout being contiguous device_ptrs */
const size_t pointer_block_end = offsetof(KernelParamsMetal, __integrator_state) +
sizeof(IntegratorStateGPU);
for (size_t offset = 0; offset < pointer_block_end; offset += sizeof(device_ptr)) {
int pointer_index = offset / sizeof(device_ptr);
int pointer_index = int(offset / sizeof(device_ptr));
MetalDevice::MetalMem *mmem = *(
MetalDevice::MetalMem **)((uint8_t *)&metal_device->launch_params + offset);
if (mmem && (mmem->mtlBuffer || mmem->mtlTexture)) {
[metal_device->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer
offset:0
atIndex:pointer_index];
MetalDevice::MetalMem **)((uint8_t *)&metal_device_->launch_params + offset);
if (mmem && mmem->mem && (mmem->mtlBuffer || mmem->mtlTexture)) {
[metal_device_->mtlBufferKernelParamsEncoder setBuffer:mmem->mtlBuffer
offset:0
atIndex:pointer_index];
}
else {
if (@available(macos 12.0, *)) {
[metal_device->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:pointer_index];
[metal_device_->mtlBufferKernelParamsEncoder setBuffer:nil offset:0 atIndex:pointer_index];
}
}
}
bytes_written = globals_offsets + sizeof(KernelParamsMetal);
const MetalKernelPipeline *metal_kernel_pso = MetalDeviceKernels::get_best_pipeline(metal_device,
kernel);
const MetalKernelPipeline *metal_kernel_pso = MetalDeviceKernels::get_best_pipeline(
metal_device_, kernel);
if (!metal_kernel_pso) {
metal_device->set_error(
metal_device_->set_error(
string_printf("No MetalKernelPipeline for %s\n", device_kernel_as_string(kernel)));
return false;
}
/* Encode ancillaries */
[metal_device->mtlAncillaryArgEncoder setArgumentBuffer:arg_buffer offset:metal_offsets];
[metal_device->mtlAncillaryArgEncoder setBuffer:metal_device->texture_bindings_2d
offset:0
atIndex:0];
[metal_device->mtlAncillaryArgEncoder setBuffer:metal_device->texture_bindings_3d
offset:0
atIndex:1];
[metal_device_->mtlAncillaryArgEncoder setArgumentBuffer:arg_buffer offset:metal_offsets];
[metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->texture_bindings_2d
offset:0
atIndex:0];
[metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->texture_bindings_3d
offset:0
atIndex:1];
if (@available(macos 12.0, *)) {
if (metal_device->use_metalrt) {
if (metal_device->bvhMetalRT) {
id<MTLAccelerationStructure> accel_struct = metal_device->bvhMetalRT->accel_struct;
[metal_device->mtlAncillaryArgEncoder setAccelerationStructure:accel_struct atIndex:2];
if (metal_device_->use_metalrt) {
if (metal_device_->bvhMetalRT) {
id<MTLAccelerationStructure> accel_struct = metal_device_->bvhMetalRT->accel_struct;
[metal_device_->mtlAncillaryArgEncoder setAccelerationStructure:accel_struct atIndex:2];
}
for (int table = 0; table < METALRT_TABLE_NUM; table++) {
@ -237,19 +462,19 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
[metal_kernel_pso->intersection_func_table[table] setBuffer:arg_buffer
offset:globals_offsets
atIndex:1];
[metal_device->mtlAncillaryArgEncoder
[metal_device_->mtlAncillaryArgEncoder
setIntersectionFunctionTable:metal_kernel_pso->intersection_func_table[table]
atIndex:3 + table];
[mtlComputeCommandEncoder useResource:metal_kernel_pso->intersection_func_table[table]
usage:MTLResourceUsageRead];
}
else {
[metal_device->mtlAncillaryArgEncoder setIntersectionFunctionTable:nil
atIndex:3 + table];
[metal_device_->mtlAncillaryArgEncoder setIntersectionFunctionTable:nil
atIndex:3 + table];
}
}
}
bytes_written = metal_offsets + metal_device->mtlAncillaryArgEncoder.encodedLength;
bytes_written = metal_offsets + metal_device_->mtlAncillaryArgEncoder.encodedLength;
}
if (arg_buffer.storageMode == MTLStorageModeManaged) {
@ -260,10 +485,10 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
[mtlComputeCommandEncoder setBuffer:arg_buffer offset:globals_offsets atIndex:1];
[mtlComputeCommandEncoder setBuffer:arg_buffer offset:metal_offsets atIndex:2];
if (metal_device->use_metalrt) {
if (metal_device_->use_metalrt) {
if (@available(macos 12.0, *)) {
auto bvhMetalRT = metal_device->bvhMetalRT;
auto bvhMetalRT = metal_device_->bvhMetalRT;
switch (kernel) {
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST:
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW:
@ -305,7 +530,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY:
/* See parallel_active_index.h for why this amount of shared memory is needed.
* Rounded up to 16 bytes for Metal */
shared_mem_bytes = round_up((num_threads_per_block + 1) * sizeof(int), 16);
shared_mem_bytes = (int)round_up((num_threads_per_block + 1) * sizeof(int), 16);
[mtlComputeCommandEncoder setThreadgroupMemoryLength:shared_mem_bytes atIndex:0];
break;
@ -319,7 +544,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
[mtlComputeCommandEncoder dispatchThreadgroups:size_threadgroups_per_dispatch
threadsPerThreadgroup:size_threads_per_threadgroup];
[mtlCommandBuffer addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) {
[mtlCommandBuffer_ addCompletedHandler:^(id<MTLCommandBuffer> command_buffer) {
NSString *kernel_name = metal_kernel_pso->function.label;
/* Enhanced command buffer errors are only available in 11.0+ */
@ -344,50 +569,117 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel,
}
}];
return !(metal_device->have_error());
if (verbose_tracing_ || is_capturing_) {
/* Force a sync we've enabled step-by-step verbose tracing or if we're capturing. */
synchronize();
/* Show queue counters and dispatch timing. */
if (verbose_tracing_) {
if (kernel == DEVICE_KERNEL_INTEGRATOR_RESET) {
printf(
"_____________________________________.____________________.______________.___________"
"______________________________________\n");
}
printf("%-40s| %7d threads |%5.2fms | buckets [",
device_kernel_as_string(kernel),
work_size,
last_completion_time_ * 1000.0);
std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex);
for (auto &it : metal_device_->metal_mem_map) {
const string c_integrator_queue_counter = "integrator_queue_counter";
if (it.first->name == c_integrator_queue_counter) {
/* Workaround "device_copy_from" being protected. */
struct MyDeviceMemory : device_memory {
void device_copy_from__IntegratorQueueCounter()
{
device_copy_from(0, data_width, 1, sizeof(IntegratorQueueCounter));
}
};
((MyDeviceMemory *)it.first)->device_copy_from__IntegratorQueueCounter();
if (IntegratorQueueCounter *queue_counter = (IntegratorQueueCounter *)
it.first->host_pointer) {
for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++)
printf("%s%d", i == 0 ? "" : ",", int(queue_counter->num_queued[i]));
}
break;
}
}
printf("]\n");
}
}
return !(metal_device_->have_error());
}
bool MetalDeviceQueue::synchronize()
{
if (metal_device->have_error()) {
if (has_captured_to_disk_ || metal_device_->have_error()) {
return false;
}
if (mtlComputeEncoder) {
if (mtlComputeEncoder_) {
close_compute_encoder();
}
close_blit_encoder();
if (mtlCommandBuffer) {
uint64_t shared_event_id = this->shared_event_id++;
if (mtlCommandBuffer_) {
scoped_timer timer;
if (@available(macos 10.14, *)) {
__block dispatch_semaphore_t block_sema = wait_semaphore;
[shared_event notifyListener:shared_event_listener
atValue:shared_event_id
block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) {
dispatch_semaphore_signal(block_sema);
}];
[mtlCommandBuffer encodeSignalEvent:shared_event value:shared_event_id];
[mtlCommandBuffer commit];
dispatch_semaphore_wait(wait_semaphore, DISPATCH_TIME_FOREVER);
if (timing_shared_event_) {
/* For per-kernel timing, add event handlers to measure & accumulate dispatch times. */
__block double completion_time = 0;
for (uint64_t i = command_buffer_start_timing_id_; i < timing_shared_event_id_; i++) {
[timing_shared_event_ notifyListener:shared_event_listener_
atValue:i
block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) {
completion_time = timer.get_time() - completion_time;
last_completion_time_ = completion_time;
for (auto label : command_encoder_labels_) {
if (label.timing_id == value) {
TimingStats &stat = timing_stats_[label.kernel];
stat.num_dispatches++;
stat.total_time += completion_time;
stat.total_work_size += label.work_size;
}
}
}];
}
}
}
[mtlCommandBuffer release];
uint64_t shared_event_id_ = this->shared_event_id_++;
for (const CopyBack &mmem : copy_back_mem) {
if (@available(macos 10.14, *)) {
__block dispatch_semaphore_t block_sema = wait_semaphore_;
[shared_event_ notifyListener:shared_event_listener_
atValue:shared_event_id_
block:^(id<MTLSharedEvent> sharedEvent, uint64_t value) {
dispatch_semaphore_signal(block_sema);
}];
[mtlCommandBuffer_ encodeSignalEvent:shared_event_ value:shared_event_id_];
[mtlCommandBuffer_ commit];
dispatch_semaphore_wait(wait_semaphore_, DISPATCH_TIME_FOREVER);
}
[mtlCommandBuffer_ release];
for (const CopyBack &mmem : copy_back_mem_) {
memcpy((uchar *)mmem.host_pointer, (uchar *)mmem.gpu_mem, mmem.size);
}
copy_back_mem.clear();
copy_back_mem_.clear();
temp_buffer_pool.process_command_buffer_completion(mtlCommandBuffer);
metal_device->flush_delayed_free_list();
temp_buffer_pool_.process_command_buffer_completion(mtlCommandBuffer_);
metal_device_->flush_delayed_free_list();
mtlCommandBuffer = nil;
mtlCommandBuffer_ = nil;
command_encoder_labels_.clear();
}
return !(metal_device->have_error());
return !(metal_device_->have_error());
}
void MetalDeviceQueue::zero_to_device(device_memory &mem)
@ -400,20 +692,20 @@ void MetalDeviceQueue::zero_to_device(device_memory &mem)
/* Allocate on demand. */
if (mem.device_pointer == 0) {
metal_device->mem_alloc(mem);
metal_device_->mem_alloc(mem);
}
/* Zero memory on device. */
assert(mem.device_pointer != 0);
std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex);
MetalDevice::MetalMem &mmem = *metal_device->metal_mem_map.at(&mem);
std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex);
MetalDevice::MetalMem &mmem = *metal_device_->metal_mem_map.at(&mem);
if (mmem.mtlBuffer) {
id<MTLBlitCommandEncoder> blitEncoder = get_blit_encoder();
[blitEncoder fillBuffer:mmem.mtlBuffer range:NSMakeRange(mmem.offset, mmem.size) value:0];
}
else {
metal_device->mem_zero(mem);
metal_device_->mem_zero(mem);
}
}
@ -425,15 +717,15 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem)
/* Allocate on demand. */
if (mem.device_pointer == 0) {
metal_device->mem_alloc(mem);
metal_device_->mem_alloc(mem);
}
assert(mem.device_pointer != 0);
assert(mem.host_pointer != nullptr);
std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex);
auto result = metal_device->metal_mem_map.find(&mem);
if (result != metal_device->metal_mem_map.end()) {
std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex);
auto result = metal_device_->metal_mem_map.find(&mem);
if (result != metal_device_->metal_mem_map.end()) {
if (mem.host_pointer == mem.shared_pointer) {
return;
}
@ -441,12 +733,12 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem)
MetalDevice::MetalMem &mmem = *result->second;
id<MTLBlitCommandEncoder> blitEncoder = get_blit_encoder();
id<MTLBuffer> buffer = temp_buffer_pool.get_buffer(mtlDevice,
mtlCommandBuffer,
mmem.size,
MTLResourceStorageModeShared,
mem.host_pointer,
stats);
id<MTLBuffer> buffer = temp_buffer_pool_.get_buffer(mtlDevice_,
mtlCommandBuffer_,
mmem.size,
MTLResourceStorageModeShared,
mem.host_pointer,
stats_);
[blitEncoder copyFromBuffer:buffer
sourceOffset:0
@ -455,7 +747,7 @@ void MetalDeviceQueue::copy_to_device(device_memory &mem)
size:mmem.size];
}
else {
metal_device->mem_copy_to(mem);
metal_device_->mem_copy_to(mem);
}
}
@ -470,8 +762,8 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem)
assert(mem.device_pointer != 0);
assert(mem.host_pointer != nullptr);
std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex);
MetalDevice::MetalMem &mmem = *metal_device->metal_mem_map.at(&mem);
std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex);
MetalDevice::MetalMem &mmem = *metal_device_->metal_mem_map.at(&mem);
if (mmem.mtlBuffer) {
const size_t size = mem.memory_size();
@ -481,8 +773,8 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem)
[blitEncoder synchronizeResource:mmem.mtlBuffer];
}
if (mem.host_pointer != mmem.hostPtr) {
if (mtlCommandBuffer) {
copy_back_mem.push_back({mem.host_pointer, mmem.hostPtr, size});
if (mtlCommandBuffer_) {
copy_back_mem_.push_back({mem.host_pointer, mmem.hostPtr, size});
}
else {
memcpy((uchar *)mem.host_pointer, (uchar *)mmem.hostPtr, size);
@ -494,16 +786,16 @@ void MetalDeviceQueue::copy_from_device(device_memory &mem)
}
}
else {
metal_device->mem_copy_from(mem);
metal_device_->mem_copy_from(mem);
}
}
void MetalDeviceQueue::prepare_resources(DeviceKernel kernel)
{
std::lock_guard<std::recursive_mutex> lock(metal_device->metal_mem_map_mutex);
std::lock_guard<std::recursive_mutex> lock(metal_device_->metal_mem_map_mutex);
/* declare resource usage */
for (auto &it : metal_device->metal_mem_map) {
for (auto &it : metal_device_->metal_mem_map) {
device_memory *mem = it.first;
MTLResourceUsage usage = MTLResourceUsageRead;
@ -513,17 +805,17 @@ void MetalDeviceQueue::prepare_resources(DeviceKernel kernel)
if (it.second->mtlBuffer) {
/* METAL_WIP - use array version (i.e. useResources) */
[mtlComputeEncoder useResource:it.second->mtlBuffer usage:usage];
[mtlComputeEncoder_ useResource:it.second->mtlBuffer usage:usage];
}
else if (it.second->mtlTexture) {
/* METAL_WIP - use array version (i.e. useResources) */
[mtlComputeEncoder useResource:it.second->mtlTexture usage:usage | MTLResourceUsageSample];
[mtlComputeEncoder_ useResource:it.second->mtlTexture usage:usage | MTLResourceUsageSample];
}
}
/* ancillaries */
[mtlComputeEncoder useResource:metal_device->texture_bindings_2d usage:MTLResourceUsageRead];
[mtlComputeEncoder useResource:metal_device->texture_bindings_3d usage:MTLResourceUsageRead];
[mtlComputeEncoder_ useResource:metal_device_->texture_bindings_2d usage:MTLResourceUsageRead];
[mtlComputeEncoder_ useResource:metal_device_->texture_bindings_3d usage:MTLResourceUsageRead];
}
id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel kernel)
@ -531,67 +823,81 @@ id<MTLComputeCommandEncoder> MetalDeviceQueue::get_compute_encoder(DeviceKernel
bool concurrent = (kernel < DEVICE_KERNEL_INTEGRATOR_NUM);
if (@available(macos 10.14, *)) {
if (mtlComputeEncoder) {
if (mtlComputeEncoder.dispatchType == concurrent ? MTLDispatchTypeConcurrent :
MTLDispatchTypeSerial) {
if (timing_shared_event_) {
/* Close the current encoder to ensure we're able to capture per-encoder timing data. */
if (mtlComputeEncoder_) {
close_compute_encoder();
}
}
if (mtlComputeEncoder_) {
if (mtlComputeEncoder_.dispatchType == concurrent ? MTLDispatchTypeConcurrent :
MTLDispatchTypeSerial) {
/* declare usage of MTLBuffers etc */
prepare_resources(kernel);
return mtlComputeEncoder;
return mtlComputeEncoder_;
}
close_compute_encoder();
}
close_blit_encoder();
if (!mtlCommandBuffer) {
mtlCommandBuffer = [mtlCommandQueue commandBuffer];
[mtlCommandBuffer retain];
if (!mtlCommandBuffer_) {
mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer];
[mtlCommandBuffer_ retain];
}
mtlComputeEncoder = [mtlCommandBuffer
mtlComputeEncoder_ = [mtlCommandBuffer_
computeCommandEncoderWithDispatchType:concurrent ? MTLDispatchTypeConcurrent :
MTLDispatchTypeSerial];
[mtlComputeEncoder setLabel:@(device_kernel_as_string(kernel))];
[mtlComputeEncoder_ setLabel:@(device_kernel_as_string(kernel))];
/* declare usage of MTLBuffers etc */
prepare_resources(kernel);
}
return mtlComputeEncoder;
return mtlComputeEncoder_;
}
id<MTLBlitCommandEncoder> MetalDeviceQueue::get_blit_encoder()
{
if (mtlBlitEncoder) {
return mtlBlitEncoder;
if (mtlBlitEncoder_) {
return mtlBlitEncoder_;
}
if (mtlComputeEncoder) {
if (mtlComputeEncoder_) {
close_compute_encoder();
}
if (!mtlCommandBuffer) {
mtlCommandBuffer = [mtlCommandQueue commandBuffer];
[mtlCommandBuffer retain];
if (!mtlCommandBuffer_) {
mtlCommandBuffer_ = [mtlCommandQueue_ commandBuffer];
[mtlCommandBuffer_ retain];
command_buffer_start_timing_id_ = timing_shared_event_id_;
}
mtlBlitEncoder = [mtlCommandBuffer blitCommandEncoder];
return mtlBlitEncoder;
mtlBlitEncoder_ = [mtlCommandBuffer_ blitCommandEncoder];
return mtlBlitEncoder_;
}
void MetalDeviceQueue::close_compute_encoder()
{
[mtlComputeEncoder endEncoding];
mtlComputeEncoder = nil;
[mtlComputeEncoder_ endEncoding];
mtlComputeEncoder_ = nil;
if (@available(macos 10.14, *)) {
if (timing_shared_event_) {
[mtlCommandBuffer_ encodeSignalEvent:timing_shared_event_ value:timing_shared_event_id_++];
}
}
}
void MetalDeviceQueue::close_blit_encoder()
{
if (mtlBlitEncoder) {
[mtlBlitEncoder endEncoding];
mtlBlitEncoder = nil;
if (mtlBlitEncoder_) {
[mtlBlitEncoder_ endEncoding];
mtlBlitEncoder_ = nil;
}
}

View File

@ -14,6 +14,8 @@
# include "util/thread.h"
# define metal_printf VLOG(4) << string_printf
CCL_NAMESPACE_BEGIN
enum MetalGPUVendor {

View File

@ -553,7 +553,8 @@ bool OptiXDevice::load_kernels(const uint kernel_features)
OptixBuiltinISOptions builtin_options = {};
# if OPTIX_ABI_VERSION >= 55
builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CATMULLROM;
builtin_options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE;
builtin_options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE |
OPTIX_BUILD_FLAG_ALLOW_COMPACTION;
builtin_options.curveEndcapFlags = OPTIX_CURVE_ENDCAP_DEFAULT; /* Disable end-caps. */
# else
builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE;
@ -1387,7 +1388,10 @@ bool OptiXDevice::build_optix_bvh(BVHOptiX *bvh,
OptixAccelBufferSizes sizes = {};
OptixAccelBuildOptions options = {};
options.operation = operation;
if (use_fast_trace_bvh) {
if (use_fast_trace_bvh ||
/* The build flags have to match the ones used to query the built-in curve intersection
program (see optixBuiltinISModuleGet above) */
build_input.type == OPTIX_BUILD_INPUT_TYPE_CURVES) {
VLOG(2) << "Using fast to trace OptiX BVH";
options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION;
}

View File

@ -23,10 +23,18 @@ HdCyclesDisplayDriver::HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *
HdCyclesDisplayDriver::~HdCyclesDisplayDriver()
{
deinit();
if (texture_) {
_hgi->DestroyTexture(&texture_);
}
if (gl_pbo_id_) {
glDeleteBuffers(1, &gl_pbo_id_);
}
gl_context_dispose();
}
void HdCyclesDisplayDriver::init()
void HdCyclesDisplayDriver::gl_context_create()
{
#ifdef _WIN32
if (!gl_context_) {
@ -64,16 +72,42 @@ void HdCyclesDisplayDriver::init()
}
}
void HdCyclesDisplayDriver::deinit()
bool HdCyclesDisplayDriver::gl_context_enable()
{
if (texture_) {
_hgi->DestroyTexture(&texture_);
#ifdef _WIN32
if (!hdc_ || !gl_context_) {
return false;
}
if (gl_pbo_id_) {
glDeleteBuffers(1, &gl_pbo_id_);
mutex_.lock();
// Do not change context if this is called in the main thread
if (wglGetCurrentContext() == nullptr) {
if (!TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_))) {
mutex_.unlock();
return false;
}
}
return true;
#else
return false;
#endif
}
void HdCyclesDisplayDriver::gl_context_disable()
{
#ifdef _WIN32
if (wglGetCurrentContext() == gl_context_) {
TF_VERIFY(wglMakeCurrent(nullptr, nullptr));
}
mutex_.unlock();
#endif
}
void HdCyclesDisplayDriver::gl_context_dispose()
{
#ifdef _WIN32
if (gl_context_) {
TF_VERIFY(wglDeleteContext((HGLRC)gl_context_));
@ -90,13 +124,9 @@ bool HdCyclesDisplayDriver::update_begin(const Params &params,
int texture_width,
int texture_height)
{
#ifdef _WIN32
if (!hdc_ || !gl_context_) {
if (!gl_context_enable()) {
return false;
}
#endif
graphics_interop_activate();
if (gl_render_sync_) {
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
@ -121,15 +151,14 @@ bool HdCyclesDisplayDriver::update_begin(const Params &params,
void HdCyclesDisplayDriver::update_end()
{
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
graphics_interop_deactivate();
gl_context_disable();
}
void HdCyclesDisplayDriver::flush()
{
graphics_interop_activate();
gl_context_enable();
if (gl_upload_sync_) {
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
@ -139,7 +168,7 @@ void HdCyclesDisplayDriver::flush()
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
}
graphics_interop_deactivate();
gl_context_disable();
}
half4 *HdCyclesDisplayDriver::map_texture_buffer()
@ -179,25 +208,12 @@ DisplayDriver::GraphicsInterop HdCyclesDisplayDriver::graphics_interop_get()
void HdCyclesDisplayDriver::graphics_interop_activate()
{
mutex_.lock();
#ifdef _WIN32
// Do not change context if this is called in the main thread
if (wglGetCurrentContext() == nullptr) {
TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_));
}
#endif
gl_context_enable();
}
void HdCyclesDisplayDriver::graphics_interop_deactivate()
{
#ifdef _WIN32
if (wglGetCurrentContext() == gl_context_) {
TF_VERIFY(wglMakeCurrent(nullptr, nullptr));
}
#endif
mutex_.unlock();
gl_context_disable();
}
void HdCyclesDisplayDriver::clear()
@ -214,7 +230,11 @@ void HdCyclesDisplayDriver::draw(const Params &params)
return;
}
init();
if (!renderBuffer->IsResourceUsed()) {
return;
}
gl_context_create();
// Cycles 'DisplayDriver' only supports 'half4' format
TF_VERIFY(renderBuffer->GetFormat() == HdFormatFloat16Vec4);
@ -255,7 +275,6 @@ void HdCyclesDisplayDriver::draw(const Params &params)
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
need_update_ = false;

View File

@ -19,9 +19,6 @@ class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver {
~HdCyclesDisplayDriver();
private:
void init();
void deinit();
void next_tile_begin() override;
bool update_begin(const Params &params, int texture_width, int texture_height) override;
@ -41,6 +38,11 @@ class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver {
void draw(const Params &params) override;
void gl_context_create();
bool gl_context_enable();
void gl_context_disable();
void gl_context_dispose();
HdCyclesSession *const _renderParam;
Hgi *const _hgi;
@ -48,7 +50,6 @@ class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver {
void *hdc_ = nullptr;
void *gl_context_ = nullptr;
#endif
CCL_NS::thread_mutex mutex_;
PXR_NS::HgiTextureHandle texture_;

View File

@ -30,11 +30,11 @@ bool HdCyclesOutputDriver::update_render_tile(const Tile &tile)
std::vector<float> pixels;
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (aovBinding == _renderParam->GetDisplayAovBinding()) {
continue; // Display AOV binding is already updated by Cycles display driver
}
if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
if (aovBinding == _renderParam->GetDisplayAovBinding() && renderBuffer->IsResourceUsed()) {
continue; // Display AOV binding is already updated by Cycles display driver
}
const HdFormat format = renderBuffer->GetFormat();
if (format == HdFormatInvalid) {
continue; // Skip invalid AOV bindings

View File

@ -35,7 +35,7 @@ bool HdCyclesRenderBuffer::Allocate(const GfVec3i &dimensions, HdFormat format,
return false;
}
const size_t oldSize = _data.size();
const size_t oldSize = _dataSize;
const size_t newSize = dimensions[0] * dimensions[1] * HdDataSizeOfFormat(format);
if (oldSize == newSize) {
return true;
@ -49,8 +49,8 @@ bool HdCyclesRenderBuffer::Allocate(const GfVec3i &dimensions, HdFormat format,
_width = dimensions[0];
_height = dimensions[1];
_format = format;
_data.resize(newSize);
_dataSize = newSize;
_resourceUsed = false;
return true;
}
@ -63,6 +63,7 @@ void HdCyclesRenderBuffer::_Deallocate()
_data.clear();
_data.shrink_to_fit();
_dataSize = 0;
_resource = VtValue();
}
@ -74,6 +75,10 @@ void *HdCyclesRenderBuffer::Map()
return nullptr;
}
if (_data.size() != _dataSize) {
_data.resize(_dataSize);
}
++_mapped;
return _data.data();
@ -103,10 +108,17 @@ void HdCyclesRenderBuffer::SetConverged(bool converged)
_converged = converged;
}
bool HdCyclesRenderBuffer::IsResourceUsed() const
{
return _resourceUsed;
}
VtValue HdCyclesRenderBuffer::GetResource(bool multiSampled) const
{
TF_UNUSED(multiSampled);
_resourceUsed = true;
return _resource;
}

View File

@ -58,6 +58,8 @@ class HdCyclesRenderBuffer final : public PXR_NS::HdRenderBuffer {
void SetConverged(bool converged);
bool IsResourceUsed() const;
PXR_NS::VtValue GetResource(bool multiSampled = false) const override;
void SetResource(const PXR_NS::VtValue &resource);
@ -74,9 +76,11 @@ class HdCyclesRenderBuffer final : public PXR_NS::HdRenderBuffer {
unsigned int _width = 0u;
unsigned int _height = 0u;
PXR_NS::HdFormat _format = PXR_NS::HdFormatInvalid;
size_t _dataSize = 0;
std::vector<uint8_t> _data;
PXR_NS::VtValue _resource;
mutable std::atomic_bool _resourceUsed = false;
std::atomic_int _mapped = 0;
std::atomic_bool _converged = false;

View File

@ -241,7 +241,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
}
ccl_gpu_kernel_postfix
#ifdef __KERNEL_METAL__
#if defined(__KERNEL_METAL_APPLE__) && defined(__METALRT__)
constant int __dummy_constant [[function_constant(0)]];
#endif
@ -256,7 +256,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
if (global_index < work_size) {
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
#ifdef __KERNEL_METAL__
#if defined(__KERNEL_METAL_APPLE__) && defined(__METALRT__)
KernelGlobals kg = NULL;
/* Workaround Ambient Occlusion and Bevel nodes not working with Metal.
* Dummy offset should not affect result, but somehow fixes bug! */

View File

@ -48,7 +48,7 @@ ccl_device float3 integrator_eval_background_shader(KernelGlobals kg,
PROFILING_SHADER(emission_sd->object, emission_sd->shader);
PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL);
shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT>(
shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_BACKGROUND>(
kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION);
L = shader_background_eval(emission_sd);

View File

@ -1700,6 +1700,8 @@ enum KernelFeatureFlag : uint32_t {
#define KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT \
(KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \
KERNEL_FEATURE_NODE_LIGHT_PATH)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_BACKGROUND \
(KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT | KERNEL_FEATURE_NODE_AOV)
#define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \
(KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \
KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \

View File

@ -661,6 +661,26 @@ Attribute *AttributeSet::find(AttributeStandard std) const
return NULL;
}
Attribute *AttributeSet::find_matching(const Attribute &other)
{
for (Attribute &attr : attributes) {
if (attr.name != other.name) {
continue;
}
if (attr.std != other.std) {
continue;
}
if (attr.type != other.type) {
continue;
}
if (attr.element != other.element) {
continue;
}
return &attr;
}
return nullptr;
}
void AttributeSet::remove(AttributeStandard std)
{
Attribute *attr = find(std);
@ -729,32 +749,24 @@ void AttributeSet::clear(bool preserve_voxel_data)
void AttributeSet::update(AttributeSet &&new_attributes)
{
/* add or update old_attributes based on the new_attributes */
/* Remove any attributes not on new_attributes. */
list<Attribute>::iterator it;
for (it = attributes.begin(); it != attributes.end();) {
const Attribute &old_attr = *it;
if (new_attributes.find_matching(old_attr) == nullptr) {
remove(it++);
continue;
}
it++;
}
/* Add or update old_attributes based on the new_attributes. */
foreach (Attribute &attr, new_attributes.attributes) {
Attribute *nattr = add(attr.name, attr.type, attr.element);
nattr->std = attr.std;
nattr->set_data_from(std::move(attr));
}
/* remove any attributes not on new_attributes */
list<Attribute>::iterator it;
for (it = attributes.begin(); it != attributes.end();) {
if (it->std != ATTR_STD_NONE) {
if (new_attributes.find(it->std) == nullptr) {
remove(it++);
continue;
}
}
else if (it->name != "") {
if (new_attributes.find(it->name) == nullptr) {
remove(it++);
continue;
}
}
it++;
}
/* If all attributes were replaced, transform is no longer applied. */
geometry->transform_applied = false;
}

View File

@ -194,6 +194,7 @@ class AttributeSet {
void remove(AttributeStandard std);
Attribute *find(AttributeRequest &req);
Attribute *find_matching(const Attribute &other);
void remove(Attribute *attribute);

View File

@ -272,17 +272,12 @@ void ImageMetaData::detect_colorspace()
compress_as_srgb = true;
}
else {
/* Always compress non-raw 8bit images as scene linear + sRGB, as a
* heuristic to keep memory usage the same without too much data loss
* due to quantization in common cases. */
compress_as_srgb = (type == IMAGE_DATA_TYPE_BYTE || type == IMAGE_DATA_TYPE_BYTE4);
/* If colorspace conversion needed, use half instead of short so we can
* represent HDR values that might result from conversion. */
if (type == IMAGE_DATA_TYPE_USHORT) {
if (type == IMAGE_DATA_TYPE_BYTE || type == IMAGE_DATA_TYPE_USHORT) {
type = IMAGE_DATA_TYPE_HALF;
}
else if (type == IMAGE_DATA_TYPE_USHORT4) {
else if (type == IMAGE_DATA_TYPE_BYTE4 || type == IMAGE_DATA_TYPE_USHORT4) {
type = IMAGE_DATA_TYPE_HALF4;
}
}

View File

@ -94,10 +94,11 @@ bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures & /*features*/,
template<TypeDesc::BASETYPE FileFormat, typename StorageType>
static void oiio_load_pixels(const ImageMetaData &metadata,
const unique_ptr<ImageInput> &in,
const bool associate_alpha,
StorageType *pixels)
{
const int width = metadata.width;
const int height = metadata.height;
const size_t width = metadata.width;
const size_t height = metadata.height;
const int depth = metadata.depth;
const int components = metadata.channels;
@ -105,12 +106,12 @@ static void oiio_load_pixels(const ImageMetaData &metadata,
StorageType *readpixels = pixels;
vector<StorageType> tmppixels;
if (components > 4) {
tmppixels.resize(((size_t)width) * height * components);
tmppixels.resize(width * height * components);
readpixels = &tmppixels[0];
}
if (depth <= 1) {
size_t scanlinesize = ((size_t)width) * components * sizeof(StorageType);
size_t scanlinesize = width * components * sizeof(StorageType);
in->read_image(FileFormat,
(uchar *)readpixels + (height - 1) * scanlinesize,
AutoStride,
@ -122,7 +123,7 @@ static void oiio_load_pixels(const ImageMetaData &metadata,
}
if (components > 4) {
size_t dimensions = ((size_t)width) * height;
size_t dimensions = width * height;
for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
pixels[i * 4 + 3] = tmppixels[i * components + 3];
pixels[i * 4 + 2] = tmppixels[i * components + 2];
@ -137,7 +138,7 @@ static void oiio_load_pixels(const ImageMetaData &metadata,
if (cmyk) {
const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
const size_t num_pixels = ((size_t)width) * height * depth;
const size_t num_pixels = width * height * depth;
for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
float c = util_image_cast_to_float(pixels[i * 4 + 0]);
float m = util_image_cast_to_float(pixels[i * 4 + 1]);
@ -149,6 +150,16 @@ static void oiio_load_pixels(const ImageMetaData &metadata,
pixels[i * 4 + 3] = one;
}
}
if (components == 4 && associate_alpha) {
size_t dimensions = width * height;
for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
const StorageType alpha = pixels[i * 4 + 3];
pixels[i * 4 + 0] = util_image_multiply_native(pixels[i * 4 + 0], alpha);
pixels[i * 4 + 1] = util_image_multiply_native(pixels[i * 4 + 1], alpha);
pixels[i * 4 + 2] = util_image_multiply_native(pixels[i * 4 + 2], alpha);
}
}
}
bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata,
@ -172,30 +183,36 @@ bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata,
ImageSpec spec = ImageSpec();
ImageSpec config = ImageSpec();
if (!associate_alpha) {
config.attribute("oiio:UnassociatedAlpha", 1);
}
/* Load without automatic OIIO alpha conversion, we do it ourselves. OIIO
* will associate alpha in the the 8bit buffer for PNGs, which leads to too
* much precision loss when we load it as half float to do a colorspace
* transform. */
config.attribute("oiio:UnassociatedAlpha", 1);
if (!in->open(filepath.string(), spec, config)) {
return false;
}
const bool do_associate_alpha = associate_alpha &&
spec.get_int_attribute("oiio:UnassociatedAlpha", 0);
switch (metadata.type) {
case IMAGE_DATA_TYPE_BYTE:
case IMAGE_DATA_TYPE_BYTE4:
oiio_load_pixels<TypeDesc::UINT8, uchar>(metadata, in, (uchar *)pixels);
oiio_load_pixels<TypeDesc::UINT8, uchar>(metadata, in, do_associate_alpha, (uchar *)pixels);
break;
case IMAGE_DATA_TYPE_USHORT:
case IMAGE_DATA_TYPE_USHORT4:
oiio_load_pixels<TypeDesc::USHORT, uint16_t>(metadata, in, (uint16_t *)pixels);
oiio_load_pixels<TypeDesc::USHORT, uint16_t>(
metadata, in, do_associate_alpha, (uint16_t *)pixels);
break;
case IMAGE_DATA_TYPE_HALF:
case IMAGE_DATA_TYPE_HALF4:
oiio_load_pixels<TypeDesc::HALF, half>(metadata, in, (half *)pixels);
oiio_load_pixels<TypeDesc::HALF, half>(metadata, in, do_associate_alpha, (half *)pixels);
break;
case IMAGE_DATA_TYPE_FLOAT:
case IMAGE_DATA_TYPE_FLOAT4:
oiio_load_pixels<TypeDesc::FLOAT, float>(metadata, in, (float *)pixels);
oiio_load_pixels<TypeDesc::FLOAT, float>(metadata, in, do_associate_alpha, (float *)pixels);
break;
case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:

View File

@ -90,7 +90,7 @@ static vector<ChannelMapping> output_channels()
return map;
}
/* Renderlayer Handling */
/* Render-layer Handling. */
bool DenoiseImageLayer::detect_denoising_channels()
{

View File

@ -78,6 +78,26 @@ template<> inline half util_image_cast_from_float(float value)
return float_to_half_image(value);
}
/* Multiply image pixels in native data format. */
template<typename T> inline T util_image_multiply_native(T a, T b);
template<> inline float util_image_multiply_native(float a, float b)
{
return a * b;
}
template<> inline uchar util_image_multiply_native(uchar a, uchar b)
{
return ((uint32_t)a * (uint32_t)b) / 255;
}
template<> inline uint16_t util_image_multiply_native(uint16_t a, uint16_t b)
{
return ((uint32_t)a * (uint32_t)b) / 65535;
}
template<> inline half util_image_multiply_native(half a, half b)
{
return float_to_half_image(half_to_float_image(a) * half_to_float_image(b));
}
CCL_NAMESPACE_END
#endif /* __UTIL_IMAGE_H__ */

View File

@ -324,16 +324,21 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
${CMAKE_CURRENT_BINARY_DIR}
)
# xdg-shell.
# `xdg-shell`.
generate_protocol_bindings(
xdg-shell
"${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml"
)
# xdg-decoration.
# `xdg-decoration`.
generate_protocol_bindings(
xdg-decoration
"${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
)
# `xdg-output`.
generate_protocol_bindings(
xdg-output
"${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml"
)
# Pointer-constraints.
generate_protocol_bindings(
pointer-constraints
@ -344,6 +349,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
relative-pointer
"${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
)
# Tablet.
generate_protocol_bindings(
tablet
"${WAYLAND_PROTOCOLS_DIR}/unstable/tablet/tablet-unstable-v2.xml"
)
add_definitions(-DWITH_GHOST_WAYLAND)
endif()

View File

@ -402,6 +402,11 @@ extern GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
int32_t x,
int32_t y);
void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode *r_mode,
GHOST_TAxisFlag *r_wrap_axis,
int r_bounds[4]);
/**
* Grabs the cursor for a modal operation, to keep receiving
* events when the mouse is outside the window. X11 only, others
@ -896,6 +901,16 @@ extern int setConsoleWindowState(GHOST_TConsoleWindowState action);
*/
extern int GHOST_UseNativePixels(void);
/**
* Warp the cursor, if supported.
*/
extern int GHOST_SupportsCursorWarp(void);
/**
* Assign the callback which generates a back-trace (may be NULL).
*/
extern void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn);
/**
* Focus window after opening, or put them in the background.
*/

View File

@ -133,6 +133,9 @@ class GHOST_ISystem {
*/
static GHOST_ISystem *getSystem();
static GHOST_TBacktraceFn getBacktraceFn();
static void setBacktraceFn(GHOST_TBacktraceFn backtrace_fn);
protected:
/**
* Constructor.
@ -304,6 +307,11 @@ class GHOST_ISystem {
*/
virtual bool useNativePixel(void) = 0;
/**
* Return true when warping the cursor is supported.
*/
virtual bool supportsCursorWarp() = 0;
/**
* Focus window after opening, or put them in the background.
*/
@ -477,6 +485,9 @@ class GHOST_ISystem {
/** The one and only system */
static GHOST_ISystem *m_system;
/** Function to call that sets the back-trace. */
static GHOST_TBacktraceFn m_backtrace_fn;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_ISystem")
#endif

View File

@ -254,6 +254,12 @@ class GHOST_IWindow {
*/
virtual GHOST_TSuccess setCursorShape(GHOST_TStandardCursor cursorShape) = 0;
virtual GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds) = 0;
virtual void getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &axis_flag,
GHOST_Rect &bounds) = 0;
/**
* Test if the standard cursor shape is supported by current platform.
* \return Indication of success.

View File

@ -42,6 +42,8 @@ GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
GHOST_DECLARE_HANDLE(GHOST_XrContextHandle);
typedef void (*GHOST_TBacktraceFn)(void *file_handle);
typedef struct {
int flags;
} GHOST_GLSettings;
@ -403,6 +405,8 @@ typedef enum {
GHOST_kGrabHide,
} GHOST_TGrabCursorMode;
#define GHOST_GRAB_NEEDS_SOFTWARE_CURSOR_FOR_WARP(grab) ((grab) == GHOST_kGrabWrap)
typedef enum {
/** Axis that cursor grab will wrap. */
GHOST_kGrabAxisNone = 0,
@ -521,7 +525,7 @@ typedef struct {
} GHOST_TEventNDOFMotionData;
typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction;
/* Good for mouse or other buttons too, hmmm? */
/* Good for mouse or other buttons too? */
typedef struct {
GHOST_TButtonAction action;

View File

@ -376,6 +376,20 @@ GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
mode, wrap_axis, bounds ? &bounds_rect : nullptr, mouse_ungrab_xy ? mouse_xy : nullptr);
}
void GHOST_GetCursorGrabState(GHOST_WindowHandle windowhandle,
GHOST_TGrabCursorMode *r_mode,
GHOST_TAxisFlag *r_axis_flag,
int r_bounds[4])
{
GHOST_IWindow *window = (GHOST_IWindow *)windowhandle;
GHOST_Rect bounds_rect;
window->getCursorGrabState(*r_mode, *r_axis_flag, bounds_rect);
r_bounds[0] = bounds_rect.m_l;
r_bounds[1] = bounds_rect.m_t;
r_bounds[2] = bounds_rect.m_r;
r_bounds[3] = bounds_rect.m_b;
}
GHOST_TSuccess GHOST_GetModifierKeyState(GHOST_SystemHandle systemhandle,
GHOST_TModifierKeyMask mask,
int *isDown)
@ -815,6 +829,17 @@ int GHOST_UseNativePixels(void)
return system->useNativePixel();
}
int GHOST_SupportsCursorWarp(void)
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();
return system->supportsCursorWarp();
}
void GHOST_SetBacktraceHandler(GHOST_TBacktraceFn backtrace_fn)
{
GHOST_ISystem::setBacktraceFn(backtrace_fn);
}
void GHOST_UseWindowFocus(int use_focus)
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();

View File

@ -31,6 +31,8 @@
GHOST_ISystem *GHOST_ISystem::m_system = nullptr;
GHOST_TBacktraceFn GHOST_ISystem::m_backtrace_fn = nullptr;
GHOST_TSuccess GHOST_ISystem::createSystem()
{
GHOST_TSuccess success;
@ -89,3 +91,13 @@ GHOST_ISystem *GHOST_ISystem::getSystem()
{
return m_system;
}
GHOST_TBacktraceFn GHOST_ISystem::getBacktraceFn()
{
return GHOST_ISystem::m_backtrace_fn;
}
void GHOST_ISystem::setBacktraceFn(GHOST_TBacktraceFn backtrace_fn)
{
GHOST_ISystem::m_backtrace_fn = backtrace_fn;
}

View File

@ -390,6 +390,11 @@ void GHOST_System::useWindowFocus(const bool use_focus)
m_windowFocus = use_focus;
}
bool GHOST_System::supportsCursorWarp()
{
return true;
}
void GHOST_System::initDebug(GHOST_Debug debug)
{
m_is_debug_enabled = debug.flags & GHOST_kDebugDefault;

View File

@ -151,10 +151,13 @@ class GHOST_System : public GHOST_ISystem {
bool useNativePixel(void);
bool m_nativePixel;
bool supportsCursorWarp(void);
/**
* Focus window after opening, or put them in the background.
*/
void useWindowFocus(const bool use_focus);
bool m_windowFocus;
/**

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,32 @@ class GHOST_WindowWayland;
struct display_t;
struct output_t {
struct wl_output *output;
int32_t width_pxl, height_pxl; /* Dimensions in pixel. */
int32_t width_mm, height_mm; /* Dimensions in millimeter. */
int transform;
int scale;
struct wl_output *wl_output = nullptr;
struct zxdg_output_v1 *xdg_output = nullptr;
/** Dimensions in pixels. */
int32_t size_native[2] = {0, 0};
/** Dimensions in millimeter. */
int32_t size_mm[2] = {0, 0};
int32_t size_logical[2] = {0, 0};
bool has_size_logical = false;
int32_t position_logical[2] = {0, 0};
bool has_position_logical = false;
int transform = 0;
int scale = 1;
/**
* The integer `scale` value should be used in almost all cases,
* as this is what is used for most API calls.
* Only use fractional scaling to calculate the DPI.
*
* \note Internally an #wl_fixed_t is used to store the scale of the display,
* so use the same value here (avoid floating point arithmetic in general).
*/
wl_fixed_t scale_fractional = wl_fixed_from_int(1);
bool has_scale_fractional = false;
std::string make;
std::string model;
};
@ -79,9 +100,9 @@ class GHOST_SystemWayland : public GHOST_System {
wl_compositor *compositor();
xdg_wm_base *shell();
xdg_wm_base *xdg_shell();
zxdg_decoration_manager_v1 *decoration_manager();
zxdg_decoration_manager_v1 *xdg_decoration_manager();
const std::vector<output_t *> &outputs() const;
@ -103,6 +124,8 @@ class GHOST_SystemWayland : public GHOST_System {
GHOST_TSuccess setCursorVisibility(bool visible);
bool supportsCursorWarp();
GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode,
const GHOST_TGrabCursorMode mode_current,
wl_surface *surface);

View File

@ -155,10 +155,31 @@ GHOST_TSuccess GHOST_Window::setCursorGrab(GHOST_TGrabCursorMode mode,
GHOST_TSuccess GHOST_Window::getCursorGrabBounds(GHOST_Rect &bounds)
{
if (m_cursorGrab != GHOST_kGrabWrap) {
return GHOST_kFailure;
}
bounds = m_cursorGrabBounds;
return (bounds.m_l == -1 && bounds.m_r == -1) ? GHOST_kFailure : GHOST_kSuccess;
}
void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &wrap_axis,
GHOST_Rect &bounds)
{
mode = m_cursorGrab;
if (m_cursorGrab == GHOST_kGrabWrap) {
bounds = m_cursorGrabBounds;
wrap_axis = m_cursorGrabAxis;
}
else {
bounds.m_l = -1;
bounds.m_r = -1;
bounds.m_t = -1;
bounds.m_b = -1;
wrap_axis = GHOST_kGrabAxisNone;
}
}
GHOST_TSuccess GHOST_Window::setCursorShape(GHOST_TStandardCursor cursorShape)
{
if (setWindowCursorShape(cursorShape)) {

View File

@ -152,6 +152,10 @@ class GHOST_Window : public GHOST_IWindow {
*/
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds);
void getCursorGrabState(GHOST_TGrabCursorMode &mode,
GHOST_TAxisFlag &axis_flag,
GHOST_Rect &bounds);
/**
* Sets the progress bar value displayed in the window/application icon
* \param progress: The progress percentage (0.0 to 1.0).

View File

@ -15,41 +15,91 @@
#include <wayland-egl.h>
#include <algorithm> /* For `std::find`. */
static constexpr size_t base_dpi = 96;
struct window_t {
GHOST_WindowWayland *w;
wl_surface *surface;
/* Outputs on which the window is currently shown on. */
std::unordered_set<const output_t *> outputs;
uint16_t dpi = 0;
int scale = 1;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
GHOST_WindowWayland *w = nullptr;
struct wl_surface *wl_surface = nullptr;
/**
* Outputs on which the window is currently shown on.
*
* This is an ordered set (whoever adds to this is responsible for keeping members unique).
* In practice this is rarely manipulated and is limited by the number of physical displays.
*/
std::vector<const output_t *> outputs;
/** The scale value written to #wl_surface_set_buffer_scale. */
int scale = 0;
/**
* The DPI, either:
* - `scale * base_dpi`
* - `wl_fixed_to_int(scale_fractional * base_dpi)`
* When fractional scaling is available.
*/
uint32_t dpi = 0;
struct xdg_surface *xdg_surface = nullptr;
struct xdg_toplevel *xdg_toplevel = nullptr;
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;
enum zxdg_toplevel_decoration_v1_mode decoration_mode;
wl_egl_window *egl_window;
int32_t pending_width, pending_height;
bool is_maximised;
bool is_fullscreen;
bool is_active;
bool is_dialog;
int32_t width, height;
enum zxdg_toplevel_decoration_v1_mode decoration_mode = (enum zxdg_toplevel_decoration_v1_mode)0;
wl_egl_window *egl_window = nullptr;
bool is_maximised = false;
bool is_fullscreen = false;
bool is_active = false;
bool is_dialog = false;
int32_t size[2] = {0, 0};
int32_t size_pending[2] = {0, 0};
};
/* -------------------------------------------------------------------- */
/** \name Wayland Interface Callbacks
*
* These callbacks are registered for Wayland interfaces and called when
* an event is received from the compositor.
/** \name Internal Utilities
* \{ */
static void toplevel_configure(
static int outputs_max_scale_or_default(const std::vector<output_t *> &outputs,
const int32_t scale_default,
uint32_t *r_dpi)
{
int scale_max = 0;
const output_t *output_max = nullptr;
for (const output_t *reg_output : outputs) {
if (scale_max < reg_output->scale) {
scale_max = reg_output->scale;
output_max = reg_output;
}
}
if (scale_max != 0) {
if (r_dpi) {
*r_dpi = output_max->has_scale_fractional ?
/* Fractional DPI. */
wl_fixed_to_int(output_max->scale_fractional * base_dpi) :
/* Simple non-fractional DPI. */
(scale_max * base_dpi);
}
return scale_max;
}
if (r_dpi) {
*r_dpi = scale_default * base_dpi;
}
return scale_default;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG Top Level), #xdg_toplevel_listener
* \{ */
static void xdg_toplevel_handle_configure(
void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states)
{
window_t *win = static_cast<window_t *>(data);
win->pending_width = width;
win->pending_height = height;
win->size_pending[0] = win->scale * width;
win->size_pending[1] = win->scale * height;
win->is_maximised = false;
win->is_fullscreen = false;
@ -77,17 +127,23 @@ static void toplevel_configure(
}
}
static void toplevel_close(void *data, xdg_toplevel * /*xdg_toplevel*/)
static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/)
{
static_cast<window_t *>(data)->w->close();
}
static const xdg_toplevel_listener toplevel_listener = {
toplevel_configure,
toplevel_close,
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
};
static void toplevel_decoration_configure(
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG Decoration Listener), #zxdg_toplevel_decoration_v1_listener
* \{ */
static void xdg_toplevel_decoration_handle_configure(
void *data,
struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/,
uint32_t mode)
@ -96,10 +152,16 @@ static void toplevel_decoration_configure(
}
static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = {
toplevel_decoration_configure,
xdg_toplevel_decoration_handle_configure,
};
static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Listener (XDG Surface Handle Configure), #xdg_surface_listener
* \{ */
static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
{
window_t *win = static_cast<window_t *>(data);
@ -107,12 +169,12 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser
return;
}
if (win->pending_width != 0 && win->pending_height != 0) {
win->width = win->scale * win->pending_width;
win->height = win->scale * win->pending_height;
wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0);
win->pending_width = 0;
win->pending_height = 0;
if (win->size_pending[0] != 0 && win->size_pending[1] != 0) {
win->size[0] = win->size_pending[0];
win->size[1] = win->size_pending[1];
wl_egl_window_resize(win->egl_window, win->size[0], win->size[1], 0, 0);
win->size_pending[0] = 0;
win->size_pending[1] = 0;
win->w->notify_size();
}
@ -126,55 +188,57 @@ static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t ser
xdg_surface_ack_configure(xdg_surface, serial);
}
static const xdg_surface_listener surface_listener = {
surface_configure,
static const xdg_surface_listener xdg_surface_listener = {
xdg_surface_handle_configure,
};
static bool update_scale(GHOST_WindowWayland *window)
{
int scale = 0;
for (const output_t *output : window->outputs_active()) {
if (output->scale > scale) {
scale = output->scale;
}
}
/** \} */
if (scale > 0 && window->scale() != scale) {
window->scale() = scale;
/* Using the real DPI will cause wrong scaling of the UI
* use a multiplier for the default DPI as workaround. */
window->dpi() = scale * base_dpi;
wl_surface_set_buffer_scale(window->surface(), scale);
return true;
}
return false;
}
/* -------------------------------------------------------------------- */
/** \name Listener (Surface), #wl_surface_listener
* \{ */
static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
static void surface_handle_enter(void *data,
struct wl_surface * /*wl_surface*/,
struct wl_output *output)
{
GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
for (const output_t *reg_output : w->outputs()) {
if (reg_output->output == output) {
w->outputs_active().insert(reg_output);
}
output_t *reg_output = w->output_find_by_wl(output);
if (reg_output == nullptr) {
return;
}
update_scale(w);
std::vector<const output_t *> &outputs = w->outputs();
auto it = std::find(outputs.begin(), outputs.end(), reg_output);
if (it != outputs.end()) {
return;
}
outputs.push_back(reg_output);
w->outputs_changed_update_scale();
}
static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
static void surface_handle_leave(void *data,
struct wl_surface * /*wl_surface*/,
struct wl_output *output)
{
GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
for (const output_t *reg_output : w->outputs()) {
if (reg_output->output == output) {
w->outputs_active().erase(reg_output);
}
output_t *reg_output = w->output_find_by_wl(output);
if (reg_output == nullptr) {
return;
}
update_scale(w);
std::vector<const output_t *> &outputs = w->outputs();
auto it = std::find(outputs.begin(), outputs.end(), reg_output);
if (it == outputs.end()) {
return;
}
outputs.erase(it);
w->outputs_changed_update_scale();
}
struct wl_surface_listener wl_surface_listener = {
surface_enter,
surface_leave,
surface_handle_enter,
surface_handle_leave,
};
/** \} */
@ -208,32 +272,51 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
{
w->w = this;
w->width = int32_t(width);
w->height = int32_t(height);
w->size[0] = int32_t(width);
w->size[1] = int32_t(height);
w->is_dialog = is_dialog;
/* NOTE(@campbellbarton): The scale set here to avoid flickering on startup.
* When all monitors use the same scale (which is quite common) there aren't any problems.
*
* When monitors have different scales there may still be a visible window resize on startup.
* Ideally it would be possible to know the scale this window will use however that's only
* known once #surface_enter callback runs (which isn't guaranteed to run at all).
*
* Using the maximum scale is best as it results in the window first being smaller,
* avoiding a large window flashing before it's made smaller. */
w->scale = outputs_max_scale_or_default(this->m_system->outputs(), 1, &w->dpi);
/* Window surfaces. */
w->surface = wl_compositor_create_surface(m_system->compositor());
wl_surface_add_listener(w->surface, &wl_surface_listener, this);
w->wl_surface = wl_compositor_create_surface(m_system->compositor());
wl_surface_set_buffer_scale(this->surface(), w->scale);
w->egl_window = wl_egl_window_create(w->surface, int(width), int(height));
wl_surface_add_listener(w->wl_surface, &wl_surface_listener, this);
w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface);
w->egl_window = wl_egl_window_create(w->wl_surface, int(w->size[0]), int(w->size[1]));
w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->xdg_shell(), w->wl_surface);
w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
if (m_system->decoration_manager()) {
/* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels.
* This has the advantage that the size limit is the same when moving the window between monitors
* with different scales set. If it was important to limit in pixels it could be re-calculated
* when the `w->scale` changed. */
xdg_toplevel_set_min_size(w->xdg_toplevel, 320, 240);
if (m_system->xdg_decoration_manager()) {
w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
m_system->decoration_manager(), w->xdg_toplevel);
m_system->xdg_decoration_manager(), w->xdg_toplevel);
zxdg_toplevel_decoration_v1_add_listener(
w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w);
zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration,
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
wl_surface_set_user_data(w->surface, this);
wl_surface_set_user_data(w->wl_surface, this);
xdg_surface_add_listener(w->xdg_surface, &surface_listener, w);
xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w);
if (parentWindow && is_dialog) {
@ -242,7 +325,7 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
}
/* Call top-level callbacks. */
wl_surface_commit(w->surface);
wl_surface_commit(w->wl_surface);
wl_display_roundtrip(m_system->display());
#ifdef GHOST_OPENGL_ALPHA
@ -296,32 +379,70 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
wl_surface *GHOST_WindowWayland::surface() const
{
return w->surface;
return w->wl_surface;
}
const std::vector<output_t *> &GHOST_WindowWayland::outputs() const
{
return m_system->outputs();
}
std::unordered_set<const output_t *> &GHOST_WindowWayland::outputs_active()
std::vector<const output_t *> &GHOST_WindowWayland::outputs()
{
return w->outputs;
}
uint16_t &GHOST_WindowWayland::dpi()
output_t *GHOST_WindowWayland::output_find_by_wl(struct wl_output *output)
{
for (output_t *reg_output : this->m_system->outputs()) {
if (reg_output->wl_output == output) {
return reg_output;
}
}
return nullptr;
}
bool GHOST_WindowWayland::outputs_changed_update_scale()
{
uint32_t dpi_next;
const int scale_next = outputs_max_scale_or_default(this->m_system->outputs(), 0, &dpi_next);
if (scale_next == 0) {
return false;
}
window_t *win = this->w;
const uint32_t dpi_curr = win->dpi;
const int scale_curr = win->scale;
bool changed = false;
if (scale_next != scale_curr) {
/* Unlikely but possible there is a pending size change is set. */
win->size_pending[0] = (win->size_pending[0] / scale_curr) * scale_next;
win->size_pending[1] = (win->size_pending[1] / scale_curr) * scale_next;
win->scale = scale_next;
wl_surface_set_buffer_scale(this->surface(), scale_next);
changed = true;
}
if (dpi_next != dpi_curr) {
/* Using the real DPI will cause wrong scaling of the UI
* use a multiplier for the default DPI as workaround. */
win->dpi = dpi_next;
changed = true;
}
return changed;
}
uint16_t GHOST_WindowWayland::dpi()
{
return w->dpi;
}
int &GHOST_WindowWayland::scale()
int GHOST_WindowWayland::scale()
{
return w->scale;
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{
return m_system->setCursorGrab(mode, m_cursorGrab, w->surface);
return m_system->setCursorGrab(mode, m_cursorGrab, w->wl_surface);
}
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape)
@ -356,22 +477,32 @@ void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const
void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const
{
bounds.set(0, 0, w->width, w->height);
bounds.set(0, 0, w->size[0], w->size[1]);
}
GHOST_TSuccess GHOST_WindowWayland::setClientWidth(uint32_t width)
{
return setClientSize(width, uint32_t(w->height));
return setClientSize(width, uint32_t(w->size[1]));
}
GHOST_TSuccess GHOST_WindowWayland::setClientHeight(uint32_t height)
{
return setClientSize(uint32_t(w->width), height);
return setClientSize(uint32_t(w->size[0]), height);
}
GHOST_TSuccess GHOST_WindowWayland::setClientSize(uint32_t width, uint32_t height)
{
wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0);
/* Override any pending size that may be set. */
w->size_pending[0] = 0;
w->size_pending[1] = 0;
w->size[0] = width;
w->size[1] = height;
notify_size();
return GHOST_kSuccess;
}
@ -403,7 +534,7 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
}
xdg_toplevel_destroy(w->xdg_toplevel);
xdg_surface_destroy(w->xdg_surface);
wl_surface_destroy(w->surface);
wl_surface_destroy(w->wl_surface);
delete w;
}
@ -494,7 +625,7 @@ void GHOST_WindowWayland::setOpaque() const
/* Make the window opaque. */
region = wl_compositor_create_region(m_system->compositor());
wl_region_add(region, 0, 0, w->width, w->height);
wl_region_add(region, 0, 0, w->size[0], w->size[1]);
wl_surface_set_opaque_region(w->surface, region);
wl_region_destroy(region);
}

View File

@ -17,7 +17,6 @@ class GHOST_SystemWayland;
struct output_t;
struct window_t;
struct wl_surface;
class GHOST_WindowWayland : public GHOST_Window {
public:
@ -40,25 +39,8 @@ class GHOST_WindowWayland : public GHOST_Window {
uint16_t getDPIHint() override;
GHOST_TSuccess close();
/* Ghost API */
GHOST_TSuccess activate();
GHOST_TSuccess deactivate();
GHOST_TSuccess notify_size();
wl_surface *surface() const;
const std::vector<output_t *> &outputs() const;
std::unordered_set<const output_t *> &outputs_active();
uint16_t &dpi();
int &scale();
protected:
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override;
GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override;
@ -109,6 +91,28 @@ class GHOST_WindowWayland : public GHOST_Window {
void setOpaque() const;
#endif
/* WAYLAND utility functions. */
GHOST_TSuccess close();
GHOST_TSuccess activate();
GHOST_TSuccess deactivate();
GHOST_TSuccess notify_size();
struct wl_surface *surface() const;
std::vector<const output_t *> &outputs();
output_t *output_find_by_wl(struct wl_output *output);
bool outputs_changed_update_scale();
uint16_t dpi();
int scale();
private:
GHOST_SystemWayland *m_system;
struct window_t *w;

View File

@ -1711,7 +1711,7 @@ uint16_t GHOST_WindowX11::getDPIHint()
XrmDestroyDatabase(xrdb);
}
/* Fallback to calculating DPI using X reported DPI, set using xrandr --dpi */
/* Fallback to calculating DPI using X reported DPI, set using `xrandr --dpi`. */
XWindowAttributes attr;
if (!XGetWindowAttributes(m_display, m_window, &attr)) {
/* Failed to get window attributes, return X11 default DPI */

View File

@ -310,7 +310,7 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
outWintabInfo.reserve(numPackets);
for (int i = 0; i < numPackets; i++) {
PACKET pkt = m_pkts[i];
const PACKET pkt = m_pkts[i];
GHOST_WintabInfoWin32 out;
/* % 3 for multiple devices ("DualTrack"). */
@ -367,11 +367,12 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
/* Some Wintab libraries don't handle relative button input, so we track button presses
* manually. */
DWORD buttonsChanged = m_buttons ^ pkt.pkButtons;
WORD buttonIndex = 0;
/* We only needed the prior button state to compare to current, so we can overwrite it now. */
m_buttons = pkt.pkButtons;
while (buttonsChanged) {
/* Iterate over button flag indices until all flags are clear. */
for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
if (buttonsChanged & 1) {
/* Find the index for the changed button from the button map. */
GHOST_TButtonMask button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
if (button != GHOST_kButtonMaskNone) {
@ -381,15 +382,11 @@ void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
}
out.button = button;
out.type = buttonsChanged & pkt.pkButtons ? GHOST_kEventButtonDown :
GHOST_kEventButtonUp;
DWORD buttonFlag = 1 << buttonIndex;
out.type = pkt.pkButtons & buttonFlag ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
}
m_buttons ^= 1 << buttonIndex;
}
buttonsChanged >>= 1;
buttonIndex++;
}
outWintabInfo.push_back(out);

View File

@ -187,7 +187,7 @@ class GHOST_Wintab {
bool m_focused = false;
/** Pressed button map. */
uint8_t m_buttons = 0;
DWORD m_buttons = 0;
/** Range of a coordinate space. */
struct Range {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -162,7 +162,7 @@ colorspaces:
equalitygroup:
bitdepth: 32f
description: |
Standard RGB Display Space
sRGB display space
isdata: false
to_reference: !<FileTransform> {src: srgb.spi1d, interpolation: linear}
from_reference: !<FileTransform> {src: srgb_inv.spi1d, interpolation: linear}
@ -193,11 +193,11 @@ colorspaces:
- !<ColorSpace>
name: Filmic sRGB
family: display
family:
equalitygroup:
bitdepth: 32f
description: |
Filmic sRGB view transform
sRGB display space with Filmic view transform
isdata: false
from_reference: !<GroupTransform>
children:

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 71e0f87dd1ba4e0ed5f619b031045a428e534230
Subproject commit 915744ad8e255d1723d77671a6c6b074773c2199

@ -1 +1 @@
Subproject commit bcb71eea69a7b83c44112a5872ccd67cae96ec6f
Subproject commit c51e0bb1793c44c7a1b7435593dd5022cf7c8eec

@ -1 +1 @@
Subproject commit 61efd17f87b45c3049091127a5619219f9d2a821
Subproject commit 95107484d076bc965239942e857c83433bfa86d7

View File

@ -726,7 +726,7 @@ class pyDiffusion2Shader(StrokeShader):
self._curvatureInfo = Curvature2DAngleF0D()
def shade(self, stroke):
for i in range(1, self._nbIter):
for _i in range(1, self._nbIter):
it = Interface0DIterator(stroke)
for svert in it:
svert.point += self._normalInfo(it) * self._lambda * self._curvatureInfo(it)
@ -911,7 +911,7 @@ class pyBluePrintCirclesShader(StrokeShader):
it = iter(stroke)
for j in range(self.__turns):
for _j in range(self.__turns):
prev_radius = radius
prev_center = center
radius += randint(-R, R)
@ -952,7 +952,7 @@ class pyBluePrintEllipsesShader(StrokeShader):
# for description of the line below, see pyBluePrintCirclesShader
directions = phase_to_direction(sv_nb)
it = iter(stroke)
for j in range(self.__turns):
for _j in range(self.__turns):
prev_radius = radius
prev_center = center
radius = radius + Vector((randint(-R, R), randint(-R, R)))
@ -1030,7 +1030,7 @@ class pyBluePrintSquaresShader(StrokeShader):
it = iter(stroke)
verticesToRemove = list()
for j in range(self.__turns):
for _j in range(self.__turns):
for i, svert in zip(range(num_segments), it):
if i < first:
svert.point = points[0] + old_vecs[0] * i / (first - 1)
@ -1125,7 +1125,7 @@ class pyBluePrintDirectedSquaresShader(StrokeShader):
it = iter(stroke)
verticesToRemove = list()
for j in range(self.__turns):
for _j in range(self.__turns):
for i, svert in zip(range(num_segments), it):
if i < first:
svert.point = points[0] + old_vecs[0] * i / (first - 1)

View File

@ -151,7 +151,7 @@ def normal_at_I0D(it: Interface0DIterator) -> Vector:
# this case sometimes has a small difference with Normal2DF0D (1e-3 -ish)
it.decrement()
a = it.object
curr, b = next(it), next(it)
_curr, b = next(it), next(it)
# give iterator back in original state
it.decrement()
return (b.point - a.point).orthogonal().normalized()
@ -229,8 +229,6 @@ def simplifyDouglasPeucker(points, tolerance):
first_stack = []
last_stack = []
new_points = []
markers[first] = 1
markers[last] = 1

View File

@ -1028,7 +1028,7 @@ class DashedLineShader(StrokeShader):
it = stroke.stroke_vertices_begin(sampling)
pattern_cycle = cycle(self.pattern)
pattern = next(pattern_cycle)
for svert in it:
for _svert in it:
pos = it.t # curvilinear abscissa
if pos - start + sampling > pattern:

View File

@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-or-later
"""
Utilities relating to text mode console interations.
Utilities relating to text mode console interactions.
"""

View File

@ -56,7 +56,7 @@ LANGUAGES = (
(21, "Arabic (ﺔﻴﺑﺮﻌﻟﺍ)", "ar_EG"),
(22, "Bulgarian (Български)", "bg_BG"),
(23, "Greek (Ελληνικά)", "el_GR"),
(24, "Korean (한국어)", "ko_KR"),
(24, "Korean (한국어)", "ko_KR"),
(25, "Nepali (नेपाली)", "ne_NP"),
# Using the utf8 flipped form of Persian (فارسی).
(26, "Persian (ﯽﺳﺭﺎﻓ)", "fa_IR"),

View File

@ -88,6 +88,10 @@ _km_hierarchy = [
_km_expand_from_toolsystem('VIEW_3D', 'SCULPT'),
]),
('Sculpt Curves', 'EMPTY', 'WINDOW', [
_km_expand_from_toolsystem('VIEW_3D', 'CURVES_SCULPT'),
]),
('Particle', 'EMPTY', 'WINDOW', [
_km_expand_from_toolsystem('VIEW_3D', 'PARTICLE'),
]),

View File

@ -12,24 +12,65 @@
# int SDNAnr, nr;
# } BHead;
__all__ = (
"read_blend_rend_chunk",
)
def read_blend_rend_chunk(path):
class RawBlendFileReader:
"""
Return a file handle to the raw blend file data (abstracting compressed formats).
"""
__slots__ = (
# The path to load.
"_filepath",
# The file base file handler or None (only set for compressed formats).
"_blendfile_base",
# The file handler to return to the caller (always uncompressed data).
"_blendfile",
)
def __init__(self, filepath):
self._filepath = filepath
self._blendfile_base = None
self._blendfile = None
def __enter__(self):
blendfile = open(self._filepath, "rb")
blendfile_base = None
head = blendfile.read(4)
blendfile.seek(0)
if head[0:2] == b'\x1f\x8b': # GZIP magic.
import gzip
blendfile_base = blendfile
blendfile = gzip.open(blendfile, "rb")
elif head[0:4] == b'\x28\xb5\x2f\xfd': # Z-standard magic.
import zstandard
blendfile_base = blendfile
blendfile = zstandard.open(blendfile, "rb")
self._blendfile_base = blendfile_base
self._blendfile = blendfile
return self._blendfile
def __exit__(self, exc_type, exc_value, exc_traceback):
self._blendfile.close()
if self._blendfile_base is not None:
self._blendfile_base.close()
return False
def _read_blend_rend_chunk_from_file(blendfile, filepath):
import struct
import sys
blendfile = open(path, "rb")
from os import SEEK_CUR
head = blendfile.read(7)
if head[0:2] == b'\x1f\x8b': # gzip magic
import gzip
blendfile.seek(0)
blendfile = gzip.open(blendfile, "rb")
head = blendfile.read(7)
if head != b'BLENDER':
print("not a blend file:", path)
blendfile.close()
sys.stderr.write("Not a blend file: %s\n" % filepath)
return []
is_64_bit = (blendfile.read(1) == b'-')
@ -37,47 +78,62 @@ def read_blend_rend_chunk(path):
# true for PPC, false for X86
is_big_endian = (blendfile.read(1) == b'V')
# Now read the bhead chunk!!!
blendfile.read(3) # skip the version
# Now read the bhead chunk!
blendfile.seek(3, SEEK_CUR) # Skip the version.
scenes = []
sizeof_bhead = 24 if is_64_bit else 20
while blendfile.read(4) == b'REND':
sizeof_bhead_left = sizeof_bhead - 4
# Should always be 4, but a malformed/corrupt file may be less.
while (bhead_id := blendfile.read(4)) != b'ENDB':
struct.unpack('>i' if is_big_endian else '<i', blendfile.read(4))[0]
sizeof_bhead_left -= 4
if len(bhead_id) != 4:
sys.stderr.write("Unable to read until ENDB block (corrupt file): %s\n" % filepath)
break
# We don't care about the rest of the bhead struct
blendfile.read(sizeof_bhead_left)
sizeof_data_left = struct.unpack('>i' if is_big_endian else '<i', blendfile.read(4))[0]
# 4 from the `head_id`, another 4 for the size of the BHEAD.
sizeof_bhead_left = sizeof_bhead - 8
# Now we want the scene name, start and end frame. this is 32bites long
start_frame, end_frame = struct.unpack('>2i' if is_big_endian else '<2i', blendfile.read(8))
# The remainder of the BHEAD struct is not used.
blendfile.seek(sizeof_bhead_left, SEEK_CUR)
scene_name = blendfile.read(64)
if bhead_id == b'REND':
# Now we want the scene name, start and end frame. this is 32bits long.
start_frame, end_frame = struct.unpack('>2i' if is_big_endian else '<2i', blendfile.read(8))
sizeof_data_left -= 8
scene_name = scene_name[:scene_name.index(b'\0')]
scene_name = blendfile.read(64)
sizeof_data_left -= 64
try:
scene_name = str(scene_name, "utf8")
except TypeError:
pass
scene_name = scene_name[:scene_name.index(b'\0')]
# It's possible old blend files are not UTF8 compliant, use `surrogateescape`.
scene_name = scene_name.decode("utf8", errors='surrogateescape')
scenes.append((start_frame, end_frame, scene_name))
scenes.append((start_frame, end_frame, scene_name))
blendfile.close()
if sizeof_data_left > 0:
blendfile.seek(sizeof_data_left, SEEK_CUR)
elif sizeof_data_left < 0:
# Very unlikely, but prevent attempting to further parse corrupt data.
sys.stderr.write("Error calculating next block (corrupt file): %s\n" % filepath)
break
return scenes
def read_blend_rend_chunk(filepath):
with RawBlendFileReader(filepath) as blendfile:
return _read_blend_rend_chunk_from_file(blendfile, filepath)
def main():
import sys
for arg in sys.argv[1:]:
if arg.lower().endswith('.blend'):
for value in read_blend_rend_chunk(arg):
print("%d %d %s" % value)
for filepath in sys.argv[1:]:
for value in read_blend_rend_chunk(filepath):
print("%d %d %s" % value)
if __name__ == '__main__':

View File

@ -463,6 +463,7 @@ def _template_items_tool_select(
# Always use the cursor operator where possible,
# needed for time-line views where we always want to be able to scrub time.
cursor_prioritize=False,
operator_props=(),
fallback=False,
):
if not params.legacy and not fallback:
@ -479,11 +480,11 @@ def _template_items_tool_select(
if select_passthrough:
return [
(operator, {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("deselect_all", True), ("select_passthrough", True)]}),
{"properties": [("deselect_all", True), ("select_passthrough", True), *operator_props]}),
(operator, {"type": 'LEFTMOUSE', "value": 'CLICK'},
{"properties": [("deselect_all", True)]}),
{"properties": [("deselect_all", True), *operator_props]}),
(operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("deselect_all", False), ("toggle", True)]}),
{"properties": [("deselect_all", False), ("toggle", True), *operator_props]}),
("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("release_confirm", True)]}),
]
@ -497,9 +498,9 @@ def _template_items_tool_select(
# the tool without selecting elements under the cursor.
return [
(operator, {"type": 'LEFTMOUSE', "value": 'CLICK' if fallback else 'PRESS'},
{"properties": [("deselect_all", True)]}),
{"properties": [("deselect_all", True), *operator_props]}),
(operator, {"type": 'LEFTMOUSE', "value": 'CLICK' if fallback else 'PRESS', "shift": True},
{"properties": [("toggle", True)]}),
{"properties": [("toggle", True), *operator_props]}),
# Fallback key-map must transform as the primary tool is expected
# to be accessed via gizmos in this case. See: T96885.
@ -3033,7 +3034,7 @@ def km_sequencerpreview(params):
return keymap
def km_sequencer_channels(params):
def km_sequencer_channels(_params):
items = []
keymap = (
"Sequencer Channels",
@ -5608,6 +5609,17 @@ def km_font(params):
return keymap
# Curves edit mode.
def km_curves(params):
items = []
keymap = (
"Curves",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items},
)
return keymap
def km_sculpt_curves(params):
items = []
@ -5944,6 +5956,8 @@ def km_standard_modal_map(_params):
("APPLY", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
("SNAP", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None),
("SNAP_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),
("SNAP", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
("SNAP_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
])
return keymap
@ -6090,10 +6104,16 @@ def km_view3d_fly_modal(_params):
("AXIS_LOCK_Z", {"type": 'Z', "value": 'PRESS'}, None),
("PRECISION_ENABLE", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None),
("PRECISION_DISABLE", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None),
("PRECISION_ENABLE", {"type": 'RIGHT_ALT', "value": 'PRESS', "any": True}, None),
("PRECISION_DISABLE", {"type": 'RIGHT_ALT', "value": 'RELEASE', "any": True}, None),
("PRECISION_ENABLE", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None),
("PRECISION_DISABLE", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None),
("PRECISION_ENABLE", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None),
("PRECISION_DISABLE", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None),
("FREELOOK_ENABLE", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None),
("FREELOOK_DISABLE", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),
("FREELOOK_ENABLE", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
("FREELOOK_DISABLE", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
])
return keymap
@ -6115,8 +6135,12 @@ def km_view3d_walk_modal(_params):
("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
("FAST_ENABLE", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None),
("FAST_DISABLE", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None),
("FAST_ENABLE", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None),
("FAST_DISABLE", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None),
("SLOW_ENABLE", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None),
("SLOW_DISABLE", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None),
("SLOW_ENABLE", {"type": 'RIGHT_ALT', "value": 'PRESS', "any": True}, None),
("SLOW_DISABLE", {"type": 'RIGHT_ALT', "value": 'RELEASE', "any": True}, None),
("FORWARD", {"type": 'W', "value": 'PRESS', "any": True}, None),
("BACKWARD", {"type": 'S', "value": 'PRESS', "any": True}, None),
("LEFT", {"type": 'A', "value": 'PRESS', "any": True}, None),
@ -6166,6 +6190,8 @@ def km_view3d_rotate_modal(_params):
("CONFIRM", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
("AXIS_SNAP_ENABLE", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None),
("AXIS_SNAP_DISABLE", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None),
("AXIS_SNAP_ENABLE", {"type": 'RIGHT_ALT', "value": 'PRESS', "any": True}, None),
("AXIS_SNAP_DISABLE", {"type": 'RIGHT_ALT', "value": 'RELEASE', "any": True}, None),
])
return keymap
@ -6255,6 +6281,7 @@ def km_sculpt_expand_modal(_params):
*((e, {"type": NUMBERS_1[i], "value": 'PRESS', "any": True}, None) for i, e in enumerate(
("FALLOFF_GEODESICS", "FALLOFF_TOPOLOGY", "FALLOFF_TOPOLOGY_DIAGONALS", "FALLOFF_SPHERICAL"))),
("SNAP_TOGGLE", {"type": 'LEFT_CTRL', "value": 'ANY'}, None),
("SNAP_TOGGLE", {"type": 'RIGHT_CTRL', "value": 'ANY'}, None),
("LOOP_COUNT_INCREASE", {"type": 'W', "value": 'PRESS', "any": True, "repeat": True}, None),
("LOOP_COUNT_DECREASE", {"type": 'Q', "value": 'PRESS', "any": True, "repeat": True}, None),
("BRUSH_GRADIENT_TOGGLE", {"type": 'B', "value": 'PRESS', "any": True}, None),
@ -6676,12 +6703,17 @@ def km_3d_view_tool_cursor(params):
def km_3d_view_tool_select(params, *, fallback):
if params.use_tweak_select_passthrough:
operator_props = (("vert_without_handles", True),)
else:
operator_props = ()
return (
_fallback_id("3D View Tool: Tweak", fallback),
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select(
params, "view3d.select", "view3d.cursor3d", fallback=fallback)),
params, "view3d.select", "view3d.cursor3d", operator_props=operator_props, fallback=fallback)),
*([] if (not params.use_fallback_tool_rmb) else _template_view3d_select(
type=params.select_mouse,
value=params.select_mouse_value,
@ -8014,6 +8046,7 @@ def generate_keymaps(params=None):
km_lattice(params),
km_particle(params),
km_font(params),
km_curves(params),
km_sculpt_curves(params),
km_object_non_modal(params),

View File

@ -129,6 +129,15 @@ class NodeAddOperator:
return result
@classmethod
def description(cls, _context, properties):
nodetype = properties["type"]
bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype)
if bl_rna is not None:
return bl_rna.description
else:
return ""
# Simple basic operator for adding a node
class NODE_OT_add_node(NodeAddOperator, Operator):

View File

@ -596,6 +596,8 @@ class MakeDupliFace(Operator):
for obj in context.selected_objects:
if obj.type == 'MESH':
linked[obj.data].append(obj)
elif obj.type == 'EMPTY' and obj.instance_type == 'COLLECTION' and obj.instance_collection:
linked[obj.instance_collection].append(obj)
for data, objects in linked.items():
face_verts = [axis for obj in objects
@ -621,7 +623,12 @@ class MakeDupliFace(Operator):
ob_new = bpy.data.objects.new(mesh.name, mesh)
context.collection.objects.link(ob_new)
ob_inst = bpy.data.objects.new(data.name, data)
if type(data) is bpy.types.Collection:
ob_inst = bpy.data.objects.new(data.name, None)
ob_inst.instance_type = 'COLLECTION'
ob_inst.instance_collection = data
else:
ob_inst = bpy.data.objects.new(data.name, data)
context.collection.objects.link(ob_inst)
ob_new.instance_type = 'FACES'
@ -855,6 +862,37 @@ class DupliOffsetFromCursor(Operator):
return {'FINISHED'}
class DupliOffsetToCursor(Operator):
"""Set cursor position to the offset used for collection instances"""
bl_idname = "object.instance_offset_to_cursor"
bl_label = "Set Cursor to Offset"
bl_options = {'INTERNAL', 'UNDO'}
def execute(self, context):
scene = context.scene
collection = context.collection
scene.cursor.location = collection.instance_offset
return {'FINISHED'}
class DupliOffsetFromObject(Operator):
"""Set offset used for collection instances based on the active object position"""
bl_idname = "object.instance_offset_from_object"
bl_label = "Set Offset from Object"
bl_options = {'INTERNAL', 'UNDO'}
@classmethod
def poll(cls, context):
return (context.active_object is not None)
def execute(self, context):
ob_eval = context.active_object.evaluated_get(context.view_layer.depsgraph)
world_loc = ob_eval.matrix_world.to_translation()
collection = context.collection
collection.instance_offset = world_loc
return {'FINISHED'}
class LoadImageAsEmpty:
bl_options = {'REGISTER', 'UNDO'}
@ -976,6 +1014,8 @@ class OBJECT_OT_assign_property_defaults(Operator):
classes = (
ClearAllRestrictRender,
DupliOffsetFromCursor,
DupliOffsetToCursor,
DupliOffsetFromObject,
IsolateTypeRender,
JoinUVs,
LoadBackgroundImage,

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from bpy.types import Panel
from bpy.types import Panel, Menu
class CollectionButtonsPanel:
@ -42,6 +42,16 @@ class COLLECTION_PT_collection_flags(CollectionButtonsPanel, Panel):
col.prop(vlc, "indirect_only", toggle=False)
class COLLECTION_MT_context_menu_instance_offset(Menu):
bl_label = "Instance Offset"
def draw(self, _context):
layout = self.layout
layout.operator("object.instance_offset_from_cursor")
layout.operator("object.instance_offset_from_object")
layout.operator("object.instance_offset_to_cursor")
class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
bl_label = "Instancing"
@ -51,8 +61,9 @@ class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
layout.use_property_decorate = False
collection = context.collection
row = layout.row()
row = layout.row(align=True)
row.prop(collection, "instance_offset")
row.menu("COLLECTION_MT_context_menu_instance_offset", icon='DOWNARROW_HLT', text="")
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
@ -80,6 +91,7 @@ class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
classes = (
COLLECTION_MT_context_menu_instance_offset,
COLLECTION_PT_collection_flags,
COLLECTION_PT_instancing,
COLLECTION_PT_lineart_collection,

View File

@ -44,6 +44,7 @@ class DATA_PT_curves_surface(DataButtonsPanel, Panel):
layout.use_property_split = True
layout.prop(ob.data, "surface")
layout.prop(ob.data, "surface_uv_map", text="UV Map")
class CURVES_MT_add_attribute(Menu):
@ -76,12 +77,12 @@ class CURVES_MT_add_attribute(Menu):
class CURVES_UL_attributes(UIList):
def filter_items(self, context, data, property):
def filter_items(self, _context, data, property):
attributes = getattr(data, property)
flags = []
indices = [i for i in range(len(attributes))]
for index, item in enumerate(attributes):
for item in attributes:
flags.append(self.bitflag_filter_item if item.is_internal else 0)
return flags, indices

View File

@ -64,6 +64,18 @@ class MESH_MT_shape_key_context_menu(Menu):
layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move to Bottom").type = 'BOTTOM'
class MESH_MT_color_attribute_context_menu(Menu):
bl_label = "Color Attribute Specials"
def draw(self, _context):
layout = self.layout
layout.operator(
"geometry.color_attribute_duplicate",
icon='DUPLICATE',
)
class MESH_MT_attribute_context_menu(Menu):
bl_label = "Attribute Specials"
@ -495,12 +507,12 @@ class MESH_UL_attributes(UIList):
'CORNER': "Face Corner",
}
def filter_items(self, context, data, property):
def filter_items(self, _context, data, property):
attributes = getattr(data, property)
flags = []
indices = [i for i in range(len(attributes))]
for index, item in enumerate(attributes):
for item in attributes:
flags.append(self.bitflag_filter_item if item.is_internal else 0)
return flags, indices
@ -584,7 +596,7 @@ class ColorAttributesListBase():
'CORNER': "Face Corner",
}
def filter_items(self, context, data, property):
def filter_items(self, _context, data, property):
attrs = getattr(data, property)
ret = []
idxs = []
@ -660,12 +672,17 @@ class DATA_PT_vertex_colors(DATA_PT_mesh_attributes, Panel):
col.operator("geometry.color_attribute_add", icon='ADD', text="")
col.operator("geometry.color_attribute_remove", icon='REMOVE', text="")
col.separator()
col.menu("MESH_MT_color_attribute_context_menu", icon='DOWNARROW_HLT', text="")
self.draw_attribute_warnings(context, layout)
classes = (
MESH_MT_vertex_group_context_menu,
MESH_MT_shape_key_context_menu,
MESH_MT_color_attribute_context_menu,
MESH_MT_attribute_context_menu,
MESH_UL_vgroups,
MESH_UL_fmaps,

View File

@ -65,12 +65,12 @@ class POINTCLOUD_MT_add_attribute(Menu):
class POINTCLOUD_UL_attributes(UIList):
def filter_items(self, context, data, property):
def filter_items(self, _context, data, property):
attributes = getattr(data, property)
flags = []
indices = [i for i in range(len(attributes))]
for index, item in enumerate(attributes):
for item in attributes:
flags.append(self.bitflag_filter_item if item.is_internal else 0)
return flags, indices

View File

@ -1317,6 +1317,8 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=False):
gp_settings = brush.gpencil_settings
if gp_settings is None:
return
tool = brush.gpencil_sculpt_tool
row = layout.row(align=True)

View File

@ -199,7 +199,8 @@ class CLIP_HT_header(Header):
row = layout.row(align=True)
row.prop(dopesheet, "sort_method", text="")
row.prop(dopesheet, "use_invert_sort",
text="Invert", toggle=True)
text="", toggle=True,
icon='SORT_DESC' if dopesheet.use_invert_sort else 'SORT_ASC')
def _draw_masking(self, context):
layout = self.layout

View File

@ -151,7 +151,7 @@ class NLA_MT_marker(Menu):
class NLA_MT_marker_select(Menu):
bl_label = 'Select'
def draw(self, context):
def draw(self, _context):
layout = self.layout
layout.operator("marker.select_all", text="All").action = 'SELECT'

View File

@ -2087,10 +2087,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel):
col = layout.column(align=True)
col.prop(strip.transform, "origin")
row = layout.row(heading="Mirror")
sub = row.row(align=True)
sub.prop(strip, "use_flip_x", text="X", toggle=True)
sub.prop(strip, "use_flip_y", text="Y", toggle=True)
col = layout.column(heading="Mirror", align=True)
col.prop(strip, "use_flip_x", text="X", toggle=True)
col.prop(strip, "use_flip_y", text="Y", toggle=True)
class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel):

View File

@ -261,9 +261,15 @@ class _defs_annotate:
class _defs_transform:
def draw_transform_sculpt_tool_settings(context, layout):
if context.mode != 'SCULPT':
return
layout.prop(context.tool_settings.sculpt, "transform_mode")
@ToolDef.from_fn
def translate():
def draw_settings(context, layout, _tool):
_defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1)
return dict(
idname="builtin.move",
@ -279,6 +285,7 @@ class _defs_transform:
@ToolDef.from_fn
def rotate():
def draw_settings(context, layout, _tool):
_defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2)
return dict(
idname="builtin.rotate",
@ -294,6 +301,7 @@ class _defs_transform:
@ToolDef.from_fn
def scale():
def draw_settings(context, layout, _tool):
_defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3)
return dict(
idname="builtin.scale",
@ -349,6 +357,7 @@ class _defs_transform:
props = tool.gizmo_group_properties("VIEW3D_GGT_xform_gizmo")
layout.prop(props, "drag_action")
_defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1)
return dict(

View File

@ -453,8 +453,13 @@ class TOPBAR_MT_file_import(Menu):
self.layout.operator(
"wm.usd_import", text="Universal Scene Description (.usd, .usdc, .usda)")
self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil")
self.layout.operator("wm.obj_import", text="Wavefront (.obj) (experimental)")
if bpy.app.build_options.io_gpencil:
self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil")
if bpy.app.build_options.io_wavefront_obj:
self.layout.operator("wm.obj_import", text="Wavefront (.obj) (experimental)")
if bpy.app.build_options.io_stl:
self.layout.operator("wm.stl_import", text="STL (.stl) (experimental)")
class TOPBAR_MT_file_export(Menu):
@ -471,14 +476,16 @@ class TOPBAR_MT_file_export(Menu):
self.layout.operator(
"wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)")
# Pugixml lib dependency
if bpy.app.build_options.pugixml:
self.layout.operator("wm.gpencil_export_svg", text="Grease Pencil as SVG")
# Haru lib dependency
if bpy.app.build_options.haru:
self.layout.operator("wm.gpencil_export_pdf", text="Grease Pencil as PDF")
if bpy.app.build_options.io_gpencil:
# Pugixml lib dependency
if bpy.app.build_options.pugixml:
self.layout.operator("wm.gpencil_export_svg", text="Grease Pencil as SVG")
# Haru lib dependency
if bpy.app.build_options.haru:
self.layout.operator("wm.gpencil_export_pdf", text="Grease Pencil as PDF")
self.layout.operator("wm.obj_export", text="Wavefront (.obj) (experimental)")
if bpy.app.build_options.io_wavefront_obj:
self.layout.operator("wm.obj_export", text="Wavefront (.obj) (experimental)")
class TOPBAR_MT_file_external_data(Menu):

View File

@ -510,30 +510,27 @@ class _draw_tool_settings_context_mode:
header=True,
)
if brush.curves_sculpt_tool == 'COMB':
curves_tool = brush.curves_sculpt_tool
if curves_tool == 'COMB':
layout.prop(brush, "falloff_shape", expand=True)
layout.popover("VIEW3D_PT_tools_brush_falloff")
if brush.curves_sculpt_tool == 'ADD':
elif curves_tool == 'ADD':
layout.prop(brush, "falloff_shape", expand=True)
layout.prop(brush.curves_sculpt_settings, "add_amount")
layout.popover("VIEW3D_PT_curves_sculpt_add_shape", text="Curve Shape")
layout.prop(brush, "use_frontface", text="Front Faces Only")
if brush.curves_sculpt_tool == 'GROW_SHRINK':
elif curves_tool == 'GROW_SHRINK':
layout.prop(brush, "direction", expand=True, text="")
layout.prop(brush, "falloff_shape", expand=True)
layout.popover("VIEW3D_PT_curves_sculpt_grow_shrink_scaling", text="Scaling")
layout.popover("VIEW3D_PT_tools_brush_falloff")
if brush.curves_sculpt_tool == 'SNAKE_HOOK':
elif curves_tool == 'SNAKE_HOOK':
layout.prop(brush, "falloff_shape", expand=True)
layout.popover("VIEW3D_PT_tools_brush_falloff")
if brush.curves_sculpt_tool == 'DELETE':
elif curves_tool == 'DELETE':
layout.prop(brush, "falloff_shape", expand=True)
if brush.curves_sculpt_tool == 'SELECTION_PAINT':
elif curves_tool == 'SELECTION_PAINT':
layout.prop(brush, "direction", expand=True, text="")
layout.prop(brush, "falloff_shape", expand=True)
layout.popover("VIEW3D_PT_tools_brush_falloff")

View File

@ -183,6 +183,24 @@ def geometry_input_node_items(context):
yield NodeItem("GeometryNodeInputSceneTime")
# Custom Menu for Geometry Node Instance Nodes.
def geometry_instance_node_items(context):
if context is None:
return
space = context.space_data
if not space:
return
yield NodeItem("GeometryNodeInstanceOnPoints")
yield NodeItem("GeometryNodeInstancesToPoints")
yield NodeItem("GeometryNodeRealizeInstances")
yield NodeItem("GeometryNodeRotateInstances")
yield NodeItem("GeometryNodeScaleInstances")
yield NodeItem("GeometryNodeTranslateInstances")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
yield NodeItem("GeometryNodeInputInstanceRotation")
yield NodeItem("GeometryNodeInputInstanceScale")
# Custom Menu for Material Nodes.
def geometry_material_node_items(context):
if context is None:
@ -635,14 +653,7 @@ geometry_node_categories = [
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=geometry_node_items),
GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items),
GeometryNodeCategory("GEO_INSTANCE", "Instances", items=[
NodeItem("GeometryNodeInstanceOnPoints"),
NodeItem("GeometryNodeInstancesToPoints"),
NodeItem("GeometryNodeRealizeInstances"),
NodeItem("GeometryNodeRotateInstances"),
NodeItem("GeometryNodeScaleInstances"),
NodeItem("GeometryNodeTranslateInstances"),
]),
GeometryNodeCategory("GEO_INSTANCE", "Instances", items=geometry_instance_node_items),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=geometry_material_node_items),
GeometryNodeCategory("GEO_MESH", "Mesh", items=mesh_node_items),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[

View File

@ -7,6 +7,11 @@
</requestedPrivileges>
</security>
</trustInfo>
<application>
<windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->

View File

@ -50,7 +50,7 @@ typedef enum eAttrDomainMask {
/* Attributes. */
bool BKE_id_attributes_supported(struct ID *id);
bool BKE_id_attributes_supported(const struct ID *id);
bool BKE_attribute_allow_procedural_access(const char *attribute_name);
/**
@ -58,25 +58,30 @@ bool BKE_attribute_allow_procedural_access(const char *attribute_name);
*/
struct CustomDataLayer *BKE_id_attribute_new(
struct ID *id, const char *name, int type, eAttrDomain domain, struct ReportList *reports);
bool BKE_id_attribute_remove(struct ID *id,
struct CustomDataLayer *layer,
struct ReportList *reports);
bool BKE_id_attribute_remove(struct ID *id, const char *name, struct ReportList *reports);
/**
* Creates a duplicate attribute layer.
*/
struct CustomDataLayer *BKE_id_attribute_duplicate(struct ID *id,
const char *name,
struct ReportList *reports);
struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id,
const char *name,
int type,
eAttrDomain domain);
struct CustomDataLayer *BKE_id_attribute_search(const struct ID *id,
struct CustomDataLayer *BKE_id_attribute_search(struct ID *id,
const char *name,
eCustomDataMask type,
eAttrDomainMask domain_mask);
eAttrDomain BKE_id_attribute_domain(const struct ID *id, const struct CustomDataLayer *layer);
int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer);
bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer);
bool BKE_id_attribute_required(const struct ID *id, const char *name);
bool BKE_id_attribute_rename(struct ID *id,
struct CustomDataLayer *layer,
const char *old_name,
const char *new_name,
struct ReportList *reports);

View File

@ -25,7 +25,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 0
#define BLENDER_FILE_SUBVERSION 1
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -100,6 +100,9 @@ typedef enum {
BKE_CB_EVT_OBJECT_BAKE_PRE,
BKE_CB_EVT_OBJECT_BAKE_COMPLETE,
BKE_CB_EVT_OBJECT_BAKE_CANCEL,
BKE_CB_EVT_COMPOSITE_PRE,
BKE_CB_EVT_COMPOSITE_POST,
BKE_CB_EVT_COMPOSITE_CANCEL,
BKE_CB_EVT_TOT,
} eCbEvent;

View File

@ -119,7 +119,7 @@ bool BKE_collection_object_add(struct Main *bmain,
/**
* Add object to given collection, similar to #BKE_collection_object_add.
*
* However, it additionnally ensures that the selected collection is also part of the given
* However, it additionally ensures that the selected collection is also part of the given
* `view_layer`, if non-NULL. Otherwise, the object is not added to any collection.
*/
bool BKE_collection_viewlayer_object_add(struct Main *bmain,

View File

@ -307,6 +307,25 @@ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
void *ownerdata,
float mat[4][4],
float ctime);
/**
* Retrieves the list of all constraint targets, including the custom space target.
* Must be followed by a call to BKE_constraint_targets_flush to free memory.
*
* \param r_targets Pointer to the list to be initialized with target data.
* \returns the number of targets stored in the list.
*/
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets);
/**
* Copies changed data from the list produced by BKE_constraint_targets_get back to the constraint
* data structures and frees memory.
*
* \param targets List of targets filled by BKE_constraint_targets_get.
* \param no_copy Only free memory without copying changes (read-only mode).
*/
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy);
/**
* Get the list of targets required for solving a constraint.
*/

View File

@ -223,9 +223,10 @@ void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*
* - listbases consist of CollectionPointerLink items and must be
* freed with BLI_freelistN!
* - the dir listbase consists of LinkData items */
* - #ListBase consists of #CollectionPointerLink items and must be
* freed with #BLI_freelistN!
* - The dir #ListBase consists of #LinkData items.
*/
/* data type, needed so we can tell between a NULL pointer and an empty list */
enum {

View File

@ -25,7 +25,7 @@ void *BKE_curves_add(struct Main *bmain, const char *name);
struct BoundBox *BKE_curves_boundbox_get(struct Object *ob);
bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer);
bool BKE_curves_customdata_required(const struct Curves *curves, const char *name);
/* Depsgraph */

View File

@ -182,6 +182,7 @@ class CurvesGeometry : public ::CurvesGeometry {
void update_curve_types();
bool has_curve_with_type(CurveType type) const;
bool has_curve_with_type(Span<CurveType> types) const;
/** Return true if all of the curves have the provided type. */
bool is_single_type(CurveType type) const;
/** Return the number of curves with each type. */
@ -264,22 +265,10 @@ class CurvesGeometry : public ::CurvesGeometry {
MutableSpan<float> nurbs_weights_for_write();
/**
* The index of a triangle (#MLoopTri) that a curve is attached to.
* The index is -1, if the curve is not attached.
* UV coordinate for each curve that encodes where the curve is attached to the surface mesh.
*/
VArray<int> surface_triangle_indices() const;
MutableSpan<int> surface_triangle_indices_for_write();
/**
* Barycentric coordinates of the attachment point within a triangle.
* Only the first two coordinates are stored. The third coordinate can be derived because the sum
* of the three coordinates is 1.
*
* When the triangle index is -1, this coordinate should be ignored.
* The span can be empty, when all triangle indices are -1.
*/
Span<float2> surface_triangle_coords() const;
MutableSpan<float2> surface_triangle_coords_for_write();
Span<float2> surface_uv_coords() const;
MutableSpan<float2> surface_uv_coords_for_write();
VArray<float> selection_point_float() const;
MutableSpan<float> selection_point_float_for_write();
@ -398,6 +387,7 @@ class CurvesGeometry : public ::CurvesGeometry {
void update_customdata_pointers();
void remove_points(IndexMask points_to_delete);
void remove_curves(IndexMask curves_to_delete);
/**
@ -406,6 +396,11 @@ class CurvesGeometry : public ::CurvesGeometry {
*/
void reverse_curves(IndexMask curves_to_reverse);
/**
* Remove any attributes that are unused based on the types in the curves.
*/
void remove_attributes_based_on_types();
/* --------------------------------------------------------------------
* Attributes.
*/
@ -722,6 +717,12 @@ inline bool CurvesGeometry::has_curve_with_type(const CurveType type) const
return this->curve_type_counts()[type] > 0;
}
inline bool CurvesGeometry::has_curve_with_type(const Span<CurveType> types) const
{
return std::any_of(
types.begin(), types.end(), [&](CurveType type) { return this->has_curve_with_type(type); });
}
inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts() const
{
BLI_assert(this->runtime->type_counts == calculate_type_counts(this->curve_types()));

View File

@ -9,8 +9,52 @@
* \brief Low-level operations for curves.
*/
#include "BLI_function_ref.hh"
#include "BLI_generic_pointer.hh"
namespace blender::bke::curves {
/**
* Copy the provided point attribute values between all curves in the #curve_ranges index
* ranges, assuming that all curves have the same number of control points in #src_curves
* and #dst_curves.
*/
void copy_point_data(const CurvesGeometry &src_curves,
const CurvesGeometry &dst_curves,
Span<IndexRange> curve_ranges,
GSpan src,
GMutableSpan dst);
void copy_point_data(const CurvesGeometry &src_curves,
const CurvesGeometry &dst_curves,
IndexMask src_curve_selection,
GSpan src,
GMutableSpan dst);
template<typename T>
void copy_point_data(const CurvesGeometry &src_curves,
const CurvesGeometry &dst_curves,
const IndexMask src_curve_selection,
const Span<T> src,
MutableSpan<T> dst)
{
copy_point_data(src_curves, dst_curves, src_curve_selection, GSpan(src), GMutableSpan(dst));
}
void fill_points(const CurvesGeometry &curves,
IndexMask curve_selection,
GPointer value,
GMutableSpan dst);
template<typename T>
void fill_points(const CurvesGeometry &curves,
const IndexMask curve_selection,
const T &value,
MutableSpan<T> dst)
{
fill_points(curves, curve_selection, &value, dst);
}
/**
* Copy the size of every curve in #curve_ranges to the corresponding index in #counts.
*/
@ -23,4 +67,18 @@ void fill_curve_counts(const bke::CurvesGeometry &curves,
*/
void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0);
IndexMask indices_for_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
const CurveType type,
const IndexMask selection,
Vector<int64_t> &r_indices);
void foreach_curve_by_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
IndexMask selection,
FunctionRef<void(IndexMask)> catmull_rom_fn,
FunctionRef<void(IndexMask)> poly_fn,
FunctionRef<void(IndexMask)> bezier_fn,
FunctionRef<void(IndexMask)> nurbs_fn);
} // namespace blender::bke::curves

View File

@ -225,6 +225,7 @@ void *CustomData_add_layer_anonymous(struct CustomData *data,
* In edit-mode, use #EDBM_data_layer_free instead of this function.
*/
bool CustomData_free_layer(struct CustomData *data, int type, int totelem, int index);
bool CustomData_free_layer_named(struct CustomData *data, const char *name, const int totelem);
/**
* Frees the layer index with the give type.

View File

@ -263,14 +263,14 @@ struct FCurve *BKE_fcurve_iter_step(struct FCurve *fcu_iter, const char rna_path
* If there is an action assigned to the `id`'s #AnimData, it will be searched for a matching
* F-curve first. Drivers are searched only if no valid action F-curve could be found.
*
* \note: Return pointer parameter (`r_driven`) is optional and may be NULL.
* \note Return pointer parameter (`r_driven`) is optional and may be NULL.
*
* \warning: In case no animation (from an Action) F-curve is found, returned value is always NULL.
* \warning In case no animation (from an Action) F-curve is found, returned value is always NULL.
* This means that this function will set `r_driven` to True in case a valid driver F-curve is
* found, but will not return said F-curve. In other words:
* - Animated with FCurve: returns the `FCurve*` and `*r_driven = false`.
* - Animated with driver: returns `NULL` and `*r_driven = true`.
* - Not animated: returns `NULL` and `*r_driven = false`.
* - Animated with FCurve: returns the `FCurve*` and `*r_driven = false`.
* - Animated with driver: returns `NULL` and `*r_driven = true`.
* - Not animated: returns `NULL` and `*r_driven = false`.
*/
struct FCurve *id_data_find_fcurve(
ID *id, void *data, struct StructRNA *type, const char *prop_name, int index, bool *r_driven);
@ -296,11 +296,11 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con
* If there is an action assigned to the `animdata`, it will be searched for a matching F-curve
* first. Drivers are searched only if no valid action F-curve could be found.
*
* \note: Typically, indices in RNA arrays are stored separately in F-curves, so the rna_path
* \note Typically, indices in RNA arrays are stored separately in F-curves, so the rna_path
* should not include them (e.g. `rna_path='location[0]'` will not match any F-Curve on an Object,
* but `rna_path='location', rna_index=0` will if it exists).
*
* \note: Return pointer parameters (`r_action`, `r_driven` and `r_special`) are all optional and
* \note Return pointer parameters (`r_action`, `r_driven` and `r_special`) are all optional and
* may be NULL.
*/
struct FCurve *BKE_animadata_fcurve_find_by_rna_path(struct AnimData *animdata,

View File

@ -76,13 +76,13 @@ struct bGPdata;
void BKE_gpencil_free_point_weights(struct MDeformVert *dvert);
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps);
void BKE_gpencil_free_stroke_editcurve(struct bGPDstroke *gps);
/* free stroke, doesn't unlink from any listbase */
/** Free stroke, doesn't unlink from any #ListBase. */
void BKE_gpencil_free_stroke(struct bGPDstroke *gps);
/* Free strokes belonging to a gp-frame */
/** Free strokes belonging to a gp-frame. */
bool BKE_gpencil_free_strokes(struct bGPDframe *gpf);
/* Free all of a gp-layer's frames */
/** Free all of a gp-layer's frames. */
void BKE_gpencil_free_frames(struct bGPDlayer *gpl);
/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */
/** Free all of the gp-layers for a viewport (list should be `&gpd->layers` or so). */
void BKE_gpencil_free_layers(struct ListBase *list);
/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */
void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all);
@ -108,9 +108,9 @@ void BKE_gpencil_batch_cache_free(struct bGPdata *gpd);
*/
void BKE_gpencil_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps);
void BKE_gpencil_curve_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps);
/* Assign unique stroke ID for selection. */
/** Assign unique stroke ID for selection. */
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps);
/* Reset unique stroke ID for selection. */
/** Reset unique stroke ID for selection. */
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps);
/**

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