diff options
198 files changed, 3690 insertions, 2296 deletions
diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index 76744aa9b0c..547bf77f8dd 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## Update and uncomment this in the release branch -set(BLENDER_VERSION 3.3) +# set(BLENDER_VERSION 3.1) function(download_source dep) set(TARGET_FILE ${${dep}_FILE}) diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index aedaf35629b..82cd009ea95 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -5,38 +5,38 @@ update-code: git: submodules: - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: release/scripts/addons - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: release/scripts/addons_contrib - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: release/datafiles/locale - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: source/tools svn: libraries: darwin-arm64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/darwin_arm64 darwin-x86_64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/darwin linux-x86_64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/linux_centos7_x86_64 windows-amd64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/win64_vc15 tests: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/tests benchmarks: diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 0176a888377..18c16a60993 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.3 +PROJECT_NUMBER = V3.4 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h index 5cc18452ac1..0ad4ca6fe83 100644 --- a/intern/cycles/blender/sync.h +++ b/intern/cycles/blender/sync.h @@ -7,6 +7,7 @@ #include "MEM_guardedalloc.h" #include "RNA_access.h" #include "RNA_blender_cpp.h" +#include "RNA_path.h" #include "RNA_types.h" #include "blender/id_map.h" diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 8ecdac6ee27..dfcd75a135e 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -355,8 +355,6 @@ set(SRC_UTIL_HEADERS ../util/types_uint4.h ../util/types_uint4_impl.h ../util/types_ushort4.h - ../util/types_vector3.h - ../util/types_vector3_impl.h ) set(LIB diff --git a/intern/cycles/kernel/device/cpu/compat.h b/intern/cycles/kernel/device/cpu/compat.h index 631e55e0d42..1e3e790ca1f 100644 --- a/intern/cycles/kernel/device/cpu/compat.h +++ b/intern/cycles/kernel/device/cpu/compat.h @@ -33,38 +33,4 @@ CCL_NAMESPACE_BEGIN #define kernel_assert(cond) assert(cond) -/* Macros to handle different memory storage on different devices */ - -#ifdef __KERNEL_SSE2__ -typedef vector3<sseb> sse3b; -typedef vector3<ssef> sse3f; -typedef vector3<ssei> sse3i; - -ccl_device_inline void print_sse3b(const char *label, sse3b &a) -{ - print_sseb(label, a.x); - print_sseb(label, a.y); - print_sseb(label, a.z); -} - -ccl_device_inline void print_sse3f(const char *label, sse3f &a) -{ - print_ssef(label, a.x); - print_ssef(label, a.y); - print_ssef(label, a.z); -} - -ccl_device_inline void print_sse3i(const char *label, sse3i &a) -{ - print_ssei(label, a.x); - print_ssei(label, a.y); - print_ssei(label, a.z); -} - -# if defined(__KERNEL_AVX__) || defined(__KERNEL_AVX2__) -typedef vector3<avxf> avx3f; -# endif - -#endif - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/oneapi/compat.h b/intern/cycles/kernel/device/oneapi/compat.h index 1b25259bcf5..ccd141aa892 100644 --- a/intern/cycles/kernel/device/oneapi/compat.h +++ b/intern/cycles/kernel/device/oneapi/compat.h @@ -149,25 +149,13 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \ /* clang-format on */ /* Types */ + /* It's not possible to use sycl types like sycl::float3, sycl::int3, etc - * because these types have different interfaces from blender version */ + * because these types have different interfaces from blender version. */ using uchar = unsigned char; using sycl::half; -struct float3 { - float x, y, z; -}; - -ccl_always_inline float3 make_float3(float x, float y, float z) -{ - return {x, y, z}; -} -ccl_always_inline float3 make_float3(float x) -{ - return {x, x, x}; -} - /* math functions */ #define fabsf(x) sycl::fabs((x)) #define copysignf(x, y) sycl::copysign((x), (y)) diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 9bc9f00e142..b33bad79e6c 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -129,8 +129,6 @@ set(SRC_HEADERS types_uint4.h types_uint4_impl.h types_ushort4.h - types_vector3.h - types_vector3_impl.h unique_ptr.h vector.h version.h diff --git a/intern/cycles/util/types.h b/intern/cycles/util/types.h index 031c2f7c4c1..26031d9e0fd 100644 --- a/intern/cycles/util/types.h +++ b/intern/cycles/util/types.h @@ -12,6 +12,7 @@ #if !defined(__KERNEL_GPU__) # include <stdint.h> +# include <stdio.h> #endif #include "util/defines.h" @@ -70,6 +71,12 @@ ccl_device_inline bool is_power_of_two(size_t x) CCL_NAMESPACE_END +/* Most GPU APIs matching native vector types, so we only need to implement them for + * CPU and oneAPI. */ +#if defined(__KERNEL_GPU__) && !defined(__KERNEL_ONEAPI__) +# define __KERNEL_NATIVE_VECTOR_TYPES__ +#endif + /* Vectorized types declaration. */ #include "util/types_uchar2.h" #include "util/types_uchar3.h" @@ -90,8 +97,6 @@ CCL_NAMESPACE_END #include "util/types_float4.h" #include "util/types_float8.h" -#include "util/types_vector3.h" - /* Vectorized types implementation. */ #include "util/types_uchar2_impl.h" #include "util/types_uchar3_impl.h" @@ -110,8 +115,6 @@ CCL_NAMESPACE_END #include "util/types_float4_impl.h" #include "util/types_float8_impl.h" -#include "util/types_vector3_impl.h" - /* SSE types. */ #ifndef __KERNEL_GPU__ # include "util/sseb.h" diff --git a/intern/cycles/util/types_float2.h b/intern/cycles/util/types_float2.h index 07b9ec0986b..f37aa1b4ad2 100644 --- a/intern/cycles/util/types_float2.h +++ b/intern/cycles/util/types_float2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT2_H__ -#define __UTIL_TYPES_FLOAT2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,18 +9,18 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct float2 { float x, y; +# ifndef __KERNEL_GPU__ __forceinline float operator[](int i) const; __forceinline float &operator[](int i); +# endif }; ccl_device_inline float2 make_float2(float x, float y); ccl_device_inline void print_float2(const char *label, const float2 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT2_H__ */ diff --git a/intern/cycles/util/types_float2_impl.h b/intern/cycles/util/types_float2_impl.h index 45fc90c52bd..9d1820fe17d 100644 --- a/intern/cycles/util/types_float2_impl.h +++ b/intern/cycles/util/types_float2_impl.h @@ -1,20 +1,16 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT2_IMPL_H__ -#define __UTIL_TYPES_FLOAT2_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline float float2::operator[](int i) const { util_assert(i >= 0); @@ -28,6 +24,7 @@ __forceinline float &float2::operator[](int i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline float2 make_float2(float x, float y) { @@ -39,8 +36,6 @@ ccl_device_inline void print_float2(const char *label, const float2 &a) { printf("%s: %.8f %.8f\n", label, (double)a.x, (double)a.y); } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT2_IMPL_H__ */ diff --git a/intern/cycles/util/types_float3.h b/intern/cycles/util/types_float3.h index c7900acaa69..4e43e928657 100644 --- a/intern/cycles/util/types_float3.h +++ b/intern/cycles/util/types_float3.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT3_H__ -#define __UTIL_TYPES_FLOAT3_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,28 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct ccl_try_align(16) float3 { -# ifdef __KERNEL_SSE__ +# ifdef __KERNEL_GPU__ + /* Compact structure for GPU. */ + float x, y, z; +# else + /* SIMD aligned structure for CPU. */ +# ifdef __KERNEL_SSE__ union { __m128 m128; struct { float x, y, z, w; }; }; +# else + float x, y, z, w; +# endif +# endif +# ifdef __KERNEL_SSE__ + /* Convenient constructors and operators for SIMD, otherwise default is enough. */ __forceinline float3(); __forceinline float3(const float3 &a); __forceinline explicit float3(const __m128 &a); @@ -29,18 +39,18 @@ struct ccl_try_align(16) float3 __forceinline operator __m128 &(); __forceinline float3 &operator=(const float3 &a); -# else /* __KERNEL_SSE__ */ - float x, y, z, w; -# endif /* __KERNEL_SSE__ */ +# endif +# ifndef __KERNEL_GPU__ __forceinline float operator[](int i) const; __forceinline float &operator[](int i); +# endif }; ccl_device_inline float3 make_float3(float f); ccl_device_inline float3 make_float3(float x, float y, float z); ccl_device_inline void print_float3(const char *label, const float3 &a); -#endif /* !defined(__KERNEL_GPU__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ /* Smaller float3 for storage. For math operations this must be converted to float3, so that on the * CPU SIMD instructions can be used. */ @@ -78,5 +88,3 @@ struct packed_float3 { static_assert(sizeof(packed_float3) == 12, "packed_float3 expected to be exactly 12 bytes"); CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT3_H__ */ diff --git a/intern/cycles/util/types_float3_impl.h b/intern/cycles/util/types_float3_impl.h index 2e6e864c8ea..cbd3f76dae4 100644 --- a/intern/cycles/util/types_float3_impl.h +++ b/intern/cycles/util/types_float3_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT3_IMPL_H__ -#define __UTIL_TYPES_FLOAT3_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline float3::float3() { @@ -45,6 +40,7 @@ __forceinline float3 &float3::operator=(const float3 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline float float3::operator[](int i) const { util_assert(i >= 0); @@ -58,23 +54,32 @@ __forceinline float &float3::operator[](int i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline float3 make_float3(float f) { -# ifdef __KERNEL_SSE__ - float3 a(_mm_set1_ps(f)); +# ifdef __KERNEL_GPU__ + float3 a = {f, f, f}; # else +# ifdef __KERNEL_SSE__ + float3 a(_mm_set1_ps(f)); +# else float3 a = {f, f, f, f}; +# endif # endif return a; } ccl_device_inline float3 make_float3(float x, float y, float z) { -# ifdef __KERNEL_SSE__ - float3 a(_mm_set_ps(0.0f, z, y, x)); +# ifdef __KERNEL_GPU__ + float3 a = {x, y, z}; # else +# ifdef __KERNEL_SSE__ + float3 a(_mm_set_ps(0.0f, z, y, x)); +# else float3 a = {x, y, z, 0.0f}; +# endif # endif return a; } @@ -83,8 +88,6 @@ ccl_device_inline void print_float3(const char *label, const float3 &a) { printf("%s: %.8f %.8f %.8f\n", label, (double)a.x, (double)a.y, (double)a.z); } -#endif /* !defined(__KERNEL_GPU__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT3_IMPL_H__ */ diff --git a/intern/cycles/util/types_float4.h b/intern/cycles/util/types_float4.h index 27453bf39e4..6b301c47362 100644 --- a/intern/cycles/util/types_float4.h +++ b/intern/cycles/util/types_float4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT4_H__ -#define __UTIL_TYPES_FLOAT4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,7 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct int4; struct ccl_try_align(16) float4 @@ -35,16 +34,16 @@ struct ccl_try_align(16) float4 float x, y, z, w; # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline float operator[](int i) const; __forceinline float &operator[](int i); +# endif }; ccl_device_inline float4 make_float4(float f); ccl_device_inline float4 make_float4(float x, float y, float z, float w); ccl_device_inline float4 make_float4(const int4 &i); ccl_device_inline void print_float4(const char *label, const float4 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT4_H__ */ diff --git a/intern/cycles/util/types_float4_impl.h b/intern/cycles/util/types_float4_impl.h index d7858f744e3..77b4fbff788 100644 --- a/intern/cycles/util/types_float4_impl.h +++ b/intern/cycles/util/types_float4_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT4_IMPL_H__ -#define __UTIL_TYPES_FLOAT4_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline float4::float4() { @@ -41,6 +36,7 @@ __forceinline float4 &float4::operator=(const float4 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline float float4::operator[](int i) const { util_assert(i >= 0); @@ -54,6 +50,7 @@ __forceinline float &float4::operator[](int i) util_assert(i < 4); return *(&x + i); } +# endif ccl_device_inline float4 make_float4(float f) { @@ -89,8 +86,6 @@ ccl_device_inline void print_float4(const char *label, const float4 &a) { printf("%s: %.8f %.8f %.8f %.8f\n", label, (double)a.x, (double)a.y, (double)a.z, (double)a.w); } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT4_IMPL_H__ */ diff --git a/intern/cycles/util/types_float8.h b/intern/cycles/util/types_float8.h index bb9798932ac..29fd632f08e 100644 --- a/intern/cycles/util/types_float8.h +++ b/intern/cycles/util/types_float8.h @@ -2,8 +2,7 @@ * Original code Copyright 2017, Intel Corporation * Modifications Copyright 2018-2022 Blender Foundation. */ -#ifndef __UTIL_TYPES_FLOAT8_H__ -#define __UTIL_TYPES_FLOAT8_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -12,7 +11,7 @@ CCL_NAMESPACE_BEGIN /* float8 is a reserved type in Metal that has not been implemented. For - * that reason this is named float8_t. */ + * that reason this is named float8_t and not using native vector types. */ #ifdef __KERNEL_GPU__ struct float8_t @@ -52,5 +51,3 @@ ccl_device_inline float8_t make_float8_t(float a, float b, float c, float d, float e, float f, float g, float h); CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT8_H__ */ diff --git a/intern/cycles/util/types_float8_impl.h b/intern/cycles/util/types_float8_impl.h index 2ab464a791b..e8576cdaf70 100644 --- a/intern/cycles/util/types_float8_impl.h +++ b/intern/cycles/util/types_float8_impl.h @@ -2,17 +2,12 @@ * Original code Copyright 2017, Intel Corporation * Modifications Copyright 2018-2022 Blender Foundation. */ -#ifndef __UTIL_TYPES_FLOAT8_IMPL_H__ -#define __UTIL_TYPES_FLOAT8_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN #ifdef __KERNEL_AVX2__ @@ -83,5 +78,3 @@ make_float8_t(float a, float b, float c, float d, float e, float f, float g, flo } CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT8_IMPL_H__ */ diff --git a/intern/cycles/util/types_int2.h b/intern/cycles/util/types_int2.h index bf69cddc653..604713dffcd 100644 --- a/intern/cycles/util/types_int2.h +++ b/intern/cycles/util/types_int2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT2_H__ -#define __UTIL_TYPES_INT2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct int2 { int x, y; +# ifndef __KERNEL_GPU__ __forceinline int operator[](int i) const; __forceinline int &operator[](int i); +# endif }; ccl_device_inline int2 make_int2(int x, int y); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT2_H__ */ diff --git a/intern/cycles/util/types_int2_impl.h b/intern/cycles/util/types_int2_impl.h index 7bdc77369ee..f48c6f46729 100644 --- a/intern/cycles/util/types_int2_impl.h +++ b/intern/cycles/util/types_int2_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT2_IMPL_H__ -#define __UTIL_TYPES_INT2_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ int int2::operator[](int i) const { util_assert(i >= 0); @@ -24,14 +24,13 @@ int &int2::operator[](int i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline int2 make_int2(int x, int y) { int2 a = {x, y}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT2_IMPL_H__ */ diff --git a/intern/cycles/util/types_int3.h b/intern/cycles/util/types_int3.h index f88ff22ac35..3196b158ee9 100644 --- a/intern/cycles/util/types_int3.h +++ b/intern/cycles/util/types_int3.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT3_H__ -#define __UTIL_TYPES_INT3_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,10 +9,15 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct ccl_try_align(16) int3 { -# ifdef __KERNEL_SSE__ +# ifdef __KERNEL_GPU__ + /* Compact structure on the GPU. */ + int x, y, z; +# else + /* SIMD aligned structure for CPU. */ +# ifdef __KERNEL_SSE__ union { __m128i m128; struct { @@ -29,19 +33,20 @@ struct ccl_try_align(16) int3 __forceinline operator __m128i &(); __forceinline int3 &operator=(const int3 &a); -# else /* __KERNEL_SSE__ */ +# else /* __KERNEL_SSE__ */ int x, y, z, w; -# endif /* __KERNEL_SSE__ */ +# endif /* __KERNEL_SSE__ */ +# endif +# ifndef __KERNEL_GPU__ __forceinline int operator[](int i) const; __forceinline int &operator[](int i); +# endif }; ccl_device_inline int3 make_int3(int i); ccl_device_inline int3 make_int3(int x, int y, int z); ccl_device_inline void print_int3(const char *label, const int3 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT3_H__ */ diff --git a/intern/cycles/util/types_int3_impl.h b/intern/cycles/util/types_int3_impl.h index 1c49e97ad32..abc0f4e4309 100644 --- a/intern/cycles/util/types_int3_impl.h +++ b/intern/cycles/util/types_int3_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT3_IMPL_H__ -#define __UTIL_TYPES_INT3_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline int3::int3() { @@ -45,6 +40,7 @@ __forceinline int3 &int3::operator=(const int3 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline int int3::operator[](int i) const { util_assert(i >= 0); @@ -58,25 +54,33 @@ __forceinline int &int3::operator[](int i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline int3 make_int3(int i) { -# ifdef __KERNEL_SSE__ - int3 a(_mm_set1_epi32(i)); +# ifdef __KERNEL_GPU__ + int3 a = {i, i, i}; # else +# ifdef __KERNEL_SSE__ + int3 a(_mm_set1_epi32(i)); +# else int3 a = {i, i, i, i}; +# endif # endif return a; } ccl_device_inline int3 make_int3(int x, int y, int z) { -# ifdef __KERNEL_SSE__ - int3 a(_mm_set_epi32(0, z, y, x)); +# ifdef __KERNEL_GPU__ + int3 a = {x, y, z}; # else +# ifdef __KERNEL_SSE__ + int3 a(_mm_set_epi32(0, z, y, x)); +# else int3 a = {x, y, z, 0}; +# endif # endif - return a; } @@ -84,8 +88,6 @@ ccl_device_inline void print_int3(const char *label, const int3 &a) { printf("%s: %d %d %d\n", label, a.x, a.y, a.z); } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT3_IMPL_H__ */ diff --git a/intern/cycles/util/types_int4.h b/intern/cycles/util/types_int4.h index 9d557c01344..0ac53ffc09c 100644 --- a/intern/cycles/util/types_int4.h +++ b/intern/cycles/util/types_int4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT4_H__ -#define __UTIL_TYPES_INT4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,7 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct float3; struct float4; @@ -37,8 +36,10 @@ struct ccl_try_align(16) int4 int x, y, z, w; # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline int operator[](int i) const; __forceinline int &operator[](int i); +# endif }; ccl_device_inline int4 make_int4(int i); @@ -46,8 +47,6 @@ ccl_device_inline int4 make_int4(int x, int y, int z, int w); ccl_device_inline int4 make_int4(const float3 &f); ccl_device_inline int4 make_int4(const float4 &f); ccl_device_inline void print_int4(const char *label, const int4 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT4_H__ */ diff --git a/intern/cycles/util/types_int4_impl.h b/intern/cycles/util/types_int4_impl.h index 11e1ede6705..2aab29df941 100644 --- a/intern/cycles/util/types_int4_impl.h +++ b/intern/cycles/util/types_int4_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT4_IMPL_H__ -#define __UTIL_TYPES_INT4_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline int4::int4() { @@ -45,6 +40,7 @@ __forceinline int4 &int4::operator=(const int4 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline int int4::operator[](int i) const { util_assert(i >= 0); @@ -58,6 +54,7 @@ __forceinline int &int4::operator[](int i) util_assert(i < 4); return *(&x + i); } +# endif ccl_device_inline int4 make_int4(int i) { @@ -105,8 +102,6 @@ ccl_device_inline void print_int4(const char *label, const int4 &a) { printf("%s: %d %d %d %d\n", label, a.x, a.y, a.z, a.w); } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT4_IMPL_H__ */ diff --git a/intern/cycles/util/types_uchar2.h b/intern/cycles/util/types_uchar2.h index 0b3c9bd0331..ce617248e6e 100644 --- a/intern/cycles/util/types_uchar2.h +++ b/intern/cycles/util/types_uchar2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UCHAR2_H__ -#define __UTIL_TYPES_UCHAR2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uchar2 { uchar x, y; +# ifndef __KERNEL_GPU__ __forceinline uchar operator[](int i) const; __forceinline uchar &operator[](int i); +# endif }; ccl_device_inline uchar2 make_uchar2(uchar x, uchar y); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UCHAR2_H__ */ diff --git a/intern/cycles/util/types_uchar2_impl.h b/intern/cycles/util/types_uchar2_impl.h index a7254d5eaf2..9f3f3a4efb9 100644 --- a/intern/cycles/util/types_uchar2_impl.h +++ b/intern/cycles/util/types_uchar2_impl.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ uchar uchar2::operator[](int i) const { util_assert(i >= 0); @@ -24,13 +25,14 @@ uchar &uchar2::operator[](int i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline uchar2 make_uchar2(uchar x, uchar y) { uchar2 a = {x, y}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uchar3.h b/intern/cycles/util/types_uchar3.h index fc213502ada..aed04c4775e 100644 --- a/intern/cycles/util/types_uchar3.h +++ b/intern/cycles/util/types_uchar3.h @@ -10,16 +10,18 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uchar3 { uchar x, y, z; +# ifndef __KERNEL_GPU__ __forceinline uchar operator[](int i) const; __forceinline uchar &operator[](int i); +# endif }; ccl_device_inline uchar3 make_uchar3(uchar x, uchar y, uchar z); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uchar3_impl.h b/intern/cycles/util/types_uchar3_impl.h index 0c24ffb488a..83eb3c99b3c 100644 --- a/intern/cycles/util/types_uchar3_impl.h +++ b/intern/cycles/util/types_uchar3_impl.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ uchar uchar3::operator[](int i) const { util_assert(i >= 0); @@ -24,13 +25,14 @@ uchar &uchar3::operator[](int i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline uchar3 make_uchar3(uchar x, uchar y, uchar z) { uchar3 a = {x, y, z}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uchar4.h b/intern/cycles/util/types_uchar4.h index a2a2c945aaa..fb13a98875e 100644 --- a/intern/cycles/util/types_uchar4.h +++ b/intern/cycles/util/types_uchar4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UCHAR4_H__ -#define __UTIL_TYPES_UCHAR4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uchar4 { uchar x, y, z, w; +# ifndef __KERNEL_GPU__ __forceinline uchar operator[](int i) const; __forceinline uchar &operator[](int i); +# endif }; ccl_device_inline uchar4 make_uchar4(uchar x, uchar y, uchar z, uchar w); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UCHAR4_H__ */ diff --git a/intern/cycles/util/types_uchar4_impl.h b/intern/cycles/util/types_uchar4_impl.h index 8ec6213a37d..244bb98f883 100644 --- a/intern/cycles/util/types_uchar4_impl.h +++ b/intern/cycles/util/types_uchar4_impl.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ uchar uchar4::operator[](int i) const { util_assert(i >= 0); @@ -24,13 +25,14 @@ uchar &uchar4::operator[](int i) util_assert(i < 4); return *(&x + i); } +# endif ccl_device_inline uchar4 make_uchar4(uchar x, uchar y, uchar z, uchar w) { uchar4 a = {x, y, z, w}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uint2.h b/intern/cycles/util/types_uint2.h index faa0955f903..4d76b628088 100644 --- a/intern/cycles/util/types_uint2.h +++ b/intern/cycles/util/types_uint2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT2_H__ -#define __UTIL_TYPES_UINT2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uint2 { uint x, y; +# ifndef __KERNEL_GPU__ __forceinline uint operator[](uint i) const; __forceinline uint &operator[](uint i); +# endif }; ccl_device_inline uint2 make_uint2(uint x, uint y); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT2_H__ */ diff --git a/intern/cycles/util/types_uint2_impl.h b/intern/cycles/util/types_uint2_impl.h index cac0ba6b531..b508aaf2543 100644 --- a/intern/cycles/util/types_uint2_impl.h +++ b/intern/cycles/util/types_uint2_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT2_IMPL_H__ -#define __UTIL_TYPES_UINT2_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline uint uint2::operator[](uint i) const { util_assert(i < 2); @@ -22,14 +22,13 @@ __forceinline uint &uint2::operator[](uint i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline uint2 make_uint2(uint x, uint y) { uint2 a = {x, y}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT2_IMPL_H__ */ diff --git a/intern/cycles/util/types_uint3.h b/intern/cycles/util/types_uint3.h index 3ff87bfc791..b1571716fc7 100644 --- a/intern/cycles/util/types_uint3.h +++ b/intern/cycles/util/types_uint3.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT3_H__ -#define __UTIL_TYPES_UINT3_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uint3 { uint x, y, z; +# ifndef __KERNEL_GPU__ __forceinline uint operator[](uint i) const; __forceinline uint &operator[](uint i); +# endif }; ccl_device_inline uint3 make_uint3(uint x, uint y, uint z); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT3_H__ */ diff --git a/intern/cycles/util/types_uint3_impl.h b/intern/cycles/util/types_uint3_impl.h index 221883a1adb..d36c9f52de9 100644 --- a/intern/cycles/util/types_uint3_impl.h +++ b/intern/cycles/util/types_uint3_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT3_IMPL_H__ -#define __UTIL_TYPES_UINT3_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline uint uint3::operator[](uint i) const { util_assert(i < 3); @@ -22,14 +22,13 @@ __forceinline uint &uint3::operator[](uint i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline uint3 make_uint3(uint x, uint y, uint z) { uint3 a = {x, y, z}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT3_IMPL_H__ */ diff --git a/intern/cycles/util/types_uint4.h b/intern/cycles/util/types_uint4.h index 504095b2383..4982b30f577 100644 --- a/intern/cycles/util/types_uint4.h +++ b/intern/cycles/util/types_uint4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT4_H__ -#define __UTIL_TYPES_UINT4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uint4 { uint x, y, z, w; +# ifndef __KERNEL_GPU__ __forceinline uint operator[](uint i) const; __forceinline uint &operator[](uint i); +# endif }; ccl_device_inline uint4 make_uint4(uint x, uint y, uint z, uint w); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT4_H__ */ diff --git a/intern/cycles/util/types_uint4_impl.h b/intern/cycles/util/types_uint4_impl.h index d78db944a1f..1cfdb9e0992 100644 --- a/intern/cycles/util/types_uint4_impl.h +++ b/intern/cycles/util/types_uint4_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT4_IMPL_H__ -#define __UTIL_TYPES_UINT4_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline uint uint4::operator[](uint i) const { util_assert(i < 3); @@ -22,14 +22,13 @@ __forceinline uint &uint4::operator[](uint i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline uint4 make_uint4(uint x, uint y, uint z, uint w) { uint4 a = {x, y, z, w}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT4_IMPL_H__ */ diff --git a/intern/cycles/util/types_ushort4.h b/intern/cycles/util/types_ushort4.h index 9a6e12095ba..aef36f63285 100644 --- a/intern/cycles/util/types_ushort4.h +++ b/intern/cycles/util/types_ushort4.h @@ -10,7 +10,7 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct ushort4 { uint16_t x, y, z, w; diff --git a/intern/cycles/util/types_vector3.h b/intern/cycles/util/types_vector3.h deleted file mode 100644 index 2e0d68e1bd0..00000000000 --- a/intern/cycles/util/types_vector3.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#ifndef __UTIL_TYPES_VECTOR3_H__ -#define __UTIL_TYPES_VECTOR3_H__ - -#ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util/types.h instead." -#endif - -CCL_NAMESPACE_BEGIN - -#ifndef __KERNEL_GPU__ -template<typename T> class vector3 { - public: - T x, y, z; - - __forceinline vector3(); - __forceinline vector3(const T &a); - __forceinline vector3(const T &x, const T &y, const T &z); -}; -#endif /* __KERNEL_GPU__ */ - -CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_VECTOR3_H__ */ diff --git a/intern/cycles/util/types_vector3_impl.h b/intern/cycles/util/types_vector3_impl.h deleted file mode 100644 index a765780e2d3..00000000000 --- a/intern/cycles/util/types_vector3_impl.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#ifndef __UTIL_TYPES_VECTOR3_IMPL_H__ -#define __UTIL_TYPES_VECTOR3_IMPL_H__ - -#ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util/types.h instead." -#endif - -CCL_NAMESPACE_BEGIN - -#ifndef __KERNEL_GPU__ -template<typename T> ccl_always_inline vector3<T>::vector3() -{ -} - -template<typename T> ccl_always_inline vector3<T>::vector3(const T &a) : x(a), y(a), z(a) -{ -} - -template<typename T> -ccl_always_inline vector3<T>::vector3(const T &x, const T &y, const T &z) : x(x), y(y), z(z) -{ -} -#endif /* __KERNEL_GPU__ */ - -CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_VECTOR3_IMPL_H__ */ diff --git a/release/datafiles/fonts/DejaVuSans.woff2 b/release/datafiles/fonts/DejaVuSans.woff2 Binary files differnew file mode 100644 index 00000000000..a391596a421 --- /dev/null +++ b/release/datafiles/fonts/DejaVuSans.woff2 diff --git a/release/datafiles/fonts/DejaVuSansMono.woff2 b/release/datafiles/fonts/DejaVuSansMono.woff2 Binary files differnew file mode 100644 index 00000000000..cf200e12fff --- /dev/null +++ b/release/datafiles/fonts/DejaVuSansMono.woff2 diff --git a/release/datafiles/fonts/MaterialIcons-Variable.woff2 b/release/datafiles/fonts/MaterialIcons-Variable.woff2 Binary files differnew file mode 100644 index 00000000000..048802a6454 --- /dev/null +++ b/release/datafiles/fonts/MaterialIcons-Variable.woff2 diff --git a/release/datafiles/fonts/Noto Sans CJK Regular.woff2 b/release/datafiles/fonts/Noto Sans CJK Regular.woff2 Binary files differnew file mode 100644 index 00000000000..5d3854b6bf7 --- /dev/null +++ b/release/datafiles/fonts/Noto Sans CJK Regular.woff2 diff --git a/release/datafiles/fonts/NotoEmoji-VariableFont_wght.woff2 b/release/datafiles/fonts/NotoEmoji-VariableFont_wght.woff2 Binary files differnew file mode 100644 index 00000000000..4d019787bca --- /dev/null +++ b/release/datafiles/fonts/NotoEmoji-VariableFont_wght.woff2 diff --git a/release/datafiles/fonts/NotoSansArabic-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansArabic-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..8ee78b73e72 --- /dev/null +++ b/release/datafiles/fonts/NotoSansArabic-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansArmenian-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansArmenian-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..c6c1ed5c2cf --- /dev/null +++ b/release/datafiles/fonts/NotoSansArmenian-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansBengali-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansBengali-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..cdac12cc8e8 --- /dev/null +++ b/release/datafiles/fonts/NotoSansBengali-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansDevanagari-Regular.woff2 b/release/datafiles/fonts/NotoSansDevanagari-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..2cb157b2c51 --- /dev/null +++ b/release/datafiles/fonts/NotoSansDevanagari-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansEthiopic-Regular.woff2 b/release/datafiles/fonts/NotoSansEthiopic-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..dc272d98964 --- /dev/null +++ b/release/datafiles/fonts/NotoSansEthiopic-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansGeorgian-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansGeorgian-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..4ebc52f0b59 --- /dev/null +++ b/release/datafiles/fonts/NotoSansGeorgian-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansGujarati-Regular.woff2 b/release/datafiles/fonts/NotoSansGujarati-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..6e66a15b1cd --- /dev/null +++ b/release/datafiles/fonts/NotoSansGujarati-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..e752468775f --- /dev/null +++ b/release/datafiles/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..4f6033c916f --- /dev/null +++ b/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansJavanese-Regular.woff2 b/release/datafiles/fonts/NotoSansJavanese-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..aeb0bbe8dab --- /dev/null +++ b/release/datafiles/fonts/NotoSansJavanese-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansKannada-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansKannada-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..56fbd8d8bce --- /dev/null +++ b/release/datafiles/fonts/NotoSansKannada-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansMalayalam-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansMalayalam-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..bdbce8a0b76 --- /dev/null +++ b/release/datafiles/fonts/NotoSansMalayalam-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansMath-Regular.woff2 b/release/datafiles/fonts/NotoSansMath-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..bb3baafeb7a --- /dev/null +++ b/release/datafiles/fonts/NotoSansMath-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansMyanmar-Regular.woff2 b/release/datafiles/fonts/NotoSansMyanmar-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..f18edac80ed --- /dev/null +++ b/release/datafiles/fonts/NotoSansMyanmar-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansSymbols-VariableFont_wght.woff2 b/release/datafiles/fonts/NotoSansSymbols-VariableFont_wght.woff2 Binary files differnew file mode 100644 index 00000000000..98f940b813e --- /dev/null +++ b/release/datafiles/fonts/NotoSansSymbols-VariableFont_wght.woff2 diff --git a/release/datafiles/fonts/NotoSansSymbols2-Regular.woff2 b/release/datafiles/fonts/NotoSansSymbols2-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..cefcc2d9c0d --- /dev/null +++ b/release/datafiles/fonts/NotoSansSymbols2-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansTamil-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansTamil-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..a3541942429 --- /dev/null +++ b/release/datafiles/fonts/NotoSansTamil-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansTelugu-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansTelugu-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..790235d3a71 --- /dev/null +++ b/release/datafiles/fonts/NotoSansTelugu-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansThai-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansThai-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..507255e6b5c --- /dev/null +++ b/release/datafiles/fonts/NotoSansThai-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/bmonofont-i18n.ttf b/release/datafiles/fonts/bmonofont-i18n.ttf Binary files differdeleted file mode 100644 index 08b3f723d61..00000000000 --- a/release/datafiles/fonts/bmonofont-i18n.ttf +++ /dev/null diff --git a/release/datafiles/fonts/droidsans.ttf b/release/datafiles/fonts/droidsans.ttf Binary files differdeleted file mode 100644 index b03e47f087e..00000000000 --- a/release/datafiles/fonts/droidsans.ttf +++ /dev/null diff --git a/release/datafiles/fonts/lastresort.woff2 b/release/datafiles/fonts/lastresort.woff2 Binary files differnew file mode 100644 index 00000000000..e5ad6f353f5 --- /dev/null +++ b/release/datafiles/fonts/lastresort.woff2 diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex d7c9cdd3a8f..eb1250cf5a5 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index df4ca9ef170..ff9b5a06fb7 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -149,37 +149,6 @@ class NODE_OT_add_node(NodeAddOperator, Operator): bl_options = {'REGISTER', 'UNDO'} -# Add a node and link it to an existing socket -class NODE_OT_add_and_link_node(NodeAddOperator, Operator): - '''Add a node to the active tree and link to an existing socket''' - bl_idname = "node.add_and_link_node" - bl_label = "Add and Link Node" - bl_options = {'REGISTER', 'UNDO'} - - link_socket_index: IntProperty( - name="Link Socket Index", - description="Index of the socket to link", - ) - - def execute(self, context): - space = context.space_data - ntree = space.edit_tree - - node = self.create_node(context) - if not node: - return {'CANCELLED'} - - to_socket = getattr(context, "link_to_socket", None) - if to_socket: - ntree.links.new(node.outputs[self.link_socket_index], to_socket) - - from_socket = getattr(context, "link_from_socket", None) - if from_socket: - ntree.links.new(from_socket, node.inputs[self.link_socket_index]) - - return {'FINISHED'} - - class NODE_OT_add_search(NodeAddOperator, Operator): '''Add a node to the active tree''' bl_idname = "node.add_search" @@ -306,7 +275,6 @@ class NODE_OT_tree_path_parent(Operator): classes = ( NodeSetting, - NODE_OT_add_and_link_node, NODE_OT_add_node, NODE_OT_add_search, NODE_OT_collapse_hide_unused_toggle, diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index e031b32247a..062476f2624 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -162,6 +162,35 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel): col.prop(props, "motion_blur_steps", text="Steps") +class RENDER_PT_eevee_next_motion_blur(RenderButtonsPanel, Panel): + bl_label = "Motion Blur" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + scene = context.scene + props = scene.eevee + self.layout.prop(props, "use_motion_blur", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + scene = context.scene + props = scene.eevee + + layout.active = props.use_motion_blur + col = layout.column() + col.prop(props, "motion_blur_position", text="Position") + col.prop(props, "motion_blur_shutter") + col.separator() + col.prop(props, "motion_blur_depth_scale") + col.prop(props, "motion_blur_steps", text="Steps") + + class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): bl_label = "Depth of Field" bl_options = {'DEFAULT_CLOSED'} @@ -756,6 +785,7 @@ classes = ( RENDER_PT_eevee_film, RENDER_PT_eevee_next_sampling, + RENDER_PT_eevee_next_motion_blur, RENDER_PT_eevee_next_film, RENDER_PT_gpencil, diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index 01bd0adc8df..78aec096510 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -79,6 +79,7 @@ class VIEWLAYER_PT_eevee_next_layer_passes_data(ViewLayerButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False + scene = context.scene view_layer = context.view_layer col = layout.column() @@ -87,7 +88,9 @@ class VIEWLAYER_PT_eevee_next_layer_passes_data(ViewLayerButtonsPanel, Panel): col.prop(view_layer, "use_pass_mist") col.prop(view_layer, "use_pass_normal") col.prop(view_layer, "use_pass_position") - col.prop(view_layer, "use_pass_vector") + sub = col.column() + sub.active = not scene.eevee.use_motion_blur + sub.prop(view_layer, "use_pass_vector") class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel): diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 83ca9158efc..75824ae056f 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -18,10 +18,10 @@ extern "C" { #define BLF_DATAFILES_FONTS_DIR "fonts" /* File name of the default variable-width font. */ -#define BLF_DEFAULT_PROPORTIONAL_FONT "droidsans.ttf" +#define BLF_DEFAULT_PROPORTIONAL_FONT "DejaVuSans.woff2" /* File name of the default fixed-pitch font. */ -#define BLF_DEFAULT_MONOSPACED_FONT "bmonofont-i18n.ttf" +#define BLF_DEFAULT_MONOSPACED_FONT "DejaVuSansMono.woff2" /* enable this only if needed (unused circa 2016) */ #define BLF_BLUR_ENABLE 0 @@ -351,6 +351,8 @@ enum { BLF_DEFAULT = 1 << 14, /** Must only be used as last font in the stack. */ BLF_LAST_RESORT = 1 << 15, + /** Failure to load this font. Don't try again. */ + BLF_BAD_FONT = 1 << 16, }; #define BLF_DRAW_STR_DUMMY_MAX 1024 diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 038e73cc928..eb974f33994 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -18,8 +18,9 @@ #include FT_FREETYPE_H #include FT_GLYPH_H -#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ +#include FT_TRUETYPE_IDS_H /* Codepoint coverage constants. */ +#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ #include "MEM_guardedalloc.h" @@ -28,6 +29,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" +#include "BLI_path_util.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BLI_string_utf8.h" @@ -836,7 +838,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, size_t i = 0, i_curr; rcti gbox_px; - if (str_len == 0) { + if (str_len == 0 || str[0] == 0) { /* early output. */ return; } @@ -1157,7 +1159,7 @@ int blf_font_ascender(FontBLF *font) char *blf_display_name(FontBLF *font) { - if (!font->face->family_name) { + if (!blf_ensure_face(font) || !font->face->family_name) { return NULL; } return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); @@ -1244,14 +1246,28 @@ static void blf_font_fill(FontBLF *font) font->glyph_cache_mutex = &blf_glyph_cache_mutex; } -FontBLF *blf_font_new(const char *name, const char *filepath) +/** + * Create an FT_Face for this font if not already existing. + */ +bool blf_ensure_face(FontBLF *font) { - FontBLF *font; + if (font->face) { + return true; + } + + if (font->flags & BLF_BAD_FONT) { + return false; + } + FT_Error err; - char *mfile; - font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - err = FT_New_Face(ft_lib, filepath, 0, &font->face); + if (font->filepath) { + err = FT_New_Face(ft_lib, font->filepath, 0, &font->face); + } + if (font->mem) { + err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face); + } + if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { printf("Format of this font file is not supported\n"); @@ -1259,8 +1275,8 @@ FontBLF *blf_font_new(const char *name, const char *filepath) else { printf("Error encountered while opening font file\n"); } - MEM_freeN(font); - return NULL; + font->flags |= BLF_BAD_FONT; + return false; } err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); @@ -1272,28 +1288,28 @@ FontBLF *blf_font_new(const char *name, const char *filepath) } if (err) { printf("Can't set a character map!\n"); - FT_Done_Face(font->face); - MEM_freeN(font); - return NULL; + font->flags |= BLF_BAD_FONT; + return false; } - mfile = blf_dir_metrics_search(filepath); - if (mfile) { - err = FT_Attach_File(font->face, mfile); - if (err) { - fprintf(stderr, "FT_Attach_File failed to load '%s' with error %d\n", filepath, (int)err); + if (font->filepath) { + char *mfile = blf_dir_metrics_search(font->filepath); + if (mfile) { + err = FT_Attach_File(font->face, mfile); + if (err) { + fprintf(stderr, + "FT_Attach_File failed to load '%s' with error %d\n", + font->filepath, + (int)err); + } + MEM_freeN(mfile); } - MEM_freeN(mfile); } if (FT_HAS_MULTIPLE_MASTERS(font->face)) { FT_Get_MM_Var(font->face, &(font->variations)); } - font->name = BLI_strdup(name); - font->filepath = BLI_strdup(filepath); - blf_font_fill(font); - /* Save TrueType table with bits to quickly test most unicode block coverage. */ TT_OS2 *os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(font->face, FT_SFNT_OS2); if (os2_table) { @@ -1303,17 +1319,11 @@ FontBLF *blf_font_new(const char *name, const char *filepath) font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4; } - /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */ - if (font->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU && - font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) { - font->flags |= BLF_LAST_RESORT; - } - if (FT_IS_FIXED_WIDTH(font->face)) { font->flags |= BLF_MONOSPACED; } - if (FT_HAS_KERNING(font->face)) { + if (FT_HAS_KERNING(font->face) && !font->kerning_cache) { /* Create kerning cache table and fill with value indicating "unset". */ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { @@ -1323,49 +1333,114 @@ FontBLF *blf_font_new(const char *name, const char *filepath) } } - return font; + return true; } -void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, int mem_size) +typedef struct eFaceDetails { + char name[50]; + unsigned int coverage1; + unsigned int coverage2; + unsigned int coverage3; + unsigned int coverage4; +} eFaceDetails; + +/* Details about the fallback fonts we ship, so that we can load only when needed. */ +static const eFaceDetails static_face_details[] = { + {"lastresort.woff2", UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX}, + {"Noto Sans CJK Regular.woff2", 0x30000083L, 0x2BDF3C10L, 0x16L, 0}, + {"NotoEmoji-VariableFont_wght.woff2", 0x80000003L, 0x241E4ACL, 0x14000000L, 0x4000000L}, + {"NotoSansArabic-VariableFont_wdth,wght.woff2", TT_UCR_ARABIC, 0, 0, 0}, + {"NotoSansArmenian-VariableFont_wdth,wght.woff2", + TT_UCR_ARMENIAN, + TT_UCR_ALPHABETIC_PRESENTATION_FORMS, + 0, + 0}, + {"NotoSansBengali-VariableFont_wdth,wght.woff2", TT_UCR_BENGALI, 0, 0, 0}, + {"NotoSansDevanagari-Regular.woff2", TT_UCR_DEVANAGARI, 0, 0, 0}, + {"NotoSansEthiopic-Regular.woff2", 0, 0, TT_UCR_ETHIOPIC, 0}, + {"NotoSansGeorgian-VariableFont_wdth,wght.woff2", TT_UCR_GEORGIAN, 0, 0, 0}, + {"NotoSansGujarati-Regular.woff2", TT_UCR_GUJARATI, 0, 0, 0}, + {"NotoSansGurmukhi-VariableFont_wdth,wght.woff2", TT_UCR_GURMUKHI, 0, 0, 0}, + {"NotoSansHebrew-VariableFont_wdth,wght.woff2", TT_UCR_HEBREW, 0, 0, 0}, + {"NotoSansJavanese-Regular.woff2", 0x80000003L, 0x2000L, 0, 0}, + {"NotoSansKannada-VariableFont_wdth,wght.woff2", TT_UCR_KANNADA, 0, 0, 0}, + {"NotoSansMalayalam-VariableFont_wdth,wght.woff2", TT_UCR_MALAYALAM, 0, 0, 0}, + {"NotoSansMath-Regular.woff2", 0, TT_UCR_MATHEMATICAL_OPERATORS, 0, 0}, + {"NotoSansMyanmar-Regular.woff2", 0, 0, TT_UCR_MYANMAR, 0}, + {"NotoSansSymbols-VariableFont_wght.woff2", 0x3L, 0x200E4B4L, 0, 0}, + {"NotoSansSymbols2-Regular.woff2", 0x80000003L, 0x200E3E4L, 0x40020L, 0x580A048L}, + {"NotoSansTamil-VariableFont_wdth,wght.woff2", TT_UCR_TAMIL, 0, 0, 0}, + {"NotoSansTelugu-VariableFont_wdth,wght.woff2", TT_UCR_TELUGU, 0, 0, 0}, + {"NotoSansThai-VariableFont_wdth,wght.woff2", TT_UCR_THAI, 0, 0, 0}, +}; + +/* Create a new font from filename OR from passed memory pointer. */ +static FontBLF *blf_font_new_ex(const char *name, + const char *filepath, + const unsigned char *mem, + const size_t mem_size) { - FT_Open_Args open; + FontBLF *font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - open.flags = FT_OPEN_MEMORY; - open.memory_base = (const FT_Byte *)mem; - open.memory_size = mem_size; - FT_Attach_Stream(font->face, &open); -} - -FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size) -{ - FontBLF *font; - FT_Error err; + font->name = BLI_strdup(name); + font->filepath = filepath ? BLI_strdup(filepath) : NULL; + if (mem) { + font->mem = (void *)mem; + font->mem_size = mem_size; + } + blf_font_fill(font); - font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new_from_mem"); - err = FT_New_Memory_Face(ft_lib, mem, mem_size, 0, &font->face); - if (err) { - MEM_freeN(font); - return NULL; + /* If we have static details about this font we don't need to load the Face. */ + const eFaceDetails *static_details = NULL; + char filename[256]; + for (int i = 0; i < (int)ARRAY_SIZE(static_face_details); i++) { + BLI_split_file_part(font->filepath, filename, sizeof(filename)); + if (STREQ(static_face_details[i].name, filename)) { + static_details = &static_face_details[i]; + font->UnicodeRanges[0] = static_details->coverage1; + font->UnicodeRanges[1] = static_details->coverage2; + font->UnicodeRanges[2] = static_details->coverage3; + font->UnicodeRanges[3] = static_details->coverage4; + break; + } } - err = FT_Select_Charmap(font->face, ft_encoding_unicode); - if (err) { - printf("Can't set the unicode character map!\n"); - FT_Done_Face(font->face); - MEM_freeN(font); - return NULL; + if (!static_details) { + if (!blf_ensure_face(font)) { + blf_font_free(font); + return NULL; + } } - if (FT_HAS_MULTIPLE_MASTERS(font->face)) { - FT_Get_MM_Var(font->face, &(font->variations)); + /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */ + if (font->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU && + font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) { + font->flags |= BLF_LAST_RESORT; } - font->name = BLI_strdup(name); - font->filepath = NULL; - blf_font_fill(font); return font; } +FontBLF *blf_font_new(const char *name, const char *filename) +{ + return blf_font_new_ex(name, filename, NULL, 0); +} + +FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, const size_t mem_size) +{ + return blf_font_new_ex(name, NULL, mem, mem_size); +} + +void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, const size_t mem_size) +{ + FT_Open_Args open; + + open.flags = FT_OPEN_MEMORY; + open.memory_base = (const FT_Byte *)mem; + open.memory_size = (FT_Long)mem_size; + FT_Attach_Stream(font->face, &open); +} + void blf_font_free(FontBLF *font) { blf_glyph_cache_clear(font); @@ -1378,7 +1453,9 @@ void blf_font_free(FontBLF *font) FT_Done_MM_Var(ft_lib, font->variations); } - FT_Done_Face(font->face); + if (font->face) { + FT_Done_Face(font->face); + } if (font->filepath) { MEM_freeN(font->filepath); } @@ -1396,6 +1473,10 @@ void blf_font_free(FontBLF *font) bool blf_font_size(FontBLF *font, float size, unsigned int dpi) { + if (!blf_ensure_face(font)) { + return false; + } + /* FreeType uses fixed-point integers in 64ths. */ FT_F26Dot6 ft_size = lroundf(size * 64.0f); /* Adjust our new size to be on even 64ths. */ diff --git a/source/blender/blenfont/intern/blf_font_default.c b/source/blender/blenfont/intern/blf_font_default.c index 1bde25b5776..63957fad003 100644 --- a/source/blender/blenfont/intern/blf_font_default.c +++ b/source/blender/blenfont/intern/blf_font_default.c @@ -66,8 +66,6 @@ void BLF_load_font_stack() } else { BLF_enable(font_id, BLF_DEFAULT); - /* TODO: FontBLF will later load FT_Face on demand. When this is in - * place we can drop this face now since we have all needed data. */ } } } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 215f79e6795..48ddbc9f920 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -584,16 +584,22 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode continue; } if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) { - glyph_index = FT_Get_Char_Index(f->face, charcode); - if (glyph_index) { - *font = f; - return glyph_index; + if (blf_ensure_face(f)) { + glyph_index = FT_Get_Char_Index(f->face, charcode); + if (glyph_index) { + *font = f; + return glyph_index; + } } } } +#ifdef DEBUG + printf("Unicode character U+%04X not found in loaded fonts. \n", charcode); +#endif + /* Not found in the stack, return from Last Resort if there is one. */ - if (last_resort) { + if (last_resort && blf_ensure_face(last_resort)) { glyph_index = FT_Get_Char_Index(last_resort->face, charcode); if (glyph_index) { *font = last_resort; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 84037ff4bd0..6207edb0107 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -39,12 +39,14 @@ void blf_font_exit(void); bool blf_font_id_is_valid(int fontid); +bool blf_ensure_face(struct FontBLF *font); + void blf_draw_buffer__start(struct FontBLF *font); void blf_draw_buffer__end(void); struct FontBLF *blf_font_new(const char *name, const char *filepath); -struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size); -void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size); +struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, size_t mem_size); +void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, size_t mem_size); /** * Change font's output size. Returns true if successful in changing the size. diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 5b55f4af0b8..018cef4540f 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -240,9 +240,13 @@ typedef struct FontBLF { /* # of times this font was loaded */ unsigned int reference_count; - /** File-path or NULL. */ + /* Full path to font file or NULL if from memory. */ char *filepath; + /* Pointer to in-memory font, or NULL if from file. */ + void *mem; + size_t mem_size; + /* Copied from the SFNT OS/2 table. Bit flags for unicode blocks and ranges * considered "functional". Cached here because face might not always exist. * See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 2067730faca..ee9c7a964d9 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -17,15 +17,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 303 +#define BLENDER_VERSION 304 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 6 +#define BLENDER_FILE_SUBVERSION 0 /* 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 diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 404ce63a5df..c14da538e7c 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -7,6 +7,7 @@ */ #include "BLI_compiler_attrs.h" +#include "BLI_sys_types.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/blenkernel/BKE_idprop.hh b/source/blender/blenkernel/BKE_idprop.hh index 1e741cc252f..6a42ab1669f 100644 --- a/source/blender/blenkernel/BKE_idprop.hh +++ b/source/blender/blenkernel/BKE_idprop.hh @@ -45,6 +45,9 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, d std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, const StringRefNull value); +/** \brief Allocate a new IDProperty of type IDP_ID, set its name and value. */ +std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, ID *id); + /** * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_INT. * diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 90dbec7ec52..02e3cefe6a5 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -374,6 +374,9 @@ typedef struct bNodeTreeType { int type; /* type identifier */ char idname[64]; /* identifier name */ + /* The ID name of group nodes for this type. */ + char group_idname[64]; + char ui_name[64]; char ui_description[256]; int ui_icon; diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 3922bfb6c0d..a24a3e05dab 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -291,7 +291,7 @@ enum { /* Draw an item in the uiList */ typedef void (*uiListDrawItemFunc)(struct uiList *ui_list, - struct bContext *C, + const struct bContext *C, struct uiLayout *layout, struct PointerRNA *dataptr, struct PointerRNA *itemptr, @@ -303,12 +303,12 @@ typedef void (*uiListDrawItemFunc)(struct uiList *ui_list, /* Draw the filtering part of an uiList */ typedef void (*uiListDrawFilterFunc)(struct uiList *ui_list, - struct bContext *C, + const struct bContext *C, struct uiLayout *layout); /* Filter items of an uiList */ typedef void (*uiListFilterItemsFunc)(struct uiList *ui_list, - struct bContext *C, + const struct bContext *C, struct PointerRNA *, const char *propname); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 3ea595cfaf0..c16d19588ed 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -53,6 +53,7 @@ #include "BIK_api.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "BLO_read_write.h" diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 861a89ea9d7..b5b00e031b2 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -43,6 +43,7 @@ #include "BLO_read_write.h" #include "RNA_access.h" +#include "RNA_path.h" #include "CLG_log.h" diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index eb4784bebff..19fef1ce825 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -53,6 +53,7 @@ #include "DEG_depsgraph_query.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "BLO_read_write.h" diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 6486be4afe0..7fb4e37f956 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1163,6 +1163,10 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, } CurvesGeometry new_curves{new_point_count, new_curve_count}; + Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); threading::parallel_invoke( 256 < new_point_count * new_curve_count, @@ -1170,8 +1174,7 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); }, [&]() { /* Copy over point attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + for (bke::AttributeTransferData &attribute : point_attributes) { threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { const IndexRange src_range = copy_point_ranges[range_i]; @@ -1182,24 +1185,29 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, {copy_point_range_dst_offsets[range_i], src_range.size()}); } }); - attribute.dst.finish(); } - + }, + [&]() { /* Copy over curve attributes. * In some cases points are just dissolved, so the the number of * curves will be the same. That could be optimized in the future. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + for (bke::AttributeTransferData &attribute : curve_attributes) { if (new_curves.curves_num() == curves.curves_num()) { attribute.dst.span.copy_from(attribute.src); } else { copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span); } - attribute.dst.finish(); } }); + for (bke::AttributeTransferData &attribute : point_attributes) { + attribute.dst.finish(); + } + for (bke::AttributeTransferData &attribute : curve_attributes) { + attribute.dst.finish(); + } + return new_curves; } @@ -1236,6 +1244,10 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } CurvesGeometry new_curves{new_tot_points, new_tot_curves}; + Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); threading::parallel_invoke( 256 < new_tot_points * new_tot_curves, @@ -1267,8 +1279,7 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, }, [&]() { /* Copy over point attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + for (bke::AttributeTransferData &attribute : point_attributes) { threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { copy_between_buffers(attribute.src.type(), @@ -1278,11 +1289,11 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_point_ranges[range_i]); } }); - attribute.dst.finish(); } + }, + [&]() { /* Copy over curve attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + for (bke::AttributeTransferData &attribute : curve_attributes) { threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { copy_between_buffers(attribute.src.type(), @@ -1292,10 +1303,16 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_curve_ranges[range_i]); } }); - attribute.dst.finish(); } }); + for (bke::AttributeTransferData &attribute : point_attributes) { + attribute.dst.finish(); + } + for (bke::AttributeTransferData &attribute : curve_attributes) { + attribute.dst.finish(); + } + return new_curves; } diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 972ff377519..f5876e48241 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -37,6 +37,7 @@ #include "BLO_read_write.h" #include "RNA_access.h" +#include "RNA_path.h" #include "CLG_log.h" diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index 5d54c5c039b..aa33bef998f 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -30,6 +30,7 @@ #include "BKE_object.h" #include "RNA_access.h" +#include "RNA_path.h" #include "atomic_ops.h" diff --git a/source/blender/blenkernel/intern/idprop_create.cc b/source/blender/blenkernel/intern/idprop_create.cc index f549393fd12..a2f58baebf7 100644 --- a/source/blender/blenkernel/intern/idprop_create.cc +++ b/source/blender/blenkernel/intern/idprop_create.cc @@ -44,6 +44,14 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_n return std::unique_ptr<IDProperty, IDPropertyDeleter>(property); } +std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, ID *value) +{ + IDPropertyTemplate prop_template{0}; + prop_template.id = value; + IDProperty *property = IDP_New(IDP_ID, &prop_template, prop_name.c_str()); + return std::unique_ptr<IDProperty, IDPropertyDeleter>(property); +} + static std::unique_ptr<IDProperty, IDPropertyDeleter> array_create(const StringRefNull prop_name, eIDPropertyType subtype, size_t array_len) diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 7ef15912567..07ce4e46e9b 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -46,6 +46,7 @@ #include "BKE_scene.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "BLO_read_write.h" diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 4ad8d26cd2a..6afbb9064a8 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -52,6 +52,7 @@ #include "PIL_time.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "RNA_types.h" diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh index 6fcc560d856..2b290e1ba7d 100644 --- a/source/blender/blenlib/BLI_index_range.hh +++ b/source/blender/blenlib/BLI_index_range.hh @@ -90,10 +90,10 @@ class IndexRange { return *this; } - constexpr Iterator operator++(int) const + constexpr Iterator operator++(int) { Iterator copied_iterator = *this; - ++copied_iterator; + ++(*this); return copied_iterator; } diff --git a/source/blender/blenlib/BLI_listbase_wrapper.hh b/source/blender/blenlib/BLI_listbase_wrapper.hh index 25e029a5616..2d631cb2441 100644 --- a/source/blender/blenlib/BLI_listbase_wrapper.hh +++ b/source/blender/blenlib/BLI_listbase_wrapper.hh @@ -50,7 +50,7 @@ template<typename T> class ListBaseWrapper { Iterator operator++(int) { Iterator iterator = *this; - ++*this; + ++(*this); return iterator; } diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 55233676ed8..95d1e344894 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -669,10 +669,10 @@ class Map { return *this; } - BaseIterator operator++(int) const + BaseIterator operator++(int) { BaseIterator copied_iterator = *this; - ++copied_iterator; + ++(*this); return copied_iterator; } diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 62de4b79e41..a1b6ad9754e 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -427,10 +427,10 @@ class Set { return *this; } - Iterator operator++(int) const + Iterator operator++(int) { Iterator copied_iterator = *this; - ++copied_iterator; + ++(*this); return copied_iterator; } diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index 5a97b2c7999..9dfa48b5822 100644 --- a/source/blender/blenlib/tests/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -532,8 +532,14 @@ TEST(set, ForwardIterator) Set<int>::iterator iter1 = set.begin(); int value1 = *iter1; Set<int>::iterator iter2 = iter1++; - EXPECT_EQ(*iter1, value1); - EXPECT_EQ(*iter2, *(++iter1)); + EXPECT_EQ(*iter2, value1); + EXPECT_EQ(*(++iter2), *iter1); + /* Interesting find: On GCC & MSVC this will succeed, as the 2nd argument is evaluated before the + * 1st. On Apple Clang it's the other way around, and the test fails. */ + // EXPECT_EQ(*iter1, *(++iter1)); + Set<int>::iterator iter3 = ++iter1; + /* Check that #iter1 itself changed. */ + EXPECT_EQ(*iter3, *iter1); } TEST(set, GenericAlgorithms) diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c index e91dab3dd6f..26f1a9e626e 100644 --- a/source/blender/bmesh/operators/bmo_connect_pair.c +++ b/source/blender/bmesh/operators/bmo_connect_pair.c @@ -83,7 +83,7 @@ typedef struct PathContext { /* only to access BMO flags */ BMesh *bm_bmoflag; - BMVert *v_a, *v_b; + BMVert *v_pair[2]; BLI_mempool *link_pool; } PathContext; @@ -593,17 +593,17 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) } pc.bm_bmoflag = bm; - pc.v_a = ((BMVert **)op_verts_slot->data.p)[0]; - pc.v_b = ((BMVert **)op_verts_slot->data.p)[1]; + pc.v_pair[0] = ((BMVert **)op_verts_slot->data.p)[0]; + pc.v_pair[1] = ((BMVert **)op_verts_slot->data.p)[1]; /* fail! */ - if (!(pc.v_a && pc.v_b)) { + if (!(pc.v_pair[0] && pc.v_pair[1])) { return; } #ifdef DEBUG_PRINT - printf("%s: v_a: %d\n", __func__, BM_elem_index_get(pc.v_a)); - printf("%s: v_b: %d\n", __func__, BM_elem_index_get(pc.v_b)); + printf("%s: v_pair[0]: %d\n", __func__, BM_elem_index_get(pc.v_pair[0])); + printf("%s: v_pair[1]: %d\n", __func__, BM_elem_index_get(pc.v_pair[1])); #endif /* tag so we won't touch ever (typically hidden faces) */ @@ -618,15 +618,15 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) /* calculate matrix */ { - bm_vert_pair_to_matrix(&pc.v_a, pc.matrix); - pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_a->co); + bm_vert_pair_to_matrix(pc.v_pair, pc.matrix); + pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_pair[0]->co); } /* add first vertex */ { PathLinkState *state; state = MEM_callocN(sizeof(*state), __func__); - state_link_add(&pc, state, (BMElem *)pc.v_a, NULL); + state_link_add(&pc, state, (BMElem *)pc.v_pair[0], NULL); BLI_heapsimple_insert(pc.states, state->dist, state); } @@ -642,7 +642,7 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) /* either we insert this into 'pc.states' or its freed */ bool continue_search; - if (state->link_last->ele == (BMElem *)pc.v_b) { + if (state->link_last->ele == (BMElem *)pc.v_pair[1]) { /* pass, wait until all are found */ #ifdef DEBUG_PRINT printf("%s: state %p loop found %.4f\n", __func__, state, state->dist); @@ -698,8 +698,8 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) } while ((link = link->next)); } - BMO_vert_flag_enable(bm, pc.v_a, VERT_OUT); - BMO_vert_flag_enable(bm, pc.v_b, VERT_OUT); + BMO_vert_flag_enable(bm, pc.v_pair[0], VERT_OUT); + BMO_vert_flag_enable(bm, pc.v_pair[1], VERT_OUT); BLI_mempool_destroy(pc.link_pool); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc index 6474f853390..129e0093d11 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc @@ -15,6 +15,8 @@ #include "BKE_animsys.h" +#include "RNA_path.h" + namespace blender::deg { /* Animated property storage. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 4cbb2ce7060..be087c0b2d4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -93,6 +93,7 @@ #include "BKE_world.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "RNA_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 0cb0b60dfb0..7a78280f1f0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -15,6 +15,7 @@ #include "DNA_ID.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_types.h" #include "BLI_string.h" diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 9a047c70d01..19339fa34ea 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -23,6 +23,7 @@ #include "DNA_scene_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "DEG_depsgraph.h" diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 9744f2e3cee..9d3b392d7b3 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -138,6 +138,7 @@ set(SRC engines/eevee_next/eevee_film.cc engines/eevee_next/eevee_instance.cc engines/eevee_next/eevee_material.cc + engines/eevee_next/eevee_motion_blur.cc engines/eevee_next/eevee_pipeline.cc engines/eevee_next/eevee_renderbuffers.cc engines/eevee_next/eevee_sampling.cc @@ -367,7 +368,12 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl engines/eevee_next/shaders/eevee_geom_world_vert.glsl + engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl + engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl + engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl + engines/eevee_next/shaders/eevee_motion_blur_lib.glsl engines/eevee_next/shaders/eevee_nodetree_lib.glsl + engines/eevee_next/shaders/eevee_sampling_lib.glsl engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl engines/eevee_next/shaders/eevee_surf_depth_frag.glsl engines/eevee_next/shaders/eevee_surf_forward_frag.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 1e7979b594e..cb02689f34a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -45,3 +45,7 @@ #define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16 #define FILM_GROUP_SIZE 16 + +#define MOTION_BLUR_GROUP_SIZE 32 + +#define MOTION_BLUR_DILATE_GROUP_SIZE 512 diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index 49f43265aa8..60e5f95d803 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -183,20 +183,17 @@ void Film::init(const int2 &extent, const rcti *output_rect) * Using the render pass ensure we store the center depth. */ render_passes |= EEVEE_RENDER_PASS_Z; } - /* TEST */ - render_passes |= EEVEE_RENDER_PASS_VECTOR; } else { /* Render Case. */ render_passes = eViewLayerEEVEEPassType(inst_.view_layer->eevee.render_passes); - render_passes |= EEVEE_RENDER_PASS_COMBINED; - #define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \ SET_FLAG_FROM_TEST(render_passes, \ (inst_.view_layer->passflag & SCE_PASS_##name_legacy) != 0, \ EEVEE_RENDER_PASS_##name_eevee); + ENABLE_FROM_LEGACY(COMBINED, COMBINED) ENABLE_FROM_LEGACY(Z, Z) ENABLE_FROM_LEGACY(MIST, MIST) ENABLE_FROM_LEGACY(NORMAL, NORMAL) @@ -209,6 +206,7 @@ void Film::init(const int2 &extent, const rcti *output_rect) ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT) ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT) ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) + ENABLE_FROM_LEGACY(VECTOR, VECTOR) #undef ENABLE_FROM_LEGACY } @@ -216,6 +214,11 @@ void Film::init(const int2 &extent, const rcti *output_rect) /* Filter obsolete passes. */ render_passes &= ~(EEVEE_RENDER_PASS_UNUSED_8 | EEVEE_RENDER_PASS_BLOOM); + if (scene_eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) { + /* Disable motion vector pass if motion blur is enabled. */ + render_passes &= ~EEVEE_RENDER_PASS_VECTOR; + } + /* TODO(@fclem): Can't we rely on depsgraph update notification? */ if (assign_if_different(enabled_passes_, render_passes)) { sampling.reset(); @@ -383,7 +386,7 @@ void Film::sync() DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[step_next])); DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx); - DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &rbuffers.combined_tx); + DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &combined_final_tx_); DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx); DRW_shgroup_uniform_texture_ref(grp, "vector_tx", &rbuffers.vector_tx); DRW_shgroup_uniform_texture_ref(grp, "diffuse_light_tx", &rbuffers.diffuse_light_tx); @@ -458,6 +461,10 @@ float2 Film::pixel_jitter_get() const eViewLayerEEVEEPassType Film::enabled_passes_get() const { + if (inst_.is_viewport() && data_.use_reprojection) { + /* Enable motion vector rendering but not the accumulation buffer. */ + return enabled_passes_ | EEVEE_RENDER_PASS_VECTOR; + } return enabled_passes_; } @@ -538,7 +545,7 @@ void Film::update_sample_table() } } -void Film::accumulate(const DRWView *view) +void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx) { if (inst_.is_viewport()) { DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); @@ -554,6 +561,8 @@ void Film::accumulate(const DRWView *view) update_sample_table(); + combined_final_tx_ = combined_final_tx; + /* Need to update the static references as there could have change from a previous swap. */ weight_src_tx_ = weight_tx_.current(); weight_dst_tx_ = weight_tx_.next(); @@ -580,12 +589,14 @@ void Film::display() BLI_assert(inst_.is_viewport()); /* Acquire dummy render buffers for correct binding. They will not be used. */ - inst_.render_buffers.acquire(int2(1), (void *)this); + inst_.render_buffers.acquire(int2(1)); DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); GPU_framebuffer_bind(dfbl->default_fb); GPU_framebuffer_viewport_set(dfbl->default_fb, UNPACK2(data_.offset), UNPACK2(data_.extent)); + combined_final_tx_ = inst_.render_buffers.combined_tx; + /* Need to update the static references as there could have change from a previous swap. */ weight_src_tx_ = weight_tx_.current(); weight_dst_tx_ = weight_tx_.next(); diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh index 1165b9a4c12..d47e3dfa24b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.hh +++ b/source/blender/draw/engines/eevee_next/eevee_film.hh @@ -50,6 +50,8 @@ class Film { /** Static reference as SwapChain does not actually move the objects when swapping. */ GPUTexture *combined_src_tx_ = nullptr; GPUTexture *combined_dst_tx_ = nullptr; + /** Incomming combined buffer with post fx applied (motion blur + depth of field). */ + GPUTexture *combined_final_tx_ = nullptr; /** Weight buffers. Double buffered to allow updating it during accumulation. */ SwapChain<Texture, 2> weight_tx_; /** Static reference as SwapChain does not actually move the objects when swapping. */ @@ -74,7 +76,7 @@ class Film { void end_sync(); /** Accumulate the newly rendered sample contained in #RenderBuffers and blit to display. */ - void accumulate(const DRWView *view); + void accumulate(const DRWView *view, GPUTexture *combined_final_tx); /** Blit to display. No rendered sample needed. */ void display(); diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 9f8cf6dc6ba..70b5cb7d98f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -60,6 +60,8 @@ void Instance::init(const int2 &output_res, sampling.init(scene); camera.init(); film.init(output_res, output_rect); + velocity.init(); + motion_blur.init(); main_view.init(); } @@ -92,15 +94,14 @@ void Instance::update_eval_members() void Instance::begin_sync() { materials.begin_sync(); - velocity.begin_sync(); + velocity.begin_sync(); /* NOTE: Also syncs camera. */ gpencil_engine_enabled = false; - render_buffers.sync(); + motion_blur.sync(); pipelines.sync(); main_view.sync(); world.sync(); - camera.sync(); film.sync(); } @@ -212,22 +213,12 @@ void Instance::render_sample() sampling.step(); main_view.render(); -} - -/** \} */ -/* -------------------------------------------------------------------- */ -/** \name Interface - * \{ */ + motion_blur.step(); +} -void Instance::render_frame(RenderLayer *render_layer, const char *view_name) +void Instance::render_read_result(RenderLayer *render_layer, const char *view_name) { - while (!sampling.finished()) { - this->render_sample(); - /* TODO(fclem) print progression. */ - } - - /* Read Results. */ eViewLayerEEVEEPassType pass_bits = film.enabled_passes_get(); for (auto i : IndexRange(EEVEE_RENDER_PASS_MAX_BIT)) { eViewLayerEEVEEPassType pass_type = eViewLayerEEVEEPassType(pass_bits & (1 << i)); @@ -240,7 +231,6 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name) if (rp) { float *result = film.read_pass(pass_type); if (result) { - std::cout << "read " << pass_name << std::endl; BLI_mutex_lock(&render->update_render_passes_mutex); /* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result. * However, on some implementation, we need a buffer with a few extra bytes for the read to @@ -252,6 +242,45 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name) } } } + + /* The vector pass is initialized to weird values. Set it to neutral value if not rendered. */ + if ((pass_bits & EEVEE_RENDER_PASS_VECTOR) == 0) { + const char *vector_pass_name = Film::pass_to_render_pass_name(EEVEE_RENDER_PASS_VECTOR); + RenderPass *vector_rp = RE_pass_find_by_name(render_layer, vector_pass_name, view_name); + if (vector_rp) { + memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Interface + * \{ */ + +void Instance::render_frame(RenderLayer *render_layer, const char *view_name) +{ + while (!sampling.finished()) { + this->render_sample(); + + /* TODO(fclem) print progression. */ +#if 0 + /* TODO(fclem): Does not currently work. But would be better to just display to 2D view like + * cycles does. */ + if (G.background == false && first_read) { + /* Allow to preview the first sample. */ + /* TODO(fclem): Might want to not do this during animation render to avoid too much stall. */ + this->render_read_result(render_layer, view_name); + first_read = false; + DRW_render_context_disable(render->re); + /* Allow the 2D viewport to grab the ticket mutex to display the render. */ + DRW_render_context_enable(render->re); + } +#endif + } + + this->render_read_result(render_layer, view_name); } void Instance::draw_viewport(DefaultFramebufferList *dfbl) @@ -260,7 +289,10 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl) render_sample(); velocity.step_swap(); - if (!sampling.finished_viewport()) { + /* Do not request redraw during viewport animation to lock the framerate to the animation + * playback rate. This is in order to preserve motion blur aspect and also to avoid TAA reset + * that can show flickering. */ + if (!sampling.finished_viewport() && !DRW_state_is_playback()) { DRW_viewport_request_redraw(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 1efda769648..d714111a3c6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -18,6 +18,7 @@ #include "eevee_camera.hh" #include "eevee_film.hh" #include "eevee_material.hh" +#include "eevee_motion_blur.hh" #include "eevee_pipeline.hh" #include "eevee_renderbuffers.hh" #include "eevee_sampling.hh" @@ -34,6 +35,7 @@ namespace blender::eevee { */ class Instance { friend VelocityModule; + friend MotionBlurModule; public: ShaderModule &shaders; @@ -41,6 +43,7 @@ class Instance { MaterialModule materials; PipelineModule pipelines; VelocityModule velocity; + MotionBlurModule motion_blur; Sampling sampling; Camera camera; Film film; @@ -76,6 +79,7 @@ class Instance { materials(*this), pipelines(*this), velocity(*this), + motion_blur(*this), sampling(*this), camera(*this), film(*this), @@ -138,6 +142,7 @@ class Instance { RenderEngine *engine, Depsgraph *depsgraph); void render_sample(); + void render_read_result(RenderLayer *render_layer, const char *view_name); void mesh_sync(Object *ob, ObjectHandle &ob_handle); diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc new file mode 100644 index 00000000000..660eb9f1e22 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +// #include "BLI_map.hh" +#include "DEG_depsgraph_query.h" + +#include "eevee_instance.hh" +#include "eevee_motion_blur.hh" +// #include "eevee_sampling.hh" +// #include "eevee_shader_shared.hh" +// #include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name MotionBlurModule + * + * \{ */ + +void MotionBlurModule::init() +{ + const Scene *scene = inst_.scene; + + enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0; + + if (!enabled_) { + motion_blur_fx_enabled_ = false; + return; + } + + /* Take into account the steps needed for fx motion blur. */ + int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1; + + time_steps_.resize(steps_count); + + initial_frame_ = scene->r.cfra; + initial_subframe_ = scene->r.subframe; + frame_time_ = initial_frame_ + initial_subframe_; + shutter_position_ = scene->eevee.motion_blur_position; + shutter_time_ = scene->eevee.motion_blur_shutter; + + data_.depth_scale = scene->eevee.motion_blur_depth_scale; + motion_blur_fx_enabled_ = true; /* TODO(fclem): UI option. */ + + /* Viewport stops here. We only do Post-FX motion blur. */ + if (inst_.is_viewport()) { + enabled_ = false; + return; + } + + /* Without this there is the possibility of the curve table not being allocated. */ + BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false); + + Vector<float> cdf(CM_TABLE); + Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf); + Sampling::cdf_invert(cdf, time_steps_); + + for (float &time : time_steps_) { + time = this->shutter_time_to_scene_time(time); + } + + step_id_ = 1; + + if (motion_blur_fx_enabled_) { + /* A bit weird but we have to sync the first 2 steps here because the step() + * function is only called after rendering a sample. */ + inst_.velocity.step_sync(STEP_PREVIOUS, time_steps_[0]); + inst_.velocity.step_sync(STEP_NEXT, time_steps_[2]); + } + inst_.set_time(time_steps_[1]); +} + +/* Runs after rendering a sample. */ +void MotionBlurModule::step() +{ + if (!enabled_) { + return; + } + + if (inst_.sampling.finished()) { + /* Restore original frame number. This is because the render pipeline expects it. */ + RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_); + } + else if (inst_.sampling.do_render_sync()) { + /* Time to change motion step. */ + BLI_assert(time_steps_.size() > step_id_ + 2); + step_id_ += 2; + + if (motion_blur_fx_enabled_) { + inst_.velocity.step_swap(); + inst_.velocity.step_sync(eVelocityStep::STEP_NEXT, time_steps_[step_id_ + 1]); + } + inst_.set_time(time_steps_[step_id_]); + } +} + +float MotionBlurModule::shutter_time_to_scene_time(float time) +{ + switch (shutter_position_) { + case SCE_EEVEE_MB_START: + /* No offset. */ + break; + case SCE_EEVEE_MB_CENTER: + time -= 0.5f; + break; + case SCE_EEVEE_MB_END: + time -= 1.0; + break; + default: + BLI_assert(!"Invalid motion blur position enum!"); + break; + } + time *= shutter_time_; + time += frame_time_; + return time; +} + +void MotionBlurModule::sync() +{ + /* Disable motion blur in viewport when changing camera projection type. + * Avoids really high velocities. */ + if (inst_.velocity.camera_changed_projection()) { + motion_blur_fx_enabled_ = false; + } + + if (!motion_blur_fx_enabled_) { + return; + } + + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + RenderBuffers &render_buffers = inst_.render_buffers; + + { + /* Create max velocity tiles. */ + DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_NO_DRAW); + eShaderType shader = (inst_.is_viewport()) ? MOTION_BLUR_TILE_FLATTEN_VIEWPORT : + MOTION_BLUR_TILE_FLATTEN_RENDER; + GPUShader *sh = inst_.shaders.static_shader_get(shader); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); + inst_.velocity.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &render_buffers.depth_tx); + DRW_shgroup_uniform_image_ref(grp, "velocity_img", &render_buffers.vector_tx); + DRW_shgroup_uniform_image_ref(grp, "out_tiles_img", &tiles_tx_); + + DRW_shgroup_call_compute_ref(grp, dispatch_flatten_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH); + } + { + /* Expand max velocity tiles by spreading them in their neighborhood. */ + DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_); + DRW_shgroup_storage_block(grp, "tile_indirection_buf", tile_indirection_buf_); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_img", &tiles_tx_); + + DRW_shgroup_call_compute_ref(grp, dispatch_dilate_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + /* Do the motion blur gather algorithm. */ + DRW_PASS_CREATE(gather_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_); + inst_.sampling.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_); + DRW_shgroup_storage_block(grp, "tile_indirection_buf", tile_indirection_buf_); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &render_buffers.vector_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "in_color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_img", &tiles_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &output_color_tx_); + + DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + } +} + +void MotionBlurModule::render(GPUTexture **input_tx, GPUTexture **output_tx) +{ + if (!motion_blur_fx_enabled_) { + return; + } + + const Texture &depth_tx = inst_.render_buffers.depth_tx; + + int2 extent = {depth_tx.width(), depth_tx.height()}; + int2 tiles_extent = math::divide_ceil(extent, int2(MOTION_BLUR_TILE_SIZE)); + + if (inst_.is_viewport()) { + float frame_delta = fabsf(inst_.velocity.step_time_delta_get(STEP_PREVIOUS, STEP_CURRENT)); + /* Avoid highly disturbing blurs, during navigation with high shutter time. */ + if (frame_delta > 0.0f && !DRW_state_is_navigating()) { + /* Rescale motion blur intensity to be shutter time relative and avoid long streak when we + * have frame skipping. Always try to stick to what the render frame would look like. */ + data_.motion_scale = float2(shutter_time_ / frame_delta); + } + else { + /* There is no time change. Motion only comes from viewport navigation and object transform. + * Apply motion blur as smoothing and only blur towards last frame. */ + data_.motion_scale = float2(1.0f, 0.0f); + + if (was_navigating_ != DRW_state_is_navigating()) { + /* Special case for navigation events that only last for one frame (for instance mouse + * scroll for zooming). For this case we have to wait for the next frame before enabling + * the navigation motion blur. */ + was_navigating_ = DRW_state_is_navigating(); + return; + } + } + was_navigating_ = DRW_state_is_navigating(); + + /* Change texture swizzling to avoid complexity in gather pass shader. */ + GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgrg"); + } + else { + data_.motion_scale = float2(1.0f); + } + /* Second motion vector is stored inverted. */ + data_.motion_scale.y = -data_.motion_scale.y; + data_.target_size_inv = 1.0f / float2(extent); + data_.push_update(); + + input_color_tx_ = *input_tx; + output_color_tx_ = *output_tx; + + dispatch_flatten_size_ = int3(tiles_extent, 1); + dispatch_dilate_size_ = int3(math::divide_ceil(tiles_extent, int2(MOTION_BLUR_GROUP_SIZE)), 1); + dispatch_gather_size_ = int3(math::divide_ceil(extent, int2(MOTION_BLUR_GROUP_SIZE)), 1); + + DRW_stats_group_start("Motion Blur"); + + tiles_tx_.acquire(tiles_extent, GPU_RGBA16F); + + GPU_storagebuf_clear_to_zero(tile_indirection_buf_); + + DRW_draw_pass(tiles_flatten_ps_); + DRW_draw_pass(tiles_dilate_ps_); + DRW_draw_pass(gather_ps_); + + tiles_tx_.release(); + + DRW_stats_group_end(); + + if (inst_.is_viewport()) { + /* Reset swizzle since this texture might be reused in other places. */ + GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgba"); + } + + /* Swap buffers so that next effect has the right input. */ + *input_tx = output_color_tx_; + *output_tx = input_color_tx_; +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh new file mode 100644 index 00000000000..310e94a702b --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Motion blur is done by accumulating scene samples over shutter time. + * Since the number of step is discrete, quite low, and not per pixel randomized, + * we couple this with a post processing motion blur. + * + * The post-fx motion blur is done in two directions, from the previous step and to the next. + * + * For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame + * centered on frame we have: + * + * |--------------------|--------------------| + * -1 0 1 Frames + * + * |-------------|-------------|-------------| + * 1 2 3 Motion steps + * + * |------|------|------|------|------|------| + * 0 1 2 4 5 6 7 Time Steps + * + * |-------------| One motion step blurs this range. + * -1 | +1 Objects and geometry steps are recorded here. + * 0 Scene is rendered here. + * + * Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation. + * + * Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0. + * This is because all GPUBatches from the DRWCache are being free when changing a frame. + * + * For viewport, we only have the current and previous step data to work with. So we center the + * blur on the current frame and extrapolate the motion. + * + * The Post-FX motion blur is based on: + * "A Fast and Stable Feature-Aware Motion Blur Filter" + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + */ + +#pragma once + +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" + +#include "eevee_sampling.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name MotionBlur + * + * \{ */ + +/** + * Manages time-steps evaluations and accumulation Motion blur. + * Also handles Post process motion blur. + */ +class MotionBlurModule { + private: + Instance &inst_; + + /** + * Array containing all steps (in scene time) we need to evaluate (not render). + * Only odd steps are rendered. The even ones are evaluated for fx motion blur. + */ + Vector<float> time_steps_; + + /** Copy of input frame and sub-frame to restore after render. */ + int initial_frame_; + float initial_subframe_; + /** Time of the frame we are rendering. */ + float frame_time_; + /** Enum controlling when the shutter opens. See SceneEEVEE.motion_blur_position. */ + int shutter_position_; + /** Time in scene frame the shutter is open. Controls the amount of blur. */ + float shutter_time_; + + /** True if motion blur is enabled as a module. */ + bool enabled_ = false; + /** True if motion blur post-fx is enabled. */ + float motion_blur_fx_enabled_ = false; + /** True if last viewport redraw state was already in navigation state. */ + bool was_navigating_ = false; + + int step_id_ = 0; + + /** Velocity tiles used to guide and speedup the gather pass. */ + TextureFromPool tiles_tx_; + + GPUTexture *input_color_tx_ = nullptr; + GPUTexture *output_color_tx_ = nullptr; + + DRWPass *tiles_flatten_ps_ = nullptr; + DRWPass *tiles_dilate_ps_ = nullptr; + DRWPass *gather_ps_ = nullptr; + + MotionBlurTileIndirectionBuf tile_indirection_buf_; + MotionBlurDataBuf data_; + /** Dispatch size for full-screen passes. */ + int3 dispatch_flatten_size_ = int3(0); + int3 dispatch_dilate_size_ = int3(0); + int3 dispatch_gather_size_ = int3(0); + + public: + MotionBlurModule(Instance &inst) : inst_(inst){}; + ~MotionBlurModule(){}; + + void init(); + + void step(); + + void sync(); + + bool postfx_enabled() const + { + return motion_blur_fx_enabled_; + } + + void render(GPUTexture **input_tx, GPUTexture **output_tx); + + private: + float shutter_time_to_scene_time(float time); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc index c60054496c1..b69fde7b26c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc +++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc @@ -24,25 +24,7 @@ namespace blender::eevee { -void RenderBuffers::sync() -{ - depth_tx.sync(); - combined_tx.sync(); - - normal_tx.sync(); - vector_tx.sync(); - diffuse_light_tx.sync(); - diffuse_color_tx.sync(); - specular_light_tx.sync(); - specular_color_tx.sync(); - volume_light_tx.sync(); - emission_tx.sync(); - environment_tx.sync(); - shadow_tx.sync(); - ambient_occlusion_tx.sync(); -} - -void RenderBuffers::acquire(int2 extent, void *owner) +void RenderBuffers::acquire(int2 extent) { auto pass_extent = [&](eViewLayerEEVEEPassType pass_bit) -> int2 { /* Use dummy texture for disabled passes. Allows correct bindings. */ @@ -53,25 +35,26 @@ void RenderBuffers::acquire(int2 extent, void *owner) eGPUTextureFormat float_format = GPU_R16F; /* Depth and combined are always needed. */ - depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner); - combined_tx.acquire(extent, color_format, owner); + depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8); + combined_tx.acquire(extent, color_format); - bool do_vector_render_pass = inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR; + bool do_vector_render_pass = (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) || + (inst_.motion_blur.postfx_enabled() && !inst_.is_viewport()); /* Only RG16F when only doing only reprojection or motion blur. */ eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F; /* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */ - vector_tx.acquire(extent, vector_format, owner); - - normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format, owner); - diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format, owner); - diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format, owner); - specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format, owner); - specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format, owner); - volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format, owner); - emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format, owner); - environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format, owner); - shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format, owner); - ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format, owner); + vector_tx.acquire(extent, vector_format); + + normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format); + diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format); + diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format); + specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format); + specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format); + volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format); + emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format); + environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format); + shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format); + ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format); const AOVsInfoData &aovs = inst_.film.aovs_info; aov_color_tx.ensure_2d_array( diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh index 8c91fed2f0f..787f5604aa4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh +++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh @@ -48,9 +48,8 @@ class RenderBuffers { public: RenderBuffers(Instance &inst) : inst_(inst){}; - void sync(); /* Acquires (also ensures) the render buffer before rendering to them. */ - void acquire(int2 extent, void *owner); + void acquire(int2 extent); void release(); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 1d320c75f16..76a0e98638b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -232,7 +232,7 @@ void Sampling::cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &c BLI_assert(cdf.size() > 1); cdf[0] = 0.0f; /* Actual CDF evaluation. */ - for (int u : cdf.index_range()) { + for (int u : IndexRange(cdf.size() - 1)) { float x = (float)(u + 1) / (float)(cdf.size() - 1); cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x); } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 7db9692783a..782e73f6dd0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -82,6 +82,14 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_film_frag"; case FILM_COMP: return "eevee_film_comp"; + case MOTION_BLUR_GATHER: + return "eevee_motion_blur_gather"; + case MOTION_BLUR_TILE_DILATE: + return "eevee_motion_blur_tiles_dilate"; + case MOTION_BLUR_TILE_FLATTEN_RENDER: + return "eevee_motion_blur_tiles_flatten_render"; + case MOTION_BLUR_TILE_FLATTEN_VIEWPORT: + return "eevee_motion_blur_tiles_flatten_viewport"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 280aaab4e1c..8dc61fbae0b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -29,6 +29,11 @@ enum eShaderType { FILM_FRAG = 0, FILM_COMP, + MOTION_BLUR_GATHER, + MOTION_BLUR_TILE_DILATE, + MOTION_BLUR_TILE_FLATTEN_RENDER, + MOTION_BLUR_TILE_FLATTEN_VIEWPORT, + MAX_SHADER_TYPE, }; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 3c10f633740..70de4101bb9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -124,7 +124,7 @@ struct CameraData { float clip_far; eCameraType type; - bool initialized; + bool1 initialized; #ifdef __cplusplus /* Small constructor to allow detecting new buffers. */ @@ -312,6 +312,40 @@ BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Motion Blur + * \{ */ + +#define MOTION_BLUR_TILE_SIZE 32 +#define MOTION_BLUR_MAX_TILE 512 /* 16384 / MOTION_BLUR_TILE_SIZE */ +struct MotionBlurData { + /** As the name suggests. Used to avoid a division in the sampling. */ + float2 target_size_inv; + /** Viewport motion scaling factor. Make blur relative to frame time not render time. */ + float2 motion_scale; + /** Depth scaling factor. Avoid blurring background behind moving objects. */ + float depth_scale; + + float _pad0, _pad1, _pad2; +}; +BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16) + +/* For some reasons some GLSL compilers do not like this struct. + * So we declare it as a uint array instead and do indexing ourselves. */ +#ifdef __cplusplus +struct MotionBlurTileIndirection { + /** + * Stores indirection to the tile with the highest velocity covering each tile. + * This is stored using velocity in the MSB to be able to use atomicMax operations. + */ + uint prev[MOTION_BLUR_MAX_TILE][MOTION_BLUR_MAX_TILE]; + uint next[MOTION_BLUR_MAX_TILE][MOTION_BLUR_MAX_TILE]; +}; +BLI_STATIC_ASSERT_ALIGN(MotionBlurTileIndirection, 16) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Ray-Tracing * \{ */ @@ -375,6 +409,8 @@ using SamplingDataBuf = draw::StorageBuffer<SamplingData>; using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>; using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>; using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>; +using MotionBlurDataBuf = draw::UniformBuffer<MotionBlurData>; +using MotionBlurTileIndirectionBuf = draw::StorageBuffer<MotionBlurTileIndirection, true>; } // namespace blender::eevee #endif diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc index 048daf1b2db..36734f0c28c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -32,13 +32,16 @@ namespace blender::eevee { void VelocityModule::init() { - if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR)) { - /* No motion blur and the vector pass was requested. Do the step sync here. */ + if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) != 0) { + /* No motion blur and the vector pass was requested. Do the steps sync here. */ const Scene *scene = inst_.scene; float initial_time = scene->r.cfra + scene->r.subframe; step_sync(STEP_PREVIOUS, initial_time - 1.0f); step_sync(STEP_NEXT, initial_time + 1.0f); + inst_.set_time(initial_time); + step_ = STEP_CURRENT; + /* Let the main sync loop handle the current step. */ } } @@ -64,10 +67,12 @@ void VelocityModule::step_camera_sync() { inst_.camera.sync(); *camera_steps[step_] = inst_.camera.data_get(); + step_time[step_] = inst_.scene->r.cfra + inst_.scene->r.subframe; /* Fix undefined camera steps when rendering is starting. */ if ((step_ == STEP_CURRENT) && (camera_steps[STEP_PREVIOUS]->initialized == false)) { *camera_steps[STEP_PREVIOUS] = *static_cast<CameraData *>(camera_steps[step_]); camera_steps[STEP_PREVIOUS]->initialized = true; + step_time[STEP_PREVIOUS] = step_time[step_]; } } @@ -212,6 +217,7 @@ void VelocityModule::step_swap() SWAP(VelocityObjectBuf *, object_steps[step_a], object_steps[step_b]); SWAP(VelocityGeometryBuf *, geometry_steps[step_a], geometry_steps[step_b]); SWAP(CameraDataBuf *, camera_steps[step_a], camera_steps[step_b]); + SWAP(float, step_time[step_a], step_time[step_b]); for (VelocityObjectData &vel : velocity_map.values()) { vel.obj.ofs[step_a] = vel.obj.ofs[step_b]; @@ -238,10 +244,7 @@ void VelocityModule::step_swap() void VelocityModule::begin_sync() { - if (inst_.is_viewport()) { - /* Viewport always evaluate current step. */ - step_ = STEP_CURRENT; - } + step_ = STEP_CURRENT; step_camera_sync(); object_steps_usage[step_] = 0; } @@ -360,6 +363,21 @@ bool VelocityModule::camera_has_motion() const *camera_steps[STEP_NEXT] != *camera_steps[STEP_CURRENT]; } +bool VelocityModule::camera_changed_projection() const +{ + /* Only valid after sync. */ + if (inst_.is_viewport()) { + return camera_steps[STEP_PREVIOUS]->type != camera_steps[STEP_CURRENT]->type; + } + /* Cannot happen in render mode since we set the type during the init phase. */ + return false; +} + +float VelocityModule::step_time_delta_get(eVelocityStep start, eVelocityStep end) const +{ + return step_time[end] - step_time[start]; +} + /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh index 826cd631a96..01b8a5fb8c1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -56,6 +56,8 @@ class VelocityModule { int3 object_steps_usage = int3(0); /** Buffer of all #VelocityIndex used in this frame. Indexed by draw manager resource id. */ VelocityIndexBuf indirection_buf; + /** Frame time at which each steps were evaluated. */ + float3 step_time; /** * Copies of camera data. One for previous and one for next time step. @@ -78,7 +80,6 @@ class VelocityModule { } for (CameraDataBuf *&step_buf : camera_steps) { step_buf = new CameraDataBuf(); - /* */ } }; @@ -112,6 +113,10 @@ class VelocityModule { void bind_resources(DRWShadingGroup *grp); bool camera_has_motion() const; + bool camera_changed_projection() const; + + /* Returns frame time difference between two steps. */ + float step_time_delta_get(eVelocityStep start, eVelocityStep end) const; private: bool object_has_velocity(const Object *ob); diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 55741bee4df..c7434a662a2 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -79,14 +79,11 @@ void ShadingView::sync() render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p); // dof_.sync(winmat_p, extent_); - // mb_.sync(extent_); // rt_buffer_opaque_.sync(extent_); // rt_buffer_refract_.sync(extent_); // inst_.hiz_back.view_sync(extent_); // inst_.hiz_front.view_sync(extent_); // inst_.gbuffer.view_sync(extent_); - - postfx_tx_.sync(); } void ShadingView::render() @@ -96,12 +93,8 @@ void ShadingView::render() } /* Query temp textures and create frame-buffers. */ - /* HACK: View name should be unique and static. - * With this, we can reuse the same texture across views. */ - DrawEngineType *owner = (DrawEngineType *)name_; - RenderBuffers &rbufs = inst_.render_buffers; - rbufs.acquire(extent_, owner); + rbufs.acquire(extent_); combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx)); prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), @@ -138,9 +131,9 @@ void ShadingView::render() // inst_.lights.debug_draw(view_fb_); // inst_.shadows.debug_draw(view_fb_); - // GPUTexture *final_radiance_tx = render_post(combined_tx_); + GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx); - inst_.film.accumulate(sub_view_); + inst_.film.accumulate(sub_view_, combined_final_tx); rbufs.release(); postfx_tx_.release(); @@ -148,23 +141,19 @@ void ShadingView::render() DRW_stats_group_end(); } -GPUTexture *ShadingView::render_post(GPUTexture *input_tx) +GPUTexture *ShadingView::render_postfx(GPUTexture *input_tx) { -#if 0 - if (!dof_.postfx_enabled() && !mb_.enabled()) { + if (/*!dof_.postfx_enabled() &&*/ !inst_.motion_blur.postfx_enabled()) { return input_tx; } - /* HACK: View name should be unique and static. - * With this, we can reuse the same texture across views. */ - postfx_tx_.acquire(extent_, GPU_RGBA16F, (void *)name_); + postfx_tx_.acquire(extent_, GPU_RGBA16F); - GPUTexture *velocity_tx = velocity_.view_vectors_get(); GPUTexture *output_tx = postfx_tx_; /* Swapping is done internally. Actual output is set to the next input. */ - dof_.render(depth_tx_, &input_tx, &output_tx); - mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx); -#endif + // dof_.render(depth_tx_, &input_tx, &output_tx); + inst_.motion_blur.render(&input_tx, &output_tx); + return input_tx; } diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index c6faebdd0e5..ac8decc7632 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -78,7 +78,7 @@ class ShadingView { void render(); - GPUTexture *render_post(GPUTexture *input_tx); + GPUTexture *render_postfx(GPUTexture *input_tx); private: void update_view(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl index b286836e8df..135507d956c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl @@ -636,6 +636,8 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth vec4 normal = texelFetch(normal_tx, film_sample.texel, 0); float depth = texelFetch(depth_tx, film_sample.texel, 0).x; vec4 vector = velocity_resolve(vector_tx, film_sample.texel, depth); + /* Transform to pixel space. */ + vector *= vec4(film_buf.render_extent, -film_buf.render_extent); film_store_depth(texel_film, depth, out_depth); film_store_data(texel_film, film_buf.normal_id, normal, out_color); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl new file mode 100644 index 00000000000..c59b7d7f4df --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl @@ -0,0 +1,116 @@ + +/** + * Dilate motion vector tiles until we covered maximum velocity. + * Outputs the largest intersecting motion vector in the neighboorhod. + * + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) + +#define DEBUG_BYPASS_DILATION 0 + +struct MotionRect { + ivec2 bottom_left; + ivec2 extent; +}; + +MotionRect compute_motion_rect(ivec2 tile, vec2 motion) +{ +#if DEBUG_BYPASS_DILATION + return MotionRect(tile, ivec2(1)); +#endif + /* Ceil to number of tile touched.*/ + ivec2 point1 = tile + ivec2(sign(motion) * ceil(abs(motion) / float(MOTION_BLUR_TILE_SIZE))); + ivec2 point2 = tile; + + ivec2 max_point = max(point1, point2); + ivec2 min_point = min(point1, point2); + /* Clamp to bounds. */ + max_point = min(max_point, imageSize(in_tiles_img) - 1); + min_point = max(min_point, ivec2(0)); + + MotionRect rect; + rect.bottom_left = min_point; + rect.extent = 1 + max_point - min_point; + return rect; +} + +struct MotionLine { + /** Origin of the line. */ + vec2 origin; + /** Normal to the line direction. */ + vec2 normal; +}; + +MotionLine compute_motion_line(ivec2 tile, vec2 motion) +{ + vec2 dir = safe_normalize(motion); + + MotionLine line; + line.origin = vec2(tile); + /* Rotate 90° Counter-Clockwise. */ + line.normal = vec2(-dir.y, dir.x); + return line; +} + +bool is_inside_motion_line(ivec2 tile, MotionLine motion_line) +{ +#if DEBUG_BYPASS_DILATION + return true; +#endif + /* NOTE: Everything in is tile unit. */ + float dist = point_line_projection_dist(vec2(tile), motion_line.origin, motion_line.normal); + /* In order to be conservative and for simplicity, we use the tiles bounding circles. + * Consider that both the tile and the line have bouding radius of M_SQRT1_2. */ + return abs(dist) < M_SQRT2; +} + +void main() +{ + ivec2 src_tile = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(src_tile, imageSize(in_tiles_img)))) { + return; + } + + vec4 max_motion = imageLoad(in_tiles_img, src_tile); + + MotionPayload payload_prv = motion_blur_tile_indirection_pack_payload(max_motion.xy, src_tile); + MotionPayload payload_nxt = motion_blur_tile_indirection_pack_payload(max_motion.zw, src_tile); + if (true) { + /* Rectangular area (in tiles) where the motion vector spreads. */ + MotionRect motion_rect = compute_motion_rect(src_tile, max_motion.xy); + MotionLine motion_line = compute_motion_line(src_tile, max_motion.xy); + /* Do a conservative rasterization of the line of the motion vector line. */ + for (int x = 0; x < motion_rect.extent.x; x++) { + for (int y = 0; y < motion_rect.extent.y; y++) { + ivec2 tile = motion_rect.bottom_left + ivec2(x, y); + if (is_inside_motion_line(tile, motion_line)) { + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_PREV, tile, payload_prv); + /* FIXME: This is a bit weird, but for some reason, we need the store the same vector in + * the motion next so that weighting in gather pass is better. */ + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_NEXT, tile, payload_nxt); + } + } + } + } + + if (true) { + MotionPayload payload = motion_blur_tile_indirection_pack_payload(max_motion.zw, src_tile); + /* Rectangular area (in tiles) where the motion vector spreads. */ + MotionRect motion_rect = compute_motion_rect(src_tile, max_motion.zw); + MotionLine motion_line = compute_motion_line(src_tile, max_motion.zw); + /* Do a conservative rasterization of the line of the motion vector line. */ + for (int x = 0; x < motion_rect.extent.x; x++) { + for (int y = 0; y < motion_rect.extent.y; y++) { + ivec2 tile = motion_rect.bottom_left + ivec2(x, y); + if (is_inside_motion_line(tile, motion_line)) { + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_NEXT, tile, payload_nxt); + /* FIXME: This is a bit weird, but for some reason, we need the store the same vector in + * the motion next so that weighting in gather pass is better. */ + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_PREV, tile, payload_prv); + } + } + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl new file mode 100644 index 00000000000..cbbeea25d20 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl @@ -0,0 +1,103 @@ + +/** + * Shaders that down-sample velocity buffer into squared tile of MB_TILE_DIVISOR pixels wide. + * Outputs the largest motion vector in the tile area. + * Also perform velocity resolve to speedup the convolution pass. + * + * Based on: + * A Fast and Stable Feature-Aware Motion Blur Filter + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + * + * Adapted from G3D Innovation Engine implementation. + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) + +shared uint payload_prev; +shared uint payload_next; +shared vec2 max_motion_prev; +shared vec2 max_motion_next; + +/* Store velocity magnitude in the MSB and thread id in the LSB. */ +uint pack_payload(vec2 motion, uvec2 thread_id) +{ + /* NOTE: We clamp max velocity to 16k pixels. */ + return (min(uint(ceil(length(motion))), 0xFFFFu) << 16u) | (thread_id.y << 8) | thread_id.x; +} + +/* Return thread index from the payload. */ +uvec2 unpack_payload(uint payload) +{ + return uvec2(payload & 0xFFu, (payload >> 8) & 0xFFu); +} + +void main() +{ + if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { + payload_prev = 0u; + payload_next = 0u; + } + barrier(); + + uint local_payload_prev = 0u; + uint local_payload_next = 0u; + vec2 local_max_motion_prev; + vec2 local_max_motion_next; + + ivec2 texel = min(ivec2(gl_GlobalInvocationID.xy), imageSize(velocity_img) - 1); + + vec2 render_size = vec2(imageSize(velocity_img).xy); + vec2 uv = (vec2(texel) + 0.5) / render_size; + float depth = texelFetch(depth_tx, texel, 0).r; + vec4 motion = velocity_resolve(imageLoad(velocity_img, texel), uv, depth); +#ifdef FLATTEN_VIEWPORT + /* imageLoad does not perform the swizzling like sampler does. Do it manually. */ + motion = motion.xyxy; +#endif + + /* Store resolved velocity to speedup the gather pass. Out of bounds writes are ignored. + * Unfortunately, we cannot convert to pixel space here since it is also used by TAA and the + * motion blur needs to remain optional. */ + imageStore(velocity_img, ivec2(gl_GlobalInvocationID.xy), velocity_pack(motion)); + /* Clip velocity to viewport bounds (in NDC space). */ + vec2 line_clip; + line_clip.x = line_unit_square_intersect_dist_safe(uv * 2.0 - 1.0, motion.xy * 2.0); + line_clip.y = line_unit_square_intersect_dist_safe(uv * 2.0 - 1.0, -motion.zw * 2.0); + motion *= min(line_clip, vec2(1.0)).xxyy; + /* Convert to pixel space. Note this is only for velocity tiles. */ + motion *= render_size.xyxy; + /* Rescale to shutter relative motion for viewport. */ + motion *= motion_blur_buf.motion_scale.xxyy; + + uint sample_payload_prev = pack_payload(motion.xy, gl_LocalInvocationID.xy); + if (local_payload_prev < sample_payload_prev) { + local_payload_prev = sample_payload_prev; + local_max_motion_prev = motion.xy; + } + + uint sample_payload_next = pack_payload(motion.zw, gl_LocalInvocationID.xy); + if (local_payload_next < sample_payload_next) { + local_payload_next = sample_payload_next; + local_max_motion_next = motion.zw; + } + + /* Compare the local payload with the other threads. */ + atomicMax(payload_prev, local_payload_prev); + atomicMax(payload_next, local_payload_next); + barrier(); + + /* Need to broadcast the result to another thread in order to issue a unique write. */ + if (all(equal(unpack_payload(payload_prev), gl_LocalInvocationID.xy))) { + max_motion_prev = local_max_motion_prev; + } + if (all(equal(unpack_payload(payload_next), gl_LocalInvocationID.xy))) { + max_motion_next = local_max_motion_next; + } + barrier(); + + if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { + ivec2 tile_co = ivec2(gl_WorkGroupID.xy); + imageStore(out_tiles_img, tile_co, vec4(max_motion_prev, max_motion_next)); + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl new file mode 100644 index 00000000000..a7329f77181 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl @@ -0,0 +1,221 @@ + +/** + * Perform two gather blur in the 2 motion blur directions + * Based on: + * A Fast and Stable Feature-Aware Motion Blur Filter + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + * + * With modification from the presentation: + * Next Generation Post Processing in Call of Duty Advanced Warfare + * by Jorge Jimenez + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) + +const int gather_sample_count = 8; + +/* Converts uv velocity into pixel space. Assumes velocity_tx is the same resolution as the + * target post-fx framebuffer. */ +vec4 motion_blur_sample_velocity(sampler2D velocity_tx, vec2 uv) +{ + /* We can load velocity without velocity_resolve() since we resovled during the flatten pass. */ + vec4 velocity = velocity_unpack(texture(velocity_tx, uv)); + return velocity * vec2(textureSize(velocity_tx, 0)).xyxy * motion_blur_buf.motion_scale.xxyy; +} + +vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length) +{ + return saturate(vec2(center_motion_length, sample_motion_length) - offset_length + 1.0); +} + +vec2 depth_compare(float center_depth, float sample_depth) +{ + vec2 depth_scale = vec2(-motion_blur_buf.depth_scale, motion_blur_buf.depth_scale); + return saturate(0.5 + depth_scale * (sample_depth - center_depth)); +} + +/* Kill contribution if not going the same direction. */ +float dir_compare(vec2 offset, vec2 sample_motion, float sample_motion_length) +{ + if (sample_motion_length < 0.5) { + return 1.0; + } + return (dot(offset, sample_motion) > 0.0) ? 1.0 : 0.0; +} + +/* Return background (x) and foreground (y) weights. */ +vec2 sample_weights(float center_depth, + float sample_depth, + float center_motion_length, + float sample_motion_length, + float offset_length) +{ + /* Classify foreground/background. */ + vec2 depth_weight = depth_compare(center_depth, sample_depth); + /* Weight if sample is overlapping or under the center pixel. */ + vec2 spread_weight = spread_compare(center_motion_length, sample_motion_length, offset_length); + return depth_weight * spread_weight; +} + +struct Accumulator { + vec4 fg; + vec4 bg; + /** x: Background, y: Foreground, z: dir. */ + vec3 weight; +}; + +void gather_sample(vec2 screen_uv, + float center_depth, + float center_motion_len, + vec2 offset, + float offset_len, + const bool next, + inout Accumulator accum) +{ + vec2 sample_uv = screen_uv - offset * motion_blur_buf.target_size_inv; + vec4 sample_vectors = motion_blur_sample_velocity(velocity_tx, sample_uv); + vec2 sample_motion = (next) ? sample_vectors.zw : sample_vectors.xy; + float sample_motion_len = length(sample_motion); + float sample_depth = texture(depth_tx, sample_uv).r; + vec4 sample_color = textureLod(in_color_tx, sample_uv, 0.0); + + sample_depth = get_view_z_from_depth(sample_depth); + + vec3 weights; + weights.xy = sample_weights( + center_depth, sample_depth, center_motion_len, sample_motion_len, offset_len); + weights.z = dir_compare(offset, sample_motion, sample_motion_len); + weights.xy *= weights.z; + + accum.fg += sample_color * weights.y; + accum.bg += sample_color * weights.x; + accum.weight += weights; +} + +void gather_blur(vec2 screen_uv, + vec2 center_motion, + float center_depth, + vec2 max_motion, + float ofs, + const bool next, + inout Accumulator accum) +{ + float center_motion_len = length(center_motion); + float max_motion_len = length(max_motion); + + /* Tile boundaries randomization can fetch a tile where there is less motion than this pixel. + * Fix this by overriding the max_motion. */ + if (max_motion_len < center_motion_len) { + max_motion_len = center_motion_len; + max_motion = center_motion; + } + + if (max_motion_len < 0.5) { + return; + } + + int i; + float t, inc = 1.0 / float(gather_sample_count); + for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) { + gather_sample(screen_uv, + center_depth, + center_motion_len, + max_motion * t, + max_motion_len * t, + next, + accum); + } + + if (center_motion_len < 0.5) { + return; + } + + for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) { + /* Also sample in center motion direction. + * Allow recovering motion where there is conflicting + * motion between foreground and background. */ + gather_sample(screen_uv, + center_depth, + center_motion_len, + center_motion * t, + center_motion_len * t, + next, + accum); + } +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(depth_tx, 0).xy); + + if (!in_texture_range(texel, depth_tx)) { + return; + } + + /* Data of the center pixel of the gather (target). */ + float center_depth = get_view_z_from_depth(texelFetch(depth_tx, texel, 0).r); + vec4 center_motion = motion_blur_sample_velocity(velocity_tx, uv); + + vec4 center_color = textureLod(in_color_tx, uv, 0.0); + + float noise_offset = sampling_rng_1D_get(SAMPLING_TIME); + /** TODO(fclem) Blue noise. */ + vec2 rand = vec2(interlieved_gradient_noise(vec2(gl_GlobalInvocationID.xy), 0, noise_offset), + interlieved_gradient_noise(vec2(gl_GlobalInvocationID.xy), 1, noise_offset)); + + /* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile. + * Note this randomize only in one direction but in practice it's enough. */ + rand.x = rand.x * 2.0 - 1.0; + ivec2 tile = (texel + ivec2(rand.x * float(MOTION_BLUR_TILE_SIZE) * 0.25)) / + MOTION_BLUR_TILE_SIZE; + tile = clamp(tile, ivec2(0), imageSize(in_tiles_img) - 1); + /* NOTE: Tile velocity is already in pixel space and with correct zw sign. */ + vec4 max_motion; + /* Load dilation result from the indirection table. */ + ivec2 tile_prev; + motion_blur_tile_indirection_load(tile_indirection_buf, MOTION_PREV, tile, tile_prev); + max_motion.xy = imageLoad(in_tiles_img, tile_prev).xy; + ivec2 tile_next; + motion_blur_tile_indirection_load(tile_indirection_buf, MOTION_NEXT, tile, tile_next); + max_motion.zw = imageLoad(in_tiles_img, tile_next).zw; + + Accumulator accum; + accum.weight = vec3(0.0, 0.0, 1.0); + accum.bg = vec4(0.0); + accum.fg = vec4(0.0); + /* First linear gather. time = [T - delta, T] */ + gather_blur(uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum); + /* Second linear gather. time = [T, T + delta] */ + gather_blur(uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum); + +#if 1 /* Own addition. Not present in reference implementation. */ + /* Avoid division by 0.0. */ + float w = 1.0 / (50.0 * float(gather_sample_count) * 4.0); + accum.bg += center_color * w; + accum.weight.x += w; + /* NOTE: In Jimenez's presentation, they used center sample. + * We use background color as it contains more information for foreground + * elements that have not enough weights. + * Yield better blur in complex motion. */ + center_color = accum.bg / accum.weight.x; +#endif + /* Merge background. */ + accum.fg += accum.bg; + accum.weight.y += accum.weight.x; + /* Balance accumulation for failed samples. + * We replace the missing foreground by the background. */ + float blend_fac = saturate(1.0 - accum.weight.y / accum.weight.z); + vec4 out_color = (accum.fg / accum.weight.z) + center_color * blend_fac; + +#if 0 /* For debugging. */ + out_color.rgb = out_color.ggg; + out_color.rg += max_motion.xy; +#endif + + imageStore(out_color_img, texel, out_color); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl new file mode 100644 index 00000000000..436fd01795a --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl @@ -0,0 +1,48 @@ + + +/* -------------------------------------------------------------------- */ +/** \name Tile indirection packing + * \{ */ + +#define MotionPayload uint + +/* Store velocity magnitude in the MSB to be able to use it with atomicMax operations. */ +MotionPayload motion_blur_tile_indirection_pack_payload(vec2 motion, uvec2 payload) +{ + /* NOTE: Clamp to 16383 pixel velocity. After that, it is tile position that determine the tile + * to dilate over. */ + uint velocity = min(uint(ceil(length(motion))), 0x3FFFu); + /* Designed for 512x512 tiles max. */ + return (velocity << 18u) | ((payload.x & 0x1FFu) << 9u) | (payload.y & 0x1FFu); +} + +/* Return thread index. */ +ivec2 motion_blur_tile_indirection_pack_payload(uint data) +{ + return ivec2((data >> 9u) & 0x1FFu, data & 0x1FFu); +} + +uint motion_blur_tile_indirection_index(uint motion_step, uvec2 tile) +{ + uint index = tile.x; + index += tile.y * MOTION_BLUR_MAX_TILE; + index += motion_step * MOTION_BLUR_MAX_TILE * MOTION_BLUR_MAX_TILE; + return index; +} + +#define MOTION_PREV 0u +#define MOTION_NEXT 1u + +#define motion_blur_tile_indirection_store(table_, step_, tile, payload_) \ + if (true) { \ + uint index = motion_blur_tile_indirection_index(step_, tile); \ + atomicMax(table_[index], payload_); \ + } + +#define motion_blur_tile_indirection_load(table_, step_, tile_, result_) \ + if (true) { \ + uint index = motion_blur_tile_indirection_index(step_, tile_); \ + result_ = motion_blur_tile_indirection_pack_payload(table_[index]); \ + } + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl new file mode 100644 index 00000000000..0c7bbaa9dc2 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl @@ -0,0 +1,104 @@ + +/** + * Sampling data accessors and random number generators. + * Also contains some sample mapping functions. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Sampling data. + * + * Return a random values from Low Discrepency Sequence in [0..1) range. + * This value is uniform (constant) for the whole scene sample. + * You might want to couple it with a noise function. + * \{ */ + +#ifdef EEVEE_SAMPLING_DATA + +float sampling_rng_1D_get(const eSamplingDimension dimension) +{ + return sampling_buf.dimensions[dimension]; +} + +vec2 sampling_rng_2D_get(const eSamplingDimension dimension) +{ + return vec2(sampling_buf.dimensions[dimension], sampling_buf.dimensions[dimension + 1u]); +} + +vec3 sampling_rng_3D_get(const eSamplingDimension dimension) +{ + return vec3(sampling_buf.dimensions[dimension], + sampling_buf.dimensions[dimension + 1u], + sampling_buf.dimensions[dimension + 2u]); +} + +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Random Number Generators. + * \{ */ + +/* Interlieved gradient noise by Jorge Jimenez + * http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare + * Seeding found by Epic Game. */ +float interlieved_gradient_noise(vec2 pixel, float seed, float offset) +{ + pixel += seed * (vec2(47, 17) * 0.695); + return fract(offset + 52.9829189 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y)); +} + +/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */ +float van_der_corput_radical_inverse(uint bits) +{ +#if 0 /* Reference */ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); +#else + bits = bitfieldReverse(bits); +#endif + /* Same as dividing by 0x100000000. */ + return float(bits) * 2.3283064365386963e-10; +} + +vec2 hammersley_2d(float i, float sample_count) +{ + vec2 rand; + rand.x = i / sample_count; + rand.y = van_der_corput_radical_inverse(uint(i)); + return rand; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Distribution mapping. + * + * Functions mapping input random numbers to sampling shapes (i.e: hemisphere). + * \{ */ + +/* Given 2 random number in [0..1] range, return a random unit disk sample. */ +vec2 sample_disk(vec2 noise) +{ + float angle = noise.x * M_2PI; + return vec2(cos(angle), sin(angle)) * sqrt(noise.y); +} + +/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the + * same range. This is because the sampling functions expect such a random sample which is + * normally precomputed. */ +vec3 sample_cylinder(vec2 rand) +{ + float theta = rand.x; + float phi = (rand.y - 0.5) * M_2PI; + float cos_phi = cos(phi); + float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi); + return vec3(theta, cos_phi, sin_phi); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl index 34ea288852a..bd32215ddc2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl @@ -73,7 +73,7 @@ void main() nodetree_surface(); - // float noise_offset = sampling_rng_1D_get(sampling_buf, SAMPLING_TRANSPARENCY); + // float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY); float noise_offset = 0.5; float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P); @@ -84,7 +84,7 @@ void main() #endif #ifdef MAT_VELOCITY - out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P - motion.next); + out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P + motion.next); out_velocity = velocity_pack(out_velocity); #endif } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl index c21456b7a5c..c0a5b976810 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl @@ -2,8 +2,6 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) -#ifdef VELOCITY_CAMERA - vec4 velocity_pack(vec4 data) { return data * 0.01; @@ -14,6 +12,8 @@ vec4 velocity_unpack(vec4 data) return data * 100.0; } +#ifdef VELOCITY_CAMERA + /** * Given a triple of position, compute the previous and next motion vectors. * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy). @@ -24,7 +24,15 @@ vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt) vec2 prev_uv = project_point(camera_prev.persmat, P_prv).xy; vec2 curr_uv = project_point(camera_curr.persmat, P).xy; vec2 next_uv = project_point(camera_next.persmat, P_nxt).xy; - + /* Fix issue with perspective division. */ + if (any(isnan(prev_uv))) { + prev_uv = curr_uv; + } + if (any(isnan(next_uv))) { + next_uv = curr_uv; + } + /* NOTE: We output both vectors in the same direction so we can reuse the same vector + * with rgrg swizzle in viewport. */ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv); /* Convert NDC velocity to UV velocity */ motion *= 0.5; @@ -45,7 +53,8 @@ vec4 velocity_background(vec3 vV) vec2 prev_uv = project_point(camera_prev.winmat, V).xy; vec2 curr_uv = project_point(camera_curr.winmat, V).xy; vec2 next_uv = project_point(camera_next.winmat, V).xy; - + /* NOTE: We output both vectors in the same direction so we can reuse the same vector + * with rgrg swizzle in viewport. */ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv); /* Convert NDC velocity to UV velocity */ motion *= 0.5; @@ -53,15 +62,8 @@ vec4 velocity_background(vec3 vV) return motion; } -/** - * Load and resolve correct velocity as some pixels might still not have correct - * motion data for performance reasons. - */ -vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth) +vec4 velocity_resolve(vec4 vector, vec2 uv, float depth) { - vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy); - vec4 vector = texelFetch(vector_tx, texel, 0); - if (vector.x == VELOCITY_INVALID) { bool is_background = (depth == 1.0); if (is_background) { @@ -78,6 +80,18 @@ vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth) return velocity_unpack(vector); } +/** + * Load and resolve correct velocity as some pixels might still not have correct + * motion data for performance reasons. + * Returns motion vector in render UV space. + */ +vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth) +{ + vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy); + vec4 vector = texelFetch(vector_tx, texel, 0); + return velocity_resolve(vector, uv, depth); +} + #endif #ifdef MAT_VELOCITY diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 2368061402c..db3cfc4a7a2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -12,8 +12,9 @@ GPU_SHADER_CREATE_INFO(eevee_shared) .typedef_source("eevee_shader_shared.hh"); GPU_SHADER_CREATE_INFO(eevee_sampling_data) + .define("EEVEE_SAMPLING_DATA") .additional_info("eevee_shared") - .uniform_buf(14, "SamplingData", "sampling_buf"); + .storage_buf(14, Qualifier::READ, "SamplingData", "sampling_buf"); /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh new file mode 100644 index 00000000000..b01d1521c5e --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh @@ -0,0 +1,44 @@ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten) + .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera") + .uniform_buf(4, "MotionBlurData", "motion_blur_buf") + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_img") + .compute_source("eevee_motion_blur_flatten_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_viewport) + .do_static_compilation(true) + .define("FLATTEN_VIEWPORT") + .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img") + .additional_info("eevee_motion_blur_tiles_flatten"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_render) + .do_static_compilation(true) + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img") + .additional_info("eevee_motion_blur_tiles_flatten"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_dilate) + .do_static_compilation(true) + .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) + .additional_info("eevee_shared") + /* NOTE: See MotionBlurTileIndirection. */ + .storage_buf(0, Qualifier::READ_WRITE, "uint", "tile_indirection_buf[]") + .image(1, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_img") + .compute_source("eevee_motion_blur_dilate_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_gather) + .do_static_compilation(true) + .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view", "eevee_sampling_data") + .uniform_buf(4, "MotionBlurData", "motion_blur_buf") + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .sampler(1, ImageType::FLOAT_2D, "velocity_tx") + .sampler(2, ImageType::FLOAT_2D, "in_color_tx") + /* NOTE: See MotionBlurTileIndirection. */ + .storage_buf(0, Qualifier::READ, "uint", "tile_indirection_buf[]") + .image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_img") + .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .compute_source("eevee_motion_blur_gather_comp.glsl"); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 8e61c25be71..954d8c49f4c 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -57,6 +57,7 @@ #include "MEM_guardedalloc.h" +#include "draw_manager.h" #include "draw_texture_pool.h" #include "BLI_math_vec_types.hh" @@ -772,50 +773,26 @@ class Texture : NonCopyable { }; class TextureFromPool : public Texture, NonMovable { - private: - GPUTexture *tx_tmp_saved_ = nullptr; - public: TextureFromPool(const char *name = "gpu::Texture") : Texture(name){}; - /* Always use `release()` after rendering and `sync()` in sync phase. */ - void acquire(int2 extent, eGPUTextureFormat format, void *owner_) + /* Always use `release()` after rendering. */ + void acquire(int2 extent, eGPUTextureFormat format) { BLI_assert(this->tx_ == nullptr); - if (this->tx_ != nullptr) { - return; - } - if (tx_tmp_saved_ != nullptr) { - if (GPU_texture_width(tx_tmp_saved_) != extent.x || - GPU_texture_height(tx_tmp_saved_) != extent.y || - GPU_texture_format(tx_tmp_saved_) != format) { - this->tx_tmp_saved_ = nullptr; - } - else { - this->tx_ = tx_tmp_saved_; - return; - } - } - DrawEngineType *owner = (DrawEngineType *)owner_; - this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner); + + this->tx_ = DRW_texture_pool_texture_acquire( + DST.vmempool->texture_pool, UNPACK2(extent), format); } void release(void) { /* Allows multiple release. */ - if (this->tx_ != nullptr) { - tx_tmp_saved_ = this->tx_; - this->tx_ = nullptr; + if (this->tx_ == nullptr) { + return; } - } - - /** - * Clears any reference. Workaround for pool texture not being able to release on demand. - * Needs to be called at during the sync phase. - */ - void sync(void) - { - tx_tmp_saved_ = nullptr; + DRW_texture_pool_texture_release(DST.vmempool->texture_pool, this->tx_); + this->tx_ = nullptr; } /** Remove methods that are forbidden with this type of textures. */ diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 0e4e67f3320..88cc71fa0dd 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -27,6 +27,7 @@ #include "BKE_duplilist.h" #include "RNA_access.h" +#include "RNA_path.h" #include "BLI_bitmap.h" #include "BLI_memblock.h" diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index e8944442607..266db22a84f 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GPU_SHADER +# pragma once + # include "GPU_shader.h" # include "GPU_shader_shared_utils.h" diff --git a/source/blender/draw/intern/draw_texture_pool.h b/source/blender/draw/intern/draw_texture_pool.h index 1c30ea88552..7396b55d53a 100644 --- a/source/blender/draw/intern/draw_texture_pool.h +++ b/source/blender/draw/intern/draw_texture_pool.h @@ -26,6 +26,7 @@ void DRW_texture_pool_free(DRWTexturePool *pool); /** * Try to find a texture corresponding to params into the texture pool. * If no texture was found, create one and add it to the pool. + * DEPRECATED: Use DRW_texture_pool_texture_acquire instead and do it just before rendering. */ GPUTexture *DRW_texture_pool_query( DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user); diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl index ae82277d9a6..cb2da9d35bf 100644 --- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl @@ -10,6 +10,11 @@ float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plan return dot(plane_normal, plane_origin - line_origin); } +float point_line_projection_dist(vec2 point, vec2 line_origin, vec2 line_normal) +{ + return dot(line_normal, line_origin - point); +} + float line_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec3 plane_origin, @@ -104,6 +109,25 @@ float line_unit_box_intersect_dist_safe(vec3 line_origin, vec3 line_direction) } /** + * Same as line_unit_box_intersect_dist but for 2D case. + */ +float line_unit_square_intersect_dist(vec2 line_origin, vec2 line_direction) +{ + vec2 first_plane = (vec2(1.0) - line_origin) / line_direction; + vec2 second_plane = (vec2(-1.0) - line_origin) / line_direction; + vec2 farthest_plane = max(first_plane, second_plane); + + return min_v2(farthest_plane); +} + +float line_unit_square_intersect_dist_safe(vec2 line_origin, vec2 line_direction) +{ + vec2 safe_line_direction = max(vec2(1e-8), abs(line_direction)) * + select(vec2(1.0), -vec2(1.0), lessThan(line_direction, vec2(0.0))); + return line_unit_square_intersect_dist(line_origin, safe_line_direction); +} + +/** * Returns clipping distance (intersection with the nearest plane) with the given axis-aligned * bound box along \a line_direction. * Safe even if \a line_direction is degenerate. diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 729e8533d50..f665ec27b07 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -43,6 +43,7 @@ #include "DNA_world_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "BKE_anim_data.h" diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index ff53ad42e84..22c14983569 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -32,6 +32,7 @@ #include "DEG_depsgraph.h" #include "RNA_access.h" +#include "RNA_path.h" #include "SEQ_sequencer.h" #include "SEQ_utils.h" diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index d9dcbf1d57e..e352b4e26fe 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -35,6 +35,7 @@ #include "ED_keyframes_keylist.h" #include "RNA_access.h" +#include "RNA_path.h" #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index f01b3522547..93d83ff5f8e 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -22,6 +22,7 @@ #include "DNA_anim_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "ED_anim_api.h" diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index effedd4307d..63794caf5a7 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -39,6 +39,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "anim_intern.h" diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 7723c221a40..40f0ac59b01 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -29,6 +29,7 @@ #include "RNA_access.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "ED_anim_api.h" #include "ED_keyframes_edit.h" diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 9084b9bb214..12f83343299 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -64,6 +64,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "anim_intern.h" diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 97b81277008..967a324ef95 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -39,6 +39,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "anim_intern.h" diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 38c99c2ef60..3e36a0d233a 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -53,6 +53,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 032e0ec077c..ea038362532 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -26,6 +26,7 @@ #include "DEG_depsgraph.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h index 2dc67fc4d37..b54f81004f2 100644 --- a/source/blender/editors/asset/ED_asset_list.h +++ b/source/blender/editors/asset/ED_asset_list.h @@ -24,7 +24,7 @@ struct wmNotifier; void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, const struct bContext *C); void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, - struct bContext *C); + const struct bContext *C); void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); /** diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index 67e253a4fcd..773838a54cd 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -97,10 +97,8 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf( RNA_enum_item_add_separator(&item, &totitem); } - int i = 0; - for (bUserAssetLibrary *user_library = (bUserAssetLibrary *)U.asset_libraries.first; - user_library; - user_library = user_library->next, i++) { + int i; + LISTBASE_FOREACH_INDEX (bUserAssetLibrary *, user_library, &U.asset_libraries, i) { /* Note that the path itself isn't checked for validity here. If an invalid library path is * used, the Asset Browser can give a nice hint on what's wrong. */ const bool is_valid = (user_library->name[0] && user_library->path[0]); diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 55167c1ed2d..b0ff5c86520 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -110,7 +110,7 @@ class AssetList : NonCopyable { void setup(); void fetch(const bContext &C); - void ensurePreviewsJob(bContext *C); + void ensurePreviewsJob(const bContext *C); void clear(bContext *C); bool needsRefetch() const; @@ -212,7 +212,7 @@ void AssetList::iterate(AssetListIterFn fn) const } } -void AssetList::ensurePreviewsJob(bContext *C) +void AssetList::ensurePreviewsJob(const bContext *C) { FileList *files = filelist_; int numfiles = filelist_files_ensure(files); @@ -422,7 +422,8 @@ void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, AssetListStorage::fetch_library(*library_reference, *C); } -void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C) +void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, + const bContext *C) { AssetList *list = AssetListStorage::lookup_list(*library_reference); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a8d25b75036..afef516b245 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2482,7 +2482,7 @@ enum uiTemplateListFlags { ENUM_OPERATORS(enum uiTemplateListFlags, UI_TEMPLATE_LIST_FLAGS_LAST); void uiTemplateList(uiLayout *layout, - struct bContext *C, + const struct bContext *C, const char *listtype_name, const char *list_id, struct PointerRNA *dataptr, @@ -2496,7 +2496,7 @@ void uiTemplateList(uiLayout *layout, int columns, enum uiTemplateListFlags flags); struct uiList *uiTemplateList_ex(uiLayout *layout, - struct bContext *C, + const struct bContext *C, const char *listtype_name, const char *list_id, struct PointerRNA *dataptr, @@ -2566,7 +2566,7 @@ enum { UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY = (1 << 2), }; void uiTemplateAssetView(struct uiLayout *layout, - struct bContext *C, + const struct bContext *C, const char *list_id, struct PointerRNA *asset_library_dataptr, const char *asset_library_propname, diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c index a9314df44a5..0f3062c3f61 100644 --- a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c @@ -24,6 +24,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "UI_interface.h" diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 0e69b4bb358..d7d1d3ce260 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -35,6 +35,7 @@ #include "UI_interface.h" #include "RNA_access.h" +#include "RNA_path.h" #include "WM_api.h" #include "WM_types.h" diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index ddd805f6010..4a5919864c7 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -40,6 +40,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "RNA_types.h" diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index 09902dd6c35..8572e938b30 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -27,6 +27,7 @@ #include "WM_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "UI_interface.h" diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 88fe866f717..f460a159a5f 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -39,6 +39,7 @@ #include "WM_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "UI_interface.h" diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 8588c7cabc0..3147deb5ad1 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -66,7 +66,7 @@ static void asset_view_item_but_drag_set(uiBut *but, } static void asset_view_draw_item(uiList *ui_list, - bContext *UNUSED(C), + const bContext *UNUSED(C), uiLayout *layout, PointerRNA *UNUSED(dataptr), PointerRNA *itemptr, @@ -183,7 +183,7 @@ static void asset_view_template_refresh_asset_collection( } void uiTemplateAssetView(uiLayout *layout, - bContext *C, + const bContext *C, const char *list_id, PointerRNA *asset_library_dataptr, const char *asset_library_propname, diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index e0b6bbb34c4..f0c91588f98 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -83,7 +83,7 @@ struct TemplateListVisualInfo { }; static void uilist_draw_item_default(struct uiList *ui_list, - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct uiLayout *layout, struct PointerRNA *UNUSED(dataptr), struct PointerRNA *itemptr, @@ -114,7 +114,7 @@ static void uilist_draw_item_default(struct uiList *ui_list, } static void uilist_draw_filter_default(struct uiList *ui_list, - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct uiLayout *layout) { PointerRNA listptr; @@ -160,7 +160,7 @@ static int cmpstringp(const void *p1, const void *p2) } static void uilist_filter_items_default(struct uiList *ui_list, - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct PointerRNA *dataptr, const char *propname) { @@ -434,7 +434,7 @@ static void ui_template_list_collect_items(PointerRNA *list_ptr, /** * Create the UI-list representation of the list items, sorted and filtered if needed. */ -static void ui_template_list_collect_display_items(bContext *C, +static void ui_template_list_collect_display_items(const bContext *C, uiList *ui_list, TemplateListInputData *input_data, const uiListFilterItemsFunc filter_items_fn, @@ -601,7 +601,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha /** * \note that \a layout_type may be null. */ -static uiList *ui_list_ensure(bContext *C, +static uiList *ui_list_ensure(const bContext *C, uiListType *ui_list_type, const char *list_id, int layout_type, @@ -656,7 +656,7 @@ static uiList *ui_list_ensure(bContext *C, return ui_list; } -static void ui_template_list_layout_draw(bContext *C, +static void ui_template_list_layout_draw(const bContext *C, uiList *ui_list, uiLayout *layout, TemplateListInputData *input_data, @@ -1156,7 +1156,7 @@ static void ui_template_list_layout_draw(bContext *C, } uiList *uiTemplateList_ex(uiLayout *layout, - bContext *C, + const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, @@ -1227,7 +1227,7 @@ uiList *uiTemplateList_ex(uiLayout *layout, } void uiTemplateList(uiLayout *layout, - bContext *C, + const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 14da5a7cd62..a9de095a2a8 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6674,7 +6674,7 @@ void uiTemplateCacheFileTimeSettings(uiLayout *layout, PointerRNA *fileptr) } static void cache_file_layer_item(uiList *UNUSED(ui_list), - bContext *UNUSED(C), + const bContext *UNUSED(C), uiLayout *layout, PointerRNA *UNUSED(dataptr), PointerRNA *itemptr, diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index bf3b71178e8..abf286afa0c 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -50,6 +50,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "ED_keyframing.h" diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc index 3d26e764211..7cefcf9815e 100644 --- a/source/blender/editors/render/render_update.cc +++ b/source/blender/editors/render/render_update.cc @@ -95,20 +95,20 @@ void ED_render_view3d_update(Depsgraph *depsgraph, CTX_free(C); } - else { - RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->shading.type); - if (updated) { - DRWUpdateContext drw_context = {nullptr}; - drw_context.bmain = bmain; - drw_context.depsgraph = depsgraph; - drw_context.scene = scene; - drw_context.view_layer = view_layer; - drw_context.region = region; - drw_context.v3d = v3d; - drw_context.engine_type = engine_type; - DRW_notify_view_update(&drw_context); - } + + if (!updated) { + continue; } + + DRWUpdateContext drw_context = {nullptr}; + drw_context.bmain = bmain; + drw_context.depsgraph = depsgraph; + drw_context.scene = scene; + drw_context.view_layer = view_layer; + drw_context.region = region; + drw_context.v3d = v3d; + drw_context.engine_type = ED_view3d_engine_type(scene, v3d->shading.type); + DRW_notify_view_update(&drw_context); } } diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 9d66debda6f..01c208bf48d 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -34,6 +34,7 @@ #include "UI_resources.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc index 5bfc8ccc667..a955a074df2 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc @@ -67,10 +67,11 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, return selection.get_internal_single() <= 0.0f ? IndexMask(0) : IndexMask(curves.curves_num()); } + const Span<float> point_selection_span = selection.get_internal_span(); return index_mask_ops::find_indices_based_on_predicate( curves.curves_range(), 512, r_indices, [&](const int curve_i) { for (const int i : curves.points_for_curve(curve_i)) { - if (selection[i] > 0.0f) { + if (point_selection_span[i] > 0.0f) { return true; } } diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index c69b73e377d..90b3cec437c 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -20,6 +20,7 @@ #include "ED_space_api.h" #include "RNA_access.h" +#include "RNA_path.h" #include "WM_api.h" #include "WM_types.h" diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 3b8c6cbd1d0..d41904d9790 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -41,6 +41,7 @@ #include "WM_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "ED_anim_api.h" diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index 160a379d3c6..bb520c0537e 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -36,6 +36,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" @@ -44,7 +45,12 @@ #include "UI_resources.h" #include "NOD_common.h" +#include "NOD_composite.h" +#include "NOD_geometry.h" +#include "NOD_shader.h" #include "NOD_socket.h" +#include "NOD_texture.h" + #include "node_intern.hh" /* own include */ namespace blender::ed::space_node { @@ -99,16 +105,16 @@ const char *node_group_idname(bContext *C) SpaceNode *snode = CTX_wm_space_node(C); if (ED_node_is_shader(snode)) { - return "ShaderNodeGroup"; + return ntreeType_Shader->group_idname; } if (ED_node_is_compositor(snode)) { - return "CompositorNodeGroup"; + return ntreeType_Composite->group_idname; } if (ED_node_is_texture(snode)) { - return "TextureNodeGroup"; + return ntreeType_Texture->group_idname; } if (ED_node_is_geometry(snode)) { - return "GeometryNodeGroup"; + return ntreeType_Geometry->group_idname; } return ""; diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 32860bc2cff..16da4f7b1dd 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -55,6 +55,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "GPU_material.h" diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index ea35a8c0fa7..1b4acda9bcf 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -30,6 +30,7 @@ #include "UI_view2d.h" #include "RNA_access.h" +#include "RNA_path.h" #include "text_format.h" #include "text_intern.h" /* own include */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index 41015c429fe..aa9c654aa7e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -270,7 +270,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, } static void segment_list_item(struct uiList *UNUSED(ui_list), - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct uiLayout *layout, struct PointerRNA *UNUSED(idataptr), struct PointerRNA *itemptr, diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 5e97909a2b8..2de0f4e470a 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -456,6 +456,7 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/basic/shaders/infos/basic_depth_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 3ca465fa57a..12a7cf49f9d 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -121,6 +121,7 @@ typedef struct GPUCodegenOutput { char *surface; char *volume; char *thickness; + char *composite; char *material_functions; GPUShaderCreateInfo *create_info; @@ -178,6 +179,8 @@ void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link); void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash); +void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link); + /** * Wrap a part of the material graph into a function. You need then need to call the function by * using something like #GPU_differentiate_float_function. @@ -218,6 +221,7 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, void *thunk); void GPU_material_compile(GPUMaterial *mat); +void GPU_material_free_single(GPUMaterial *material); void GPU_material_free(struct ListBase *gpumaterial); void GPU_material_acquire(GPUMaterial *mat); @@ -319,6 +323,16 @@ struct GHash *GPU_uniform_attr_list_hash_new(const char *info); void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, GPUUniformAttrList *src); void GPU_uniform_attr_list_free(GPUUniformAttrList *set); +/* A callback passed to GPU_material_from_callbacks to construct the material graph by adding and + * linking the necessary GPU material nodes. */ +typedef void (*ConstructGPUMaterialFn)(void *thunk, GPUMaterial *material); + +/* Construct a GPU material from a set of callbacks. See the callback types for more information. + * The given thunk will be passed as the first parameter of each callback. */ +GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb, + GPUCodegenCallbackFn generate_code_function_cb, + void *thunk); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 3460d33fe68..c154f1adc8b 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -177,7 +177,9 @@ void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, fl void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]); void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]); void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]); +void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2]); void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]); +void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3]); void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]); void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]); diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index 82441c3c89c..4a45a3e63ed 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -280,6 +280,7 @@ class GPUCodegen { void node_serialize(std::stringstream &eval_ss, const GPUNode *node); char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link); + char *graph_serialize(eGPUNodeTag tree_tag); static char *extract_c_str(std::stringstream &stream) { @@ -500,6 +501,19 @@ char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link return eval_c_str; } +char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag) +{ + std::stringstream eval_ss; + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + if (node->tag & tree_tag) { + node_serialize(eval_ss, node); + } + } + char *eval_c_str = extract_c_str(eval_ss); + BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size()); + return eval_c_str; +} + void GPUCodegen::generate_uniform_buffer() { /* Extract uniform inputs. */ @@ -539,6 +553,9 @@ void GPUCodegen::generate_graphs() output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume); output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement); output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness); + if (!BLI_listbase_is_empty(&graph.outlink_compositor)) { + output.composite = graph_serialize(GPU_NODE_TAG_COMPOSITOR); + } if (!BLI_listbase_is_empty(&graph.material_functions)) { std::stringstream eval_ss; @@ -569,9 +586,10 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GPUCodegenCallbackFn finalize_source_cb, void *thunk) { - /* Prune the unused nodes and extract attributes before compiling so the - * generated VBOs are ready to accept the future shader. */ gpu_node_graph_prune_unused(graph); + + /* Extract attributes before compiling so the generated VBOs are ready to accept the future + * shader. */ gpu_node_graph_finalize_uniform_attrs(graph); GPUCodegen codegen(material, graph); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 4d3ea3e0c99..a4842ef0e43 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -141,7 +141,7 @@ static void gpu_material_ramp_texture_build(GPUMaterial *mat) mat->coba_builder = NULL; } -static void gpu_material_free_single(GPUMaterial *material) +void GPU_material_free_single(GPUMaterial *material) { bool do_free = atomic_sub_and_fetch_uint32(&material->refcount, 1) == 0; if (!do_free) { @@ -173,7 +173,7 @@ void GPU_material_free(ListBase *gpumaterial) LISTBASE_FOREACH (LinkData *, link, gpumaterial) { GPUMaterial *material = link->data; DRW_deferred_shader_remove(material); - gpu_material_free_single(material); + GPU_material_free_single(material); } BLI_freelistN(gpumaterial); } @@ -538,6 +538,13 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, BLI_addtail(&material->graph.outlink_aovs, aov_link); } +void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link) +{ + GPUNodeGraphOutputLink *compositor_link = MEM_callocN(sizeof(GPUNodeGraphOutputLink), __func__); + compositor_link->outlink = link; + BLI_addtail(&material->graph.outlink_compositor, compositor_link); +} + char *GPU_material_split_sub_function(GPUMaterial *material, eGPUType return_type, GPUNodeLink **link) @@ -721,7 +728,7 @@ void GPU_material_acquire(GPUMaterial *mat) void GPU_material_release(GPUMaterial *mat) { - gpu_material_free_single(mat); + GPU_material_free_single(mat); } void GPU_material_compile(GPUMaterial *mat) @@ -772,3 +779,42 @@ void GPU_materials_free(Main *bmain) // BKE_world_defaults_free_gpu(); BKE_material_defaults_free_gpu(); } + +GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb, + GPUCodegenCallbackFn generate_code_function_cb, + void *thunk) +{ + /* Allocate a new material and its material graph, and initialize its reference count. */ + GPUMaterial *material = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"); + material->graph.used_libraries = BLI_gset_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries"); + material->refcount = 1; + + /* Construct the material graph by adding and linking the necessary GPU material nodes. */ + construct_function_cb(thunk, material); + + /* Create and initialize the texture storing color bands used by Ramp and Curve nodes. */ + gpu_material_ramp_texture_build(material); + + /* Lookup an existing pass in the cache or generate a new one. */ + material->pass = GPU_generate_pass(material, &material->graph, generate_code_function_cb, thunk); + + /* The pass already exists in the pass cache but its shader already failed to compile. */ + if (material->pass == NULL) { + material->status = GPU_MAT_FAILED; + gpu_node_graph_free(&material->graph); + return material; + } + + /* The pass already exists in the pass cache and its shader is already compiled. */ + GPUShader *shader = GPU_pass_shader_get(material->pass); + if (shader != NULL) { + material->status = GPU_MAT_SUCCESS; + gpu_node_graph_free_nodes(&material->graph); + return material; + } + + /* The material was created successfully but still needs to be compiled. */ + material->status = GPU_MAT_CREATED; + return material; +} diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 684070dbdc0..c7b2fde492f 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -75,9 +75,26 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType if (STR_ELEM(name, "set_value", "set_rgb", "set_rgba") && (input->type == type)) { input = MEM_dupallocN(outnode->inputs.first); + + switch (input->source) { + case GPU_SOURCE_ATTR: + input->attr->users++; + break; + case GPU_SOURCE_UNIFORM_ATTR: + input->uniform_attr->users++; + break; + case GPU_SOURCE_TEX: + case GPU_SOURCE_TEX_TILED_MAPPING: + input->texture->users++; + break; + default: + break; + } + if (input->link) { input->link->users++; } + BLI_addtail(&node->inputs, input); return; } @@ -179,35 +196,21 @@ static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat, BLI_assert(socket != NULL); BLI_assert(socket->in_out == in_out); - if ((socket->flag & SOCK_HIDE_VALUE) == 0) { - GPUNodeLink *link; - switch (socket->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *socket_data = socket->default_value; - link = GPU_uniform(&socket_data->value); - break; - } - case SOCK_VECTOR: { - bNodeSocketValueVector *socket_data = socket->default_value; - link = GPU_uniform(socket_data->value); - break; - } - case SOCK_RGBA: { - bNodeSocketValueRGBA *socket_data = socket->default_value; - link = GPU_uniform(socket_data->value); - break; - } - default: - return NULL; - break; - } + if (socket->flag & SOCK_HIDE_VALUE) { + return NULL; + } - if (in_out == SOCK_IN) { - GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link); - } - return link; + if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA)) { + return NULL; + } + + GPUNodeLink *link = GPU_uniform(stack->vec); + + if (in_out == SOCK_IN) { + GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link); } - return NULL; + + return link; } static void gpu_node_input_socket( @@ -803,6 +806,7 @@ void gpu_node_graph_free(GPUNodeGraph *graph) { BLI_freelistN(&graph->outlink_aovs); BLI_freelistN(&graph->material_functions); + BLI_freelistN(&graph->outlink_compositor); gpu_node_graph_free_nodes(graph); BLI_freelistN(&graph->textures); @@ -855,6 +859,9 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph) LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) { gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION); } + LISTBASE_FOREACH (GPUNodeGraphOutputLink *, compositor_link, &graph->outlink_compositor) { + gpu_nodes_tag(compositor_link->outlink, GPU_NODE_TAG_COMPOSITOR); + } for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) { next = node->next; diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index ae472d5b7aa..08ff8bbef58 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -59,6 +59,7 @@ typedef enum { GPU_NODE_TAG_THICKNESS = (1 << 3), GPU_NODE_TAG_AOV = (1 << 4), GPU_NODE_TAG_FUNCTION = (1 << 5), + GPU_NODE_TAG_COMPOSITOR = (1 << 6), } eGPUNodeTag; ENUM_OPERATORS(eGPUNodeTag, GPU_NODE_TAG_FUNCTION) @@ -158,6 +159,8 @@ typedef struct GPUNodeGraph { ListBase outlink_aovs; /* List of GPUNodeGraphFunctionLink */ ListBase material_functions; + /* List of GPUNodeGraphOutputLink */ + ListBase outlink_compositor; /* Requested attributes and textures. */ ListBase attributes; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index fe9aacb95f9..8a630d9e3ca 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -7,6 +7,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_math_matrix.h" #include "BLI_string_utils.h" #include "GPU_capabilities.h" @@ -382,6 +383,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) sources.append(resources.c_str()); sources.append(layout.c_str()); sources.extend(code); + sources.extend(info.dependencies_generated); + sources.append(info.compute_source_generated.c_str()); shader->compute_shader_from_glsl(sources); } @@ -702,12 +705,25 @@ void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4] GPU_shader_uniform_vector(sh, loc, 4, 1, data); } +void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector_int(sh, loc, 2, 1, data); +} + void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]) { const int loc = GPU_shader_get_uniform(sh, name); GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data); } +void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3]) +{ + float matrix[4][4]; + copy_m4_m3(matrix, data); + GPU_shader_uniform_mat4(sh, name, matrix); +} + void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]) { const int loc = GPU_shader_get_uniform(sh, name); diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 8e05412d0ee..fb8efbb209a 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -298,6 +298,7 @@ struct ShaderCreateInfo { /** Manually set generated code. */ std::string vertex_source_generated = ""; std::string fragment_source_generated = ""; + std::string compute_source_generated = ""; std::string geometry_source_generated = ""; std::string typedef_source_generated = ""; /** Manually set generated dependencies. */ @@ -818,6 +819,7 @@ struct ShaderCreateInfo { TEST_EQUAL(*this, b, builtins_); TEST_EQUAL(*this, b, vertex_source_generated); TEST_EQUAL(*this, b, fragment_source_generated); + TEST_EQUAL(*this, b, compute_source_generated); TEST_EQUAL(*this, b, typedef_source_generated); TEST_VECTOR_EQUAL(*this, b, vertex_inputs_); TEST_EQUAL(*this, b, geometry_layout_); diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index d91e15243f3..aff7df9ac33 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -593,7 +593,8 @@ struct GPUSource { bool is_from_material_library() const { return (filename.startswith("gpu_shader_material_") || - filename.startswith("gpu_shader_common_")) && + filename.startswith("gpu_shader_common_") || + filename.startswith("gpu_shader_compositor_")) && filename.endswith(".glsl"); } }; diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp index 04a7a81c0a6..fe90dc5d5fa 100644 --- a/source/blender/io/collada/BCAnimationCurve.cpp +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2008 Blender Foundation. All rights reserved. */ +#include "RNA_path.h" + #include "BCAnimationCurve.h" BCAnimationCurve::BCAnimationCurve() diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index d49d0906aa7..29795519719 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -96,7 +96,7 @@ typedef enum eAssetLibraryType { } eAssetLibraryType; /** - * Information to identify a asset library. May be either one of the predefined types (current + * Information to identify an asset library. May be either one of the predefined types (current * 'Main', builtin library, project library), or a custom type as defined in the Preferences. * * If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 0af50b2bd4f..345fa141514 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -37,7 +37,7 @@ typedef enum eViewLayerEEVEEPassType { EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16), EEVEE_RENDER_PASS_VECTOR = (1 << 17), } eViewLayerEEVEEPassType; -#define EEVEE_RENDER_PASS_MAX_BIT 17 +#define EEVEE_RENDER_PASS_MAX_BIT 18 /* #ViewLayerAOV.type */ typedef enum eViewLayerAOVType { diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 005228bea72..de9fa60aa5d 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -475,247 +475,6 @@ bool RNA_property_copy( bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index); bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop); -/* Path - * - * Experimental method to refer to structs and properties with a string, - * using a syntax like: scenes[0].objects["Cube"].data.verts[7].co - * - * This provides a way to refer to RNA data while being detached from any - * particular pointers, which is useful in a number of applications, like - * UI code or Actions, though efficiency is a concern. */ - -char *RNA_path_append( - const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); -#if 0 /* UNUSED. */ -char *RNA_path_back(const char *path); -#endif - -/** - * Search for the start of the 'rna array index' part of the given `rna_path`. - * - * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first - * character in `rna_path` that is part of the array index for the given property. Return NULL if - * none can be found, e.g. because the property is not an RNA array. - * - * \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path. - * Only used to ensure it is a valid array property. - */ -const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop); - -/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */ - -/** - * Resolve the given RNA Path to find the pointer and/or property - * indicated by fully resolving the path. - * - * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers, - * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr, - * and a NULL \a r_prop... - * - * \note Assumes all pointers provided are valid - * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" - */ -bool RNA_path_resolve(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop); - -/** - * Resolve the given RNA Path to find the pointer and/or property + array index - * indicated by fully resolving the path. - * - * \note Assumes all pointers provided are valid. - * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" - */ -bool RNA_path_resolve_full(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index); -/** - * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data. - * - * \note While it's correct to ignore the value of #PointerRNA.data - * most callers need to know if the resulting pointer was found and not null. - */ -bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index); - -/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */ - -/** - * Resolve the given RNA Path to find both the pointer AND property - * indicated by fully resolving the path. - * - * This is a convenience method to avoid logic errors and ugly syntax. - * \note Assumes all pointers provided are valid - * \return True only if both a valid pointer and property are found after resolving the path - */ -bool RNA_path_resolve_property(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop); - -/** - * Resolve the given RNA Path to find the pointer AND property (as well as the array index) - * indicated by fully resolving the path. - * - * This is a convenience method to avoid logic errors and ugly syntax. - * \note Assumes all pointers provided are valid - * \return True only if both a valid pointer and property are found after resolving the path - */ -bool RNA_path_resolve_property_full(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index); - -/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist, - * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */ - -/** - * Resolve the given RNA Path to find both the pointer AND property - * indicated by fully resolving the path, and get the value of the Pointer property - * (or item of the collection). - * - * This is a convenience method to avoid logic errors and ugly syntax, - * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call. - * \note Assumes all pointers provided are valid. - * \param r_item_ptr: The final Pointer or Collection item value. - * You must check for its validity before use! - * \return True only if both a valid pointer and property are found after resolving the path - */ -bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - PointerRNA *r_item_ptr); - -/** - * Resolve the given RNA Path to find both the pointer AND property (as well as the array index) - * indicated by fully resolving the path, - * and get the value of the Pointer property (or item of the collection). - * - * This is a convenience method to avoid logic errors and ugly syntax, - * it combines both \a RNA_path_resolve_full and - * \a RNA_path_resolve_property_full in a single call. - * \note Assumes all pointers provided are valid. - * \param r_item_ptr: The final Pointer or Collection item value. - * You must check for its validity before use! - * \return True only if both a valid pointer and property are found after resolving the path - */ -bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index, - PointerRNA *r_item_ptr); - -typedef struct PropertyElemRNA PropertyElemRNA; -struct PropertyElemRNA { - PropertyElemRNA *next, *prev; - PointerRNA ptr; - PropertyRNA *prop; - int index; -}; -/** - * Resolve the given RNA Path into a linked list of #PropertyElemRNA's. - * - * To be used when complex operations over path are needed, like e.g. get relative paths, - * to avoid too much string operations. - * - * \return True if there was no error while resolving the path - * \note Assumes all pointers provided are valid - */ -bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements); - -/** - * Find the path from the structure referenced by the pointer to the runtime RNA-defined - * #IDProperty object. - * - * \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties). - * - * \param ptr: Reference to the object owning the custom property storage. - * \param needle: Custom property object to find. - * \return Relative path or NULL. - */ -char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle); - -/** - * Find the actual ID pointer and path from it to the given ID. - * - * \param id: ID reference to search the global owner for. - * \param[out] r_path: Path from the real ID to the initial ID. - * \return The ID pointer, or NULL in case of failure. - */ -struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path); - -char *RNA_path_from_ID_to_struct(const PointerRNA *ptr); - -char *RNA_path_from_real_ID_to_struct(struct Main *bmain, - const PointerRNA *ptr, - struct ID **r_real); - -char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop); -/** - * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc. - * \param index: The *flattened* index to use when \a `index_dim > 0`, - * this is expanded when used with multi-dimensional arrays. - */ -char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, - PropertyRNA *prop, - int index_dim, - int index); - -char *RNA_path_from_real_ID_to_property_index(struct Main *bmain, - const PointerRNA *ptr, - PropertyRNA *prop, - int index_dim, - int index, - struct ID **r_real_id); - -/** - * \return the path to given ptr/prop from the closest ancestor of given type, - * if any (else return NULL). - */ -char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, - PropertyRNA *prop, - const struct StructRNA *type); - -/** - * Get the ID as a python representation, eg: - * bpy.data.foo["bar"] - */ -char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id); -/** - * Get the ID.struct as a python representation, eg: - * bpy.data.foo["bar"].some_struct - */ -char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr); -/** - * Get the ID.struct.property as a python representation, eg: - * bpy.data.foo["bar"].some_struct.some_prop[10] - */ -char *RNA_path_full_property_py_ex( - struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback); -char *RNA_path_full_property_py(struct Main *bmain, - const PointerRNA *ptr, - PropertyRNA *prop, - int index); -/** - * Get the struct.property as a python representation, eg: - * some_struct.some_prop[10] - */ -char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index); -/** - * Get the struct.property as a python representation, eg: - * some_prop[10] - */ -char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index); - /* Quick name based property access * * These are just an easier way to access property values without having to diff --git a/source/blender/makesrna/RNA_path.h b/source/blender/makesrna/RNA_path.h new file mode 100644 index 00000000000..7ab8c6fa313 --- /dev/null +++ b/source/blender/makesrna/RNA_path.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup RNA + * + * RNA paths are a way to refer to pointers and properties with a string, + * using a syntax like: scenes[0].objects["Cube"].data.verts[7].co + * + * This provides a way to refer to RNA data while being detached from any + * particular pointers, which is useful in a number of applications, like + * UI code or Actions, though efficiency is a concern. + */ + +#include "RNA_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ListBase; +struct IDProperty; + +char *RNA_path_append( + const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); +#if 0 /* UNUSED. */ +char *RNA_path_back(const char *path); +#endif + +/** + * Search for the start of the 'rna array index' part of the given `rna_path`. + * + * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first + * character in `rna_path` that is part of the array index for the given property. Return NULL if + * none can be found, e.g. because the property is not an RNA array. + * + * \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path. + * Only used to ensure it is a valid array property. + */ +const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop); + +/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */ + +/** + * Resolve the given RNA Path to find the pointer and/or property + * indicated by fully resolving the path. + * + * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers, + * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr, + * and a NULL \a r_prop... + * + * \note Assumes all pointers provided are valid + * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" + */ +bool RNA_path_resolve(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop); + +/** + * Resolve the given RNA Path to find the pointer and/or property + array index + * indicated by fully resolving the path. + * + * \note Assumes all pointers provided are valid. + * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" + */ +bool RNA_path_resolve_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); +/** + * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data. + * + * \note While it's correct to ignore the value of #PointerRNA.data + * most callers need to know if the resulting pointer was found and not null. + */ +bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); + +/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */ + +/** + * Resolve the given RNA Path to find both the pointer AND property + * indicated by fully resolving the path. + * + * This is a convenience method to avoid logic errors and ugly syntax. + * \note Assumes all pointers provided are valid + * \return True only if both a valid pointer and property are found after resolving the path + */ +bool RNA_path_resolve_property(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop); + +/** + * Resolve the given RNA Path to find the pointer AND property (as well as the array index) + * indicated by fully resolving the path. + * + * This is a convenience method to avoid logic errors and ugly syntax. + * \note Assumes all pointers provided are valid + * \return True only if both a valid pointer and property are found after resolving the path + */ +bool RNA_path_resolve_property_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); + +/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist, + * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */ + +/** + * Resolve the given RNA Path to find both the pointer AND property + * indicated by fully resolving the path, and get the value of the Pointer property + * (or item of the collection). + * + * This is a convenience method to avoid logic errors and ugly syntax, + * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call. + * \note Assumes all pointers provided are valid. + * \param r_item_ptr: The final Pointer or Collection item value. + * You must check for its validity before use! + * \return True only if both a valid pointer and property are found after resolving the path + */ +bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + PointerRNA *r_item_ptr); + +/** + * Resolve the given RNA Path to find both the pointer AND property (as well as the array index) + * indicated by fully resolving the path, + * and get the value of the Pointer property (or item of the collection). + * + * This is a convenience method to avoid logic errors and ugly syntax, + * it combines both \a RNA_path_resolve_full and + * \a RNA_path_resolve_property_full in a single call. + * \note Assumes all pointers provided are valid. + * \param r_item_ptr: The final Pointer or Collection item value. + * You must check for its validity before use! + * \return True only if both a valid pointer and property are found after resolving the path + */ +bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index, + PointerRNA *r_item_ptr); + +typedef struct PropertyElemRNA PropertyElemRNA; +struct PropertyElemRNA { + PropertyElemRNA *next, *prev; + PointerRNA ptr; + PropertyRNA *prop; + int index; +}; +/** + * Resolve the given RNA Path into a linked list of #PropertyElemRNA's. + * + * To be used when complex operations over path are needed, like e.g. get relative paths, + * to avoid too much string operations. + * + * \return True if there was no error while resolving the path + * \note Assumes all pointers provided are valid + */ +bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements); + +/** + * Find the path from the structure referenced by the pointer to the runtime RNA-defined + * #IDProperty object. + * + * \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties). + * + * \param ptr: Reference to the object owning the custom property storage. + * \param needle: Custom property object to find. + * \return Relative path or NULL. + */ +char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle); + +/** + * Find the actual ID pointer and path from it to the given ID. + * + * \param id: ID reference to search the global owner for. + * \param[out] r_path: Path from the real ID to the initial ID. + * \return The ID pointer, or NULL in case of failure. + */ +struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path); + +char *RNA_path_from_ID_to_struct(const PointerRNA *ptr); + +char *RNA_path_from_real_ID_to_struct(struct Main *bmain, + const PointerRNA *ptr, + struct ID **r_real); + +char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop); +/** + * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc. + * \param index: The *flattened* index to use when \a `index_dim > 0`, + * this is expanded when used with multi-dimensional arrays. + */ +char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index); + +char *RNA_path_from_real_ID_to_property_index(struct Main *bmain, + const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index, + struct ID **r_real_id); + +/** + * \return the path to given ptr/prop from the closest ancestor of given type, + * if any (else return NULL). + */ +char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, + PropertyRNA *prop, + const struct StructRNA *type); + +/** + * Get the ID as a python representation, eg: + * bpy.data.foo["bar"] + */ +char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id); +/** + * Get the ID.struct as a python representation, eg: + * bpy.data.foo["bar"].some_struct + */ +char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr); +/** + * Get the ID.struct.property as a python representation, eg: + * bpy.data.foo["bar"].some_struct.some_prop[10] + */ +char *RNA_path_full_property_py_ex( + struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback); +char *RNA_path_full_property_py(struct Main *bmain, + const PointerRNA *ptr, + PropertyRNA *prop, + int index); +/** + * Get the struct.property as a python representation, eg: + * some_struct.some_prop[10] + */ +char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index); +/** + * Get the struct.property as a python representation, eg: + * some_prop[10] + */ +char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index aaadd36341f..8124804de2b 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -159,6 +159,7 @@ set(SRC_RNA_INC ../RNA_documentation.h ../RNA_enum_items.h ../RNA_enum_types.h + ../RNA_path.h ../RNA_types.h ) @@ -416,6 +417,7 @@ add_custom_command( set(SRC rna_access.c rna_access_compare_override.c + rna_path.cc ${GENSRC} ${SRC_RNA_INC} diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index cf4182243b9..04707c01d6b 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -46,6 +46,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "WM_api.h" #include "WM_message.h" @@ -738,7 +739,7 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier) } /* Find the property which uses the given nested struct */ -static PropertyRNA *RNA_struct_find_nested(PointerRNA *ptr, StructRNA *srna) +PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna) { PropertyRNA *prop = NULL; @@ -1422,7 +1423,7 @@ StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop) return cprop->item_type; } } - /* ignore other types, RNA_struct_find_nested calls with unchecked props */ + /* ignore other types, rna_struct_find_nested calls with unchecked props */ return &RNA_UnknownType; } @@ -4821,1337 +4822,6 @@ PointerRNA rna_array_lookup_int( return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize); } -/* RNA Path - Experiment */ - -/** - * Extract the first token from `path`. - * - * \param path: Extract the token from path, step the pointer to the beginning of the next token - * \return The nil terminated token. - */ -static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen) -{ - int len = 0; - - /* Get data until `.` or `[`. */ - const char *p = *path; - while (*p && !ELEM(*p, '.', '[')) { - len++; - p++; - } - - /* Empty, return. */ - if (UNLIKELY(len == 0)) { - return NULL; - } - - /* Try to use fixed buffer if possible. */ - char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__); - memcpy(buf, *path, sizeof(char) * len); - buf[len] = '\0'; - - if (*p == '.') { - p++; - } - *path = p; - - return buf; -} - -/** - * Extract the first token in brackets from `path` (with quoted text support). - * - * - `[0]` -> `0` - * - `["Some\"Quote"]` -> `Some"Quote` - * - * \param path: Extract the token from path, step the pointer to the beginning of the next token - * (past quoted text and brackets). - * \return The nil terminated token. - */ -static char *rna_path_token_in_brackets(const char **path, - char *fixedbuf, - int fixedlen, - bool *r_quoted) -{ - int len = 0; - bool quoted = false; - - BLI_assert(r_quoted != NULL); - - /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */ - if (UNLIKELY(**path != '[')) { - return NULL; - } - - (*path)++; - const char *p = *path; - - /* 2 kinds of look-ups now, quoted or unquoted. */ - if (*p == '"') { - /* Find the matching quote. */ - (*path)++; - p = *path; - const char *p_end = BLI_str_escape_find_quote(p); - if (p_end == NULL) { - /* No Matching quote. */ - return NULL; - } - /* Exclude the last quote from the length. */ - len += (p_end - p); - - /* Skip the last quoted char to get the `]`. */ - p_end += 1; - p = p_end; - quoted = true; - } - else { - /* Find the matching bracket. */ - while (*p && (*p != ']')) { - len++; - p++; - } - } - - if (UNLIKELY(*p != ']')) { - return NULL; - } - - /* Empty, return. */ - if (UNLIKELY(len == 0)) { - return NULL; - } - - /* Try to use fixed buffer if possible. */ - char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__); - - /* Copy string, taking into account escaped ']' */ - if (quoted) { - BLI_str_unescape(buf, *path, len); - /* +1 to step over the last quote. */ - BLI_assert((*path)[len] == '"'); - p = (*path) + len + 1; - } - else { - memcpy(buf, *path, sizeof(char) * len); - buf[len] = '\0'; - } - /* Set path to start of next token. */ - if (*p == ']') { - p++; - } - if (*p == '.') { - p++; - } - *path = p; - - *r_quoted = quoted; - - return buf; -} - -/** - * \return true when the key in the path is correctly parsed and found in the collection - * or when the path is empty. - */ -static bool rna_path_parse_collection_key(const char **path, - PointerRNA *ptr, - PropertyRNA *prop, - PointerRNA *r_nextptr) -{ - char fixedbuf[256]; - int intkey; - - *r_nextptr = *ptr; - - /* end of path, ok */ - if (!(**path)) { - return true; - } - - bool found = false; - if (**path == '[') { - bool quoted; - char *token; - - /* resolve the lookup with [] brackets */ - token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); - - if (!token) { - return false; - } - - /* check for "" to see if it is a string */ - if (quoted) { - if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) { - found = true; - } - else { - r_nextptr->data = NULL; - } - } - else { - /* otherwise do int lookup */ - intkey = atoi(token); - if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) { - return false; /* we can be sure the fixedbuf was used in this case */ - } - if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) { - found = true; - } - else { - r_nextptr->data = NULL; - } - } - - if (token != fixedbuf) { - MEM_freeN(token); - } - } - else { - if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) { - found = true; - } - else { - /* ensure we quit on invalid values */ - r_nextptr->data = NULL; - } - } - - return found; -} - -static bool rna_path_parse_array_index(const char **path, - PointerRNA *ptr, - PropertyRNA *prop, - int *r_index) -{ - char fixedbuf[256]; - int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0}; - int len[RNA_MAX_ARRAY_DIMENSION]; - const int dim = RNA_property_array_dimension(ptr, prop, len); - int i; - - *r_index = -1; - - /* end of path, ok */ - if (!(**path)) { - return true; - } - - for (i = 0; i < dim; i++) { - int temp_index = -1; - char *token; - - /* multi index resolve */ - if (**path == '[') { - bool quoted; - token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); - - if (token == NULL) { - /* invalid syntax blah[] */ - return false; - } - /* check for "" to see if it is a string */ - if (quoted) { - temp_index = RNA_property_array_item_index(prop, *token); - } - else { - /* otherwise do int lookup */ - temp_index = atoi(token); - - if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) { - if (token != fixedbuf) { - MEM_freeN(token); - } - - return false; - } - } - } - else if (dim == 1) { - /* location.x || scale.X, single dimension arrays only */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf)); - if (token == NULL) { - /* invalid syntax blah. */ - return false; - } - temp_index = RNA_property_array_item_index(prop, *token); - } - else { - /* just to avoid uninitialized pointer use */ - token = fixedbuf; - } - - if (token != fixedbuf) { - MEM_freeN(token); - } - - /* out of range */ - if (temp_index < 0 || temp_index >= len[i]) { - return false; - } - - index_arr[i] = temp_index; - /* end multi index resolve */ - } - - /* arrays always contain numbers so further values are not valid */ - if (**path) { - return false; - } - - /* flatten index over all dimensions */ - { - int totdim = 1; - int flat_index = 0; - - for (i = dim - 1; i >= 0; i--) { - flat_index += index_arr[i] * totdim; - totdim *= len[i]; - } - - *r_index = flat_index; - } - return true; -} - -/** - * Generic rna path parser. - * - * \note All parameters besides \a ptr and \a path are optional. - * - * \param ptr: The root of given RNA path. - * \param path: The RNA path. - * \param r_ptr: The final RNA data holding the last property in \a path. - * \param r_prop: The final property of \a r_ptr, from \a path. - * \param r_index: The final index in the \a r_prop, if defined by \a path. - * \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the - * pointer, or of the collection item. - * Mutually exclusive with \a eval_pointer option. - * \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA - * that represent the whole given \a path). - * \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a - * Collection property, \a r_ptr will be set to the value of that property, - * and \a r_prop will be NULL. - * Mutually exclusive with \a r_item_ptr. - * - * \return \a true on success, \a false if the path is somehow invalid. - */ -static bool rna_path_parse(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index, - PointerRNA *r_item_ptr, - ListBase *r_elements, - const bool eval_pointer) -{ - BLI_assert(r_item_ptr == NULL || !eval_pointer); - PropertyRNA *prop; - PointerRNA curptr, nextptr; - PropertyElemRNA *prop_elem = NULL; - int index = -1; - char fixedbuf[256]; - int type; - const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer; - - if (do_item_ptr) { - RNA_POINTER_INVALIDATE(&nextptr); - } - - prop = NULL; - curptr = *ptr; - - if (path == NULL || *path == '\0') { - return false; - } - - while (*path) { - if (do_item_ptr) { - RNA_POINTER_INVALIDATE(&nextptr); - } - - const bool use_id_prop = (*path == '['); - /* custom property lookup ? - * C.object["someprop"] - */ - - if (!curptr.data) { - return false; - } - - /* look up property name in current struct */ - bool quoted = false; - char *token = use_id_prop ? - rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), "ed) : - rna_path_token(&path, fixedbuf, sizeof(fixedbuf)); - if (!token) { - return false; - } - - prop = NULL; - if (use_id_prop) { /* look up property name in current struct */ - IDProperty *group = RNA_struct_idprops(&curptr, 0); - if (group && quoted) { - prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token); - } - } - else { - prop = RNA_struct_find_property(&curptr, token); - } - - if (token != fixedbuf) { - MEM_freeN(token); - } - - if (!prop) { - return false; - } - - if (r_elements) { - prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__); - prop_elem->ptr = curptr; - prop_elem->prop = prop; - prop_elem->index = -1; /* index will be added later, if needed. */ - BLI_addtail(r_elements, prop_elem); - } - - type = RNA_property_type(prop); - - /* now look up the value of this property if it is a pointer or - * collection, otherwise return the property rna so that the - * caller can read the value of the property itself */ - switch (type) { - case PROP_POINTER: { - /* resolve pointer if further path elements follow - * or explicitly requested - */ - if (do_item_ptr || eval_pointer || *path != '\0') { - nextptr = RNA_property_pointer_get(&curptr, prop); - } - - if (eval_pointer || *path != '\0') { - curptr = nextptr; - prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */ - index = -1; - } - break; - } - case PROP_COLLECTION: { - /* Resolve pointer if further path elements follow. - * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway, - * so do_item_ptr is of no use in that case. - */ - if (*path) { - if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) { - return false; - } - - if (eval_pointer || *path != '\0') { - curptr = nextptr; - prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */ - index = -1; - } - } - break; - } - default: - if (r_index || prop_elem) { - if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) { - return false; - } - - if (prop_elem) { - prop_elem->index = index; - } - } - break; - } - } - - if (r_ptr) { - *r_ptr = curptr; - } - if (r_prop) { - *r_prop = prop; - } - if (r_index) { - *r_index = index; - } - if (r_item_ptr && do_item_ptr) { - *r_item_ptr = nextptr; - } - - if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop || - prop_elem->index != index)) { - prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__); - prop_elem->ptr = curptr; - prop_elem->prop = prop; - prop_elem->index = index; - BLI_addtail(r_elements, prop_elem); - } - - return true; -} - -bool RNA_path_resolve(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop) -{ - if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) { - return false; - } - - return r_ptr->data != NULL; -} - -bool RNA_path_resolve_full( - const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) -{ - if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) { - return false; - } - - return r_ptr->data != NULL; -} - -bool RNA_path_resolve_full_maybe_null( - const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) -{ - return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true); -} - -bool RNA_path_resolve_property(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop) -{ - if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) { - return false; - } - - return r_ptr->data != NULL && *r_prop != NULL; -} - -bool RNA_path_resolve_property_full( - const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) -{ - if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) { - return false; - } - - return r_ptr->data != NULL && *r_prop != NULL; -} - -bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - PointerRNA *r_item_ptr) -{ - if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) { - return false; - } - - return r_ptr->data != NULL && *r_prop != NULL; -} - -bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, - const char *path, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index, - PointerRNA *r_item_ptr) -{ - if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false)) { - return false; - } - - return r_ptr->data != NULL && *r_prop != NULL; -} -bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements) -{ - return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false); -} - -char *RNA_path_append(const char *path, - const PointerRNA *UNUSED(ptr), - PropertyRNA *prop, - int intkey, - const char *strkey) -{ - DynStr *dynstr; - char *result; - - dynstr = BLI_dynstr_new(); - - /* add .identifier */ - if (path) { - BLI_dynstr_append(dynstr, path); - if (*path) { - BLI_dynstr_append(dynstr, "."); - } - } - - BLI_dynstr_append(dynstr, RNA_property_identifier(prop)); - - if (RNA_property_type(prop) == PROP_COLLECTION) { - /* add ["strkey"] or [intkey] */ - BLI_dynstr_append(dynstr, "["); - - if (strkey) { - const int strkey_esc_max_size = (strlen(strkey) * 2) + 1; - char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size); - BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size); - BLI_dynstr_append(dynstr, "\""); - BLI_dynstr_append(dynstr, strkey_esc); - BLI_dynstr_append(dynstr, "\""); - } - else { - char appendstr[128]; - BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey); - BLI_dynstr_append(dynstr, appendstr); - } - - BLI_dynstr_append(dynstr, "]"); - } - - result = BLI_dynstr_get_cstring(dynstr); - BLI_dynstr_free(dynstr); - - return result; -} - -/* Having both path append & back seems like it could be useful, - * this function isn't used at the moment. */ -static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path) -{ - char fixedbuf[256]; - const char *previous, *current; - char *result; - int i; - - if (!path) { - return NULL; - } - - previous = NULL; - current = path; - - /* parse token by token until the end, then we back up to the previous - * position and strip of the next token to get the path one step back */ - while (*current) { - char *token; - - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf)); - - if (!token) { - return NULL; - } - if (token != fixedbuf) { - MEM_freeN(token); - } - - /* in case of collection we also need to strip off [] */ - bool quoted; - token = rna_path_token_in_brackets(¤t, fixedbuf, sizeof(fixedbuf), "ed); - if (token && token != fixedbuf) { - MEM_freeN(token); - } - - if (!*current) { - break; - } - - previous = current; - } - - if (!previous) { - return NULL; - } - - /* copy and strip off last token */ - i = previous - path; - result = BLI_strdup(path); - - if (i > 0 && result[i - 1] == '.') { - i--; - } - result[i] = 0; - - return result; -} - -const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop) -{ - if (array_prop != NULL) { - if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { - BLI_assert(array_prop->arraydimension == 0); - return NULL; - } - if (array_prop->arraydimension == 0) { - return NULL; - } - } - - /* Valid 'array part' of a rna path can only have '[', ']' and digit characters. - * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */ - off_t rna_path_len = (off_t)strlen(rna_path); - if (rna_path[rna_path_len] != ']') { - return NULL; - } - const char *last_valid_index_token_start = NULL; - for (rna_path_len--; rna_path_len >= 0; rna_path_len--) { - switch (rna_path[rna_path_len]) { - case '[': - if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') { - return &rna_path[rna_path_len]; - } - last_valid_index_token_start = &rna_path[rna_path_len]; - rna_path_len--; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - default: - return last_valid_index_token_start; - } - } - return last_valid_index_token_start; -} - -/* generic path search func - * if its needed this could also reference the IDProperty direct */ -typedef struct IDP_Chain { - struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */ - - const char *name; - int index; - -} IDP_Chain; - -static char *rna_idp_path_create(IDP_Chain *child_link) -{ - DynStr *dynstr = BLI_dynstr_new(); - char *path; - bool is_first = true; - - int tot = 0; - IDP_Chain *link = child_link; - - /* reverse the list */ - IDP_Chain *link_prev; - link_prev = NULL; - while (link) { - IDP_Chain *link_next = link->up; - link->up = link_prev; - link_prev = link; - link = link_next; - tot++; - } - - for (link = link_prev; link; link = link->up) { - /* pass */ - if (link->index >= 0) { - BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index); - } - else { - BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name); - } - - is_first = false; - } - - path = BLI_dynstr_get_cstring(dynstr); - BLI_dynstr_free(dynstr); - - if (*path == '\0') { - MEM_freeN(path); - path = NULL; - } - - return path; -} - -static char *rna_idp_path(PointerRNA *ptr, - IDProperty *haystack, - IDProperty *needle, - IDP_Chain *parent_link) -{ - char *path = NULL; - IDP_Chain link; - - IDProperty *iter; - int i; - - BLI_assert(haystack->type == IDP_GROUP); - - link.up = parent_link; - /* Always set both name and index, else a stale value might get used. */ - link.name = NULL; - link.index = -1; - - for (i = 0, iter = haystack->data.group.first; iter; iter = iter->next, i++) { - if (needle == iter) { /* found! */ - link.name = iter->name; - link.index = -1; - path = rna_idp_path_create(&link); - break; - } - - /* Early out in case the IDProperty type cannot contain RNA properties. */ - if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) { - continue; - } - - /* Ensure this is RNA. */ - /* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name - * collides with an actual fully static RNA property of the same struct (which would then not - * be flagged with `PROP_IDPROPERTY`). - * - * That case must be ignored here, we only want to deal with runtime RNA properties stored in - * IDProps. - * - * See T84091. */ - PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name); - if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) { - continue; - } - - if (iter->type == IDP_GROUP) { - if (prop->type == PROP_POINTER) { - PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop); - if (RNA_pointer_is_null(&child_ptr)) { - /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL - * value is perfectly valid. Just means it won't match the searched needle. */ - continue; - } - - link.name = iter->name; - link.index = -1; - if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) { - break; - } - } - } - else if (iter->type == IDP_IDPARRAY) { - if (prop->type == PROP_COLLECTION) { - IDProperty *array = IDP_IDPArray(iter); - if (needle >= array && needle < (iter->len + array)) { /* found! */ - link.name = iter->name; - link.index = (int)(needle - array); - path = rna_idp_path_create(&link); - break; - } - int j; - link.name = iter->name; - for (j = 0; j < iter->len; j++, array++) { - PointerRNA child_ptr; - if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) { - if (RNA_pointer_is_null(&child_ptr)) { - /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case - * a NULL value is perfectly valid. Just means it won't match the searched needle. */ - continue; - } - link.index = j; - if ((path = rna_idp_path(&child_ptr, array, needle, &link))) { - break; - } - } - } - if (path) { - break; - } - } - } - } - - return path; -} - -char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle) -{ - IDProperty *haystack = RNA_struct_idprops(ptr, false); - - if (haystack) { /* can fail when called on bones */ - return rna_idp_path(ptr, haystack, needle, NULL); - } - return NULL; -} - -static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr) -{ - PointerRNA id_ptr; - - BLI_assert(ptr->owner_id != NULL); - - /* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID. - * See example in T25746. - * Unless this is added only way to find this is to also search - * all bones and pose bones of an armature or object. - */ - RNA_id_pointer_create(ptr->owner_id, &id_ptr); - - return RNA_path_from_struct_to_idproperty(&id_ptr, ptr->data); -} - -ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) -{ - if (r_path) { - *r_path = ""; - } - - if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) { - return id; - } - - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - if (r_path) { - switch (GS(id->name)) { - case ID_NT: - *r_path = "node_tree"; - break; - case ID_GR: - *r_path = "collection"; - break; - default: - BLI_assert_msg(0, "Missing handling of embedded id type."); - } - } - - if (id_type->owner_get == NULL) { - BLI_assert_msg(0, "Missing handling of embedded id type."); - return id; - } - return id_type->owner_get(bmain, id); -} - -static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id) -{ - if (r_real_id != NULL) { - *r_real_id = NULL; - } - - const char *prefix; - ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix); - - if (r_real_id != NULL) { - *r_real_id = real_id; - } - - if (path != NULL) { - char *new_path = NULL; - - if (real_id) { - if (prefix[0]) { - new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path); - } - else { - return path; - } - } - - MEM_freeN(path); - return new_path; - } - return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL; -} - -char *RNA_path_from_ID_to_struct(const PointerRNA *ptr) -{ - char *ptrpath = NULL; - - if (!ptr->owner_id || !ptr->data) { - return NULL; - } - - if (!RNA_struct_is_ID(ptr->type)) { - if (ptr->type->path) { - /* if type has a path to some ID, use it */ - ptrpath = ptr->type->path((PointerRNA *)ptr); - } - else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) { - PointerRNA parentptr; - PropertyRNA *userprop; - - /* find the property in the struct we're nested in that references this struct, and - * use its identifier as the first part of the path used... - */ - RNA_id_pointer_create(ptr->owner_id, &parentptr); - userprop = RNA_struct_find_nested(&parentptr, ptr->type); - - if (userprop) { - ptrpath = BLI_strdup(RNA_property_identifier(userprop)); - } - else { - return NULL; /* can't do anything about this case yet... */ - } - } - else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) { - /* special case, easier to deal with here than in ptr->type->path() */ - return rna_path_from_ID_to_idpgroup(ptr); - } - else { - return NULL; - } - } - - return ptrpath; -} - -char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real) -{ - char *path = RNA_path_from_ID_to_struct(ptr); - - /* NULL path is valid in that case, when given struct is an ID one... */ - return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real); -} - -static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH], - const int totdims, - const int index_dim, - int index, - int r_index_multi[RNA_MAX_ARRAY_LENGTH]) -{ - int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1]; - int i = totdims - 1; - dimsize_step[i + 1] = 1; - dimsize_step[i] = dimsize[i]; - while (--i != -1) { - dimsize_step[i] = dimsize[i] * dimsize_step[i + 1]; - } - while (++i != index_dim) { - int index_round = index / dimsize_step[i + 1]; - r_index_multi[i] = index_round; - index -= (index_round * dimsize_step[i + 1]); - } - BLI_assert(index == 0); -} - -static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr, - PropertyRNA *prop, - int index_dim, - int index, - char *index_str, - int index_str_len) -{ - int dimsize[RNA_MAX_ARRAY_LENGTH]; - int totdims = RNA_property_array_dimension(ptr, prop, dimsize); - int index_multi[RNA_MAX_ARRAY_LENGTH]; - - rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi); - - for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) { - offset += BLI_snprintf_rlen( - &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]); - } -} - -char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, - PropertyRNA *prop, - int index_dim, - int index) -{ - const bool is_rna = (prop->magic == RNA_MAGIC); - const char *propname; - char *ptrpath, *path; - - if (!ptr->owner_id || !ptr->data) { - return NULL; - } - - /* path from ID to the struct holding this property */ - ptrpath = RNA_path_from_ID_to_struct(ptr); - - propname = RNA_property_identifier(prop); - - /* support indexing w/ multi-dimensional arrays */ - char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1]; - if (index_dim == 0) { - index_str[0] = '\0'; - } - else { - rna_path_array_multi_string_from_flat_index( - ptr, prop, index_dim, index, index_str, sizeof(index_str)); - } - - if (ptrpath) { - if (is_rna) { - path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str); - } - else { - char propname_esc[MAX_IDPROP_NAME * 2]; - BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); - path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str); - } - MEM_freeN(ptrpath); - } - else if (RNA_struct_is_ID(ptr->type)) { - if (is_rna) { - path = BLI_sprintfN("%s%s", propname, index_str); - } - else { - char propname_esc[MAX_IDPROP_NAME * 2]; - BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); - path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str); - } - } - else { - path = NULL; - } - - return path; -} - -char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop) -{ - return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1); -} - -char *RNA_path_from_real_ID_to_property_index(Main *bmain, - const PointerRNA *ptr, - PropertyRNA *prop, - int index_dim, - int index, - ID **r_real_id) -{ - char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index); - - /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part - * of the path either. */ - return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL; -} - -char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, - PropertyRNA *prop, - const StructRNA *type) -{ - /* Try to recursively find an "type"'d ancestor, - * to handle situations where path from ID is not enough. */ - PointerRNA idptr; - ListBase path_elems = {NULL}; - char *path = NULL; - char *full_path = RNA_path_from_ID_to_property(ptr, prop); - - if (full_path == NULL) { - return NULL; - } - - RNA_id_pointer_create(ptr->owner_id, &idptr); - - if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) { - PropertyElemRNA *prop_elem; - - for (prop_elem = path_elems.last; prop_elem; prop_elem = prop_elem->prev) { - if (RNA_struct_is_a(prop_elem->ptr.type, type)) { - char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr); - if (ref_path) { - path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */ - MEM_freeN(ref_path); - } - break; - } - } - - BLI_freelistN(&path_elems); - } - - MEM_freeN(full_path); - return path; -} - -char *RNA_path_full_ID_py(Main *bmain, ID *id) -{ - const char *path; - ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path); - - if (id_real) { - id = id_real; - } - else { - path = ""; - } - - char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4]; - if (ID_IS_LINKED(id)) { - int ofs = 0; - memcpy(lib_filepath_esc, ", \"", 3); - ofs += 3; - ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc)); - memcpy(lib_filepath_esc + ofs, "\"", 2); - } - else { - lib_filepath_esc[0] = '\0'; - } - - char id_esc[(sizeof(id->name) - 2) * 2]; - BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc)); - - return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s", - BKE_idtype_idcode_to_name_plural(GS(id->name)), - id_esc, - lib_filepath_esc, - path[0] ? "." : "", - path); -} - -char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr) -{ - char *id_path; - char *data_path; - - char *ret; - - if (!ptr->owner_id) { - return NULL; - } - - /* never fails */ - id_path = RNA_path_full_ID_py(bmain, ptr->owner_id); - - data_path = RNA_path_from_ID_to_struct(ptr); - - /* XXX data_path may be NULL (see T36788), - * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */ - ret = BLI_sprintfN("%s.%s", id_path, data_path); - - if (data_path) { - MEM_freeN(data_path); - } - MEM_freeN(id_path); - - return ret; -} - -char *RNA_path_full_property_py_ex( - Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback) -{ - char *id_path; - const char *data_delim; - const char *data_path; - bool data_path_free; - - char *ret; - - if (!ptr->owner_id) { - return NULL; - } - - /* never fails */ - id_path = RNA_path_full_ID_py(bmain, ptr->owner_id); - - data_path = RNA_path_from_ID_to_property(ptr, prop); - if (data_path) { - data_delim = (data_path[0] == '[') ? "" : "."; - data_path_free = true; - } - else { - if (use_fallback) { - /* Fuzzy fallback. Be explicit in our ignorance. */ - data_path = RNA_property_identifier(prop); - data_delim = " ... "; - } - else { - data_delim = "."; - } - data_path_free = false; - } - - if ((index == -1) || (RNA_property_array_check(prop) == false)) { - ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path); - } - else { - ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index); - } - MEM_freeN(id_path); - if (data_path_free) { - MEM_freeN((void *)data_path); - } - - return ret; -} - -char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index) -{ - return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false); -} - -char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index) -{ - char *data_path; - - char *ret; - - if (!ptr->owner_id) { - return NULL; - } - - data_path = RNA_path_from_ID_to_property(ptr, prop); - - if (data_path == NULL) { - /* This may not be an ID at all, check for simple when pointer owns property. - * TODO: more complex nested case. */ - if (!RNA_struct_is_ID(ptr->type)) { - const char *prop_identifier = RNA_property_identifier(prop); - if (RNA_struct_find_property(ptr, prop_identifier) == prop) { - data_path = BLI_strdup(prop_identifier); - } - } - } - - if ((index == -1) || (RNA_property_array_check(prop) == false)) { - ret = BLI_strdup(data_path); - } - else { - ret = BLI_sprintfN("%s[%d]", data_path, index); - } - - if (data_path) { - MEM_freeN(data_path); - } - - return ret; -} - -char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index) -{ - const bool is_rna = (prop->magic == RNA_MAGIC); - const char *propname = RNA_property_identifier(prop); - char *ret; - - if ((index == -1) || (RNA_property_array_check(prop) == false)) { - if (is_rna) { - ret = BLI_strdup(propname); - } - else { - char propname_esc[MAX_IDPROP_NAME * 2]; - BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); - ret = BLI_sprintfN("[\"%s\"]", propname_esc); - } - } - else { - if (is_rna) { - ret = BLI_sprintfN("%s[%d]", propname, index); - } - else { - char propname_esc[MAX_IDPROP_NAME * 2]; - BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); - ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index); - } - } - - return ret; -} - /* Quick name based property access */ bool RNA_boolean_get(PointerRNA *ptr, const char *name) diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 17c00923efa..d1df54df3cd 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -38,6 +38,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "rna_access_internal.h" diff --git a/source/blender/makesrna/intern/rna_access_internal.h b/source/blender/makesrna/intern/rna_access_internal.h index a7dd35af670..384ff417fc9 100644 --- a/source/blender/makesrna/intern/rna_access_internal.h +++ b/source/blender/makesrna/intern/rna_access_internal.h @@ -10,6 +10,10 @@ #include "rna_internal_types.h" +#ifdef __cplusplus +extern "C" { +#endif + struct IDProperty; struct PropertyRNAOrID; @@ -26,3 +30,9 @@ void rna_property_rna_or_id_get(PropertyRNA *prop, void rna_idproperty_touch(struct IDProperty *idprop); struct IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name); + +PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 92cdcc6d781..2a85da42483 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -23,6 +23,7 @@ #ifdef RNA_RUNTIME # include "RNA_access.h" +# include "RNA_path.h" # include "DNA_image_types.h" # include "DNA_material_types.h" diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 833060e40f8..370455302b6 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -14,6 +14,10 @@ #include "UI_resources.h" +#ifdef __cplusplus +extern "C" { +#endif + #define RNA_MAGIC ((int)~0) struct AssetLibraryReference; @@ -691,3 +695,7 @@ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values); : -FLT_MAX, double \ : -DBL_MAX) #endif + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc new file mode 100644 index 00000000000..3e0fc9add80 --- /dev/null +++ b/source/blender/makesrna/intern/rna_path.cc @@ -0,0 +1,1360 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup RNA + */ + +#include <cstdlib> +#include <stdlib.h> +#include <string.h> + +#include "BLI_alloca.h" +#include "BLI_dynstr.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_idprop.h" +#include "BKE_idtype.h" + +#include "DNA_ID.h" /* For ID properties. */ + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_path.h" +#include "RNA_prototypes.h" + +#include "rna_access_internal.h" +#include "rna_internal.h" + +/** + * Extract the first token from `path`. + * + * \param path: Extract the token from path, step the pointer to the beginning of the next token + * \return The nil terminated token. + */ +static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen) +{ + int len = 0; + + /* Get data until `.` or `[`. */ + const char *p = *path; + while (*p && !ELEM(*p, '.', '[')) { + len++; + p++; + } + + /* Empty, return. */ + if (UNLIKELY(len == 0)) { + return NULL; + } + + /* Try to use fixed buffer if possible. */ + char *buf = (len + 1 < fixedlen) ? fixedbuf : + (char *)MEM_mallocN(sizeof(char) * (len + 1), __func__); + memcpy(buf, *path, sizeof(char) * len); + buf[len] = '\0'; + + if (*p == '.') { + p++; + } + *path = p; + + return buf; +} + +/** + * Extract the first token in brackets from `path` (with quoted text support). + * + * - `[0]` -> `0` + * - `["Some\"Quote"]` -> `Some"Quote` + * + * \param path: Extract the token from path, step the pointer to the beginning of the next token + * (past quoted text and brackets). + * \return The nil terminated token. + */ +static char *rna_path_token_in_brackets(const char **path, + char *fixedbuf, + int fixedlen, + bool *r_quoted) +{ + int len = 0; + bool quoted = false; + + BLI_assert(r_quoted != NULL); + + /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */ + if (UNLIKELY(**path != '[')) { + return NULL; + } + + (*path)++; + const char *p = *path; + + /* 2 kinds of look-ups now, quoted or unquoted. */ + if (*p == '"') { + /* Find the matching quote. */ + (*path)++; + p = *path; + const char *p_end = BLI_str_escape_find_quote(p); + if (p_end == NULL) { + /* No Matching quote. */ + return NULL; + } + /* Exclude the last quote from the length. */ + len += (p_end - p); + + /* Skip the last quoted char to get the `]`. */ + p_end += 1; + p = p_end; + quoted = true; + } + else { + /* Find the matching bracket. */ + while (*p && (*p != ']')) { + len++; + p++; + } + } + + if (UNLIKELY(*p != ']')) { + return NULL; + } + + /* Empty, return. */ + if (UNLIKELY(len == 0)) { + return NULL; + } + + /* Try to use fixed buffer if possible. */ + char *buf = (len + 1 < fixedlen) ? fixedbuf : + (char *)MEM_mallocN(sizeof(char) * (len + 1), __func__); + + /* Copy string, taking into account escaped ']' */ + if (quoted) { + BLI_str_unescape(buf, *path, len); + /* +1 to step over the last quote. */ + BLI_assert((*path)[len] == '"'); + p = (*path) + len + 1; + } + else { + memcpy(buf, *path, sizeof(char) * len); + buf[len] = '\0'; + } + /* Set path to start of next token. */ + if (*p == ']') { + p++; + } + if (*p == '.') { + p++; + } + *path = p; + + *r_quoted = quoted; + + return buf; +} + +/** + * \return true when the key in the path is correctly parsed and found in the collection + * or when the path is empty. + */ +static bool rna_path_parse_collection_key(const char **path, + PointerRNA *ptr, + PropertyRNA *prop, + PointerRNA *r_nextptr) +{ + char fixedbuf[256]; + int intkey; + + *r_nextptr = *ptr; + + /* end of path, ok */ + if (!(**path)) { + return true; + } + + bool found = false; + if (**path == '[') { + bool quoted; + char *token; + + /* resolve the lookup with [] brackets */ + token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); + + if (!token) { + return false; + } + + /* check for "" to see if it is a string */ + if (quoted) { + if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) { + found = true; + } + else { + r_nextptr->data = NULL; + } + } + else { + /* otherwise do int lookup */ + intkey = atoi(token); + if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) { + return false; /* we can be sure the fixedbuf was used in this case */ + } + if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) { + found = true; + } + else { + r_nextptr->data = NULL; + } + } + + if (token != fixedbuf) { + MEM_freeN(token); + } + } + else { + if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) { + found = true; + } + else { + /* ensure we quit on invalid values */ + r_nextptr->data = NULL; + } + } + + return found; +} + +static bool rna_path_parse_array_index(const char **path, + PointerRNA *ptr, + PropertyRNA *prop, + int *r_index) +{ + char fixedbuf[256]; + int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0}; + int len[RNA_MAX_ARRAY_DIMENSION]; + const int dim = RNA_property_array_dimension(ptr, prop, len); + int i; + + *r_index = -1; + + /* end of path, ok */ + if (!(**path)) { + return true; + } + + for (i = 0; i < dim; i++) { + int temp_index = -1; + char *token; + + /* multi index resolve */ + if (**path == '[') { + bool quoted; + token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); + + if (token == NULL) { + /* invalid syntax blah[] */ + return false; + } + /* check for "" to see if it is a string */ + if (quoted) { + temp_index = RNA_property_array_item_index(prop, *token); + } + else { + /* otherwise do int lookup */ + temp_index = atoi(token); + + if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) { + if (token != fixedbuf) { + MEM_freeN(token); + } + + return false; + } + } + } + else if (dim == 1) { + /* location.x || scale.X, single dimension arrays only */ + token = rna_path_token(path, fixedbuf, sizeof(fixedbuf)); + if (token == NULL) { + /* invalid syntax blah. */ + return false; + } + temp_index = RNA_property_array_item_index(prop, *token); + } + else { + /* just to avoid uninitialized pointer use */ + token = fixedbuf; + } + + if (token != fixedbuf) { + MEM_freeN(token); + } + + /* out of range */ + if (temp_index < 0 || temp_index >= len[i]) { + return false; + } + + index_arr[i] = temp_index; + /* end multi index resolve */ + } + + /* arrays always contain numbers so further values are not valid */ + if (**path) { + return false; + } + + /* flatten index over all dimensions */ + { + int totdim = 1; + int flat_index = 0; + + for (i = dim - 1; i >= 0; i--) { + flat_index += index_arr[i] * totdim; + totdim *= len[i]; + } + + *r_index = flat_index; + } + return true; +} + +/** + * Generic rna path parser. + * + * \note All parameters besides \a ptr and \a path are optional. + * + * \param ptr: The root of given RNA path. + * \param path: The RNA path. + * \param r_ptr: The final RNA data holding the last property in \a path. + * \param r_prop: The final property of \a r_ptr, from \a path. + * \param r_index: The final index in the \a r_prop, if defined by \a path. + * \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the + * pointer, or of the collection item. + * Mutually exclusive with \a eval_pointer option. + * \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA + * that represent the whole given \a path). + * \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a + * Collection property, \a r_ptr will be set to the value of that property, + * and \a r_prop will be NULL. + * Mutually exclusive with \a r_item_ptr. + * + * \return \a true on success, \a false if the path is somehow invalid. + */ +static bool rna_path_parse(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index, + PointerRNA *r_item_ptr, + ListBase *r_elements, + const bool eval_pointer) +{ + BLI_assert(r_item_ptr == NULL || !eval_pointer); + PropertyRNA *prop; + PointerRNA curptr, nextptr; + PropertyElemRNA *prop_elem = NULL; + int index = -1; + char fixedbuf[256]; + int type; + const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer; + + if (do_item_ptr) { + RNA_POINTER_INVALIDATE(&nextptr); + } + + prop = NULL; + curptr = *ptr; + + if (path == NULL || *path == '\0') { + return false; + } + + while (*path) { + if (do_item_ptr) { + RNA_POINTER_INVALIDATE(&nextptr); + } + + const bool use_id_prop = (*path == '['); + /* custom property lookup ? + * C.object["someprop"] + */ + + if (!curptr.data) { + return false; + } + + /* look up property name in current struct */ + bool quoted = false; + char *token = use_id_prop ? + rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), "ed) : + rna_path_token(&path, fixedbuf, sizeof(fixedbuf)); + if (!token) { + return false; + } + + prop = NULL; + if (use_id_prop) { /* look up property name in current struct */ + IDProperty *group = RNA_struct_idprops(&curptr, 0); + if (group && quoted) { + prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token); + } + } + else { + prop = RNA_struct_find_property(&curptr, token); + } + + if (token != fixedbuf) { + MEM_freeN(token); + } + + if (!prop) { + return false; + } + + if (r_elements) { + prop_elem = MEM_cnew<PropertyElemRNA>(__func__); + prop_elem->ptr = curptr; + prop_elem->prop = prop; + prop_elem->index = -1; /* index will be added later, if needed. */ + BLI_addtail(r_elements, prop_elem); + } + + type = RNA_property_type(prop); + + /* now look up the value of this property if it is a pointer or + * collection, otherwise return the property rna so that the + * caller can read the value of the property itself */ + switch (type) { + case PROP_POINTER: { + /* resolve pointer if further path elements follow + * or explicitly requested + */ + if (do_item_ptr || eval_pointer || *path != '\0') { + nextptr = RNA_property_pointer_get(&curptr, prop); + } + + if (eval_pointer || *path != '\0') { + curptr = nextptr; + prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */ + index = -1; + } + break; + } + case PROP_COLLECTION: { + /* Resolve pointer if further path elements follow. + * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway, + * so do_item_ptr is of no use in that case. + */ + if (*path) { + if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) { + return false; + } + + if (eval_pointer || *path != '\0') { + curptr = nextptr; + prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */ + index = -1; + } + } + break; + } + default: + if (r_index || prop_elem) { + if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) { + return false; + } + + if (prop_elem) { + prop_elem->index = index; + } + } + break; + } + } + + if (r_ptr) { + *r_ptr = curptr; + } + if (r_prop) { + *r_prop = prop; + } + if (r_index) { + *r_index = index; + } + if (r_item_ptr && do_item_ptr) { + *r_item_ptr = nextptr; + } + + if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop || + prop_elem->index != index)) { + prop_elem = MEM_cnew<PropertyElemRNA>(__func__); + prop_elem->ptr = curptr; + prop_elem->prop = prop; + prop_elem->index = index; + BLI_addtail(r_elements, prop_elem); + } + + return true; +} + +bool RNA_path_resolve(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop) +{ + if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) { + return false; + } + + return r_ptr->data != NULL; +} + +bool RNA_path_resolve_full( + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) +{ + if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) { + return false; + } + + return r_ptr->data != NULL; +} + +bool RNA_path_resolve_full_maybe_null( + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) +{ + return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true); +} + +bool RNA_path_resolve_property(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop) +{ + if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) { + return false; + } + + return r_ptr->data != NULL && *r_prop != NULL; +} + +bool RNA_path_resolve_property_full( + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) +{ + if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) { + return false; + } + + return r_ptr->data != NULL && *r_prop != NULL; +} + +bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + PointerRNA *r_item_ptr) +{ + if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) { + return false; + } + + return r_ptr->data != NULL && *r_prop != NULL; +} + +bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index, + PointerRNA *r_item_ptr) +{ + if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false)) { + return false; + } + + return r_ptr->data != NULL && *r_prop != NULL; +} +bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements) +{ + return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false); +} + +char *RNA_path_append(const char *path, + const PointerRNA *UNUSED(ptr), + PropertyRNA *prop, + int intkey, + const char *strkey) +{ + DynStr *dynstr; + char *result; + + dynstr = BLI_dynstr_new(); + + /* add .identifier */ + if (path) { + BLI_dynstr_append(dynstr, path); + if (*path) { + BLI_dynstr_append(dynstr, "."); + } + } + + BLI_dynstr_append(dynstr, RNA_property_identifier(prop)); + + if (RNA_property_type(prop) == PROP_COLLECTION) { + /* add ["strkey"] or [intkey] */ + BLI_dynstr_append(dynstr, "["); + + if (strkey) { + const int strkey_esc_max_size = (strlen(strkey) * 2) + 1; + char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size); + BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size); + BLI_dynstr_append(dynstr, "\""); + BLI_dynstr_append(dynstr, strkey_esc); + BLI_dynstr_append(dynstr, "\""); + } + else { + char appendstr[128]; + BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey); + BLI_dynstr_append(dynstr, appendstr); + } + + BLI_dynstr_append(dynstr, "]"); + } + + result = BLI_dynstr_get_cstring(dynstr); + BLI_dynstr_free(dynstr); + + return result; +} + +/* Having both path append & back seems like it could be useful, + * this function isn't used at the moment. */ +static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path) +{ + char fixedbuf[256]; + const char *previous, *current; + char *result; + int i; + + if (!path) { + return NULL; + } + + previous = NULL; + current = path; + + /* parse token by token until the end, then we back up to the previous + * position and strip of the next token to get the path one step back */ + while (*current) { + char *token; + + token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf)); + + if (!token) { + return NULL; + } + if (token != fixedbuf) { + MEM_freeN(token); + } + + /* in case of collection we also need to strip off [] */ + bool quoted; + token = rna_path_token_in_brackets(¤t, fixedbuf, sizeof(fixedbuf), "ed); + if (token && token != fixedbuf) { + MEM_freeN(token); + } + + if (!*current) { + break; + } + + previous = current; + } + + if (!previous) { + return NULL; + } + + /* copy and strip off last token */ + i = previous - path; + result = BLI_strdup(path); + + if (i > 0 && result[i - 1] == '.') { + i--; + } + result[i] = 0; + + return result; +} + +const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop) +{ + if (array_prop != NULL) { + if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { + BLI_assert(array_prop->arraydimension == 0); + return NULL; + } + if (array_prop->arraydimension == 0) { + return NULL; + } + } + + /* Valid 'array part' of a rna path can only have '[', ']' and digit characters. + * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */ + off_t rna_path_len = (off_t)strlen(rna_path); + if (rna_path[rna_path_len] != ']') { + return NULL; + } + const char *last_valid_index_token_start = NULL; + for (rna_path_len--; rna_path_len >= 0; rna_path_len--) { + switch (rna_path[rna_path_len]) { + case '[': + if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') { + return &rna_path[rna_path_len]; + } + last_valid_index_token_start = &rna_path[rna_path_len]; + rna_path_len--; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + return last_valid_index_token_start; + } + } + return last_valid_index_token_start; +} + +/* generic path search func + * if its needed this could also reference the IDProperty direct */ +typedef struct IDP_Chain { + struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */ + + const char *name; + int index; + +} IDP_Chain; + +static char *rna_idp_path_create(IDP_Chain *child_link) +{ + DynStr *dynstr = BLI_dynstr_new(); + char *path; + bool is_first = true; + + int tot = 0; + IDP_Chain *link = child_link; + + /* reverse the list */ + IDP_Chain *link_prev; + link_prev = NULL; + while (link) { + IDP_Chain *link_next = link->up; + link->up = link_prev; + link_prev = link; + link = link_next; + tot++; + } + + for (link = link_prev; link; link = link->up) { + /* pass */ + if (link->index >= 0) { + BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index); + } + else { + BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name); + } + + is_first = false; + } + + path = BLI_dynstr_get_cstring(dynstr); + BLI_dynstr_free(dynstr); + + if (*path == '\0') { + MEM_freeN(path); + path = NULL; + } + + return path; +} + +static char *rna_idp_path(PointerRNA *ptr, + IDProperty *haystack, + IDProperty *needle, + IDP_Chain *parent_link) +{ + char *path = NULL; + IDP_Chain link; + + IDProperty *iter; + int i; + + BLI_assert(haystack->type == IDP_GROUP); + + link.up = parent_link; + /* Always set both name and index, else a stale value might get used. */ + link.name = NULL; + link.index = -1; + + for (i = 0, iter = reinterpret_cast<IDProperty *>(haystack->data.group.first); iter; + iter = iter->next, i++) { + if (needle == iter) { /* found! */ + link.name = iter->name; + link.index = -1; + path = rna_idp_path_create(&link); + break; + } + + /* Early out in case the IDProperty type cannot contain RNA properties. */ + if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) { + continue; + } + + /* Ensure this is RNA. */ + /* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name + * collides with an actual fully static RNA property of the same struct (which would then not + * be flagged with `PROP_IDPROPERTY`). + * + * That case must be ignored here, we only want to deal with runtime RNA properties stored in + * IDProps. + * + * See T84091. */ + PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name); + if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) { + continue; + } + + if (iter->type == IDP_GROUP) { + if (prop->type == PROP_POINTER) { + PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop); + if (RNA_pointer_is_null(&child_ptr)) { + /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL + * value is perfectly valid. Just means it won't match the searched needle. */ + continue; + } + + link.name = iter->name; + link.index = -1; + if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) { + break; + } + } + } + else if (iter->type == IDP_IDPARRAY) { + if (prop->type == PROP_COLLECTION) { + IDProperty *array = IDP_IDPArray(iter); + if (needle >= array && needle < (iter->len + array)) { /* found! */ + link.name = iter->name; + link.index = (int)(needle - array); + path = rna_idp_path_create(&link); + break; + } + int j; + link.name = iter->name; + for (j = 0; j < iter->len; j++, array++) { + PointerRNA child_ptr; + if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) { + if (RNA_pointer_is_null(&child_ptr)) { + /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case + * a NULL value is perfectly valid. Just means it won't match the searched needle. */ + continue; + } + link.index = j; + if ((path = rna_idp_path(&child_ptr, array, needle, &link))) { + break; + } + } + } + if (path) { + break; + } + } + } + } + + return path; +} + +char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle) +{ + IDProperty *haystack = RNA_struct_idprops(ptr, false); + + if (haystack) { /* can fail when called on bones */ + return rna_idp_path(ptr, haystack, needle, NULL); + } + return NULL; +} + +static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr) +{ + PointerRNA id_ptr; + + BLI_assert(ptr->owner_id != NULL); + + /* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID. + * See example in T25746. + * Unless this is added only way to find this is to also search + * all bones and pose bones of an armature or object. + */ + RNA_id_pointer_create(ptr->owner_id, &id_ptr); + + return RNA_path_from_struct_to_idproperty(&id_ptr, reinterpret_cast<IDProperty *>(ptr->data)); +} + +ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) +{ + if (r_path) { + *r_path = ""; + } + + if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) { + return id; + } + + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (r_path) { + switch (GS(id->name)) { + case ID_NT: + *r_path = "node_tree"; + break; + case ID_GR: + *r_path = "collection"; + break; + default: + BLI_assert_msg(0, "Missing handling of embedded id type."); + } + } + + if (id_type->owner_get == NULL) { + BLI_assert_msg(0, "Missing handling of embedded id type."); + return id; + } + return id_type->owner_get(bmain, id); +} + +static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id) +{ + if (r_real_id != NULL) { + *r_real_id = NULL; + } + + const char *prefix; + ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix); + + if (r_real_id != NULL) { + *r_real_id = real_id; + } + + if (path != NULL) { + char *new_path = NULL; + + if (real_id) { + if (prefix[0]) { + new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path); + } + else { + return path; + } + } + + MEM_freeN(path); + return new_path; + } + return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL; +} + +char *RNA_path_from_ID_to_struct(const PointerRNA *ptr) +{ + char *ptrpath = NULL; + + if (!ptr->owner_id || !ptr->data) { + return NULL; + } + + if (!RNA_struct_is_ID(ptr->type)) { + if (ptr->type->path) { + /* if type has a path to some ID, use it */ + ptrpath = ptr->type->path((PointerRNA *)ptr); + } + else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) { + PointerRNA parentptr; + PropertyRNA *userprop; + + /* find the property in the struct we're nested in that references this struct, and + * use its identifier as the first part of the path used... + */ + RNA_id_pointer_create(ptr->owner_id, &parentptr); + userprop = rna_struct_find_nested(&parentptr, ptr->type); + + if (userprop) { + ptrpath = BLI_strdup(RNA_property_identifier(userprop)); + } + else { + return NULL; /* can't do anything about this case yet... */ + } + } + else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) { + /* special case, easier to deal with here than in ptr->type->path() */ + return rna_path_from_ID_to_idpgroup(ptr); + } + else { + return NULL; + } + } + + return ptrpath; +} + +char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real) +{ + char *path = RNA_path_from_ID_to_struct(ptr); + + /* NULL path is valid in that case, when given struct is an ID one... */ + return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real); +} + +static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH], + const int totdims, + const int index_dim, + int index, + int r_index_multi[RNA_MAX_ARRAY_LENGTH]) +{ + int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1]; + int i = totdims - 1; + dimsize_step[i + 1] = 1; + dimsize_step[i] = dimsize[i]; + while (--i != -1) { + dimsize_step[i] = dimsize[i] * dimsize_step[i + 1]; + } + while (++i != index_dim) { + int index_round = index / dimsize_step[i + 1]; + r_index_multi[i] = index_round; + index -= (index_round * dimsize_step[i + 1]); + } + BLI_assert(index == 0); +} + +static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index, + char *index_str, + int index_str_len) +{ + int dimsize[RNA_MAX_ARRAY_LENGTH]; + int totdims = RNA_property_array_dimension(ptr, prop, dimsize); + int index_multi[RNA_MAX_ARRAY_LENGTH]; + + rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi); + + for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) { + offset += BLI_snprintf_rlen( + &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]); + } +} + +char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index) +{ + const bool is_rna = (prop->magic == RNA_MAGIC); + const char *propname; + char *ptrpath, *path; + + if (!ptr->owner_id || !ptr->data) { + return NULL; + } + + /* path from ID to the struct holding this property */ + ptrpath = RNA_path_from_ID_to_struct(ptr); + + propname = RNA_property_identifier(prop); + + /* support indexing w/ multi-dimensional arrays */ + char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1]; + if (index_dim == 0) { + index_str[0] = '\0'; + } + else { + rna_path_array_multi_string_from_flat_index( + ptr, prop, index_dim, index, index_str, sizeof(index_str)); + } + + if (ptrpath) { + if (is_rna) { + path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str); + } + else { + char propname_esc[MAX_IDPROP_NAME * 2]; + BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); + path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str); + } + MEM_freeN(ptrpath); + } + else if (RNA_struct_is_ID(ptr->type)) { + if (is_rna) { + path = BLI_sprintfN("%s%s", propname, index_str); + } + else { + char propname_esc[MAX_IDPROP_NAME * 2]; + BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); + path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str); + } + } + else { + path = NULL; + } + + return path; +} + +char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop) +{ + return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1); +} + +char *RNA_path_from_real_ID_to_property_index(Main *bmain, + const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index, + ID **r_real_id) +{ + char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index); + + /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part + * of the path either. */ + return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL; +} + +char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, + PropertyRNA *prop, + const StructRNA *type) +{ + /* Try to recursively find an "type"'d ancestor, + * to handle situations where path from ID is not enough. */ + PointerRNA idptr; + ListBase path_elems = {NULL}; + char *path = NULL; + char *full_path = RNA_path_from_ID_to_property(ptr, prop); + + if (full_path == NULL) { + return NULL; + } + + RNA_id_pointer_create(ptr->owner_id, &idptr); + + if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) { + LISTBASE_FOREACH_BACKWARD (PropertyElemRNA *, prop_elem, &path_elems) { + if (RNA_struct_is_a(prop_elem->ptr.type, type)) { + char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr); + if (ref_path) { + path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */ + MEM_freeN(ref_path); + } + break; + } + } + + BLI_freelistN(&path_elems); + } + + MEM_freeN(full_path); + return path; +} + +char *RNA_path_full_ID_py(Main *bmain, ID *id) +{ + const char *path; + ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path); + + if (id_real) { + id = id_real; + } + else { + path = ""; + } + + char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4]; + if (ID_IS_LINKED(id)) { + int ofs = 0; + memcpy(lib_filepath_esc, ", \"", 3); + ofs += 3; + ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc)); + memcpy(lib_filepath_esc + ofs, "\"", 2); + } + else { + lib_filepath_esc[0] = '\0'; + } + + char id_esc[(sizeof(id->name) - 2) * 2]; + BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc)); + + return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s", + BKE_idtype_idcode_to_name_plural(GS(id->name)), + id_esc, + lib_filepath_esc, + path[0] ? "." : "", + path); +} + +char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr) +{ + char *id_path; + char *data_path; + + char *ret; + + if (!ptr->owner_id) { + return NULL; + } + + /* never fails */ + id_path = RNA_path_full_ID_py(bmain, ptr->owner_id); + + data_path = RNA_path_from_ID_to_struct(ptr); + + /* XXX data_path may be NULL (see T36788), + * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */ + ret = BLI_sprintfN("%s.%s", id_path, data_path); + + if (data_path) { + MEM_freeN(data_path); + } + MEM_freeN(id_path); + + return ret; +} + +char *RNA_path_full_property_py_ex( + Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback) +{ + char *id_path; + const char *data_delim; + const char *data_path; + bool data_path_free; + + char *ret; + + if (!ptr->owner_id) { + return NULL; + } + + /* never fails */ + id_path = RNA_path_full_ID_py(bmain, ptr->owner_id); + + data_path = RNA_path_from_ID_to_property(ptr, prop); + if (data_path) { + data_delim = (data_path[0] == '[') ? "" : "."; + data_path_free = true; + } + else { + if (use_fallback) { + /* Fuzzy fallback. Be explicit in our ignorance. */ + data_path = RNA_property_identifier(prop); + data_delim = " ... "; + } + else { + data_delim = "."; + } + data_path_free = false; + } + + if ((index == -1) || (RNA_property_array_check(prop) == false)) { + ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path); + } + else { + ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index); + } + MEM_freeN(id_path); + if (data_path_free) { + MEM_freeN((void *)data_path); + } + + return ret; +} + +char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index) +{ + return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false); +} + +char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index) +{ + char *data_path; + + char *ret; + + if (!ptr->owner_id) { + return NULL; + } + + data_path = RNA_path_from_ID_to_property(ptr, prop); + + if (data_path == NULL) { + /* This may not be an ID at all, check for simple when pointer owns property. + * TODO: more complex nested case. */ + if (!RNA_struct_is_ID(ptr->type)) { + const char *prop_identifier = RNA_property_identifier(prop); + if (RNA_struct_find_property(ptr, prop_identifier) == prop) { + data_path = BLI_strdup(prop_identifier); + } + } + } + + if ((index == -1) || (RNA_property_array_check(prop) == false)) { + ret = BLI_strdup(data_path); + } + else { + ret = BLI_sprintfN("%s[%d]", data_path, index); + } + + if (data_path) { + MEM_freeN(data_path); + } + + return ret; +} + +char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index) +{ + const bool is_rna = (prop->magic == RNA_MAGIC); + const char *propname = RNA_property_identifier(prop); + char *ret; + + if ((index == -1) || (RNA_property_array_check(prop) == false)) { + if (is_rna) { + ret = BLI_strdup(propname); + } + else { + char propname_esc[MAX_IDPROP_NAME * 2]; + BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); + ret = BLI_sprintfN("[\"%s\"]", propname_esc); + } + } + else { + if (is_rna) { + ret = BLI_sprintfN("%s[%d]", propname, index); + } + else { + char propname_esc[MAX_IDPROP_NAME * 2]; + BLI_str_escape(propname_esc, propname, sizeof(propname_esc)); + ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index); + } + } + + return ret; +} diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index dabb89bcd5e..adb959944b5 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -473,7 +473,7 @@ static int rna_UIList_list_id_length(PointerRNA *ptr) } static void uilist_draw_item(uiList *ui_list, - bContext *C, + const bContext *C, uiLayout *layout, PointerRNA *dataptr, PointerRNA *itemptr, @@ -507,7 +507,7 @@ static void uilist_draw_item(uiList *ui_list, RNA_parameter_list_free(&list); } -static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout) +static void uilist_draw_filter(uiList *ui_list, const bContext *C, uiLayout *layout) { extern FunctionRNA rna_UIList_draw_filter_func; @@ -527,7 +527,7 @@ static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout) } static void uilist_filter_items(uiList *ui_list, - bContext *C, + const bContext *C, PointerRNA *dataptr, const char *propname) { diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 223e4b757b7..72978d6410a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -40,7 +40,7 @@ #include "BKE_geometry_fields.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_global.h" -#include "BKE_idprop.h" +#include "BKE_idprop.hh" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -416,15 +416,16 @@ static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int soc return field_interface.inputs[socket_index] != InputSocketFieldType::None; } -static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) +static std::unique_ptr<IDProperty, blender::bke::idprop::IDPropertyDeleter> +id_property_create_from_socket(const bNodeSocket &socket) { + using namespace blender; switch (socket.type) { case SOCK_FLOAT: { - bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.f = value->value; - IDProperty *property = IDP_New(IDP_FLOAT, &idprop, socket.identifier); - IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, value->value); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->min = ui_data->soft_min = (double)value->min; ui_data->max = ui_data->soft_max = (double)value->max; @@ -432,11 +433,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_INT: { - bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value; - IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier); - IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property); + const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, value->value); + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->min = ui_data->soft_min = value->min; ui_data->max = ui_data->soft_max = value->max; @@ -444,13 +444,11 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_VECTOR: { - bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.array.len = 3; - idprop.array.type = IDP_FLOAT; - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); - copy_v3_v3((float *)IDP_Array(property), value->value); - IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>( + socket.default_value); + auto property = bke::idprop::create( + socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]}); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->min = ui_data->soft_min = (double)value->min; ui_data->max = ui_data->soft_max = (double)value->max; @@ -462,13 +460,12 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_RGBA: { - bNodeSocketValueRGBA *value = (bNodeSocketValueRGBA *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.array.len = 4; - idprop.array.type = IDP_FLOAT; - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); - copy_v4_v4((float *)IDP_Array(property), value->value); - IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>( + socket.default_value); + auto property = bke::idprop::create( + socket.identifier, + Span<float>{value->value[0], value->value[1], value->value[2], value->value[3]}); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = PROP_COLOR; ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__); ui_data->default_array_len = 4; @@ -482,53 +479,48 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_BOOLEAN: { - bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value != 0; - IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier); - IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property); + const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, int(value->value)); + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get()); ui_data->min = ui_data->soft_min = 0; ui_data->max = ui_data->soft_max = 1; ui_data->default_value = value->value != 0; return property; } case SOCK_STRING: { - bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value; - IDProperty *property = IDP_NewString( - value->value, socket.identifier, BLI_strnlen(value->value, sizeof(value->value)) + 1); - IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(property); + const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, value->value); + IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure( + property.get()); ui_data->default_value = BLI_strdup(value->value); return property; } case SOCK_OBJECT: { - bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_COLLECTION: { - bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueCollection *value = static_cast<const bNodeSocketValueCollection *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_TEXTURE: { - bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_IMAGE: { - bNodeSocketValueImage *value = (bNodeSocketValueImage *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_MATERIAL: { - bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } } return nullptr; @@ -658,7 +650,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) int socket_index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { - IDProperty *new_prop = id_property_create_from_socket(*socket); + IDProperty *new_prop = id_property_create_from_socket(*socket).release(); if (new_prop == nullptr) { /* Out of the set of supported input sockets, only * geometry sockets aren't added to the modifier. */ diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e43d2b4ad85..5cf4e21ea68 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1863,6 +1863,7 @@ bool NodeParamsProvider::lazy_require_input(StringRef identifier) void NodeParamsProvider::set_input_unused(StringRef identifier) { + BLI_assert(node_supports_laziness(this->dnode)); const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 32b5d98a556..6efe6f231f5 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -183,6 +183,7 @@ void register_node_tree_type_cmp() tt->type = NTREE_COMPOSIT; strcpy(tt->idname, "CompositorNodeTree"); + strcpy(tt->group_idname, "CompositorNodeGroup"); strcpy(tt->ui_name, N_("Compositor")); tt->ui_icon = ICON_NODE_COMPOSITING; strcpy(tt->ui_description, N_("Compositing nodes")); diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 38e914b9a9f..e3998322741 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -109,6 +109,7 @@ void register_node_tree_type_geo() MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type")); tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); + strcpy(tt->group_idname, "GeometryNodeGroup"); strcpy(tt->ui_name, N_("Geometry Node Editor")); tt->ui_icon = ICON_GEOMETRY_NODES; strcpy(tt->ui_description, N_("Geometry nodes")); diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 43dbf5060bd..02ac54f4b2b 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -166,6 +166,7 @@ void register_node_tree_type_sh() tt->type = NTREE_SHADER; strcpy(tt->idname, "ShaderNodeTree"); + strcpy(tt->group_idname, "ShaderNodeGroup"); strcpy(tt->ui_name, N_("Shader Editor")); tt->ui_icon = ICON_NODE_MATERIAL; strcpy(tt->ui_description, N_("Shader nodes")); diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 03dc61af9a2..ac105b5bcb9 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -140,6 +140,7 @@ void register_node_tree_type_tex(void) tt->type = NTREE_TEXTURE; strcpy(tt->idname, "TextureNodeTree"); + strcpy(tt->group_idname, "TextureNodeGroup"); strcpy(tt->ui_name, N_("Texture Node Editor")); tt->ui_icon = ICON_NODE_TEXTURE; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Texture nodes")); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 14be9ceda94..d9c004fb6fa 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -15,6 +15,7 @@ #include <float.h> /* FLT_MIN/MAX */ #include <stddef.h> +#include "RNA_path.h" #include "RNA_types.h" #include "BLI_bitmap.h" diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index f25e9d0bbbc..d4a164d9482 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -32,6 +32,7 @@ #include "RNA_access.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 315e4c994ad..1dc2307ba14 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -81,6 +81,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "UI_interface.h" diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c index be153195cee..5c745c32d2c 100644 --- a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c +++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c @@ -20,6 +20,7 @@ #include "message_bus/intern/wm_message_bus_intern.h" #include "RNA_access.h" +#include "RNA_path.h" /* -------------------------------------------------------------------- */ /** \name Internal Utilities diff --git a/source/creator/creator.c b/source/creator/creator.c index 6c95ee3e490..e7a803d383f 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -41,7 +41,6 @@ #include "BKE_global.h" #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" -#include "BKE_image.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_modifier.h" |