WIP: Rewrite asset browser as separate editor #104978
|
@ -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)
|
||||
|
|
|
@ -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\""
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¶ms_,
|
||||
|
|
|
@ -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 ----------------------------------*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
# include "util/thread.h"
|
||||
|
||||
# define metal_printf VLOG(4) << string_printf
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
enum MetalGPUVendor {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ¶ms,
|
|||
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 ¶ms,
|
|||
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 ¶ms)
|
|||
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 ¶ms)
|
|||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
glFlush();
|
||||
|
||||
need_update_ = false;
|
||||
|
|
|
@ -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 ¶ms, int texture_width, int texture_height) override;
|
||||
|
@ -41,6 +38,11 @@ class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver {
|
|||
|
||||
void draw(const Params ¶ms) 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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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! */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 | \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -194,6 +194,7 @@ class AttributeSet {
|
|||
void remove(AttributeStandard std);
|
||||
|
||||
Attribute *find(AttributeRequest &req);
|
||||
Attribute *find_matching(const Attribute &other);
|
||||
|
||||
void remove(Attribute *attribute);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -90,7 +90,7 @@ static vector<ChannelMapping> output_channels()
|
|||
return map;
|
||||
}
|
||||
|
||||
/* Renderlayer Handling */
|
||||
/* Render-layer Handling. */
|
||||
|
||||
bool DenoiseImageLayer::detect_denoising_channels()
|
||||
{
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
@ -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:
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 71e0f87dd1ba4e0ed5f619b031045a428e534230
|
||||
Subproject commit 915744ad8e255d1723d77671a6c6b074773c2199
|
|
@ -1 +1 @@
|
|||
Subproject commit bcb71eea69a7b83c44112a5872ccd67cae96ec6f
|
||||
Subproject commit c51e0bb1793c44c7a1b7435593dd5022cf7c8eec
|
|
@ -1 +1 @@
|
|||
Subproject commit 61efd17f87b45c3049091127a5619219f9d2a821
|
||||
Subproject commit 95107484d076bc965239942e857c83433bfa86d7
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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'),
|
||||
]),
|
||||
|
|
|
@ -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__':
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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=[
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|