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

This commit is contained in:
Hans Goudey 2022-12-12 21:58:24 -06:00
commit 8dff7bb4eb
113 changed files with 2122 additions and 888 deletions

View File

@ -12,7 +12,6 @@ if(UNIX)
automake
bison
${_libtoolize_name}
meson
ninja
pkg-config
tclsh

View File

@ -26,5 +26,6 @@ endif()
add_dependencies(
external_epoxy
# Needed for `MESON`.
external_python_site_packages
)

View File

@ -18,6 +18,7 @@ ExternalProject_Add(external_fribidi
add_dependencies(
external_fribidi
external_python
# Needed for `MESON`.
external_python_site_packages
)

View File

@ -5,7 +5,7 @@ if(WIN32)
set(HARFBUZZ_PKG_ENV FREETYPE_DIR=${LIBDIR}/freetype)
else()
set(HARFBUZZ_CONFIGURE_ENV ${CONFIGURE_ENV})
set(HARFBUZZ_PKG_ENV PKG_CONFIG_PATH=${LIBDIR}/freetype/lib/pkgconfig:${LIBDIR}/brotli/lib/pkgconfig:$PKG_CONFIG_PATH)
set(HARFBUZZ_PKG_ENV PKG_CONFIG_PATH=${LIBDIR}/freetype/lib/pkgconfig:${LIBDIR}/brotli/lib/pkgconfig:${LIBDIR}/lib/python3.10/pkgconfig:$PKG_CONFIG_PATH)
endif()
set(HARFBUZZ_EXTRA_OPTIONS
@ -13,6 +13,9 @@ set(HARFBUZZ_EXTRA_OPTIONS
-Dfreetype=enabled
-Dglib=disabled
-Dgobject=disabled
# Only used for command line utilities,
# disable as this would add an addition & unnecessary build-dependency.
-Dcairo=disabled
)
ExternalProject_Add(external_harfbuzz
@ -30,6 +33,7 @@ ExternalProject_Add(external_harfbuzz
add_dependencies(
external_harfbuzz
external_python
# Needed for `MESON`.
external_python_site_packages
)

View File

@ -268,6 +268,10 @@ harvest(haru/include haru/include "*.h")
harvest(haru/lib haru/lib "*.a")
harvest(zstd/include zstd/include "*.h")
harvest(zstd/lib zstd/lib "*.a")
harvest(shaderc shaderc "*")
harvest(vulkan_headers vulkan "*")
harvest_rpath_lib(vulkan_loader/lib vulkan/lib "*${SHAREDLIBEXT}*")
harvest(vulkan_loader/loader vulkan/loader "*")
if(UNIX AND NOT APPLE)
harvest(libglu/lib mesa/lib "*${SHAREDLIBEXT}*")

View File

@ -33,6 +33,8 @@ set(MESA_EXTRA_FLAGS
# At some point we will likely want to support Wayland.
# Disable for now since it's not officially supported.
-Dplatforms=x11
# Needed to find the local expat.
--pkg-config-path=${LIBDIR}/expat/lib/pkgconfig
--native-file ${BUILD_DIR}/mesa/tmp/native-file.ini
)
@ -53,4 +55,8 @@ add_dependencies(
external_mesa
ll
external_zlib
# Run-time dependency.
external_expat
# Needed for `MESON`.
external_python_site_packages
)

View File

@ -117,7 +117,7 @@ else()
set(LIBEXT ".a")
set(LIBPREFIX "lib")
set(MESON ${LIBDIR}/python/bin/meson)
if(APPLE)
if(APPLE)
set(SHAREDLIBEXT ".dylib")
# Use same Xcode detection as Blender itself.

View File

@ -5,7 +5,11 @@ if(WIN32 AND BUILD_MODE STREQUAL Debug)
# zstandard is determined to build and link release mode libs in a debug
# configuration, the only way to make it happy is to bend to its will
# and give it a library to link with.
set(PIP_CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}_d.lib ${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib)
set(
PIP_CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}_d.lib
${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib
)
else()
set(PIP_CONFIGURE_COMMAND echo ".")
endif()
@ -15,9 +19,23 @@ ExternalProject_Add(external_python_site_packages
CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND}
BUILD_COMMAND ""
PREFIX ${BUILD_DIR}/site_packages
# setuptools is downgraded to 63.2.0 (same as python 3.10.8) since numpy 1.23.x seemingly has
# setuptools is downgraded to 63.2.0 (same as python 3.10.8) since numpy 1.23.x seemingly has
# issues building on windows with the newer versions that ships with python 3.10.9+
INSTALL_COMMAND ${PYTHON_BINARY} -m pip install --no-cache-dir ${SITE_PACKAGES_EXTRA} setuptools==63.2.0 cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} autopep8==${AUTOPEP8_VERSION} pycodestyle==${PYCODESTYLE_VERSION} toml==${TOML_VERSION} meson==${MESON_VERSION} --no-binary :all:
INSTALL_COMMAND ${PYTHON_BINARY} -m pip install --no-cache-dir ${SITE_PACKAGES_EXTRA}
setuptools==63.2.0
cython==${CYTHON_VERSION}
idna==${IDNA_VERSION}
charset-normalizer==${CHARSET_NORMALIZER_VERSION}
urllib3==${URLLIB3_VERSION}
certifi==${CERTIFI_VERSION}
requests==${REQUESTS_VERSION}
zstandard==${ZSTANDARD_VERSION}
autopep8==${AUTOPEP8_VERSION}
pycodestyle==${PYCODESTYLE_VERSION}
toml==${TOML_VERSION}
meson==${MESON_VERSION}
--no-binary :all:
)
if(USE_PIP_NUMPY)

View File

@ -1,9 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-or-later
set(SNDFILE_EXTRA_ARGS)
set(SNDFILE_ENV PKG_CONFIG_PATH=${mingw_LIBDIR}/ogg/lib/pkgconfig:${mingw_LIBDIR}/vorbis/lib/pkgconfig:${mingw_LIBDIR}/flac/lib/pkgconfig:${mingw_LIBDIR}/opus/lib/pkgconfig:${mingw_LIBDIR})
set(SNDFILE_ENV)
if(WIN32)
set(SNDFILE_ENV PKG_CONFIG_PATH=${mingw_LIBDIR}/ogg/lib/pkgconfig:${mingw_LIBDIR}/vorbis/lib/pkgconfig:${mingw_LIBDIR}/flac/lib/pkgconfig:${mingw_LIBDIR}/opus/lib/pkgconfig:${mingw_LIBDIR})
set(SNDFILE_ENV set ${SNDFILE_ENV} &&)
# Shared for windows because static libs will drag in a libgcc dependency.
set(SNDFILE_OPTIONS --disable-static --enable-shared )
@ -11,6 +12,16 @@ else()
set(SNDFILE_OPTIONS --enable-static --disable-shared )
endif()
if(UNIX AND NOT APPLE)
# NOTE(@campbellbarton): For some reason OPUS is alone in referencing the sub-directory,
# manipulate the package-config file to prevent this from happening.
# There is no problem with applying this change multiple times.
#
# Replace: Cflags: -I${includedir}/opus
# With: Cflags: -I${includedir}
set(SNDFILE_ENV sed -i s/{includedir}\\/opus/{includedir}/g ${LIBDIR}/opus/lib/pkgconfig/opus.pc && ${SNDFILE_ENV})
endif()
ExternalProject_Add(external_sndfile
URL file://${PACKAGE_DIR}/${SNDFILE_FILE}
DOWNLOAD_DIR ${DOWNLOAD_DIR}

View File

@ -203,7 +203,7 @@ set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz)
# NOTE: When updating the python version, it's required to check the versions of
# it wants to use in PCbuild/get_externals.bat for the following dependencies:
# BZIP2, FFI, SQLITE and change the versions in this file as well. For compliance
# BZIP2, FFI, SQLITE and change the versions in this file as well. For compliance
# reasons there can be no exceptions to this.
set(PYTHON_VERSION 3.10.9)
@ -229,20 +229,34 @@ set(OPENVDB_HASH 64301c737e16b26c8f3085a31e6397e9)
set(OPENVDB_HASH_TYPE MD5)
set(OPENVDB_FILE openvdb-${OPENVDB_VERSION}.tar.gz)
# ------------------------------------------------------------------------------
# Python Modules
# Needed by: TODO.
set(IDNA_VERSION 3.3)
# Needed by: TODO.
set(CHARSET_NORMALIZER_VERSION 2.0.10)
# Needed by: TODO.
set(URLLIB3_VERSION 1.26.8)
set(URLLIB3_CPE "cpe:2.3:a:urllib3:urllib3:${URLLIB3_VERSION}:*:*:*:*:*:*:*")
# Needed by: Python's `requests` module (so add-ons can authenticate against trusted certificates).
set(CERTIFI_VERSION 2021.10.8)
# Needed by: Some of Blender's add-ons (to support convenient interaction with online services).
set(REQUESTS_VERSION 2.27.1)
# Needed by: Python's `numpy` module (used by some add-ons).
set(CYTHON_VERSION 0.29.30)
# The version of the zstd library used to build the Python package should match ZSTD_VERSION
# Needed by: Python scripts that read `.blend` files, as files may use Z-standard compression.
# The version of the ZSTD library used to build the Python package should match ZSTD_VERSION
# defined below. At this time of writing, 0.17.0 was already released,
# but built against zstd 1.5.1, while we use 1.5.0.
# but built against ZSTD 1.5.1, while we use 1.5.0.
set(ZSTANDARD_VERSION 0.16.0)
# Auto-format Python source (developer tool, not used by Blender at run-time).
set(AUTOPEP8_VERSION 1.6.0)
# Needed by: `autopep8` (so the version doesn't change on rebuild).
set(PYCODESTYLE_VERSION 2.8.0)
# Needed by: `autopep8` (so the version doesn't change on rebuild).
set(TOML_VERSION 0.10.2)
# Build system for other packages (not used by Blender at run-time).
set(MESON_VERSION 0.63.0)
set(NUMPY_VERSION 1.23.5)

View File

@ -30,6 +30,17 @@ set(VULKAN_LOADER_EXTRA_ARGS
-DVULKAN_HEADERS_INSTALL_DIR=${LIBDIR}/vulkan_headers
)
if(UNIX AND NOT APPLE)
# These are used in `cmake/FindWayland.cmake` from `external_vulkan_loader`.
# NOTE: When upgrading to CMAKE 3.22 we it would be cleaner to use: `PKG_CONFIG_ARGN`,
# so `pkgconfig` would find wayland.
set(VULKAN_LOADER_EXTRA_ARGS
${VULKAN_LOADER_EXTRA_ARGS}
-DPKG_WAYLAND_INCLUDE_DIRS=${LIBDIR}/wayland/include
-DPKG_WAYLAND_LIBRARY_DIRS=${LIBDIR}/wayland/lib64
)
endif()
ExternalProject_Add(external_vulkan_loader
URL file://${PACKAGE_DIR}/${VULKAN_LOADER_FILE}
URL_HASH ${VULKAN_LOADER_HASH_TYPE}=${VULKAN_LOADER_HASH}
@ -43,7 +54,12 @@ add_dependencies(
external_vulkan_headers
)
if(WIN32)
if(UNIX AND NOT APPLE)
add_dependencies(
external_vulkan_loader
external_wayland
)
elseif(WIN32)
if(BUILD_MODE STREQUAL Release)
ExternalProject_Add_Step(external_vulkan_loader after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/vulkan_loader/ ${HARVEST_TARGET}/vulkan

View File

@ -5,7 +5,6 @@ ExternalProject_Add(external_wayland
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH ${WAYLAND_HASH_TYPE}=${WAYLAND_HASH}
PREFIX ${BUILD_DIR}/wayland
PATCH_COMMAND ${PATCH_CMD} -d ${BUILD_DIR}/wayland/src/external_wayland < ${PATCH_DIR}/wayland.diff
# Use `-E` so the `PKG_CONFIG_PATH` can be defined to link against our own LIBEXPAT & LIBXML2.
#
# NOTE: passing link args "ffi/lib" should not be needed, but
@ -24,4 +23,7 @@ add_dependencies(
external_expat
external_xml2
external_ffi
# Needed for `MESON`.
external_python_site_packages
)

View File

@ -15,4 +15,6 @@ ExternalProject_Add(external_wayland_protocols
add_dependencies(
external_wayland_protocols
external_wayland
# Needed for `MESON`.
external_python_site_packages
)

View File

@ -1,11 +0,0 @@
--- meson.build.orig 2022-06-30 22:59:11.000000000 +0100
+++ meson.build 2022-09-27 13:21:26.428517668 +0100
@@ -2,7 +2,7 @@
'wayland', 'c',
version: '1.21.0',
license: 'MIT',
- meson_version: '>= 0.56.0',
+ meson_version: '>= 0.55.1',
default_options: [
'warning_level=2',
'buildtype=debugoptimized',

View File

@ -105,9 +105,10 @@ if(WITH_VULKAN_BACKEND)
set(VULKAN_ROOT_DIR ${LIBDIR}/vulkan/macOS)
set(VULKAN_INCLUDE_DIR ${VULKAN_ROOT_DIR}/include)
set(VULKAN_LIBRARY ${VULKAN_ROOT_DIR}/lib/libvulkan.1.dylib)
set(SHADERC_LIBRARY ${VULKAN_ROOT_DIR}/lib/libshaderc_combined.a)
set(VULKAN_INCLUDE_DIRS ${VULKAN_INCLUDE_DIR} ${MOLTENVK_INCLUDE_DIRS})
set(VULKAN_LIBRARIES ${VULKAN_LIBRARY} ${MOLTENVK_LIBRARIES})
set(VULKAN_LIBRARIES ${VULKAN_LIBRARY} ${SHADERC_LIBRARY} ${MOLTENVK_LIBRARIES})
else()
message(WARNING "Vulkan SDK was not found, disabling WITH_VULKAN_BACKEND")
set(WITH_VULKAN_BACKEND OFF)

View File

@ -163,10 +163,10 @@ void device_hip_info(vector<DeviceInfo> &devices)
/* If device has a kernel timeout and no compute preemption, we assume
* it is connected to a display and will freeze the display while doing
* computations. */
int timeout_attr = 0, preempt_attr = 0;
int timeout_attr = 0;
hipDeviceGetAttribute(&timeout_attr, hipDeviceAttributeKernelExecTimeout, num);
if (timeout_attr && !preempt_attr) {
if (timeout_attr) {
VLOG_INFO << "Device is recognized as display.";
info.description += " (Display)";
info.display_device = true;

View File

@ -26,9 +26,12 @@ class HdCyclesVolumeLoader : public VDBImageLoader {
HdCyclesVolumeLoader(const std::string &filePath, const std::string &gridName)
: VDBImageLoader(gridName)
{
/* Disably delay loading and file copying, this has poor performance
* on network drivers. */
const bool delay_load = false;
openvdb::io::File file(filePath);
file.setCopyMaxBytes(0);
if (file.open()) {
if (file.open(delay_load)) {
grid = file.readGrid(gridName);
}
}

View File

@ -255,8 +255,9 @@ ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
float3 inplane;
if (in_volume_segment) {
/* FIXME: handle rectangular light. */
inplane = ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
inplane = sample_rectangle ?
rectangle_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv) :
ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
ls->P += inplane;
ls->pdf = invarea;
}

View File

@ -30,8 +30,13 @@ typedef struct LightSample {
ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv)
{
to_unit_disk(&randu, &randv);
return ru * randu + rv * randv;
const float2 rand = concentric_sample_disk(randu, randv);
return ru * rand.x + rv * rand.y;
}
ccl_device_inline float3 rectangle_sample(float3 ru, float3 rv, float randu, float randv)
{
return ru * (2.0f * randu - 1.0f) + rv * (2.0f * randv - 1.0f);
}
ccl_device float3 disk_light_sample(float3 v, float randu, float randv)

View File

@ -59,41 +59,9 @@ ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
{
/* Sample light index from distribution. */
const int index = light_distribution_sample(kg, &randu);
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(light_distribution,
index);
const int prim = kdistribution->prim;
if (prim >= 0) {
/* Mesh light. */
const int object = kdistribution->mesh_light.object_id;
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) {
return false;
}
const int shader_flag = kdistribution->mesh_light.shader_flag;
if (!triangle_light_sample<in_volume_segment>(kg, prim, object, randu, randv, time, ls, P)) {
return false;
}
ls->shader |= shader_flag;
}
else {
const int lamp = -prim - 1;
if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) {
return false;
}
if (!light_sample<in_volume_segment>(kg, lamp, randu, randv, P, path_flag, ls)) {
return false;
}
ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights;
}
ls->pdf *= ls->pdf_selection;
return (ls->pdf > 0.0f);
const float pdf_selection = kernel_data.integrator.distribution_pdf_lights;
return light_sample<in_volume_segment>(
kg, randu, randv, time, P, bounce, path_flag, index, pdf_selection, ls);
}
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)

View File

@ -14,6 +14,13 @@
CCL_NAMESPACE_BEGIN
/* Light info. */
ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce)
{
return (bounce > kernel_data_fetch(lights, index).max_bounces);
}
/* Sample point on an individual light. */
template<bool in_volume_segment>
@ -90,6 +97,68 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
return in_volume_segment || (ls->pdf > 0.0f);
}
/* Sample a point on the chosen emitter. */
template<bool in_volume_segment>
ccl_device_noinline bool light_sample(KernelGlobals kg,
const float randu,
const float randv,
const float time,
const float3 P,
const int bounce,
const uint32_t path_flag,
const int emitter_index,
const float pdf_selection,
ccl_private LightSample *ls)
{
int prim;
MeshLight mesh_light;
if (kernel_data.integrator.use_light_tree) {
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
emitter_index);
prim = kemitter->prim;
mesh_light = kemitter->mesh_light;
}
else {
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(
light_distribution, emitter_index);
prim = kdistribution->prim;
mesh_light = kdistribution->mesh_light;
}
/* A different value would be assigned in `triangle_light_sample()` if `!use_light_tree`. */
ls->pdf_selection = pdf_selection;
if (prim >= 0) {
/* Mesh light. */
const int object = mesh_light.object_id;
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) {
return false;
}
const int shader_flag = mesh_light.shader_flag;
if (!triangle_light_sample<in_volume_segment>(kg, prim, object, randu, randv, time, ls, P)) {
return false;
}
ls->shader |= shader_flag;
}
else {
if (UNLIKELY(light_select_reached_max_bounces(kg, ~prim, bounce))) {
return false;
}
if (!light_sample<in_volume_segment>(kg, ~prim, randu, randv, P, path_flag, ls)) {
return false;
}
}
ls->pdf *= ls->pdf_selection;
return in_volume_segment || (ls->pdf > 0.0f);
}
/* Intersect ray with individual light. */
ccl_device bool lights_intersect(KernelGlobals kg,
@ -230,11 +299,4 @@ ccl_device_forceinline void light_update_position(KernelGlobals kg,
}
}
/* Light info. */
ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce)
{
return (bounce > kernel_data_fetch(lights, index).max_bounces);
}
CCL_NAMESPACE_END

View File

@ -22,16 +22,15 @@
CCL_NAMESPACE_BEGIN
/* TODO: this seems like a relative expensive computation, and we can make it a lot cheaper
* by using a bounding sphere instead of a bounding box. This will be more inaccurate, but it
* might be fine when used along with the adaptive splitting. */
/* TODO: this seems like a relative expensive computation. We can make it a lot cheaper by using a
* bounding sphere instead of a bounding box, but this will reduce the accuracy sometimes. */
ccl_device float light_tree_cos_bounding_box_angle(const BoundingBox bbox,
const float3 P,
const float3 point_to_centroid)
{
if (P.x > bbox.min.x && P.y > bbox.min.y && P.z > bbox.min.z && P.x < bbox.max.x &&
P.y < bbox.max.y && P.z < bbox.max.z) {
/* If P is inside the bbox, `theta_u` covers the whole sphere */
/* If P is inside the bbox, `theta_u` covers the whole sphere. */
return -1.0f;
}
float cos_theta_u = 1.0f;
@ -53,7 +52,7 @@ ccl_device_forceinline float sin_from_cos(const float c)
return safe_sqrtf(1.0f - sqr(c));
}
/* Compute vector v as in Fig .8. P_v is the corresponding point along the ray ccl_device float3 */
/* Compute vector v as in Fig .8. P_v is the corresponding point along the ray. */
ccl_device float3 compute_v(
const float3 centroid, const float3 P, const float3 D, const float3 bcone_axis, const float t)
{
@ -95,12 +94,12 @@ ccl_device void light_tree_importance(const float3 N_or_D,
const float sin_theta_u = sin_from_cos(cos_theta_u);
/* cos(theta_i') in the paper, omitted for volume */
/* cos(theta_i') in the paper, omitted for volume. */
float cos_min_incidence_angle = 1.0f;
float cos_max_incidence_angle = 1.0f;
/* when sampling the light tree for the second time in `shade_volume.h` and when query the pdf in
* `sample.h` */
/* When sampling the light tree for the second time in `shade_volume.h` and when query the pdf in
* `sample.h`. */
const bool in_volume = is_zero(N_or_D);
if (!in_volume_segment && !in_volume) {
const float3 N = N_or_D;
@ -116,7 +115,7 @@ ccl_device void light_tree_importance(const float3 N_or_D,
/* If the node is guaranteed to be behind the surface we're sampling, and the surface is
* opaque, then we can give the node an importance of 0 as it contributes nothing to the
* surface. This is more accurate than the bbox test if we are calculating the importance of
* an emitter with radius */
* an emitter with radius. */
if (!has_transmission && cos_min_incidence_angle < 0) {
return;
}
@ -133,8 +132,8 @@ ccl_device void light_tree_importance(const float3 N_or_D,
float cos_theta_o, sin_theta_o;
fast_sincosf(bcone.theta_o, &sin_theta_o, &cos_theta_o);
/* minimum angle an emitters axis would form with the direction to the shading point,
* cos(theta') in the paper */
/* Minimum angle an emitters axis would form with the direction to the shading point,
* cos(theta') in the paper. */
float cos_min_outgoing_angle;
if ((cos_theta >= cos_theta_u) || (cos_theta_minus_theta_u >= cos_theta_o)) {
/* theta - theta_o - theta_u <= 0 */
@ -151,7 +150,7 @@ ccl_device void light_tree_importance(const float3 N_or_D,
sin_theta_minus_theta_u * sin_theta_o;
}
else {
/* cluster invisible */
/* Cluster is invisible. */
return;
}
@ -190,7 +189,7 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
ccl_private float3 &centroid,
ccl_private packed_float3 &dir)
{
const int prim_id = kemitter->prim_id;
const int prim_id = kemitter->prim;
if (prim_id < 0) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ~prim_id);
centroid = klight->co;
@ -200,14 +199,14 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
dir = klight->spot.dir;
break;
case LIGHT_POINT:
/* Disk-oriented normal */
/* Disk-oriented normal. */
dir = safe_normalize(P - centroid);
break;
case LIGHT_AREA:
dir = klight->area.dir;
break;
case LIGHT_BACKGROUND:
/* Aarbitrary centroid and direction */
/* Arbitrary centroid and direction. */
centroid = make_float3(0.0f, 0.0f, 1.0f);
dir = make_float3(0.0f, 0.0f, -1.0f);
return !in_volume_segment;
@ -224,14 +223,14 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
triangle_world_space_vertices(kg, object, prim_id, -1.0f, vertices);
centroid = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
if (kemitter->mesh_light.emission_sampling == EMISSION_SAMPLING_FRONT) {
if (kemitter->emission_sampling == EMISSION_SAMPLING_FRONT) {
dir = safe_normalize(cross(vertices[1] - vertices[0], vertices[2] - vertices[0]));
}
else if (kemitter->mesh_light.emission_sampling == EMISSION_SAMPLING_BACK) {
else if (kemitter->emission_sampling == EMISSION_SAMPLING_BACK) {
dir = -safe_normalize(cross(vertices[1] - vertices[0], vertices[2] - vertices[0]));
}
else {
/* Double sided: any vector in the plane. */
/* Double-sided: any vector in the plane. */
dir = safe_normalize(vertices[0] - vertices[1]);
}
}
@ -265,13 +264,13 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
return;
}
const int prim_id = kemitter->prim_id;
const int prim_id = kemitter->prim;
if (in_volume_segment) {
const float3 D = N_or_D;
/* Closest point */
/* Closest point. */
P_c = P + dot(centroid - P, D) * D;
/* minimal distance of the ray to the cluster */
/* Minimal distance of the ray to the cluster. */
distance.x = len(centroid - P_c);
distance.y = distance.x;
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
@ -284,7 +283,7 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
if (prim_id < 0) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ~prim_id);
switch (klight->type) {
/* Function templates only modifies cos_theta_u when in_volume_segment = true */
/* Function templates only modifies cos_theta_u when in_volume_segment = true. */
case LIGHT_SPOT:
is_visible = spot_light_tree_parameters<in_volume_segment>(
klight, centroid, P_c, cos_theta_u, distance, point_to_centroid);
@ -310,7 +309,7 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
return;
}
}
else { /* mesh light */
else { /* Mesh light. */
is_visible = triangle_light_tree_parameters<in_volume_segment>(
kg, kemitter, centroid, P_c, N_or_D, bcone, cos_theta_u, distance, point_to_centroid);
}
@ -346,7 +345,7 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
max_importance = 0.0f;
min_importance = 0.0f;
if (knode->num_prims == 1) {
/* At a leaf node with only one emitter */
/* At a leaf node with only one emitter. */
light_tree_emitter_importance<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, -knode->child_index, max_importance, min_importance);
}
@ -358,7 +357,7 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
float cos_theta_u;
float distance;
if (knode->bit_trail == 1) {
/* distant light node */
/* Distant light node. */
if (in_volume_segment) {
return;
}
@ -372,7 +371,7 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
if (in_volume_segment) {
const float3 D = N_or_D;
const float3 closest_point = P + dot(centroid - P, D) * D;
/* minimal distance of the ray to the cluster */
/* Minimal distance of the ray to the cluster. */
distance = len(centroid - closest_point);
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
@ -393,7 +392,7 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
point_to_centroid = normalize_len(centroid - P, &distance);
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, P, point_to_centroid);
}
/* clamp distance to half the radius of the cluster when splitting is disabled */
/* Clamp distance to half the radius of the cluster when splitting is disabled. */
distance = fmaxf(0.5f * len(centroid - bbox.max), distance);
}
/* TODO: currently max_distance = min_distance, max_importance = min_importance for the
@ -436,8 +435,8 @@ ccl_device void sample_resevoir(const int current_index,
return;
}
/* pick an emitter from a leaf node using resevoir sampling, keep two reservoirs for upper and
* lower bounds */
/* Pick an emitter from a leaf node using resevoir sampling, keep two reservoirs for upper and
* lower bounds. */
template<bool in_volume_segment>
ccl_device int light_tree_cluster_select_emitter(KernelGlobals kg,
ccl_private float &rand,
@ -452,11 +451,11 @@ ccl_device int light_tree_cluster_select_emitter(KernelGlobals kg,
float total_importance[2] = {0.0f, 0.0f};
int selected_index = -1;
/* Mark emitters with zero importance. Used for resevoir when total minimum importance = 0 */
/* Mark emitters with zero importance. Used for resevoir when total minimum importance = 0. */
kernel_assert(knode->num_prims <= sizeof(uint) * 8);
uint has_importance = 0;
const bool sample_max = (rand > 0.5f); /* sampling using the maximum importance */
const bool sample_max = (rand > 0.5f); /* Sampling using the maximum importance. */
rand = rand * 2.0f - float(sample_max);
for (int i = 0; i < knode->num_prims; i++) {
@ -485,7 +484,7 @@ ccl_device int light_tree_cluster_select_emitter(KernelGlobals kg,
}
if (total_importance[1] == 0.0f) {
/* uniformly sample emitters with positive maximum importance */
/* Uniformly sample emitters with positive maximum importance. */
if (sample_max) {
selected_importance[1] = 1.0f;
total_importance[1] = float(popcount(has_importance));
@ -540,7 +539,7 @@ ccl_device bool get_left_probability(KernelGlobals kg,
}
const float total_min_importance = min_left_importance + min_right_importance;
/* average two probabilities of picking the left child node using lower and upper bounds */
/* Average two probabilities of picking the left child node using lower and upper bounds. */
const float probability_max = max_left_importance / total_max_importance;
const float probability_min = total_min_importance > 0 ?
min_left_importance / total_min_importance :
@ -569,31 +568,31 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION);
float pdf_leaf = 1.0f;
float pdf_emitter_from_leaf = 1.0f;
int selected_light = -1;
float pdf_selection = 1.0f;
int selected_emitter = -1;
int node_index = 0; /* root node */
int node_index = 0; /* Root node. */
/* Traverse the light tree until a leaf node is reached. */
while (true) {
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes, node_index);
if (knode->child_index <= 0) {
/* At a leaf node, we pick an emitter */
selected_light = light_tree_cluster_select_emitter<in_volume_segment>(
kg, randv, P, N_or_D, t, has_transmission, knode, &pdf_emitter_from_leaf);
/* At a leaf node, we pick an emitter. */
selected_emitter = light_tree_cluster_select_emitter<in_volume_segment>(
kg, randv, P, N_or_D, t, has_transmission, knode, &pdf_selection);
break;
}
/* At an interior node, the left child is directly after the parent,
* while the right child is stored as the child index. */
/* At an interior node, the left child is directly after the parent, while the right child is
* stored as the child index. */
const int left_index = node_index + 1;
const int right_index = knode->child_index;
float left_prob;
if (!get_left_probability<in_volume_segment>(
kg, P, N_or_D, t, has_transmission, left_index, right_index, left_prob)) {
return false; /* both child nodes have zero importance */
return false; /* Both child nodes have zero importance. */
}
float discard;
@ -603,46 +602,14 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
pdf_leaf *= (node_index == left_index) ? left_prob : (1.0f - left_prob);
}
if (selected_light < 0) {
if (selected_emitter < 0) {
return false;
}
/* Sample a point on the chosen emitter */
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
selected_light);
pdf_selection *= pdf_leaf;
/* TODO: this is the same code as light_distribution_sample, except the index is determined
* differently. Would it be better to refactor this into a separate function? */
const int prim = kemitter->prim_id;
if (prim >= 0) {
/* Mesh light. */
const int object = kemitter->mesh_light.object_id;
/* Exclude synthetic meshes from shadow catcher pass. */
if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) &&
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) {
return false;
}
const int mesh_shader_flag = kemitter->mesh_light.shader_flag;
if (!triangle_light_sample<in_volume_segment>(kg, prim, object, randu, randv, time, ls, P)) {
return false;
}
ls->shader |= mesh_shader_flag;
}
else {
if (UNLIKELY(light_select_reached_max_bounces(kg, ~prim, bounce))) {
return false;
}
if (!light_sample<in_volume_segment>(kg, ~prim, randu, randv, P, path_flag, ls)) {
return false;
}
}
ls->pdf_selection = pdf_leaf * pdf_emitter_from_leaf;
ls->pdf *= ls->pdf_selection;
return (ls->pdf > 0);
return light_sample<in_volume_segment>(
kg, randu, randv, time, P, bounce, path_flag, selected_emitter, pdf_selection, ls);
}
/* We need to be able to find the probability of selecting a given light for MIS. */
@ -650,7 +617,7 @@ ccl_device float light_tree_pdf(
KernelGlobals kg, const float3 P, const float3 N, const int path_flag, const int prim)
{
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
/* Target emitter info */
/* Target emitter info. */
const int target_emitter = (prim >= 0) ? kernel_data_fetch(triangle_to_tree, prim) :
kernel_data_fetch(light_to_tree, ~prim);
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
@ -659,11 +626,11 @@ ccl_device float light_tree_pdf(
ccl_global const KernelLightTreeNode *kleaf = &kernel_data_fetch(light_tree_nodes, target_leaf);
uint bit_trail = kleaf->bit_trail;
int node_index = 0; /* root node */
int node_index = 0; /* Root node. */
float pdf = 1.0f;
/* Traverse the light tree until we reach the target leaf node */
/* Traverse the light tree until we reach the target leaf node. */
while (true) {
const ccl_global KernelLightTreeNode *knode = &kernel_data_fetch(light_tree_nodes, node_index);
@ -671,7 +638,7 @@ ccl_device float light_tree_pdf(
break;
}
/* Interior node */
/* Interior node. */
const int left_index = node_index + 1;
const int right_index = knode->child_index;

View File

@ -306,7 +306,7 @@ ccl_device_forceinline bool triangle_light_tree_parameters(
const int object = kemitter->mesh_light.object_id;
float3 vertices[3];
triangle_world_space_vertices(kg, object, kemitter->prim_id, -1.0f, vertices);
triangle_world_space_vertices(kg, object, kemitter->prim, -1.0f, vertices);
bool shape_above_surface = false;
for (int i = 0; i < 3; i++) {

View File

@ -1338,13 +1338,15 @@ typedef struct KernelLight {
} KernelLight;
static_assert_align(KernelLight, 16);
using MeshLight = struct MeshLight {
int shader_flag;
int object_id;
};
typedef struct KernelLightDistribution {
float totarea;
int prim;
struct {
int shader_flag;
int object_id;
} mesh_light;
MeshLight mesh_light;
} KernelLightDistribution;
static_assert_align(KernelLightDistribution, 16);
@ -1393,12 +1395,9 @@ typedef struct KernelLightTreeEmitter {
float energy;
/* prim_id denotes the location in the lights or triangles array. */
int prim_id;
struct {
int shader_flag;
int object_id;
EmissionSampling emission_sampling;
} mesh_light;
int prim;
MeshLight mesh_light;
EmissionSampling emission_sampling;
/* Parent. */
int parent_index;

View File

@ -386,6 +386,46 @@ void ConstantFolder::fold_mix_color(NodeMix type, bool clamp_factor, bool clamp)
}
}
void ConstantFolder::fold_mix_float(bool clamp_factor, bool clamp) const
{
ShaderInput *fac_in = node->input("Factor");
ShaderInput *float1_in = node->input("A");
ShaderInput *float2_in = node->input("B");
float fac = clamp_factor ? saturatef(node->get_float(fac_in->socket_type)) :
node->get_float(fac_in->socket_type);
bool fac_is_zero = !fac_in->link && fac == 0.0f;
bool fac_is_one = !fac_in->link && fac == 1.0f;
/* remove no-op node when factor is 0.0 */
if (fac_is_zero) {
if (try_bypass_or_make_constant(float1_in, clamp)) {
return;
}
}
/* remove useless mix floats nodes */
if (float1_in->link && float2_in->link) {
if (float1_in->link == float2_in->link) {
try_bypass_or_make_constant(float1_in, clamp);
return;
}
}
else if (!float1_in->link && !float2_in->link) {
float value1 = node->get_float(float1_in->socket_type);
float value2 = node->get_float(float2_in->socket_type);
if (value1 == value2) {
try_bypass_or_make_constant(float1_in, clamp);
return;
}
}
/* remove no-op mix float node when factor is 1.0 */
if (fac_is_one) {
try_bypass_or_make_constant(float2_in, clamp);
return;
}
}
void ConstantFolder::fold_math(NodeMathType type) const
{
ShaderInput *value1_in = node->input("Value1");

View File

@ -52,6 +52,7 @@ class ConstantFolder {
/* Specific nodes. */
void fold_mix(NodeMix type, bool clamp) const;
void fold_mix_color(NodeMix type, bool clamp_factor, bool clamp) const;
void fold_mix_float(bool clamp_factor, bool clamp) const;
void fold_math(NodeMathType type) const;
void fold_vector_math(NodeVectorMathType type) const;
void fold_mapping(NodeMappingType type) const;

View File

@ -88,7 +88,7 @@ NODE_DEFINE(Film)
{
NodeType *type = NodeType::add("film", create);
SOCKET_FLOAT(exposure, "Exposure", 0.8f);
SOCKET_FLOAT(exposure, "Exposure", 1.0f);
SOCKET_FLOAT(pass_alpha_threshold, "Pass Alpha Threshold", 0.0f);
static NodeEnum filter_enum;

View File

@ -253,7 +253,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->use_light_tree = scene->integrator->use_light_tree;
if (light_sampling_threshold > 0.0f) {
kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold;
kintegrator->light_inv_rr_threshold = scene->film->get_exposure() / light_sampling_threshold;
}
else {
kintegrator->light_inv_rr_threshold = 0.0f;

View File

@ -616,18 +616,16 @@ void LightManager::device_update_tree(Device *,
shader_flag |= SHADER_EXCLUDE_SHADOW_CATCHER;
}
light_tree_emitters[emitter_index].prim_id = prim.prim_id + mesh->prim_offset;
light_tree_emitters[emitter_index].prim = prim.prim_id + mesh->prim_offset;
light_tree_emitters[emitter_index].mesh_light.shader_flag = shader_flag;
light_tree_emitters[emitter_index].mesh_light.emission_sampling =
shader->emission_sampling;
light_tree_emitters[emitter_index].emission_sampling = shader->emission_sampling;
triangle_array[prim.prim_id + object_lookup_offsets[prim.object_id]] = emitter_index;
}
else {
light_tree_emitters[emitter_index].prim_id = prim.prim_id;
light_tree_emitters[emitter_index].prim = prim.prim_id;
light_tree_emitters[emitter_index].mesh_light.shader_flag = 0;
light_tree_emitters[emitter_index].mesh_light.object_id = OBJECT_NONE;
light_tree_emitters[emitter_index].mesh_light.emission_sampling =
EMISSION_SAMPLING_FRONT_BACK;
light_tree_emitters[emitter_index].emission_sampling = EMISSION_SAMPLING_FRONT_BACK;
light_array[~prim.prim_id] = emitter_index;
}
@ -726,7 +724,6 @@ void LightManager::device_update_background(Device *device,
foreach (ShaderNode *node, shader->graph->nodes) {
if (node->type == EnvironmentTextureNode::get_node_type()) {
EnvironmentTextureNode *env = (EnvironmentTextureNode *)node;
ImageMetaData metadata;
if (!env->handle.empty()) {
ImageMetaData metadata = env->handle.metadata();
environment_res.x = max(environment_res.x, (int)metadata.width);

View File

@ -181,7 +181,9 @@ LightTreePrimitive::LightTreePrimitive(Scene *scene, int prim_id, int object_id)
strength *= lamp->get_shader()->emission_estimate;
}
energy = average(strength);
/* Use absolute value of energy so lights with negative strength are properly
* supported in the light tree. */
energy = fabsf(average(strength));
}
}

View File

@ -349,7 +349,7 @@ void Shader::estimate_emission()
}
ShaderInput *surf = graph->output()->input("Surface");
emission_estimate = output_estimate_emission(surf->link, emission_is_constant);
emission_estimate = fabs(output_estimate_emission(surf->link, emission_is_constant));
if (is_zero(emission_estimate)) {
emission_sampling = EMISSION_SAMPLING_NONE;

View File

@ -5132,6 +5132,9 @@ void MixFloatNode::constant_fold(const ConstantFolder &folder)
}
folder.make_constant(a * (1 - fac) + b * fac);
}
else {
folder.fold_mix_float(use_clamp, false);
}
}
/* Mix Vector */
@ -5185,6 +5188,9 @@ void MixVectorNode::constant_fold(const ConstantFolder &folder)
}
folder.make_constant(a * (one_float3() - fac) + b * fac);
}
else {
folder.fold_mix_color(NODE_MIX_BLEND, use_clamp, false);
}
}
/* Mix Vector Non Uniform */

View File

@ -32,8 +32,10 @@ class MotionPathButtonsPanel:
col.prop(mps, "frame_step", text="Step")
elif mps.type == 'RANGE':
col = layout.column(align=True)
col.prop(mps, "frame_start", text="Frame Range Start")
col.prop(mps, "frame_end", text="End")
start_end_group = col.column(align=True)
start_end_group.active = mps.range == 'MANUAL'
start_end_group.prop(mps, "frame_start", text="Frame Range Start")
start_end_group.prop(mps, "frame_end", text="End")
col.prop(mps, "frame_step", text="Step")
# Calculation Range

View File

@ -46,10 +46,13 @@ blender_add_lib(bf_asset_system "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
set(TEST_SRC
tests/asset_catalog_test.cc
tests/asset_catalog_path_test.cc
tests/asset_catalog_test.cc
tests/asset_catalog_tree_test.cc
tests/asset_library_service_test.cc
tests/asset_library_test.cc
tests/asset_library_test_common.hh
)
set(TEST_LIB
bf_asset_system

View File

@ -17,6 +17,8 @@
#include "testing/testing.h"
#include "asset_library_test_common.hh"
namespace blender::asset_system::tests {
/* UUIDs from lib/tests/asset_library/blender_assets.cats.txt */
@ -76,130 +78,8 @@ class TestableAssetCatalogService : public AssetCatalogService {
}
};
class AssetCatalogTest : public testing::Test {
class AssetCatalogTest : public AssetLibraryTestBase {
protected:
CatalogFilePath asset_library_root_;
CatalogFilePath temp_library_path_;
static void SetUpTestSuite()
{
testing::Test::SetUpTestSuite();
CLG_init();
}
static void TearDownTestSuite()
{
CLG_exit();
testing::Test::TearDownTestSuite();
}
void SetUp() override
{
const std::string test_files_dir = blender::tests::flags_test_asset_dir();
if (test_files_dir.empty()) {
FAIL();
}
asset_library_root_ = test_files_dir + SEP_STR + "asset_library";
temp_library_path_ = "";
}
void TearDown() override
{
if (!temp_library_path_.empty()) {
BLI_delete(temp_library_path_.c_str(), true, true);
temp_library_path_ = "";
}
}
/* Register a temporary path, which will be removed at the end of the test.
* The returned path ends in a slash. */
CatalogFilePath use_temp_path()
{
BKE_tempdir_init("");
const CatalogFilePath tempdir = BKE_tempdir_session();
temp_library_path_ = tempdir + "test-temporary-path" + SEP_STR;
return temp_library_path_;
}
CatalogFilePath create_temp_path()
{
CatalogFilePath path = use_temp_path();
BLI_dir_create_recursive(path.c_str());
return path;
}
void assert_expected_item(const AssetCatalogPath &expected_path,
const AssetCatalogTreeItem &actual_item)
{
if (expected_path != actual_item.catalog_path().str()) {
/* This will fail, but with a nicer error message than just calling FAIL(). */
EXPECT_EQ(expected_path, actual_item.catalog_path());
return;
}
/* Is the catalog name as expected? "character", "Ellie", ... */
EXPECT_EQ(expected_path.name(), actual_item.get_name());
/* Does the computed number of parents match? */
const std::string expected_path_str = expected_path.str();
const size_t expected_parent_count = std::count(
expected_path_str.begin(), expected_path_str.end(), AssetCatalogPath::SEPARATOR);
EXPECT_EQ(expected_parent_count, actual_item.count_parents());
}
/**
* Recursively iterate over all tree items using #AssetCatalogTree::foreach_item() and check if
* the items map exactly to \a expected_paths.
*/
void assert_expected_tree_items(AssetCatalogTree *tree,
const std::vector<AssetCatalogPath> &expected_paths)
{
int i = 0;
tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) {
ASSERT_LT(i, expected_paths.size())
<< "More catalogs in tree than expected; did not expect " << actual_item.catalog_path();
assert_expected_item(expected_paths[i], actual_item);
i++;
});
}
/**
* Iterate over the root items of \a tree and check if the items map exactly to \a
* expected_paths. Similar to #assert_expected_tree_items() but calls
* #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item().
*/
void assert_expected_tree_root_items(AssetCatalogTree *tree,
const std::vector<AssetCatalogPath> &expected_paths)
{
int i = 0;
tree->foreach_root_item([&](const AssetCatalogTreeItem &actual_item) {
ASSERT_LT(i, expected_paths.size())
<< "More catalogs in tree root than expected; did not expect "
<< actual_item.catalog_path();
assert_expected_item(expected_paths[i], actual_item);
i++;
});
}
/**
* Iterate over the child items of \a parent_item and check if the items map exactly to \a
* expected_paths. Similar to #assert_expected_tree_items() but calls
* #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item().
*/
void assert_expected_tree_item_child_items(AssetCatalogTreeItem *parent_item,
const std::vector<AssetCatalogPath> &expected_paths)
{
int i = 0;
parent_item->foreach_child([&](const AssetCatalogTreeItem &actual_item) {
ASSERT_LT(i, expected_paths.size())
<< "More catalogs in tree item than expected; did not expect "
<< actual_item.catalog_path();
assert_expected_item(expected_paths[i], actual_item);
i++;
});
}
/* Used by on_blendfile_save__from_memory_into_existing_asset_lib* test functions. */
void save_from_memory_into_existing_asset_lib(const bool should_top_level_cdf_exist)
{
@ -356,149 +236,6 @@ TEST_F(AssetCatalogTest, is_first_loaded_flag)
<< "The first-seen definition of a catalog should be returned";
}
TEST_F(AssetCatalogTest, insert_item_into_tree)
{
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog_empty_path = AssetCatalog::from_path("");
tree.insert_item(*catalog_empty_path);
assert_expected_tree_items(&tree, {});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("item");
tree.insert_item(*catalog);
assert_expected_tree_items(&tree, {"item"});
/* Insert child after parent already exists. */
std::unique_ptr<AssetCatalog> child_catalog = AssetCatalog::from_path("item/child");
tree.insert_item(*catalog);
assert_expected_tree_items(&tree, {"item", "item/child"});
std::vector<AssetCatalogPath> expected_paths;
/* Test inserting multi-component sub-path. */
std::unique_ptr<AssetCatalog> grandgrandchild_catalog = AssetCatalog::from_path(
"item/child/grandchild/grandgrandchild");
tree.insert_item(*catalog);
expected_paths = {
"item", "item/child", "item/child/grandchild", "item/child/grandchild/grandgrandchild"};
assert_expected_tree_items(&tree, expected_paths);
std::unique_ptr<AssetCatalog> root_level_catalog = AssetCatalog::from_path("root level");
tree.insert_item(*catalog);
expected_paths = {"item",
"item/child",
"item/child/grandchild",
"item/child/grandchild/grandgrandchild",
"root level"};
assert_expected_tree_items(&tree, expected_paths);
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("item/child");
tree.insert_item(*catalog);
assert_expected_tree_items(&tree, {"item", "item/child"});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("white space");
tree.insert_item(*catalog);
assert_expected_tree_items(&tree, {"white space"});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("/item/white space");
tree.insert_item(*catalog);
assert_expected_tree_items(&tree, {"item", "item/white space"});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog_unicode_path = AssetCatalog::from_path("Ružena");
tree.insert_item(*catalog_unicode_path);
assert_expected_tree_items(&tree, {"Ružena"});
catalog_unicode_path = AssetCatalog::from_path("Ružena/Ružena");
tree.insert_item(*catalog_unicode_path);
assert_expected_tree_items(&tree, {"Ružena", "Ružena/Ružena"});
}
}
TEST_F(AssetCatalogTest, load_single_file_into_tree)
{
AssetCatalogService service(asset_library_root_);
service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt");
/* Contains not only paths from the CDF but also the missing parents (implicitly defined
* catalogs). */
std::vector<AssetCatalogPath> expected_paths{
"character",
"character/Ellie",
"character/Ellie/backslashes",
"character/Ellie/poselib",
"character/Ellie/poselib/tailslash",
"character/Ellie/poselib/white space",
"character/Ružena",
"character/Ružena/poselib",
"character/Ružena/poselib/face",
"character/Ružena/poselib/hand",
"path", /* Implicit. */
"path/without", /* Implicit. */
"path/without/simplename", /* From CDF. */
};
AssetCatalogTree *tree = service.get_catalog_tree();
assert_expected_tree_items(tree, expected_paths);
}
TEST_F(AssetCatalogTest, foreach_in_tree)
{
{
AssetCatalogTree tree{};
const std::vector<AssetCatalogPath> no_catalogs{};
assert_expected_tree_items(&tree, no_catalogs);
assert_expected_tree_root_items(&tree, no_catalogs);
/* Need a root item to check child items. */
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("something");
tree.insert_item(*catalog);
tree.foreach_root_item([&no_catalogs, this](AssetCatalogTreeItem &item) {
assert_expected_tree_item_child_items(&item, no_catalogs);
});
}
AssetCatalogService service(asset_library_root_);
service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt");
std::vector<AssetCatalogPath> expected_root_items{{"character", "path"}};
AssetCatalogTree *tree = service.get_catalog_tree();
assert_expected_tree_root_items(tree, expected_root_items);
/* Test if the direct children of the root item are what's expected. */
std::vector<std::vector<AssetCatalogPath>> expected_root_child_items = {
/* Children of the "character" root item. */
{"character/Ellie", "character/Ružena"},
/* Children of the "path" root item. */
{"path/without"},
};
int i = 0;
tree->foreach_root_item([&expected_root_child_items, &i, this](AssetCatalogTreeItem &item) {
assert_expected_tree_item_child_items(&item, expected_root_child_items[i]);
i++;
});
}
TEST_F(AssetCatalogTest, find_catalog_by_path)
{
TestableAssetCatalogService service(asset_library_root_);
@ -832,7 +569,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf)
};
AssetCatalogTree *tree = service.get_catalog_tree();
assert_expected_tree_items(tree, expected_paths);
AssetCatalogTreeTestFunctions::expect_tree_items(tree, expected_paths);
}
TEST_F(AssetCatalogTest, delete_catalog_parent_by_id)
@ -886,7 +623,7 @@ TEST_F(AssetCatalogTest, delete_catalog_parent_by_path)
};
AssetCatalogTree *tree = service.get_catalog_tree();
assert_expected_tree_items(tree, expected_paths);
AssetCatalogTreeTestFunctions::expect_tree_items(tree, expected_paths);
}
TEST_F(AssetCatalogTest, delete_catalog_write_to_disk)

View File

@ -0,0 +1,241 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2020 Blender Foundation. All rights reserved. */
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "BKE_appdir.h"
#include "BKE_preferences.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "DNA_asset_types.h"
#include "DNA_userdef_types.h"
#include "CLG_log.h"
#include "testing/testing.h"
#include "asset_library_test_common.hh"
namespace blender::asset_system::tests {
static void compare_item_with_path(const AssetCatalogPath &expected_path,
const AssetCatalogTreeItem &actual_item)
{
if (expected_path != actual_item.catalog_path().str()) {
/* This will fail, but with a nicer error message than just calling FAIL(). */
EXPECT_EQ(expected_path, actual_item.catalog_path());
return;
}
/* Is the catalog name as expected? "character", "Ellie", ... */
EXPECT_EQ(expected_path.name(), actual_item.get_name());
/* Does the computed number of parents match? */
const std::string expected_path_str = expected_path.str();
const size_t expected_parent_count = std::count(
expected_path_str.begin(), expected_path_str.end(), AssetCatalogPath::SEPARATOR);
EXPECT_EQ(expected_parent_count, actual_item.count_parents());
}
/**
* Recursively iterate over all tree items using #AssetCatalogTree::foreach_item() and check if
* the items map exactly to \a expected_paths.
*/
void AssetCatalogTreeTestFunctions::expect_tree_items(
AssetCatalogTree *tree, const std::vector<AssetCatalogPath> &expected_paths)
{
int i = 0;
tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) {
ASSERT_LT(i, expected_paths.size())
<< "More catalogs in tree than expected; did not expect " << actual_item.catalog_path();
compare_item_with_path(expected_paths[i], actual_item);
i++;
});
}
/**
* Iterate over the root items of \a tree and check if the items map exactly to \a
* expected_paths. Similar to #assert_expected_tree_items() but calls
* #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item().
*/
void AssetCatalogTreeTestFunctions::expect_tree_root_items(
AssetCatalogTree *tree, const std::vector<AssetCatalogPath> &expected_paths)
{
int i = 0;
tree->foreach_root_item([&](const AssetCatalogTreeItem &actual_item) {
ASSERT_LT(i, expected_paths.size())
<< "More catalogs in tree root than expected; did not expect "
<< actual_item.catalog_path();
compare_item_with_path(expected_paths[i], actual_item);
i++;
});
}
/**
* Iterate over the child items of \a parent_item and check if the items map exactly to \a
* expected_paths. Similar to #assert_expected_tree_items() but calls
* #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item().
*/
void AssetCatalogTreeTestFunctions::expect_tree_item_child_items(
AssetCatalogTreeItem *parent_item, const std::vector<AssetCatalogPath> &expected_paths)
{
int i = 0;
parent_item->foreach_child([&](const AssetCatalogTreeItem &actual_item) {
ASSERT_LT(i, expected_paths.size())
<< "More catalogs in tree item than expected; did not expect "
<< actual_item.catalog_path();
compare_item_with_path(expected_paths[i], actual_item);
i++;
});
}
class AssetCatalogTreeTest : public AssetLibraryTestBase, public AssetCatalogTreeTestFunctions {
};
TEST_F(AssetCatalogTreeTest, insert_item_into_tree)
{
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog_empty_path = AssetCatalog::from_path("");
tree.insert_item(*catalog_empty_path);
expect_tree_items(&tree, {});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("item");
tree.insert_item(*catalog);
expect_tree_items(&tree, {"item"});
/* Insert child after parent already exists. */
std::unique_ptr<AssetCatalog> child_catalog = AssetCatalog::from_path("item/child");
tree.insert_item(*catalog);
expect_tree_items(&tree, {"item", "item/child"});
std::vector<AssetCatalogPath> expected_paths;
/* Test inserting multi-component sub-path. */
std::unique_ptr<AssetCatalog> grandgrandchild_catalog = AssetCatalog::from_path(
"item/child/grandchild/grandgrandchild");
tree.insert_item(*catalog);
expected_paths = {
"item", "item/child", "item/child/grandchild", "item/child/grandchild/grandgrandchild"};
expect_tree_items(&tree, expected_paths);
std::unique_ptr<AssetCatalog> root_level_catalog = AssetCatalog::from_path("root level");
tree.insert_item(*catalog);
expected_paths = {"item",
"item/child",
"item/child/grandchild",
"item/child/grandchild/grandgrandchild",
"root level"};
expect_tree_items(&tree, expected_paths);
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("item/child");
tree.insert_item(*catalog);
expect_tree_items(&tree, {"item", "item/child"});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("white space");
tree.insert_item(*catalog);
expect_tree_items(&tree, {"white space"});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("/item/white space");
tree.insert_item(*catalog);
expect_tree_items(&tree, {"item", "item/white space"});
}
{
AssetCatalogTree tree;
std::unique_ptr<AssetCatalog> catalog_unicode_path = AssetCatalog::from_path("Ružena");
tree.insert_item(*catalog_unicode_path);
expect_tree_items(&tree, {"Ružena"});
catalog_unicode_path = AssetCatalog::from_path("Ružena/Ružena");
tree.insert_item(*catalog_unicode_path);
expect_tree_items(&tree, {"Ružena", "Ružena/Ružena"});
}
}
TEST_F(AssetCatalogTreeTest, load_single_file_into_tree)
{
AssetCatalogService service(asset_library_root_);
service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt");
/* Contains not only paths from the CDF but also the missing parents (implicitly defined
* catalogs). */
std::vector<AssetCatalogPath> expected_paths{
"character",
"character/Ellie",
"character/Ellie/backslashes",
"character/Ellie/poselib",
"character/Ellie/poselib/tailslash",
"character/Ellie/poselib/white space",
"character/Ružena",
"character/Ružena/poselib",
"character/Ružena/poselib/face",
"character/Ružena/poselib/hand",
"path", /* Implicit. */
"path/without", /* Implicit. */
"path/without/simplename", /* From CDF. */
};
AssetCatalogTree *tree = service.get_catalog_tree();
expect_tree_items(tree, expected_paths);
}
TEST_F(AssetCatalogTreeTest, foreach_in_tree)
{
{
AssetCatalogTree tree{};
const std::vector<AssetCatalogPath> no_catalogs{};
expect_tree_items(&tree, no_catalogs);
expect_tree_root_items(&tree, no_catalogs);
/* Need a root item to check child items. */
std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path("something");
tree.insert_item(*catalog);
tree.foreach_root_item([&no_catalogs](AssetCatalogTreeItem &item) {
expect_tree_item_child_items(&item, no_catalogs);
});
}
AssetCatalogService service(asset_library_root_);
service.load_from_disk(asset_library_root_ + SEP_STR + "blender_assets.cats.txt");
std::vector<AssetCatalogPath> expected_root_items{{"character", "path"}};
AssetCatalogTree *tree = service.get_catalog_tree();
expect_tree_root_items(tree, expected_root_items);
/* Test if the direct children of the root item are what's expected. */
std::vector<std::vector<AssetCatalogPath>> expected_root_child_items = {
/* Children of the "character" root item. */
{"character/Ellie", "character/Ružena"},
/* Children of the "path" root item. */
{"path/without"},
};
int i = 0;
tree->foreach_root_item([&expected_root_child_items, &i](AssetCatalogTreeItem &item) {
expect_tree_item_child_items(&item, expected_root_child_items[i]);
i++;
});
}
} // namespace blender::asset_system::tests

View File

@ -0,0 +1,109 @@
/* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include <string>
#include <vector>
#include "BKE_appdir.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "CLG_log.h"
#include "testing/testing.h"
namespace blender::asset_system {
class AssetCatalogTree;
class AssetCatalogTreeItem;
class AssetCatalogPath;
} // namespace blender::asset_system
namespace blender::asset_system::tests {
/**
* Functionality to setup and access directories on disk within which asset library related testing
* can be done.
*/
class AssetLibraryTestBase : public testing::Test {
protected:
std::string asset_library_root_;
std::string temp_library_path_;
static void SetUpTestSuite()
{
testing::Test::SetUpTestSuite();
CLG_init();
}
static void TearDownTestSuite()
{
CLG_exit();
testing::Test::TearDownTestSuite();
}
void SetUp() override
{
const std::string test_files_dir = blender::tests::flags_test_asset_dir();
if (test_files_dir.empty()) {
FAIL();
}
asset_library_root_ = test_files_dir + SEP_STR + "asset_library";
temp_library_path_ = "";
}
void TearDown() override
{
if (!temp_library_path_.empty()) {
BLI_delete(temp_library_path_.c_str(), true, true);
temp_library_path_ = "";
}
}
/* Register a temporary path, which will be removed at the end of the test.
* The returned path ends in a slash. */
std::string use_temp_path()
{
BKE_tempdir_init("");
const std::string tempdir = BKE_tempdir_session();
temp_library_path_ = tempdir + "test-temporary-path" + SEP_STR;
return temp_library_path_;
}
std::string create_temp_path()
{
std::string path = use_temp_path();
BLI_dir_create_recursive(path.c_str());
return path;
}
};
class AssetCatalogTreeTestFunctions {
public:
/**
* Recursively iterate over all tree items using #AssetCatalogTree::foreach_item() and check if
* the items map exactly to \a expected_paths.
*/
static void expect_tree_items(AssetCatalogTree *tree,
const std::vector<AssetCatalogPath> &expected_paths);
/**
* Iterate over the root items of \a tree and check if the items map exactly to \a
* expected_paths. Similar to #assert_expected_tree_items() but calls
* #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item().
*/
static void expect_tree_root_items(AssetCatalogTree *tree,
const std::vector<AssetCatalogPath> &expected_paths);
/**
* Iterate over the child items of \a parent_item and check if the items map exactly to \a
* expected_paths. Similar to #assert_expected_tree_items() but calls
* #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item().
*/
static void expect_tree_item_child_items(AssetCatalogTreeItem *parent_item,
const std::vector<AssetCatalogPath> &expected_paths);
};
} // namespace blender::asset_system::tests

View File

@ -480,6 +480,7 @@ set(SRC
BKE_type_conversions.hh
BKE_undo_system.h
BKE_unit.h
BKE_uv_islands.hh
BKE_vfont.h
BKE_vfontdata.h
BKE_viewer_path.h
@ -504,6 +505,7 @@ set(SRC
intern/multires_unsubdivide.h
intern/ocean_intern.h
intern/pbvh_intern.h
intern/pbvh_uv_islands.hh
intern/subdiv_converter.h
intern/subdiv_inline.h
)

View File

@ -1127,7 +1127,7 @@ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa)
/* decide on jumping & liftoff */
if (bpa->data.mode == eBoidMode_OnLand) {
/* fuzziness makes boids capable of misjudgement */
/* Fuzziness makes boids capable of misjudgment. */
float mul = 1.0f + state->rule_fuzziness;
if (boids->options & BOID_ALLOW_FLIGHT && bbd->wanted_co[2] > 0.0f) {

View File

@ -1876,7 +1876,7 @@ KeyBlock *BKE_keyblock_add_ctime(Key *key, const char *name, const bool do_force
const float cpos = key->ctime / 100.0f;
/* In case of absolute keys, there is no point in adding more than one key with the same pos.
* Hence only set new keybloc pos to current time if none previous one already use it.
* Hence only set new key-block pos to current time if none previous one already use it.
* Now at least people just adding absolute keys without touching to ctime
* won't have to systematically use retiming func (and have ordering issues, too). See T39897.
*/

View File

@ -505,14 +505,19 @@ static int customdata_compare(
for (int i1 = 0; i1 < c1->totlayer; i1++) {
l1 = c1->layers + i1;
if (l1->anonymous_id != nullptr) {
continue;
}
bool found_corresponding_layer = false;
for (int i2 = 0; i2 < c2->totlayer; i2++) {
l2 = c2->layers + i2;
if (l1->type != l2->type || !STREQ(l1->name, l2->name) || l1->anonymous_id != nullptr ||
l2->anonymous_id != nullptr) {
if (l1->type != l2->type || !STREQ(l1->name, l2->name) || l2->anonymous_id != nullptr) {
continue;
}
/* At this point `l1` and `l2` have the same name and type, so they should be compared. */
found_corresponding_layer = true;
switch (l1->type) {
/* We're order-agnostic for edges here. */
case CD_MEDGE: {
@ -708,6 +713,11 @@ static int customdata_compare(
}
}
}
if (!found_corresponding_layer) {
if ((1 << l1->type) & CD_MASK_PROP_ALL) {
return MESHCMP_CDLAYERS_MISMATCH;
}
}
}
return 0;

View File

@ -361,8 +361,10 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
return;
}
uv_islands::MeshData mesh_data(
pbvh->looptri, pbvh->totprim, pbvh->totvert, pbvh->mloop, ldata_uv);
uv_islands::MeshData mesh_data({pbvh->looptri, pbvh->totprim},
{pbvh->mloop, pbvh->totprim},
pbvh->totvert,
{ldata_uv, pbvh->totprim});
uv_islands::UVIslands islands(mesh_data);
uv_islands::UVIslandsMask uv_masks;
@ -385,7 +387,7 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
islands.extend_borders(uv_masks);
update_geom_primitives(*pbvh, mesh_data);
UVPrimitiveLookup uv_primitive_lookup(mesh_data.looptri_len, islands);
UVPrimitiveLookup uv_primitive_lookup(mesh_data.looptris.size(), islands);
EncodePixelsUserData user_data;
user_data.pbvh = pbvh;

View File

@ -95,8 +95,8 @@ rctf MeshPrimitive::uv_bounds() const
static void mesh_data_init_vertices(MeshData &mesh_data)
{
mesh_data.vertices.reserve(mesh_data.vert_len);
for (int64_t i = 0; i < mesh_data.vert_len; i++) {
mesh_data.vertices.reserve(mesh_data.verts_num);
for (int64_t i = 0; i < mesh_data.verts_num; i++) {
MeshVertex vert;
vert.v = i;
mesh_data.vertices.append(vert);
@ -105,9 +105,9 @@ static void mesh_data_init_vertices(MeshData &mesh_data)
static void mesh_data_init_primitives(MeshData &mesh_data)
{
mesh_data.primitives.reserve(mesh_data.looptri_len);
for (int64_t i = 0; i < mesh_data.looptri_len; i++) {
const MLoopTri &tri = mesh_data.looptri[i];
mesh_data.primitives.reserve(mesh_data.looptris.size());
for (int64_t i = 0; i < mesh_data.looptris.size(); i++) {
const MLoopTri &tri = mesh_data.looptris[i];
MeshPrimitive primitive;
primitive.index = i;
primitive.poly = tri.poly;
@ -115,7 +115,7 @@ static void mesh_data_init_primitives(MeshData &mesh_data)
for (int j = 0; j < 3; j++) {
MeshUVVert uv_vert;
uv_vert.loop = tri.tri[j];
uv_vert.vertex = &mesh_data.vertices[mesh_data.mloop[uv_vert.loop].v];
uv_vert.vertex = &mesh_data.vertices[mesh_data.loops[uv_vert.loop].v];
uv_vert.uv = mesh_data.mloopuv[uv_vert.loop].uv;
primitive.vertices.append(uv_vert);
}
@ -125,14 +125,14 @@ static void mesh_data_init_primitives(MeshData &mesh_data)
static void mesh_data_init_edges(MeshData &mesh_data)
{
mesh_data.edges.reserve(mesh_data.looptri_len * 2);
EdgeHash *eh = BLI_edgehash_new_ex(__func__, mesh_data.looptri_len * 3);
for (int64_t i = 0; i < mesh_data.looptri_len; i++) {
const MLoopTri &tri = mesh_data.looptri[i];
mesh_data.edges.reserve(mesh_data.looptris.size() * 2);
EdgeHash *eh = BLI_edgehash_new_ex(__func__, mesh_data.looptris.size() * 3);
for (int64_t i = 0; i < mesh_data.looptris.size(); i++) {
const MLoopTri &tri = mesh_data.looptris[i];
MeshPrimitive &primitive = mesh_data.primitives[i];
for (int j = 0; j < 3; j++) {
int v1 = mesh_data.mloop[tri.tri[j]].v;
int v2 = mesh_data.mloop[tri.tri[(j + 1) % 3]].v;
int v1 = mesh_data.loops[tri.tri[j]].v;
int v2 = mesh_data.loops[tri.tri[(j + 1) % 3]].v;
void **edge_index_ptr;
int64_t edge_index;
@ -215,16 +215,11 @@ static void mesh_data_init(MeshData &mesh_data)
mesh_data_init_primitive_uv_island_ids(mesh_data);
}
MeshData::MeshData(const MLoopTri *looptri,
const int64_t looptri_len,
const int64_t vert_len,
const MLoop *mloop,
const MLoopUV *mloopuv)
: looptri(looptri),
looptri_len(looptri_len),
vert_len(vert_len),
mloop(mloop),
mloopuv(mloopuv)
MeshData::MeshData(const Span<MLoopTri> looptris,
const Span<MLoop> loops,
const int verts_num,
const Span<MLoopUV> mloopuv)
: looptris(looptris), verts_num(verts_num), loops(loops), mloopuv(mloopuv)
{
mesh_data_init(*this);
}

View File

@ -92,11 +92,10 @@ struct MeshPrimitive {
*/
struct MeshData {
public:
const MLoopTri *looptri;
const int64_t looptri_len;
const int64_t vert_len;
const MLoop *mloop;
const MLoopUV *mloopuv;
const Span<MLoopTri> looptris;
const int64_t verts_num;
const Span<MLoop> loops;
const Span<MLoopUV> mloopuv;
Vector<MeshPrimitive> primitives;
Vector<MeshEdge> edges;
@ -105,11 +104,10 @@ struct MeshData {
int64_t uv_island_len;
public:
explicit MeshData(const MLoopTri *looptri,
const int64_t looptri_len,
const int64_t vert_len,
const MLoop *mloop,
const MLoopUV *mloopuv);
explicit MeshData(const Span<MLoopTri> looptris,
const Span<MLoop> loops,
const int verts_num,
const Span<MLoopUV> mloopuv);
};
struct UVVertex {

View File

@ -320,12 +320,15 @@ struct VolumeGrid {
openvdb::io::File file(filepath);
/* Isolate file loading since that's potentially multithreaded and we are
/* Isolate file loading since that's potentially multi-threaded and we are
* holding a mutex lock. */
blender::threading::isolate_task([&] {
try {
/* Disable delay loading and file copying, this has poor performance
* on network drivers. */
const bool delay_load = false;
file.setCopyMaxBytes(0);
file.open();
file.open(delay_load);
openvdb::GridBase::Ptr vdb_grid = file.readGrid(name());
entry->grid->setTree(vdb_grid->baseTreePtr());
}
@ -883,8 +886,11 @@ bool BKE_volume_load(const Volume *volume, const Main *bmain)
openvdb::GridPtrVec vdb_grids;
try {
/* Disable delay loading and file copying, this has poor performance
* on network drivers. */
const bool delay_load = false;
file.setCopyMaxBytes(0);
file.open();
file.open(delay_load);
vdb_grids = *(file.readAllGridMetadata());
grids.metadata = file.getMetadata();
}

View File

@ -20,9 +20,9 @@ vec3 compute_chromatic_distortion_scale(float distance_squared)
/* Compute the image coordinates after distortion by the given distortion scale computed by the
* compute_distortion_scale function. Note that the function expects centered normalized UV
* coordinates but outputs non-centered image coordinates. */
vec2 compute_distorted_uv(vec2 uv, float scale)
vec2 compute_distorted_uv(vec2 uv, float uv_scale)
{
return (uv * scale + 0.5) * texture_size(input_tx) - 0.5;
return (uv * uv_scale + 0.5) * texture_size(input_tx) - 0.5;
}
/* Compute the number of integration steps that should be used to approximate the distorted pixel

View File

@ -1,35 +1,35 @@
/* A shorthand for 1D textureSize with a zero LOD. */
int texture_size(sampler1D sampler)
int texture_size(sampler1D sampler_1d)
{
return textureSize(sampler, 0);
return textureSize(sampler_1d, 0);
}
/* A shorthand for 1D texelFetch with zero LOD and bounded access clamped to border. */
vec4 texture_load(sampler1D sampler, int x)
vec4 texture_load(sampler1D sampler_1d, int x)
{
const int texture_bound = texture_size(sampler) - 1;
return texelFetch(sampler, clamp(x, 0, texture_bound), 0);
const int texture_bound = texture_size(sampler_1d) - 1;
return texelFetch(sampler_1d, clamp(x, 0, texture_bound), 0);
}
/* A shorthand for 2D textureSize with a zero LOD. */
ivec2 texture_size(sampler2D sampler)
ivec2 texture_size(sampler2D sampler_2d)
{
return textureSize(sampler, 0);
return textureSize(sampler_2d, 0);
}
/* A shorthand for 2D texelFetch with zero LOD and bounded access clamped to border. */
vec4 texture_load(sampler2D sampler, ivec2 texel)
vec4 texture_load(sampler2D sampler_2d, ivec2 texel)
{
const ivec2 texture_bounds = texture_size(sampler) - ivec2(1);
return texelFetch(sampler, clamp(texel, ivec2(0), texture_bounds), 0);
const ivec2 texture_bounds = texture_size(sampler_2d) - ivec2(1);
return texelFetch(sampler_2d, clamp(texel, ivec2(0), texture_bounds), 0);
}
/* A shorthand for 2D texelFetch with zero LOD and a fallback value for out-of-bound access. */
vec4 texture_load(sampler2D sampler, ivec2 texel, vec4 fallback)
vec4 texture_load(sampler2D sampler_2d, ivec2 texel, vec4 fallback)
{
const ivec2 texture_bounds = texture_size(sampler) - ivec2(1);
const ivec2 texture_bounds = texture_size(sampler_2d) - ivec2(1);
if (any(lessThan(texel, ivec2(0))) || any(greaterThan(texel, texture_bounds))) {
return fallback;
}
return texelFetch(sampler, texel, 0);
return texelFetch(sampler_2d, texel, 0);
}

View File

@ -280,6 +280,7 @@ set(SRC
engines/image/image_buffer_cache.hh
engines/image/image_drawing_mode.hh
engines/image/image_engine.h
engines/image/image_enums.hh
engines/image/image_instance_data.hh
engines/image/image_partial_updater.hh
engines/image/image_private.hh
@ -534,6 +535,7 @@ set(GLSL_SRC
intern/draw_command_shared.hh
intern/draw_common_shader_shared.h
intern/draw_defines.h
intern/draw_pointcloud_private.hh
intern/draw_shader_shared.h
engines/gpencil/shaders/gpencil_frag.glsl

View File

@ -1158,7 +1158,7 @@ World *EEVEE_world_default_get(void)
* Source is provided separately, rather than via create-info as source is manipulated
* by `eevee_shader_material_create_info_amend`.
*
* We also retain the previous behaviour for ensuring library includes occur in the
* We also retain the previous behavior for ensuring library includes occur in the
* correct order. */
static const char *eevee_get_vert_info(int options, char **r_src)
{
@ -1288,7 +1288,7 @@ static char *eevee_get_defines(int options)
* CreateInfo's for EEVEE materials are declared in:
* `eevee/shaders/infos/eevee_legacy_material_info.hh`
*
* This function should only contain defines which alter behaviour, but do not affect shader
* This function should only contain defines which alter behavior, but do not affect shader
* resources. */
if ((options & VAR_WORLD_BACKGROUND) != 0) {

View File

@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(eevee_legacy_irradiance_lib)
GPU_SHADER_CREATE_INFO(eevee_legacy_common_utiltex_lib)
.sampler(2, ImageType::FLOAT_2D_ARRAY, "utilTex");
/* Raytrace lib. */
/* Ray-trace lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_raytrace_lib)
.additional_info("draw_view")
.additional_info("eevee_legacy_common_lib")
@ -33,7 +33,7 @@ GPU_SHADER_CREATE_INFO(eevee_legacy_ambient_occlusion_lib)
.additional_info("eevee_legacy_raytrace_lib")
.sampler(5, ImageType::FLOAT_2D, "horizonBuffer");
/* Lightprobe lib. */
/* Light-probe lib. */
GPU_SHADER_CREATE_INFO(eevee_legacy_lightprobe_lib)
.additional_info("eevee_legacy_common_lib")
.additional_info("eevee_legacy_common_utiltex_lib")
@ -128,7 +128,7 @@ GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_hair)
GPU_SHADER_CREATE_INFO(eevee_legacy_surface_lib_pointcloud)
.define("USE_SURFACE_LIB_POINTCLOUD")
/* Pointcloud still uses the common interface as well. */
/* Point-cloud still uses the common interface as well. */
.additional_info("eevee_legacy_surface_lib_common")
.vertex_out(eevee_legacy_surface_point_cloud_iface);

View File

@ -2,7 +2,7 @@
#pragma once
/* Voluemtric iface. */
/* Volumetric iface. */
GPU_SHADER_INTERFACE_INFO(legacy_volume_vert_geom_iface, "volumetric_vert_iface")
.smooth(Type::VEC4, "vPos");

View File

@ -9,7 +9,9 @@
* dragging larger headers into the createInfo pipeline which would cause problems.
*/
#pragma once
#ifndef GPU_SHADER
# pragma once
#endif
/* Hierarchical Z down-sampling. */
#define HIZ_MIP_COUNT 8

View File

@ -26,7 +26,7 @@ shared uint bg_min_coc;
shared uint bg_max_coc;
shared uint bg_min_intersectable_coc;
const uint dof_tile_large_coc_uint = floatBitsToUint(dof_tile_large_coc);
uint dof_tile_large_coc_uint = floatBitsToUint(dof_tile_large_coc);
void main()
{

View File

@ -13,8 +13,12 @@ vec2 proj(vec4 pos)
return (0.5 * (pos.xy / pos.w) + 0.5) * sizeViewport.xy;
}
#define SET_INTENSITY(A, B, C, min, max) \
(((1.0 - (float(C - B) / float(C - A))) * (max - min)) + min)
float calc_intensity(int segment_start, int segment_current, int segment_end, float min, float max)
{
return ((1.0 - (float(segment_end - segment_current) / float(segment_end - segment_start))) *
(max - min)) +
min;
}
void main()
{
@ -39,10 +43,10 @@ void main()
else {
/* black - before frameCurrent */
if (selected) {
intensity = SET_INTENSITY(frameStart, frame, frameCurrent, 0.25, 0.75);
intensity = calc_intensity(frameStart, frame, frameCurrent, 0.25, 0.75);
}
else {
intensity = SET_INTENSITY(frameStart, frame, frameCurrent, 0.68, 0.92);
intensity = calc_intensity(frameStart, frame, frameCurrent, 0.68, 0.92);
}
interp.color.rgb = mix(colorWire.rgb, blend_base, intensity);
}
@ -55,10 +59,10 @@ void main()
else {
/* blue - after frameCurrent */
if (selected) {
intensity = SET_INTENSITY(frameCurrent, frame, frameEnd, 0.25, 0.75);
intensity = calc_intensity(frameCurrent, frame, frameEnd, 0.25, 0.75);
}
else {
intensity = SET_INTENSITY(frameCurrent, frame, frameEnd, 0.68, 0.92);
intensity = calc_intensity(frameCurrent, frame, frameEnd, 0.68, 0.92);
}
interp.color.rgb = mix(colorBonePose.rgb, blend_base, intensity);

View File

@ -5,5 +5,6 @@ void main()
{
vData.pos = pos;
vData.frontPosition = point_object_to_ndc(pos);
vData.backPosition = point_object_to_ndc(pos + lightDirection * lightDistance);
vec3 back_pos = pos + lightDirection * lightDistance;
vData.backPosition = point_object_to_ndc(back_pos);
}

View File

@ -217,4 +217,7 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer
void workbench_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
if ((view_layer->passflag & SCE_PASS_Z) != 0) {
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_Z, 1, "Z", SOCK_FLOAT);
}
}

View File

@ -569,7 +569,7 @@ SamplerState PointSampler
# define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0)
# endif
#endif
#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) || defined(GPU_METAL)
#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) || defined(GPU_METAL) || defined(GPU_VULKAN)
# define SMAATexture2D(tex) sampler2D tex
# define SMAATexturePass2D(tex) tex
# define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0)
@ -583,8 +583,28 @@ SamplerState PointSampler
# define lerp(a, b, t) mix(a, b, t)
# define saturate(a) clamp(a, 0.0, 1.0)
# if defined(SMAA_GLSL_4)
# define mad(a, b, c) fma(a, b, c)
# define SMAAGather(tex, coord) textureGather(tex, coord)
# endif
# if defined(SMAA_GLSL_4)
# define mad(a, b, c) fma(a, b, c)
# elif defined(GPU_VULKAN)
/* NOTE(Vulkan) mad macro doesn't work, define each override as work-around. */
vec4 mad(vec4 a, vec4 b, vec4 c)
{
return fma(a, b, c);
}
vec3 mad(vec3 a, vec3 b, vec3 c)
{
return fma(a, b, c);
}
vec2 mad(vec2 a, vec2 b, vec2 c)
{
return fma(a, b, c);
}
float mad(float a, float b, float c)
{
return fma(a, b, c);
}
# else
# define mad(a, b, c) (a * b + c)
# endif

View File

@ -234,12 +234,28 @@ uniform mat4 ModelMatrixInverse;
(ProjectionMatrix * (ViewMatrix * vec4((ModelMatrix * vec4(p, 1.0)).xyz, 1.0)))
#define point_object_to_view(p) ((ViewMatrix * vec4((ModelMatrix * vec4(p, 1.0)).xyz, 1.0)).xyz)
#define point_object_to_world(p) ((ModelMatrix * vec4(p, 1.0)).xyz)
#define point_view_to_ndc(p) (ProjectionMatrix * vec4(p, 1.0))
#define point_view_to_object(p) ((ModelMatrixInverse * (ViewMatrixInverse * vec4(p, 1.0))).xyz)
#define point_view_to_world(p) ((ViewMatrixInverse * vec4(p, 1.0)).xyz)
#define point_world_to_ndc(p) (ProjectionMatrix * (ViewMatrix * vec4(p, 1.0)))
#define point_world_to_object(p) ((ModelMatrixInverse * vec4(p, 1.0)).xyz)
#define point_world_to_view(p) ((ViewMatrix * vec4(p, 1.0)).xyz)
vec4 point_view_to_ndc(vec3 p)
{
return ProjectionMatrix * vec4(p, 1.0);
}
vec3 point_view_to_world(vec3 p)
{
return (ViewMatrixInverse * vec4(p, 1.0)).xyz;
}
vec4 point_world_to_ndc(vec3 p)
{
return ProjectionMatrix * (ViewMatrix * vec4(p, 1.0));
}
vec3 point_world_to_view(vec3 p)
{
return (ViewMatrix * vec4(p, 1.0)).xyz;
}
/* Due to some shader compiler bug, we somewhat need to access gl_VertexID
* to make vertex shaders work. even if it's actually dead code. */

View File

@ -344,9 +344,16 @@ void animviz_motionpath_compute_range(Object *ob, Scene *scene)
{
bAnimVizSettings *avs = ob->mode == OB_MODE_POSE ? &ob->pose->avs : &ob->avs;
if (avs->path_range == MOTIONPATH_RANGE_MANUAL) {
/* Don't touch manually-determined ranges. */
return;
}
const bool has_action = ob->adt && ob->adt->action;
if (avs->path_range == MOTIONPATH_RANGE_SCENE || !has_action ||
BLI_listbase_is_empty(&ob->adt->action->curves)) {
/* Default to the scene (preview) range if there is no animation data to
* find selected keys in. */
avs->path_sf = PSFRA;
avs->path_ef = PEFRA;
return;
@ -367,6 +374,7 @@ void animviz_motionpath_compute_range(Object *ob, Scene *scene)
case MOTIONPATH_RANGE_KEYS_ALL:
ED_keylist_all_keys_frame_range(keylist, &frame_range);
break;
case MOTIONPATH_RANGE_MANUAL:
case MOTIONPATH_RANGE_SCENE:
BLI_assert_msg(false, "This should not happen, function should have exited earlier.");
};

View File

@ -2,6 +2,7 @@
#pragma once
#include "BLI_vector_set.hh"
#include "ED_node.h"
struct SpaceNode;
@ -11,6 +12,8 @@ struct bNodeTree;
namespace blender::ed::space_node {
VectorSet<bNode *> get_selected_nodes(bNodeTree &node_tree);
void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region);
/**

View File

@ -995,7 +995,7 @@ static void ui_apply_but_funcs_after(bContext *C)
BLI_listbase_clear(&UIAfterFuncs);
LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) {
uiAfterFunc after = *afterf; /* copy to avoid memleak on exit() */
uiAfterFunc after = *afterf; /* Copy to avoid memory leak on exit(). */
BLI_freelinkN(&funcs, afterf);
if (after.context) {

View File

@ -2312,7 +2312,7 @@ int UI_icon_from_rnaptr(const bContext *C, PointerRNA *ptr, int rnaicon, const b
return rnaicon;
}
/* try ID, material, texture or dynapaint slot */
/* Try ID, material, texture or dynamic-paint slot. */
if (RNA_struct_is_ID(ptr->type)) {
id = ptr->owner_id;
}

View File

@ -33,6 +33,7 @@
#include "DEG_depsgraph_build.h"
#include "ED_node.h" /* own include */
#include "ED_node.hh"
#include "ED_render.h"
#include "ED_screen.h"

View File

@ -183,7 +183,6 @@ void node_keymap(wmKeyConfig *keyconf);
rctf node_frame_rect_inside(const bNode &node);
bool node_or_socket_isect_event(const bContext &C, const wmEvent &event);
VectorSet<bNode *> get_selected_nodes(bNodeTree &node_tree);
void node_deselect_all(SpaceNode &snode);
void node_socket_select(bNode *node, bNodeSocket &sock);
void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node);

View File

@ -26,7 +26,8 @@
#include "BKE_node_tree_update.h"
#include "BKE_workspace.h"
#include "ED_node.h" /* own include */
#include "ED_node.h" /* own include */
#include "ED_node.hh" /* own include */
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_view3d.h"

View File

@ -524,7 +524,7 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w
const int snap_on = data_intern->snap_on;
wmKeyMap *keymap = WM_keymap_active(wm, data_intern->keymap);
for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &keymap->items) {
if (kmi->flag & KMI_INACTIVE) {
continue;
}

View File

@ -1336,7 +1336,7 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa
t->state = TRANS_RUNNING;
/* avoid calculating PET */
/* Avoid calculating proportional editing. */
t->options = CTX_NO_PET;
t->mode = TFM_DUMMY;
@ -1859,9 +1859,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
* lead to keymap conflicts for other modes (see T31584)
*/
if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
wmKeyMapItem *kmi;
for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &t->keymap->items) {
if (kmi->flag & KMI_INACTIVE) {
continue;
}

View File

@ -23,10 +23,6 @@
extern "C" {
#endif
/* use node center for transform instead of upper-left corner.
* disabled since it makes absolute snapping not work so nicely
*/
// #define USE_NODE_CENTER
/* -------------------------------------------------------------------- */
/** \name Types/

View File

@ -709,7 +709,7 @@ static int countAndCleanTransDataContainer(TransInfo *t)
static void init_proportional_edit(TransInfo *t)
{
/* NOTE: PET is not usable in pose mode yet T32444. */
/* NOTE: Proportional editing is not usable in pose mode yet T32444. */
if (!ELEM(t->data_type,
&TransConvertType_Action,
&TransConvertType_Curve,
@ -726,7 +726,7 @@ static void init_proportional_edit(TransInfo *t)
&TransConvertType_Node,
&TransConvertType_Object,
&TransConvertType_Particle)) {
/* Disable PET */
/* Disable proportional editing */
t->options |= CTX_NO_PET;
t->flag &= ~T_PROP_EDIT_ALL;
return;

View File

@ -131,7 +131,8 @@ static void createTransCurveVerts(bContext *UNUSED(C), TransInfo *t)
}
}
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
tc->data_len = 0;
continue;

View File

@ -52,7 +52,8 @@ static void createTransLatticeVerts(bContext *UNUSED(C), TransInfo *t)
bp++;
}
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
tc->data_len = 0;
continue;

View File

@ -44,7 +44,8 @@ static void createTransMBallVerts(bContext *UNUSED(C), TransInfo *t)
}
}
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
tc->data_len = 0;
continue;

View File

@ -843,7 +843,8 @@ void transform_convert_mesh_islands_calc(struct BMEditMesh *em,
MEM_freeN(group_index);
}
/* for PET we need islands of 1 so connected vertices can use it with V3D_AROUND_LOCAL_ORIGINS */
/* for proportional editing we need islands of 1 so connected vertices can use it with
* V3D_AROUND_LOCAL_ORIGINS */
if (calc_single_islands) {
BMIter viter;
BMVert *v;
@ -1484,7 +1485,8 @@ static void createTransEditVerts(bContext *UNUSED(C), TransInfo *t)
* transform data is created by selected vertices.
*/
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
continue;
}

View File

@ -94,7 +94,8 @@ static void createTransMeshSkin(bContext *UNUSED(C), TransInfo *t)
continue;
}
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
continue;
}

View File

@ -314,7 +314,8 @@ static void createTransUVs(bContext *C, TransInfo *t)
float *prop_dists = NULL;
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
goto finally;
}

View File

@ -77,7 +77,8 @@ static void createTransMeshVertCData(bContext *UNUSED(C), TransInfo *t)
struct TransMirrorData mirror_data = {NULL};
struct TransMeshDataCrazySpace crazyspace_data = {NULL};
/* Support other objects using PET to adjust these, unless connected is enabled. */
/* Support other objects using proportional editing to adjust these, unless connected is
* enabled. */
if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
continue;
}

View File

@ -9,8 +9,8 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_rect.h"
#include "BKE_context.h"
@ -39,58 +39,55 @@ struct TransCustomDataNode {
/** \name Node Transform Creation
* \{ */
/* transcribe given node into TransData2D for Transforming */
static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const float dpi_fac)
static void create_transform_data_for_node(TransData &td,
TransData2D &td2d,
bNode &node,
const float dpi_fac)
{
float locx, locy;
/* account for parents (nested nodes) */
if (node->parent) {
nodeToView(node->parent, node->locx, node->locy, &locx, &locy);
if (node.parent) {
nodeToView(node.parent, node.locx, node.locy, &locx, &locy);
}
else {
locx = node->locx;
locy = node->locy;
locx = node.locx;
locy = node.locy;
}
/* use top-left corner as the transform origin for nodes */
/* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */
#ifdef USE_NODE_CENTER
td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->runtime->totr) * +0.5f);
td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->runtime->totr) * -0.5f);
#else
td2d->loc[0] = locx * dpi_fac;
td2d->loc[1] = locy * dpi_fac;
#endif
td2d->loc[2] = 0.0f;
td2d->loc2d = td2d->loc; /* current location */
td2d.loc[0] = locx * dpi_fac;
td2d.loc[1] = locy * dpi_fac;
td2d.loc[2] = 0.0f;
td2d.loc2d = td2d.loc; /* current location */
td->loc = td2d->loc;
copy_v3_v3(td->iloc, td->loc);
td.loc = td2d.loc;
copy_v3_v3(td.iloc, td.loc);
/* use node center instead of origin (top-left corner) */
td->center[0] = td2d->loc[0];
td->center[1] = td2d->loc[1];
td->center[2] = 0.0f;
td.center[0] = td2d.loc[0];
td.center[1] = td2d.loc[1];
td.center[2] = 0.0f;
memset(td->axismtx, 0, sizeof(td->axismtx));
td->axismtx[2][2] = 1.0f;
memset(td.axismtx, 0, sizeof(td.axismtx));
td.axismtx[2][2] = 1.0f;
td->ext = nullptr;
td->val = nullptr;
td.ext = nullptr;
td.val = nullptr;
td->flag = TD_SELECTED;
td->dist = 0.0f;
td.flag = TD_SELECTED;
td.dist = 0.0f;
unit_m3(td->mtx);
unit_m3(td->smtx);
unit_m3(td.mtx);
unit_m3(td.smtx);
td->extra = node;
td.extra = &node;
}
static bool is_node_parent_select(bNode *node)
static bool is_node_parent_select(const bNode *node)
{
while ((node = node->parent)) {
if (node->flag & NODE_TRANSFORM) {
if (node->flag & NODE_SELECT) {
return true;
}
}
@ -99,8 +96,13 @@ static bool is_node_parent_select(bNode *node)
static void createTransNodeData(bContext * /*C*/, TransInfo *t)
{
const float dpi_fac = UI_DPI_FAC;
using namespace blender;
using namespace blender::ed;
SpaceNode *snode = static_cast<SpaceNode *>(t->area->spacedata.first);
bNodeTree *node_tree = snode->edittree;
if (!node_tree) {
return;
}
/* Custom data to enable edge panning during the node transform */
TransCustomDataNode *customdata = MEM_cnew<TransCustomDataNode>(__func__);
@ -119,37 +121,21 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t)
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
tc->data_len = 0;
if (!snode->edittree) {
return;
}
/* Nodes don't support PET and probably never will. */
/* Nodes don't support proportional editing and probably never will. */
t->flag = t->flag & ~T_PROP_EDIT_ALL;
/* set transform flags on nodes */
for (bNode *node : snode->edittree->all_nodes()) {
if (node->flag & NODE_SELECT && !is_node_parent_select(node)) {
node->flag |= NODE_TRANSFORM;
tc->data_len++;
}
else {
node->flag &= ~NODE_TRANSFORM;
}
}
if (tc->data_len == 0) {
VectorSet<bNode *> nodes = space_node::get_selected_nodes(*node_tree);
nodes.remove_if([&](bNode *node) { return is_node_parent_select(node); });
if (nodes.is_empty()) {
return;
}
TransData *td = tc->data = MEM_cnew_array<TransData>(tc->data_len, __func__);
TransData2D *td2d = tc->data_2d = MEM_cnew_array<TransData2D>(tc->data_len, __func__);
tc->data_len = nodes.size();
tc->data = MEM_cnew_array<TransData>(tc->data_len, __func__);
tc->data_2d = MEM_cnew_array<TransData2D>(tc->data_len, __func__);
for (bNode *node : snode->edittree->all_nodes()) {
if (node->flag & NODE_TRANSFORM) {
NodeToTransData(td++, td2d++, node, dpi_fac);
}
for (const int i : nodes.index_range()) {
create_transform_data_for_node(tc->data[i], tc->data_2d[i], *nodes[i], UI_DPI_FAC);
}
}
@ -161,43 +147,41 @@ static void createTransNodeData(bContext * /*C*/, TransInfo *t)
static void node_snap_grid_apply(TransInfo *t)
{
int i;
using namespace blender;
if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) {
return;
}
float grid_size[2];
copy_v2_v2(grid_size, t->snap_spatial);
float2 grid_size = t->snap_spatial;
if (t->modifiers & MOD_PRECISION) {
mul_v2_fl(grid_size, t->snap_spatial_precision);
grid_size *= t->snap_spatial_precision;
}
/* Early exit on unusable grid size. */
if (is_zero_v2(grid_size)) {
if (math::is_zero(grid_size)) {
return;
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td;
for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
for (const int i : IndexRange(tc->data_len)) {
TransData &td = tc->data[i];
float iloc[2], loc[2], tvec[2];
if (td->flag & TD_SKIP) {
if (td.flag & TD_SKIP) {
continue;
}
if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
if ((t->flag & T_PROP_EDIT) && (td.factor == 0.0f)) {
continue;
}
copy_v2_v2(iloc, td->loc);
copy_v2_v2(iloc, td.loc);
loc[0] = roundf(iloc[0] / grid_size[0]) * grid_size[0];
loc[1] = roundf(iloc[1] / grid_size[1]) * grid_size[1];
sub_v2_v2v2(tvec, loc, iloc);
add_v2_v2(td->loc, tvec);
add_v2_v2(td.loc, tvec);
}
}
}
@ -246,11 +230,6 @@ static void flushTransNodes(TransInfo *t)
float loc[2];
add_v2_v2v2(loc, td2d->loc, offset);
#ifdef USE_NODE_CENTER
loc[0] -= 0.5f * BLI_rctf_size_x(&node->runtime->totr);
loc[1] += 0.5f * BLI_rctf_size_y(&node->runtime->totr);
#endif
/* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */
loc[0] /= dpi_fac;
loc[1] /= dpi_fac;

View File

@ -578,7 +578,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->flag |= T_NO_MIRROR;
}
/* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */
/* Setting proportional editing flag only if property exist in operator. Otherwise, assume it's
* not supported. */
if (op && (prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) {
if (RNA_property_is_set(op->ptr, prop)) {
if (RNA_property_boolean_get(op->ptr, prop)) {
@ -669,7 +670,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
}
/* Mirror is not supported with PET, turn it off. */
/* Mirror is not supported with proportional editing, turn it off. */
#if 0
if (t->flag & T_PROP_EDIT) {
t->flag &= ~T_MIRROR;

View File

@ -71,7 +71,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
*td->val = td->ival * ratio;
}
/* apply PET */
/* Apply proportional editing. */
*td->val = interpf(*td->val, td->ival, td->factor);
CLAMP_MIN(*td->val, 0.0f);
}

View File

@ -73,7 +73,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
/* Apply proportional editing. */
*td->val = interpf(*td->val, td->ival, td->factor);
CLAMP(*td->val, 0.0f, 1.0f);
}

View File

@ -73,7 +73,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
/* Apply proportional editing. */
*td->val = interpf(*td->val, td->ival, td->factor);
if (*td->val <= 0.0f) {
*td->val = 0.001f;

View File

@ -89,7 +89,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
*td->val = td->ival * ratio;
}
/* apply PET */
/* Apply proportional editing. */
*td->val = interpf(*td->val, td->ival, td->factor);
if (*td->val <= 0.0f) {
*td->val = 0.001f;

View File

@ -1190,36 +1190,19 @@ static void TargetSnapOffset(TransInfo *t, TransData *td)
if (t->spacetype == SPACE_NODE && td != nullptr) {
bNode *node = static_cast<bNode *>(td->extra);
char border = t->tsnap.snapNodeBorder;
float width = BLI_rctf_size_x(&node->runtime->totr);
float height = BLI_rctf_size_y(&node->runtime->totr);
#ifdef USE_NODE_CENTER
if (border & NODE_LEFT) {
t->tsnap.snapTarget[0] -= 0.5f * width;
}
if (border & NODE_RIGHT) {
t->tsnap.snapTarget[0] += 0.5f * width;
}
if (border & NODE_BOTTOM) {
t->tsnap.snapTarget[1] -= 0.5f * height;
}
if (border & NODE_TOP) {
t->tsnap.snapTarget[1] += 0.5f * height;
}
#else
if (border & NODE_LEFT) {
t->tsnap.snapTarget[0] -= 0.0f;
}
if (border & NODE_RIGHT) {
t->tsnap.snapTarget[0] += width;
t->tsnap.snapTarget[0] += BLI_rctf_size_x(&node->runtime->totr);
}
if (border & NODE_BOTTOM) {
t->tsnap.snapTarget[1] -= height;
t->tsnap.snapTarget[1] -= BLI_rctf_size_y(&node->runtime->totr);
}
if (border & NODE_TOP) {
t->tsnap.snapTarget[1] += 0.0f;
}
#endif
}
}

View File

@ -63,6 +63,7 @@ set(SRC
../include/ED_mball.h
../include/ED_mesh.h
../include/ED_node.h
../include/ED_node.hh
../include/ED_numinput.h
../include/ED_object.h
../include/ED_outliner.h

View File

@ -43,6 +43,7 @@ set(INC
set(INC_SYS
${Epoxy_INCLUDE_DIRS}
${VULKAN_INCLUDE_DIRS}
)
set(SRC
@ -197,6 +198,7 @@ set(VULKAN_SRC
vulkan/vk_pixel_buffer.cc
vulkan/vk_query.cc
vulkan/vk_shader.cc
vulkan/vk_shader_log.cc
vulkan/vk_storage_buffer.cc
vulkan/vk_texture.cc
vulkan/vk_uniform_buffer.cc
@ -212,6 +214,7 @@ set(VULKAN_SRC
vulkan/vk_pixel_buffer.hh
vulkan/vk_query.hh
vulkan/vk_shader.hh
vulkan/vk_shader_log.hh
vulkan/vk_storage_buffer.hh
vulkan/vk_texture.hh
vulkan/vk_uniform_buffer.hh
@ -275,6 +278,7 @@ endif()
set(LIB
${Epoxy_LIBRARIES}
${VULKAN_LIBRARIES}
)
if(WITH_VULKAN_BACKEND)

View File

@ -187,7 +187,7 @@ typedef enum eGPUDataFormat {
} eGPUDataFormat;
/** Texture usage flags.
* Texture usage flags allow backend implementations to contextually optimise texture resources.
* Texture usage flags allow backend implementations to contextually optimize texture resources.
* Any texture with an explicit flag should not perform operations which are not explicitly
* specified in the usage flags. If usage is unknown upfront, then GPU_TEXTURE_USAGE_GENERAL can be
* used.
@ -202,7 +202,7 @@ typedef enum eGPUTextureUsage {
GPU_TEXTURE_USAGE_SHADER_READ = (1 << 0),
/* Whether the texture is written to by a shader using imageStore. */
GPU_TEXTURE_USAGE_SHADER_WRITE = (1 << 1),
/* Whether a texture is used as an attachment in a framebuffer. */
/* Whether a texture is used as an attachment in a frame-buffer. */
GPU_TEXTURE_USAGE_ATTACHMENT = (1 << 2),
/* Whether the texture is used as a texture view, uses mip-map layer adjustment,
* OR, uses swizzle access masks. Mip-map base layer adjustment and texture channel swizzling
@ -226,11 +226,11 @@ unsigned int GPU_texture_memory_usage_get(void);
* \note \a data is expected to be float. If the \a format is not compatible with float data or if
* the data is not in float format, use GPU_texture_update to upload the data with the right data
* format.
* NOTE: _ex variants of texure creation functions allow specification of explicit usage for
* NOTE: `_ex` variants of texture creation functions allow specification of explicit usage for
* optimal performance. Using standard texture creation will use the `GPU_TEXTURE_USAGE_GENERAL`.
*
* Textures created via other means will either inherit usage from the source resource, or also
* be initialised with `GPU_TEXTURE_USAGE_GENERAL`.
* be initialized with `GPU_TEXTURE_USAGE_GENERAL`.
*
* flag. \a mips is the number of mip level to allocate. It must be >= 1.
*/

View File

@ -98,6 +98,9 @@ static void standard_defines(Vector<const char *> &sources)
case GPU_BACKEND_METAL:
sources.append("#define GPU_METAL\n");
break;
case GPU_BACKEND_VULKAN:
sources.append("#define GPU_VULKAN\n");
break;
default:
BLI_assert(false && "Invalid GPU Backend Type");
break;

View File

@ -54,6 +54,12 @@ void ShaderBuilder::init()
break;
#endif
#ifdef WITH_VULKAN_BACKEND
case GPU_BACKEND_VULKAN:
glSettings.context_type = GHOST_kDrawingContextTypeVulkan;
break;
#endif
default:
BLI_assert_unreachable();
break;
@ -100,6 +106,9 @@ int main(int argc, const char *argv[])
backends_to_validate.append({"OpenGL", GPU_BACKEND_OPENGL});
#ifdef WITH_METAL_BACKEND
backends_to_validate.append({"Metal", GPU_BACKEND_METAL});
#endif
#ifdef WITH_VULKAN_BACKEND
backends_to_validate.append({"Vulkan", GPU_BACKEND_VULKAN});
#endif
for (NamedBackend &backend : backends_to_validate) {
GPU_backend_type_selection_set(backend.backend);
@ -114,6 +123,9 @@ int main(int argc, const char *argv[])
printf("Shader compilation failed for %s backend\n", backend.name.c_str());
exit_code = 1;
}
else {
printf("%s backend shader compilation succeeded.\n", backend.name.c_str());
}
builder.exit();
}

View File

@ -357,13 +357,13 @@ void gpu_shader_create_info_init()
basic_depth_pointcloud_conservative_clipped =
basic_depth_pointcloud_conservative_no_geom_clipped;
/* Overlay prepass wire. */
/* Overlay pre-pass wire. */
overlay_outline_prepass_wire = overlay_outline_prepass_wire_no_geom;
/* Edit UV Edges. */
overlay_edit_uv_edges = overlay_edit_uv_edges_no_geom;
/* Downsample Cube/Proe rendering. */
/* Down-sample Cube/Probe rendering. */
eevee_legacy_effect_downsample_cube = eevee_legacy_effect_downsample_cube_no_geom;
eevee_legacy_probe_filter_glossy = eevee_legacy_probe_filter_glossy_no_geom;
eevee_legacy_lightprobe_planar_downsample = eevee_legacy_lightprobe_planar_downsample_no_geom;

View File

@ -845,7 +845,7 @@ struct ShaderCreateInfo {
/* -------------------------------------------------------------------- */
/** \name Transform feedback properties
*
* Transform feedback enablement and output binding assignmnt.
* Transform feedback enablement and output binding assignment.
* \{ */
Self &transform_feedback_mode(eGPUShaderTFBType tf_mode)

View File

@ -82,7 +82,7 @@ class Texture {
eGPUTextureFormatFlag format_flag_;
/** Texture type. */
eGPUTextureType type_;
/** Texutre usage flags */
/** Texture usage flags. */
eGPUTextureUsage gpu_image_usage_flags_;
/** Number of mipmaps this texture has (Max miplvl). */

View File

@ -533,7 +533,7 @@ id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_fi
}
/* Ensure Context Render Pipeline State is fully setup and ready to execute the draw.
* This should happen after all other final rendeirng setup is complete. */
* This should happen after all other final rendering setup is complete. */
MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
if (!ctx->ensure_render_pipeline_state(mtl_prim_type)) {
printf("FAILED TO ENSURE RENDER PIPELINE STATE");

View File

@ -223,7 +223,7 @@ id<MTLTexture> gpu::MTLTexture::get_metal_handle()
if (mip_swizzle_view_ != nil || texture_view_dirty_flags_) {
bake_mip_swizzle_view();
/* Optimisation: If texture view does not change mip parameters, no texture view will be
/* Optimization: If texture view does not change mip parameters, no texture view will be
* baked. This is because texture views remove the ability to perform lossless compression.
*/
if (mip_swizzle_view_ != nil) {
@ -620,7 +620,7 @@ void gpu::MTLTexture::update_sub(
BLI_assert(blit_encoder != nil);
/* If we need to use a texture view to write texture data as the source
* format is unwritable, if our texture has not been initialised with
* format is unwritable, if our texture has not been initialized with
* texture view support, use a staging texture. */
if ((compatible_write_format != destination_format) &&
!(gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW)) {
@ -646,7 +646,7 @@ void gpu::MTLTexture::update_sub(
/* Allocate stating texture if needed. */
if (use_staging_texture) {
/* Create staging texture to avoid shader-write limiting optimisation. */
/* Create staging texture to avoid shader-write limiting optimization. */
BLI_assert(texture_descriptor_ != nullptr);
MTLTextureUsage original_usage = texture_descriptor_.usage;
texture_descriptor_.usage = original_usage | MTLTextureUsageShaderWrite |

View File

@ -1,40 +1,25 @@
#ifdef USE_WORLD_CLIP_PLANES
# if defined(GPU_VERTEX_SHADER) || defined(GPU_GEOMETRY_SHADER)
# ifndef USE_GPU_SHADER_CREATE_INFO
uniform vec4 WorldClipPlanes[6];
# endif
# define _world_clip_planes_calc_clip_distance(wpos, _clipplanes) \
{ \
vec4 _pos = vec4(wpos, 1.0); \
gl_ClipDistance[0] = dot(_clipplanes[0], _pos); \
gl_ClipDistance[1] = dot(_clipplanes[1], _pos); \
gl_ClipDistance[2] = dot(_clipplanes[2], _pos); \
gl_ClipDistance[3] = dot(_clipplanes[3], _pos); \
gl_ClipDistance[4] = dot(_clipplanes[4], _pos); \
gl_ClipDistance[5] = dot(_clipplanes[5], _pos); \
}
/* When all shaders are builtin shaders are migrated this could be applied directly. */
# ifdef USE_GPU_SHADER_CREATE_INFO
# define WorldClipPlanes clipPlanes.world
# else
uniform vec4 WorldClipPlanes[6];
# endif
/* HACK Dirty hack to be able to override the definition in common_view_lib.glsl.
* Not doing this would require changing the include order in every shaders. */
# define world_clip_planes_calc_clip_distance(wpos) \
_world_clip_planes_calc_clip_distance(wpos, WorldClipPlanes)
void world_clip_planes_calc_clip_distance(vec3 wpos)
{
vec4 pos = vec4(wpos, 1.0);
gl_ClipDistance[0] = dot(WorldClipPlanes[0], pos);
gl_ClipDistance[1] = dot(WorldClipPlanes[1], pos);
gl_ClipDistance[2] = dot(WorldClipPlanes[2], pos);
gl_ClipDistance[3] = dot(WorldClipPlanes[3], pos);
gl_ClipDistance[4] = dot(WorldClipPlanes[4], pos);
gl_ClipDistance[5] = dot(WorldClipPlanes[5], pos);
}
# endif
# define world_clip_planes_set_clip_distance(c) \
{ \
gl_ClipDistance[0] = (c)[0]; \
gl_ClipDistance[1] = (c)[1]; \
gl_ClipDistance[2] = (c)[2]; \
gl_ClipDistance[3] = (c)[3]; \
gl_ClipDistance[4] = (c)[4]; \
gl_ClipDistance[5] = (c)[5]; \
}
#endif

View File

@ -5,6 +5,7 @@
* \ingroup gpu
*/
#include "gpu_capabilities_private.hh"
#include "gpu_platform_private.hh"
#include "vk_batch.hh"
@ -144,4 +145,18 @@ void VKBackend::render_step()
{
}
shaderc::Compiler &VKBackend::get_shaderc_compiler()
{
return shaderc_compiler_;
}
void VKBackend::capabilities_init(VKContext &context)
{
/* Reset all capabilities from previous context. */
GCaps = {};
GCaps.compute_shader_support = true;
GCaps.shader_storage_buffer_objects_support = true;
GCaps.shader_image_load_store_support = true;
}
} // namespace blender::gpu

View File

@ -9,9 +9,21 @@
#include "gpu_backend.hh"
#ifdef __APPLE__
# include <MoltenVK/vk_mvk_moltenvk.h>
#else
# include <vulkan/vulkan.h>
#endif
#include "shaderc/shaderc.hpp"
namespace blender::gpu {
class VKContext;
class VKBackend : public GPUBackend {
private:
shaderc::Compiler shaderc_compiler_;
public:
VKBackend()
{
@ -50,6 +62,10 @@ class VKBackend : public GPUBackend {
void render_end() override;
void render_step() override;
shaderc::Compiler &get_shaderc_compiler();
static void capabilities_init(VKContext &context);
private:
static void init_platform();
static void platform_exit();

View File

@ -7,6 +7,8 @@
#include "vk_context.hh"
#include "vk_backend.hh"
#include "GHOST_C-api.h"
namespace blender::gpu {
@ -32,6 +34,8 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
info.device = device_;
info.instance = instance_;
vmaCreateAllocator(&info, &mem_allocator_);
VKBackend::capabilities_init(*this);
}
VKContext::~VKContext()

View File

@ -47,6 +47,16 @@ class VKContext : public Context {
void debug_group_begin(const char *, int) override;
void debug_group_end() override;
static VKContext *get(void)
{
return static_cast<VKContext *>(Context::get());
}
VkDevice device_get() const
{
return device_;
}
VmaAllocator mem_allocator_get() const
{
return mem_allocator_;

View File

@ -7,26 +7,679 @@
#include "vk_shader.hh"
#include "vk_backend.hh"
#include "vk_shader_log.hh"
#include "BLI_string_utils.h"
#include "BLI_vector.hh"
using namespace blender::gpu::shader;
extern "C" char datatoc_glsl_shader_defines_glsl[];
namespace blender::gpu {
void VKShader::vertex_shader_from_glsl(MutableSpan<const char *> /*sources*/)
/* -------------------------------------------------------------------- */
/** \name Create Info
* \{ */
static const char *to_string(const Interpolation &interp)
{
switch (interp) {
case Interpolation::SMOOTH:
return "smooth";
case Interpolation::FLAT:
return "flat";
case Interpolation::NO_PERSPECTIVE:
return "noperspective";
default:
return "unknown";
}
}
void VKShader::geometry_shader_from_glsl(MutableSpan<const char *> /*sources*/)
static const char *to_string(const Type &type)
{
switch (type) {
case Type::FLOAT:
return "float";
case Type::VEC2:
return "vec2";
case Type::VEC3:
return "vec3";
case Type::VEC4:
return "vec4";
case Type::MAT3:
return "mat3";
case Type::MAT4:
return "mat4";
case Type::UINT:
return "uint";
case Type::UVEC2:
return "uvec2";
case Type::UVEC3:
return "uvec3";
case Type::UVEC4:
return "uvec4";
case Type::INT:
return "int";
case Type::IVEC2:
return "ivec2";
case Type::IVEC3:
return "ivec3";
case Type::IVEC4:
return "ivec4";
case Type::BOOL:
return "bool";
default:
return "unknown";
}
}
void VKShader::fragment_shader_from_glsl(MutableSpan<const char *> /*sources*/)
static const char *to_string(const eGPUTextureFormat &type)
{
switch (type) {
case GPU_RGBA8UI:
return "rgba8ui";
case GPU_RGBA8I:
return "rgba8i";
case GPU_RGBA8:
return "rgba8";
case GPU_RGBA32UI:
return "rgba32ui";
case GPU_RGBA32I:
return "rgba32i";
case GPU_RGBA32F:
return "rgba32f";
case GPU_RGBA16UI:
return "rgba16ui";
case GPU_RGBA16I:
return "rgba16i";
case GPU_RGBA16F:
return "rgba16f";
case GPU_RGBA16:
return "rgba16";
case GPU_RG8UI:
return "rg8ui";
case GPU_RG8I:
return "rg8i";
case GPU_RG8:
return "rg8";
case GPU_RG32UI:
return "rg32ui";
case GPU_RG32I:
return "rg32i";
case GPU_RG32F:
return "rg32f";
case GPU_RG16UI:
return "rg16ui";
case GPU_RG16I:
return "rg16i";
case GPU_RG16F:
return "rg16f";
case GPU_RG16:
return "rg16";
case GPU_R8UI:
return "r8ui";
case GPU_R8I:
return "r8i";
case GPU_R8:
return "r8";
case GPU_R32UI:
return "r32ui";
case GPU_R32I:
return "r32i";
case GPU_R32F:
return "r32f";
case GPU_R16UI:
return "r16ui";
case GPU_R16I:
return "r16i";
case GPU_R16F:
return "r16f";
case GPU_R16:
return "r16";
case GPU_R11F_G11F_B10F:
return "r11f_g11f_b10f";
case GPU_RGB10_A2:
return "rgb10_a2";
default:
return "unknown";
}
}
void VKShader::compute_shader_from_glsl(MutableSpan<const char *> /*sources*/)
static const char *to_string(const PrimitiveIn &layout)
{
switch (layout) {
case PrimitiveIn::POINTS:
return "points";
case PrimitiveIn::LINES:
return "lines";
case PrimitiveIn::LINES_ADJACENCY:
return "lines_adjacency";
case PrimitiveIn::TRIANGLES:
return "triangles";
case PrimitiveIn::TRIANGLES_ADJACENCY:
return "triangles_adjacency";
default:
return "unknown";
}
}
static const char *to_string(const PrimitiveOut &layout)
{
switch (layout) {
case PrimitiveOut::POINTS:
return "points";
case PrimitiveOut::LINE_STRIP:
return "line_strip";
case PrimitiveOut::TRIANGLE_STRIP:
return "triangle_strip";
default:
return "unknown";
}
}
static const char *to_string(const DepthWrite &value)
{
switch (value) {
case DepthWrite::ANY:
return "depth_any";
case DepthWrite::GREATER:
return "depth_greater";
case DepthWrite::LESS:
return "depth_less";
default:
return "depth_unchanged";
}
}
static void print_image_type(std::ostream &os,
const ImageType &type,
const ShaderCreateInfo::Resource::BindType bind_type)
{
switch (type) {
case ImageType::INT_BUFFER:
case ImageType::INT_1D:
case ImageType::INT_1D_ARRAY:
case ImageType::INT_2D:
case ImageType::INT_2D_ARRAY:
case ImageType::INT_3D:
case ImageType::INT_CUBE:
case ImageType::INT_CUBE_ARRAY:
os << "i";
break;
case ImageType::UINT_BUFFER:
case ImageType::UINT_1D:
case ImageType::UINT_1D_ARRAY:
case ImageType::UINT_2D:
case ImageType::UINT_2D_ARRAY:
case ImageType::UINT_3D:
case ImageType::UINT_CUBE:
case ImageType::UINT_CUBE_ARRAY:
os << "u";
break;
default:
break;
}
if (bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
os << "image";
}
else {
os << "sampler";
}
switch (type) {
case ImageType::FLOAT_BUFFER:
case ImageType::INT_BUFFER:
case ImageType::UINT_BUFFER:
os << "Buffer";
break;
case ImageType::FLOAT_1D:
case ImageType::FLOAT_1D_ARRAY:
case ImageType::INT_1D:
case ImageType::INT_1D_ARRAY:
case ImageType::UINT_1D:
case ImageType::UINT_1D_ARRAY:
os << "1D";
break;
case ImageType::FLOAT_2D:
case ImageType::FLOAT_2D_ARRAY:
case ImageType::INT_2D:
case ImageType::INT_2D_ARRAY:
case ImageType::UINT_2D:
case ImageType::UINT_2D_ARRAY:
case ImageType::SHADOW_2D:
case ImageType::SHADOW_2D_ARRAY:
case ImageType::DEPTH_2D:
case ImageType::DEPTH_2D_ARRAY:
os << "2D";
break;
case ImageType::FLOAT_3D:
case ImageType::INT_3D:
case ImageType::UINT_3D:
os << "3D";
break;
case ImageType::FLOAT_CUBE:
case ImageType::FLOAT_CUBE_ARRAY:
case ImageType::INT_CUBE:
case ImageType::INT_CUBE_ARRAY:
case ImageType::UINT_CUBE:
case ImageType::UINT_CUBE_ARRAY:
case ImageType::SHADOW_CUBE:
case ImageType::SHADOW_CUBE_ARRAY:
case ImageType::DEPTH_CUBE:
case ImageType::DEPTH_CUBE_ARRAY:
os << "Cube";
break;
default:
break;
}
switch (type) {
case ImageType::FLOAT_1D_ARRAY:
case ImageType::FLOAT_2D_ARRAY:
case ImageType::FLOAT_CUBE_ARRAY:
case ImageType::INT_1D_ARRAY:
case ImageType::INT_2D_ARRAY:
case ImageType::INT_CUBE_ARRAY:
case ImageType::UINT_1D_ARRAY:
case ImageType::UINT_2D_ARRAY:
case ImageType::UINT_CUBE_ARRAY:
case ImageType::SHADOW_2D_ARRAY:
case ImageType::SHADOW_CUBE_ARRAY:
case ImageType::DEPTH_2D_ARRAY:
case ImageType::DEPTH_CUBE_ARRAY:
os << "Array";
break;
default:
break;
}
switch (type) {
case ImageType::SHADOW_2D:
case ImageType::SHADOW_2D_ARRAY:
case ImageType::SHADOW_CUBE:
case ImageType::SHADOW_CUBE_ARRAY:
os << "Shadow";
break;
default:
break;
}
os << " ";
}
static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifiers)
{
if (bool(qualifiers & Qualifier::NO_RESTRICT) == false) {
os << "restrict ";
}
if (bool(qualifiers & Qualifier::READ) == false) {
os << "writeonly ";
}
if (bool(qualifiers & Qualifier::WRITE) == false) {
os << "readonly ";
}
return os;
}
static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
{
os << "layout(binding = " << res.slot;
if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
os << ", " << to_string(res.image.format);
}
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
os << ", std140";
}
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
os << ", std430";
}
os << ") ";
int64_t array_offset;
StringRef name_no_array;
switch (res.bind_type) {
case ShaderCreateInfo::Resource::BindType::SAMPLER:
os << "uniform ";
print_image_type(os, res.sampler.type, res.bind_type);
os << res.sampler.name << ";\n";
break;
case ShaderCreateInfo::Resource::BindType::IMAGE:
os << "uniform ";
print_qualifier(os, res.image.qualifiers);
print_image_type(os, res.image.type, res.bind_type);
os << res.image.name << ";\n";
break;
case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
array_offset = res.uniformbuf.name.find_first_of("[");
name_no_array = (array_offset == -1) ? res.uniformbuf.name :
StringRef(res.uniformbuf.name.c_str(), array_offset);
os << "uniform " << name_no_array << " { " << res.uniformbuf.type_name << " _"
<< res.uniformbuf.name << "; };\n";
break;
case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
array_offset = res.storagebuf.name.find_first_of("[");
name_no_array = (array_offset == -1) ? res.storagebuf.name :
StringRef(res.storagebuf.name.c_str(), array_offset);
print_qualifier(os, res.storagebuf.qualifiers);
os << "buffer ";
os << name_no_array << " { " << res.storagebuf.type_name << " _" << res.storagebuf.name
<< "; };\n";
break;
}
}
static void print_resource_alias(std::ostream &os, const ShaderCreateInfo::Resource &res)
{
int64_t array_offset;
StringRef name_no_array;
switch (res.bind_type) {
case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
array_offset = res.uniformbuf.name.find_first_of("[");
name_no_array = (array_offset == -1) ? res.uniformbuf.name :
StringRef(res.uniformbuf.name.c_str(), array_offset);
os << "#define " << name_no_array << " (_" << name_no_array << ")\n";
break;
case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
array_offset = res.storagebuf.name.find_first_of("[");
name_no_array = (array_offset == -1) ? res.storagebuf.name :
StringRef(res.storagebuf.name.c_str(), array_offset);
os << "#define " << name_no_array << " (_" << name_no_array << ")\n";
break;
default:
break;
}
}
inline int get_location_count(const Type &type)
{
if (type == shader::Type::MAT4) {
return 4;
}
else if (type == shader::Type::MAT3) {
return 3;
}
return 1;
}
static void print_interface(std::ostream &os,
const std::string &prefix,
const StageInterfaceInfo &iface,
int &location,
const StringRefNull &suffix = "")
{
if (iface.instance_name.is_empty()) {
for (const StageInterfaceInfo::InOut &inout : iface.inouts) {
os << "layout(location=" << location << ") " << prefix << " " << to_string(inout.interp)
<< " " << to_string(inout.type) << " " << inout.name << ";\n";
location += get_location_count(inout.type);
}
}
else {
std::string struct_name = prefix + iface.name;
std::string iface_attribute;
if (iface.instance_name.is_empty()) {
iface_attribute = "iface_";
}
else {
iface_attribute = iface.instance_name;
}
std::string flat = "";
if (prefix == "in") {
flat = "flat ";
}
const bool add_defines = iface.instance_name.is_empty();
os << "struct " << struct_name << " {\n";
for (const StageInterfaceInfo::InOut &inout : iface.inouts) {
os << " " << to_string(inout.type) << " " << inout.name << ";\n";
}
os << "};\n";
os << "layout(location=" << location << ") " << prefix << " " << flat << struct_name << " "
<< iface_attribute << suffix << ";\n";
if (add_defines) {
for (const StageInterfaceInfo::InOut &inout : iface.inouts) {
os << "#define " << inout.name << " (" << iface_attribute << "." << inout.name << ")\n";
}
}
for (const StageInterfaceInfo::InOut &inout : iface.inouts) {
location += get_location_count(inout.type);
}
}
}
/** \} */
static std::string main_function_wrapper(std::string &pre_main, std::string &post_main)
{
std::stringstream ss;
/* Prototype for the original main. */
ss << "\n";
ss << "void main_function_();\n";
/* Wrapper to the main function in order to inject code processing on globals. */
ss << "void main() {\n";
ss << pre_main;
ss << " main_function_();\n";
ss << post_main;
ss << "}\n";
/* Rename the original main. */
ss << "#define main main_function_\n";
ss << "\n";
return ss.str();
}
static const std::string to_stage_name(shaderc_shader_kind stage)
{
switch (stage) {
case shaderc_vertex_shader:
return std::string("vertex");
case shaderc_geometry_shader:
return std::string("geometry");
case shaderc_fragment_shader:
return std::string("fragment");
case shaderc_compute_shader:
return std::string("compute");
default:
BLI_assert_msg(false, "Do not know how to convert shaderc_shader_kind to stage name.");
break;
}
return std::string("unknown stage");
}
static char *glsl_patch_get()
{
static char patch[2048] = "\0";
if (patch[0] != '\0') {
return patch;
}
size_t slen = 0;
/* Version need to go first. */
STR_CONCAT(patch, slen, "#version 450\n");
STR_CONCAT(patch, slen, "#define gl_VertexID gl_VertexIndex\n");
STR_CONCAT(patch, slen, "#define gpu_BaseInstance (0)\n");
STR_CONCAT(patch, slen, "#define gpu_InstanceIndex (gl_InstanceIndex)\n");
STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n");
STR_CONCAT(patch, slen, "#define gl_InstanceID gpu_InstanceIndex\n");
STR_CONCAT(patch, slen, "#define DFDX_SIGN 1.0\n");
STR_CONCAT(patch, slen, "#define DFDY_SIGN 1.0\n");
/* GLSL Backend Lib. */
STR_CONCAT(patch, slen, datatoc_glsl_shader_defines_glsl);
BLI_assert(slen < sizeof(patch));
return patch;
}
static std::string combine_sources(Span<const char *> sources)
{
char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size());
return std::string(sources_combined);
}
Vector<uint32_t> VKShader::compile_glsl_to_spirv(Span<const char *> sources,
shaderc_shader_kind stage)
{
std::string combined_sources = combine_sources(sources);
VKBackend &backend = static_cast<VKBackend &>(*VKBackend::get());
shaderc::Compiler &compiler = backend.get_shaderc_compiler();
shaderc::CompileOptions options;
options.SetOptimizationLevel(shaderc_optimization_level_performance);
shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv(
combined_sources, stage, name, options);
if (module.GetNumErrors() != 0 || module.GetNumWarnings() != 0) {
std::string log = module.GetErrorMessage();
Vector<char> logcstr(log.c_str(), log.c_str() + log.size() + 1);
VKLogParser parser;
print_log(sources,
logcstr.data(),
to_stage_name(stage).c_str(),
module.GetCompilationStatus() != shaderc_compilation_status_success,
&parser);
}
if (module.GetCompilationStatus() != shaderc_compilation_status_success) {
compilation_failed_ = true;
return Vector<uint32_t>();
}
return Vector<uint32_t>(module.cbegin(), module.cend());
}
void VKShader::build_shader_module(Span<uint32_t> spirv_module, VkShaderModule *r_shader_module)
{
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.codeSize = spirv_module.size() * sizeof(uint32_t);
create_info.pCode = spirv_module.data();
VKContext &context = *static_cast<VKContext *>(VKContext::get());
VkResult result = vkCreateShaderModule(
context.device_get(), &create_info, nullptr, r_shader_module);
if (result != VK_SUCCESS) {
compilation_failed_ = true;
*r_shader_module = VK_NULL_HANDLE;
}
}
VKShader::VKShader(const char *name) : Shader(name)
{
context_ = VKContext::get();
}
VKShader::~VKShader()
{
VkDevice device = context_->device_get();
if (vertex_module_ != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, vertex_module_, nullptr);
vertex_module_ = VK_NULL_HANDLE;
}
if (geometry_module_ != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, geometry_module_, nullptr);
geometry_module_ = VK_NULL_HANDLE;
}
if (fragment_module_ != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, fragment_module_, nullptr);
fragment_module_ = VK_NULL_HANDLE;
}
if (compute_module_ != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, compute_module_, nullptr);
compute_module_ = VK_NULL_HANDLE;
}
}
void VKShader::build_shader_module(MutableSpan<const char *> sources,
shaderc_shader_kind stage,
VkShaderModule *r_shader_module)
{
BLI_assert_msg(ELEM(stage,
shaderc_vertex_shader,
shaderc_geometry_shader,
shaderc_fragment_shader,
shaderc_compute_shader),
"Only forced ShaderC shader kinds are supported.");
sources[0] = glsl_patch_get();
Vector<uint32_t> spirv_module = compile_glsl_to_spirv(sources, stage);
build_shader_module(spirv_module, &compute_module_);
}
void VKShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
{
build_shader_module(sources, shaderc_vertex_shader, &vertex_module_);
}
void VKShader::geometry_shader_from_glsl(MutableSpan<const char *> sources)
{
build_shader_module(sources, shaderc_geometry_shader, &geometry_module_);
}
void VKShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
{
build_shader_module(sources, shaderc_fragment_shader, &fragment_module_);
}
void VKShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
{
build_shader_module(sources, shaderc_compute_shader, &compute_module_);
}
bool VKShader::finalize(const shader::ShaderCreateInfo * /*info*/)
{
return false;
if (compilation_failed_) {
return false;
}
if (vertex_module_ != VK_NULL_HANDLE) {
BLI_assert(fragment_module_ != VK_NULL_HANDLE);
BLI_assert(compute_module_ == VK_NULL_HANDLE);
VkPipelineShaderStageCreateInfo vertex_stage_info = {};
vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_stage_info.module = vertex_module_;
vertex_stage_info.pName = "main";
pipeline_infos_.append(vertex_stage_info);
if (geometry_module_ != VK_NULL_HANDLE) {
VkPipelineShaderStageCreateInfo geo_stage_info = {};
geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
geo_stage_info.module = geometry_module_;
geo_stage_info.pName = "main";
pipeline_infos_.append(geo_stage_info);
}
VkPipelineShaderStageCreateInfo fragment_stage_info = {};
fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_stage_info.module = fragment_module_;
fragment_stage_info.pName = "main";
pipeline_infos_.append(fragment_stage_info);
}
else {
BLI_assert(vertex_module_ == VK_NULL_HANDLE);
BLI_assert(geometry_module_ == VK_NULL_HANDLE);
BLI_assert(fragment_module_ == VK_NULL_HANDLE);
BLI_assert(compute_module_ != VK_NULL_HANDLE);
VkPipelineShaderStageCreateInfo compute_stage_info = {};
compute_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
compute_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
compute_stage_info.module = geometry_module_;
compute_stage_info.pName = "main";
pipeline_infos_.append(compute_stage_info);
}
return true;
}
void VKShader::transform_feedback_names_set(Span<const char *> /*name_list*/,
@ -64,34 +717,222 @@ void VKShader::uniform_int(int /*location*/,
{
}
std::string VKShader::resources_declare(const shader::ShaderCreateInfo & /*info*/) const
std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) const
{
return std::string();
std::stringstream ss;
ss << "\n/* Pass Resources. */\n";
for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
print_resource(ss, res);
}
for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
print_resource_alias(ss, res);
}
ss << "\n/* Batch Resources. */\n";
for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
print_resource(ss, res);
}
for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
print_resource_alias(ss, res);
}
if (!info.push_constants_.is_empty()) {
ss << "\n/* Push Constants. */\n";
ss << "layout(push_constant) uniform constants\n";
ss << "{\n";
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
ss << " " << to_string(uniform.type) << " pc_" << uniform.name;
if (uniform.array_size > 0) {
ss << "[" << uniform.array_size << "]";
}
ss << ";\n";
}
ss << "} PushConstants;\n";
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
ss << "#define " << uniform.name << " (PushConstants.pc_" << uniform.name << ")\n";
}
}
ss << "\n";
return ss.str();
}
std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo & /*info*/) const
std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo &info) const
{
return std::string();
std::stringstream ss;
std::string post_main;
ss << "\n/* Inputs. */\n";
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
ss << "layout(location = " << attr.index << ") ";
ss << "in " << to_string(attr.type) << " " << attr.name << ";\n";
}
/* NOTE(D4490): Fix a bug where shader without any vertex attributes do not behave correctly.
*/
if (GPU_type_matches_ex(GPU_DEVICE_APPLE, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL) &&
info.vertex_inputs_.is_empty()) {
ss << "in float gpu_dummy_workaround;\n";
}
ss << "\n/* Interfaces. */\n";
int location = 0;
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {
print_interface(ss, "out", *iface, location);
}
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
/* Need this for stable barycentric. */
ss << "flat out vec4 gpu_pos_flat;\n";
ss << "out vec4 gpu_pos;\n";
post_main += " gpu_pos = gpu_pos_flat = gl_Position;\n";
}
ss << "\n";
if (post_main.empty() == false) {
std::string pre_main;
ss << main_function_wrapper(pre_main, post_main);
}
return ss.str();
}
std::string VKShader::fragment_interface_declare(const shader::ShaderCreateInfo & /*info*/) const
std::string VKShader::fragment_interface_declare(const shader::ShaderCreateInfo &info) const
{
return std::string();
std::stringstream ss;
std::string pre_main;
ss << "\n/* Interfaces. */\n";
const Vector<StageInterfaceInfo *> &in_interfaces = info.geometry_source_.is_empty() ?
info.vertex_out_interfaces_ :
info.geometry_out_interfaces_;
int location = 0;
for (const StageInterfaceInfo *iface : in_interfaces) {
print_interface(ss, "in", *iface, location);
}
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
std::cout << "native" << std::endl;
/* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry
* shader workaround if this extension/feature is detected. */
ss << "\n/* Stable Barycentric Coordinates. */\n";
ss << "flat in vec4 gpu_pos_flat;\n";
ss << "__explicitInterpAMD in vec4 gpu_pos;\n";
/* Globals. */
ss << "vec3 gpu_BaryCoord;\n";
ss << "vec3 gpu_BaryCoordNoPersp;\n";
ss << "\n";
ss << "vec2 stable_bary_(vec2 in_bary) {\n";
ss << " vec3 bary = vec3(in_bary, 1.0 - in_bary.x - in_bary.y);\n";
ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { return bary.zxy; }\n";
ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n";
ss << " return bary.xyz;\n";
ss << "}\n";
ss << "\n";
ss << "vec4 gpu_position_at_vertex(int v) {\n";
ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { v = (v + 2) % 3; }\n";
ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { v = (v + 1) % 3; }\n";
ss << " return interpolateAtVertexAMD(gpu_pos, v);\n";
ss << "}\n";
pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n";
pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n";
}
if (info.early_fragment_test_) {
ss << "layout(early_fragment_tests) in;\n";
}
ss << "layout(" << to_string(info.depth_write_) << ") out float gl_FragDepth;\n";
ss << "\n/* Outputs. */\n";
for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) {
ss << "layout(location = " << output.index;
switch (output.blend) {
case DualBlend::SRC_0:
ss << ", index = 0";
break;
case DualBlend::SRC_1:
ss << ", index = 1";
break;
default:
break;
}
ss << ") ";
ss << "out " << to_string(output.type) << " " << output.name << ";\n";
}
ss << "\n";
if (pre_main.empty() == false) {
std::string post_main;
ss << main_function_wrapper(pre_main, post_main);
}
return ss.str();
}
std::string VKShader::geometry_interface_declare(const shader::ShaderCreateInfo & /*info*/) const
std::string VKShader::geometry_interface_declare(const shader::ShaderCreateInfo &info) const
{
return std::string();
int max_verts = info.geometry_layout_.max_vertices;
int invocations = info.geometry_layout_.invocations;
std::stringstream ss;
ss << "\n/* Geometry Layout. */\n";
ss << "layout(" << to_string(info.geometry_layout_.primitive_in);
if (invocations != -1) {
ss << ", invocations = " << invocations;
}
ss << ") in;\n";
ss << "layout(" << to_string(info.geometry_layout_.primitive_out)
<< ", max_vertices = " << max_verts << ") out;\n";
ss << "\n";
return ss.str();
}
std::string VKShader::geometry_layout_declare(const shader::ShaderCreateInfo & /*info*/) const
static StageInterfaceInfo *find_interface_by_name(const Vector<StageInterfaceInfo *> &ifaces,
const StringRefNull &name)
{
return std::string();
for (auto *iface : ifaces) {
if (iface->instance_name == name) {
return iface;
}
}
return nullptr;
}
std::string VKShader::compute_layout_declare(const shader::ShaderCreateInfo & /*info*/) const
std::string VKShader::geometry_layout_declare(const shader::ShaderCreateInfo &info) const
{
return std::string();
std::stringstream ss;
ss << "\n/* Interfaces. */\n";
int location = 0;
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {
bool has_matching_output_iface = find_interface_by_name(info.geometry_out_interfaces_,
iface->instance_name) != nullptr;
const char *suffix = (has_matching_output_iface) ? "_in[]" : "[]";
print_interface(ss, "in", *iface, location, suffix);
}
ss << "\n";
for (const StageInterfaceInfo *iface : info.geometry_out_interfaces_) {
bool has_matching_input_iface = find_interface_by_name(info.vertex_out_interfaces_,
iface->instance_name) != nullptr;
const char *suffix = (has_matching_input_iface) ? "_out" : "";
print_interface(ss, "out", *iface, location, suffix);
}
ss << "\n";
return ss.str();
}
std::string VKShader::compute_layout_declare(const shader::ShaderCreateInfo &info) const
{
std::stringstream ss;
ss << "\n/* Compute Layout. */\n";
ss << "layout(local_size_x = " << info.compute_layout_.local_size_x;
if (info.compute_layout_.local_size_y != -1) {
ss << ", local_size_y = " << info.compute_layout_.local_size_y;
}
if (info.compute_layout_.local_size_z != -1) {
ss << ", local_size_z = " << info.compute_layout_.local_size_z;
}
ss << ") in;\n";
ss << "\n";
return ss.str();
}
int VKShader::program_handle_get() const

View File

@ -9,13 +9,26 @@
#include "gpu_shader_private.hh"
#include "vk_backend.hh"
#include "vk_context.hh"
#include "BLI_string_ref.hh"
namespace blender::gpu {
class VKShader : public Shader {
private:
VKContext *context_ = nullptr;
VkShaderModule vertex_module_ = VK_NULL_HANDLE;
VkShaderModule geometry_module_ = VK_NULL_HANDLE;
VkShaderModule fragment_module_ = VK_NULL_HANDLE;
VkShaderModule compute_module_ = VK_NULL_HANDLE;
bool compilation_failed_ = false;
Vector<VkPipelineShaderStageCreateInfo> pipeline_infos_;
public:
VKShader(const char *name) : Shader(name)
{
}
VKShader(const char *name);
virtual ~VKShader();
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
@ -43,6 +56,13 @@ class VKShader : public Shader {
/* DEPRECATED: Kept only because of BGL API. */
int program_handle_get() const override;
private:
Vector<uint32_t> compile_glsl_to_spirv(Span<const char *> sources, shaderc_shader_kind kind);
void build_shader_module(Span<uint32_t> spirv_module, VkShaderModule *r_shader_module);
void build_shader_module(MutableSpan<const char *> sources,
shaderc_shader_kind stage,
VkShaderModule *r_shader_module);
};
} // namespace blender::gpu

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