Compare commits

..

18 Commits

Author SHA1 Message Date
a41c2a5137 Merge branch 'master' into temp-image-buffer-rasterizer 2022-03-02 16:03:01 +01:00
a23b442991 Added empty line at end of file. 2022-03-02 15:09:41 +01:00
0f784485bd Added blending modes. 2022-03-02 15:08:04 +01:00
1ddde2d83c Split rasterizer target to support other drawing targets. 2022-03-02 13:54:43 +01:00
6afa238ced Reverted default constructor. 2022-03-02 10:17:06 +01:00
82c0dbe793 Fix incorrect buffer operations. 2022-03-02 10:14:13 +01:00
b5176e90bf Improved rasterizing quality. 2022-03-01 15:54:54 +01:00
6d1eaf2d87 Check for quality. 2022-02-21 13:42:21 +01:00
15182f0941 Use a constant fragment adder per triangle. 2022-02-21 09:52:58 +01:00
65b806d5f3 Fix glitches around mid vertex. 2022-02-21 08:09:23 +01:00
2778b0690e Added back check when 2 verts are on the same rasterline. 2022-02-18 17:03:48 +01:00
89cecb088f Improve quality by center pixel clamping. 2022-02-18 16:43:38 +01:00
80144db42d Add check if vertex and fragment shader can be linked. 2022-02-18 12:57:51 +01:00
7a5cde3074 Added documentation. 2022-02-18 12:36:07 +01:00
4e721dcd07 Renamed uv->coords, small fixes. 2022-02-18 10:07:03 +01:00
29a3d61df3 Fixed issue with sorting verices. 2022-02-16 16:45:55 +01:00
c51692a568 WIP - Testing other winding order. 2022-02-16 14:24:37 +01:00
8839d30989 Image buffer rasterizer using CPP templates.
For the 3d texture brush project we need a fast CPU based rasterizer.
This is an initial implementation for a rasterizer that easy to extend
and optimize.

The idea is to implement a rasterizer on top of the ImBuf structure. The
implementation uses CPP templates, resulting each usage to be optimized
by the compiler individually.

A user of the rasterizer can define a vertex shader, fragment shader,
the inputs and interface, similar to existing concepts when using
OpenGL.

The rasterizer only supports triangles.

[Future extensions]

Currently the rasterlines are buffered and when the buffer is full it
will be flushed. This is a tradeoff between local memory and branch
prediction. We expect that adding triangles are typically done by a loop
by the caller. But in certain cases we could buffer the input triangles
and take this responsibility for additional performance.

Configurable clamping. When rasterizing the clamping is done to a corner
of a image pixel. Ideally clamping should consired center pixels or use
a pixel coverage to identify how to clamp during rasterization.

Currently only supports float4 as a fragment output type. float, byte
and int textures aren't supported.

Rasterline discard function. For cases that rasterlines don't need to be
drawn based on vertex data. A use case could be that an influence factor
is 0 for the whole triangle.

Current implementation is single threaded. When using multiple threads
with their own rasterizer could lead to render artifacts. We could
provide a scheduler that collects work in buckets based on the
rasterline y.

[Todos]

* Only supports one winding directional. Should be able to support any
  winding direction.
* Use coord as name for the frag position. Current UV is too related to
  a specific usecase.
* Add more test cases.

Differential Revision: https://developer.blender.org/D14126
2022-02-16 12:04:57 +01:00
1805 changed files with 37050 additions and 60532 deletions

View File

@@ -256,7 +256,7 @@ endif()
if(UNIX AND NOT APPLE)
option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF)
option(WITH_SYSTEM_GLES "Use OpenGL ES library provided by the operating system" ON)
option(WITH_SYSTEM_GLEW "Use GLEW OpenGL wrapper library provided by the operating system" OFF)
option(WITH_SYSTEM_FREETYPE "Use the freetype library provided by the operating system" OFF)
else()
# not an option for other OS's
@@ -284,7 +284,6 @@ option(WITH_IMAGE_TIFF "Enable LibTIFF Support" ON)
option(WITH_IMAGE_DDS "Enable DDS Image Support" ON)
option(WITH_IMAGE_CINEON "Enable CINEON and DPX Image Support" ON)
option(WITH_IMAGE_HDR "Enable HDR Image Support" ON)
option(WITH_IMAGE_WEBP "Enable WebP Image Support" OFF)
# Audio/Video format support
option(WITH_CODEC_AVI "Enable Blenders own AVI file support (raw/jpeg)" ON)
@@ -409,8 +408,6 @@ option(WITH_CYCLES_DEBUG "Build Cycles with options useful for debug
option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF)
option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF)
option(WITH_CYCLES_HYDRA_RENDER_DELEGATE "Build Cycles Hydra render delegate" OFF)
option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF)
option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF)
option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF)
@@ -446,7 +443,7 @@ if(NOT APPLE)
endif()
option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF)
set(CYCLES_HIP_BINARIES_ARCH gfx900 gfx906 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for")
set(CYCLES_HIP_BINARIES_ARCH gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for")
mark_as_advanced(WITH_CYCLES_DEVICE_HIP)
mark_as_advanced(CYCLES_HIP_BINARIES_ARCH)
endif()
@@ -505,7 +502,7 @@ else()
set(WITH_EXPERIMENTAL_FEATURES OFF)
endif()
# Unit testing
# Unit testsing
option(WITH_GTESTS "Enable GTest unit testing" OFF)
option(WITH_OPENGL_RENDER_TESTS "Enable OpenGL render related unit testing (Experimental)" OFF)
option(WITH_OPENGL_DRAW_TESTS "Enable OpenGL UI drawing related unit testing (Experimental)" OFF)
@@ -534,19 +531,6 @@ mark_as_advanced(
WITH_GPU_SHADER_BUILDER
)
# Metal
if (APPLE)
option(WITH_METAL_BACKEND "Use Metal for graphics instead of (or as well as) OpenGL on macOS." OFF)
mark_as_advanced(WITH_METAL_BACKEND)
else()
set(WITH_METAL_BACKEND OFF)
endif()
if (WITH_METAL_BACKEND)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE)
endif()
if(WIN32)
option(WITH_GL_ANGLE "Link with the ANGLE library, an OpenGL ES 2.0 implementation based on Direct3D, instead of the system OpenGL library." OFF)
mark_as_advanced(WITH_GL_ANGLE)
@@ -745,10 +729,9 @@ endif()
#-----------------------------------------------------------------------------
# Check for conflicting/unsupported configurations
if(NOT WITH_BLENDER AND NOT WITH_CYCLES_STANDALONE AND NOT WITH_CYCLES_HYDRA_RENDER_DELEGATE)
if(NOT WITH_BLENDER AND NOT WITH_CYCLES_STANDALONE)
message(FATAL_ERROR
"At least one of WITH_BLENDER or WITH_CYCLES_STANDALONE "
"or WITH_CYCLES_HYDRA_RENDER_DELEGATE "
"must be enabled, nothing to do!"
)
endif()
@@ -1291,16 +1274,6 @@ else()
list(APPEND GL_DEFINITIONS -DWITH_GL_PROFILE_CORE)
endif()
#-----------------------------------------------------------------------------
# Configure Metal.
if (WITH_METAL_BACKEND)
add_definitions(-DWITH_METAL_BACKEND)
# No need to add frameworks here, all the ones we need for Metal and
# Metal-OpenGL Interop are already being added by
# build_files/cmake/platform/platform_apple.cmake
endif()
#-----------------------------------------------------------------------------
# Configure OpenMP.
if(WITH_OPENMP)
@@ -1695,8 +1668,6 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_DUPLICATE_ENUM -Wno-duplicate-enum)
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_UNDEF -Wno-undef)
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_MISSING_NORETURN -Wno-missing-noreturn)
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_UNUSED_BUT_SET_VARIABLE -Wno-unused-but-set-variable)
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_DEPRECATED_DECLARATIONS -Wno-deprecated-declarations)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_PARAMETER -Wno-unused-parameter)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_PRIVATE_FIELD -Wno-unused-private-field)
@@ -1911,13 +1882,14 @@ if(WITH_BLENDER)
# source after intern and extern to gather all
# internal and external library information first, for test linking
add_subdirectory(source)
elseif(WITH_CYCLES_STANDALONE OR WITH_CYCLES_HYDRA_RENDER_DELEGATE)
elseif(WITH_CYCLES_STANDALONE)
add_subdirectory(intern/glew-mx)
add_subdirectory(intern/guardedalloc)
add_subdirectory(intern/libc_compat)
add_subdirectory(intern/sky)
add_subdirectory(intern/cycles)
add_subdirectory(extern/clew)
if(WITH_CYCLES_LOGGING)
if(NOT WITH_SYSTEM_GFLAGS)
add_subdirectory(extern/gflags)
@@ -1974,7 +1946,7 @@ if(FIRST_RUN)
set(_msg " - ${_setting}")
string(LENGTH "${_msg}" _len)
while("36" GREATER "${_len}")
while("32" GREATER "${_len}")
string(APPEND _msg " ")
math(EXPR _len "${_len} + 1")
endwhile()

View File

@@ -32,7 +32,7 @@ Other Convenience Targets
* config: Run cmake configuration tool to set build options.
* deps: Build library dependencies (intended only for platform maintainers).
The existence of locally build dependencies overrides the pre-built dependencies from subversion.
The existance of locally build dependencies overrides the pre-built dependencies from subversion.
These must be manually removed from '../lib/' to go back to using the pre-compiled libraries.
Project Files

View File

@@ -1,16 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-or-later
## Update and uncomment this in the release branch
# set(BLENDER_VERSION 3.1)
function(download_source dep)
set(TARGET_FILE ${${dep}_FILE})
set(TARGET_HASH_TYPE ${${dep}_HASH_TYPE})
set(TARGET_HASH ${${dep}_HASH})
if(PACKAGE_USE_UPSTREAM_SOURCES)
set(TARGET_URI ${${dep}_URI})
elseif(BLENDER_VERSION)
set(TARGET_URI https://svn.blender.org/svnroot/bf-blender/tags/blender-${BLENDER_VERSION}-release/lib/packages/${TARGET_FILE})
else()
set(TARGET_URI https://svn.blender.org/svnroot/bf-blender/trunk/lib/packages/${TARGET_FILE})
endif()

View File

@@ -52,14 +52,6 @@ add_dependencies(
external_boost
)
# Since USD 21.11 the libraries are prefixed with "usd_", i.e. "libusd_m.a" became "libusd_usd_m.a".
# See https://github.com/PixarAnimationStudios/USD/blob/release/CHANGELOG.md#2111---2021-11-01
if (USD_VERSION VERSION_LESS 21.11)
set(PXR_LIB_PREFIX "")
else()
set(PXR_LIB_PREFIX "usd_")
endif()
if(WIN32)
# USD currently demands python be available at build time
# and then proceeds not to use it, but still checks that the
@@ -73,14 +65,14 @@ if(WIN32)
if(BUILD_MODE STREQUAL Release)
ExternalProject_Add_Step(external_usd after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/usd/ ${HARVEST_TARGET}/usd
COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Release/${PXR_LIB_PREFIX}usd_m.lib ${HARVEST_TARGET}/usd/lib/lib${PXR_LIB_PREFIX}usd_m.lib
COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Release/usd_m.lib ${HARVEST_TARGET}/usd/lib/libusd_m.lib
DEPENDEES install
)
endif()
if(BUILD_MODE STREQUAL Debug)
ExternalProject_Add_Step(external_usd after_install
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/usd/lib ${HARVEST_TARGET}/usd/lib
COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Debug/${PXR_LIB_PREFIX}usd_m_d.lib ${HARVEST_TARGET}/usd/lib/lib${PXR_LIB_PREFIX}usd_m_d.lib
COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/Debug/usd_m_d.lib ${HARVEST_TARGET}/usd/lib/libusd_m_d.lib
DEPENDEES install
)
endif()
@@ -92,7 +84,7 @@ else()
# case (only the shared library). As a result, we need to grab the `libusd_m.a`
# file from the build directory instead of from the install directory.
ExternalProject_Add_Step(external_usd after_install
COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/lib${PXR_LIB_PREFIX}usd_m.a ${HARVEST_TARGET}/usd/lib/lib${PXR_LIB_PREFIX}usd_m.a
COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/usd/src/external_usd-build/pxr/libusd_m.a ${HARVEST_TARGET}/usd/lib/libusd_m.a
DEPENDEES install
)
endif()

View File

@@ -432,8 +432,8 @@ PYTHON_MODULES_PIP=(
)
BOOST_VERSION="1.78.0"
BOOST_VERSION_SHORT="1.78"
BOOST_VERSION="1.73.0"
BOOST_VERSION_SHORT="1.73"
BOOST_VERSION_MIN="1.49"
BOOST_VERSION_MEX="2.0"
BOOST_FORCE_BUILD=false
@@ -442,15 +442,15 @@ BOOST_SKIP=false
TBB_VERSION="2020"
TBB_VERSION_SHORT="2020"
TBB_VERSION_UPDATE="_U3" # Used for source packages...
TBB_VERSION_UPDATE="_U2" # Used for source packages...
TBB_VERSION_MIN="2018"
TBB_VERSION_MEX="2022"
TBB_FORCE_BUILD=false
TBB_FORCE_REBUILD=false
TBB_SKIP=false
OCIO_VERSION="2.1.1"
OCIO_VERSION_SHORT="2.1"
OCIO_VERSION="2.0.0"
OCIO_VERSION_SHORT="2.0"
OCIO_VERSION_MIN="2.0"
OCIO_VERSION_MEX="3.0"
OCIO_FORCE_BUILD=false
@@ -466,10 +466,10 @@ OPENEXR_FORCE_REBUILD=false
OPENEXR_SKIP=false
_with_built_openexr=false
OIIO_VERSION="2.3.13.0"
OIIO_VERSION_SHORT="2.3"
OIIO_VERSION="2.2.15.1"
OIIO_VERSION_SHORT="2.2"
OIIO_VERSION_MIN="2.1.12"
OIIO_VERSION_MEX="2.4.0"
OIIO_VERSION_MEX="2.3.0"
OIIO_FORCE_BUILD=false
OIIO_FORCE_REBUILD=false
OIIO_SKIP=false
@@ -493,7 +493,7 @@ OSL_FORCE_REBUILD=false
OSL_SKIP=false
# OpenSubdiv needs to be compiled for now
OSD_VERSION="3.4.4"
OSD_VERSION="3.4.3"
OSD_VERSION_SHORT="3.4"
OSD_VERSION_MIN="3.4"
OSD_VERSION_MEX="4.0"
@@ -502,19 +502,19 @@ OSD_FORCE_REBUILD=false
OSD_SKIP=false
# OpenVDB needs to be compiled for now
OPENVDB_BLOSC_VERSION="1.21.1"
OPENVDB_BLOSC_VERSION="1.5.0"
OPENVDB_VERSION="9.0.0"
OPENVDB_VERSION_SHORT="9.0"
OPENVDB_VERSION_MIN="9.0"
OPENVDB_VERSION_MEX="9.1"
OPENVDB_VERSION="8.0.1"
OPENVDB_VERSION_SHORT="8.0"
OPENVDB_VERSION_MIN="8.0"
OPENVDB_VERSION_MEX="8.1"
OPENVDB_FORCE_BUILD=false
OPENVDB_FORCE_REBUILD=false
OPENVDB_SKIP=false
# Alembic needs to be compiled for now
ALEMBIC_VERSION="1.8.3"
ALEMBIC_VERSION_SHORT="1.8"
ALEMBIC_VERSION="1.7.16"
ALEMBIC_VERSION_SHORT="1.7"
ALEMBIC_VERSION_MIN="1.7"
ALEMBIC_VERSION_MEX="2.0"
ALEMBIC_FORCE_BUILD=false
@@ -537,15 +537,15 @@ OPENCOLLADA_FORCE_BUILD=false
OPENCOLLADA_FORCE_REBUILD=false
OPENCOLLADA_SKIP=false
EMBREE_VERSION="3.13.3"
EMBREE_VERSION_SHORT="3.13"
EMBREE_VERSION_MIN="3.13"
EMBREE_VERSION="3.10.0"
EMBREE_VERSION_SHORT="3.10"
EMBREE_VERSION_MIN="3.10"
EMBREE_VERSION_MEX="4.0"
EMBREE_FORCE_BUILD=false
EMBREE_FORCE_REBUILD=false
EMBREE_SKIP=false
OIDN_VERSION="1.4.3"
OIDN_VERSION="1.4.1"
OIDN_VERSION_SHORT="1.4"
OIDN_VERSION_MIN="1.4.0"
OIDN_VERSION_MEX="1.5"
@@ -553,7 +553,7 @@ OIDN_FORCE_BUILD=false
OIDN_FORCE_REBUILD=false
OIDN_SKIP=false
ISPC_VERSION="1.17.0"
ISPC_VERSION="1.16.0"
FFMPEG_VERSION="4.4"
FFMPEG_VERSION_SHORT="4.4"
@@ -1029,7 +1029,7 @@ OPENEXR_SOURCE_REPO_UID="0ac2ea34c8f3134148a5df4052e40f155b76f6fb"
#~ OPENEXR_SOURCE=( "https://github.com/openexr/openexr/archive/$OPENEXR_SOURCE_REPO_UID.tar.gz" )
OIIO_USE_REPO=false
OIIO_SOURCE=( "https://github.com/OpenImageIO/oiio/archive/refs/tags/v$OIIO_VERSION.tar.gz" )
OIIO_SOURCE=( "https://github.com/OpenImageIO/oiio/archive/Release-$OIIO_VERSION.tar.gz" )
#~ OIIO_SOURCE_REPO=( "https://github.com/OpenImageIO/oiio.git" )
#~ OIIO_SOURCE_REPO_UID="c9e67275a0b248ead96152f6d2221cc0c0f278a4"
@@ -2034,7 +2034,7 @@ compile_OIIO() {
else
download OIIO_SOURCE[@] "$_src.tar.gz"
INFO "Unpacking OpenImageIO-$OIIO_VERSION"
tar -C $SRC --transform "s,(.*/?)oiio-[^/]*(.*),\1OpenImageIO-$OIIO_VERSION\2,x" -xf $_src.tar.gz
tar -C $SRC --transform "s,(.*/?)oiio-Release-[^/]*(.*),\1OpenImageIO-$OIIO_VERSION\2,x" -xf $_src.tar.gz
fi
fi

View File

@@ -32,12 +32,9 @@ FIND_PATH(USD_INCLUDE_DIR
DOC "Universal Scene Description (USD) header files"
)
# Since USD 21.11 the libraries are prefixed with "usd_", i.e. "libusd_m.a" became "libusd_usd_m.a".
# See https://github.com/PixarAnimationStudios/USD/blob/release/CHANGELOG.md#2111---2021-11-01
FIND_LIBRARY(USD_LIBRARY
NAMES
usd_usd_m usd_usd_ms usd_m usd_ms
${PXR_LIB_PREFIX}usd
usd_m usd_ms
NAMES_PER_DIR
HINTS
${_usd_SEARCH_DIRS}

View File

@@ -1,77 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright 2022 Blender Foundation.
# - Find WebP library
# Find the native WebP includes and library
# This module defines
# WEBP_INCLUDE_DIRS, where to find WebP headers, Set when WebP is found.
# WEBP_LIBRARIES, libraries to link against to use WebP.
# WEBP_ROOT_DIR, The base directory to search for WebP.
# This can also be an environment variable.
# WEBP_FOUND, If false, do not try to use WebP.
#
# also defined, but not for general use are
# WEBP_LIBRARY, where to find the WEBP library.
# If WEBP_ROOT_DIR was defined in the environment, use it.
IF(NOT WEBP_ROOT_DIR AND NOT $ENV{WEBP_ROOT_DIR} STREQUAL "")
SET(WEBP_ROOT_DIR $ENV{WEBP_ROOT_DIR})
ENDIF()
SET(_webp_SEARCH_DIRS
${WEBP_ROOT_DIR}
/opt/lib/webp
)
FIND_PATH(WEBP_INCLUDE_DIR
NAMES
webp/types.h
HINTS
${_webp_SEARCH_DIRS}
PATH_SUFFIXES
include
)
SET(_webp_FIND_COMPONENTS
webp
webpmux
webpdemux
)
SET(_webp_LIBRARIES)
FOREACH(COMPONENT ${_webp_FIND_COMPONENTS})
STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
FIND_LIBRARY(WEBP_${UPPERCOMPONENT}_LIBRARY
NAMES
${COMPONENT}
NAMES_PER_DIR
HINTS
${_webp_SEARCH_DIRS}
PATH_SUFFIXES
lib64 lib lib/static
)
LIST(APPEND _webp_LIBRARIES "${WEBP_${UPPERCOMPONENT}_LIBRARY}")
ENDFOREACH()
IF(${WEBP_WEBP_LIBRARY_NOTFOUND})
set(WEBP_FOUND FALSE)
ELSE()
# handle the QUIETLY and REQUIRED arguments and set WEBP_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WebP DEFAULT_MSG _webp_LIBRARIES WEBP_INCLUDE_DIR)
IF(WEBP_FOUND)
get_filename_component(WEBP_LIBRARY_DIR ${WEBP_WEBP_LIBRARY} DIRECTORY)
SET(WEBP_INCLUDE_DIRS ${WEBP_INCLUDE_DIR})
SET(WEBP_LIBRARIES ${_webp_LIBRARIES})
ELSE()
SET(WEBPL_PUGIXML_FOUND FALSE)
ENDIF()
ENDIF()
MARK_AS_ADVANCED(
WEBP_INCLUDE_DIR
WEBP_LIBRARY_DIR
)

View File

@@ -879,7 +879,7 @@ function(delayed_install
destination)
foreach(f ${files})
if(IS_ABSOLUTE ${f} OR "${base}" STREQUAL "")
if(IS_ABSOLUTE ${f})
set_property(GLOBAL APPEND PROPERTY DELAYED_INSTALL_FILES ${f})
else()
set_property(GLOBAL APPEND PROPERTY DELAYED_INSTALL_FILES ${base}/${f})

View File

@@ -106,8 +106,8 @@ if(WIN32)
set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium)
endif()
set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "Blender")
set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "Blender")
set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "blender")
set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "blender")
include(CPack)

View File

@@ -232,15 +232,6 @@ if(WITH_IMAGE_TIFF)
endif()
endif()
if(WITH_IMAGE_WEBP)
set(WEBP_ROOT_DIR ${LIBDIR}/webp)
find_package(WebP)
if(NOT WEBP_FOUND)
message(WARNING "WebP not found, disabling WITH_IMAGE_WEBP")
set(WITH_IMAGE_WEBP OFF)
endif()
endif()
if(WITH_BOOST)
set(Boost_NO_BOOST_CMAKE ON)
set(BOOST_ROOT ${LIBDIR}/boost)

View File

@@ -368,14 +368,6 @@ if(WITH_PUGIXML)
endif()
endif()
if(WITH_IMAGE_WEBP)
set(WEBP_ROOT_DIR ${LIBDIR}/webp)
find_package_wrapper(WebP)
if(NOT WEBP_FOUND)
set(WITH_IMAGE_WEBP OFF)
endif()
endif()
if(WITH_OPENIMAGEIO)
find_package_wrapper(OpenImageIO)
set(OPENIMAGEIO_LIBRARIES
@@ -866,7 +858,7 @@ endif()
# If atomic operations are possible without libatomic then linker flags are left as-is.
function(CONFIGURE_ATOMIC_LIB_IF_NEEDED)
# Source which is used to enforce situation when software emulation of atomics is required.
# Assume that using 64bit integer gives a definitive answer (as in, if 64bit atomic operations
# Assume that using 64bit integer gives a definitive asnwer (as in, if 64bit atomic operations
# are possible using assembly/intrinsics 8, 16, and 32 bit operations will also be possible.
set(_source
"#include <atomic>

View File

@@ -39,12 +39,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(WITH_WINDOWS_STRIPPED_PDB OFF)
endif()
else()
if(WITH_BLENDER AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16
message(FATAL_ERROR "Compiler is unsupported, MSVC 2019 16.9.16 or newer is required for building blender.")
endif()
endif()
if(WITH_BLENDER AND NOT WITH_PYTHON_MODULE)
if(NOT WITH_PYTHON_MODULE)
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT blender)
endif()
@@ -238,6 +238,7 @@ else()
endif()
if(NOT DEFINED LIBDIR)
# Setup 64bit and 64bit windows systems
if(CMAKE_CL_64)
message(STATUS "64 bit compiler detected.")
@@ -251,9 +252,6 @@ if(NOT DEFINED LIBDIR)
elseif(MSVC_VERSION GREATER 1919)
message(STATUS "Visual Studio 2019 detected.")
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
elseif(MSVC_VERSION GREATER 1909)
message(STATUS "Visual Studio 2017 detected.")
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
endif()
else()
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
@@ -302,8 +300,9 @@ set(ZLIB_INCLUDE_DIR ${LIBDIR}/zlib/include)
set(ZLIB_LIBRARY ${LIBDIR}/zlib/lib/libz_st.lib)
set(ZLIB_DIR ${LIBDIR}/zlib)
windows_find_package(ZLIB) # we want to find before finding things that depend on it like png
windows_find_package(PNG)
windows_find_package(zlib) # we want to find before finding things that depend on it like png
windows_find_package(png)
if(NOT PNG_FOUND)
warn_hardcoded_paths(libpng)
set(PNG_PNG_INCLUDE_DIR ${LIBDIR}/png/include)
@@ -314,9 +313,9 @@ if(NOT PNG_FOUND)
endif()
set(JPEG_NAMES ${JPEG_NAMES} libjpeg)
windows_find_package(JPEG REQUIRED)
windows_find_package(jpeg REQUIRED)
if(NOT JPEG_FOUND)
warn_hardcoded_paths(libjpeg)
warn_hardcoded_paths(jpeg)
set(JPEG_INCLUDE_DIR ${LIBDIR}/jpeg/include)
set(JPEG_LIBRARIES ${LIBDIR}/jpeg/lib/libjpeg.lib)
endif()
@@ -334,7 +333,7 @@ set(FREETYPE_LIBRARIES
${LIBDIR}/brotli/lib/brotlidec-static.lib
${LIBDIR}/brotli/lib/brotlicommon-static.lib
)
windows_find_package(Freetype REQUIRED)
windows_find_package(freetype REQUIRED)
if(WITH_FFTW3)
set(FFTW3 ${LIBDIR}/fftw3)
@@ -343,16 +342,6 @@ if(WITH_FFTW3)
set(FFTW3_LIBPATH ${FFTW3}/lib)
endif()
windows_find_package(WebP)
if(NOT WEBP_FOUND)
if(EXISTS ${LIBDIR}/webp)
set(WEBP_INCLUDE_DIRS ${LIBDIR}/webp/include)
set(WEBP_ROOT_DIR ${LIBDIR}/webp)
set(WEBP_LIBRARIES ${LIBDIR}/webp/lib/webp.lib ${LIBDIR}/webp/lib/webpdemux.lib ${LIBDIR}/webp/lib/webpmux.lib)
set(WEBP_FOUND ON)
endif()
endif()
if(WITH_OPENCOLLADA)
set(OPENCOLLADA ${LIBDIR}/opencollada)
@@ -400,9 +389,9 @@ if(WITH_CODEC_FFMPEG)
${LIBDIR}/ffmpeg/include
${LIBDIR}/ffmpeg/include/msvc
)
windows_find_package(FFmpeg)
windows_find_package(FFMPEG)
if(NOT FFMPEG_FOUND)
warn_hardcoded_paths(FFmpeg)
warn_hardcoded_paths(ffmpeg)
set(FFMPEG_LIBRARIES
${LIBDIR}/ffmpeg/lib/avcodec.lib
${LIBDIR}/ffmpeg/lib/avformat.lib
@@ -414,10 +403,10 @@ if(WITH_CODEC_FFMPEG)
endif()
if(WITH_IMAGE_OPENEXR)
windows_find_package(OpenEXR REQUIRED)
set(OPENEXR_ROOT_DIR ${LIBDIR}/openexr)
set(OPENEXR_VERSION "2.1")
windows_find_package(OPENEXR REQUIRED)
if(NOT OPENEXR_FOUND)
set(OPENEXR_ROOT_DIR ${LIBDIR}/openexr)
set(OPENEXR_VERSION "2.1")
warn_hardcoded_paths(OpenEXR)
set(OPENEXR ${LIBDIR}/openexr)
set(OPENEXR_INCLUDE_DIR ${OPENEXR}/include)
@@ -635,23 +624,21 @@ if(WITH_IMAGE_OPENJPEG)
endif()
if(WITH_OPENSUBDIV)
set(OPENSUBDIV_INCLUDE_DIRS ${LIBDIR}/opensubdiv/include)
set(OPENSUBDIV_LIBPATH ${LIBDIR}/opensubdiv/lib)
set(OPENSUBDIV_LIBRARIES
optimized ${OPENSUBDIV_LIBPATH}/osdCPU.lib
optimized ${OPENSUBDIV_LIBPATH}/osdGPU.lib
debug ${OPENSUBDIV_LIBPATH}/osdCPU_d.lib
debug ${OPENSUBDIV_LIBPATH}/osdGPU_d.lib
)
set(OPENSUBDIV_HAS_OPENMP TRUE)
set(OPENSUBDIV_HAS_TBB FALSE)
set(OPENSUBDIV_HAS_OPENCL TRUE)
set(OPENSUBDIV_HAS_CUDA FALSE)
set(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK TRUE)
set(OPENSUBDIV_HAS_GLSL_COMPUTE TRUE)
windows_find_package(OpenSubdiv)
if (NOT OpenSubdiv_FOUND)
set(OPENSUBDIV_INCLUDE_DIRS ${LIBDIR}/opensubdiv/include)
set(OPENSUBDIV_LIBPATH ${LIBDIR}/opensubdiv/lib)
set(OPENSUBDIV_LIBRARIES
optimized ${OPENSUBDIV_LIBPATH}/osdCPU.lib
optimized ${OPENSUBDIV_LIBPATH}/osdGPU.lib
debug ${OPENSUBDIV_LIBPATH}/osdCPU_d.lib
debug ${OPENSUBDIV_LIBPATH}/osdGPU_d.lib
)
set(OPENSUBDIV_HAS_OPENMP TRUE)
set(OPENSUBDIV_HAS_TBB FALSE)
set(OPENSUBDIV_HAS_OPENCL TRUE)
set(OPENSUBDIV_HAS_CUDA FALSE)
set(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK TRUE)
set(OPENSUBDIV_HAS_GLSL_COMPUTE TRUE)
endif()
endif()
if(WITH_SDL)
@@ -672,15 +659,12 @@ if(WITH_SYSTEM_AUDASPACE)
endif()
if(WITH_TBB)
windows_find_package(TBB)
if (NOT TBB_FOUND)
set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib)
set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include)
set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR})
if(WITH_TBB_MALLOC_PROXY)
set(TBB_MALLOC_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbbmalloc.lib debug ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib)
add_definitions(-DWITH_TBB_MALLOC)
endif()
set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib)
set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include)
set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR})
if(WITH_TBB_MALLOC_PROXY)
set(TBB_MALLOC_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbbmalloc.lib debug ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib)
add_definitions(-DWITH_TBB_MALLOC)
endif()
endif()

View File

@@ -5,7 +5,7 @@ for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X)
for %%X in (git.exe) do (set GIT=%%~$PATH:X)
REM For python, default on 39 but if that does not exist also check
REM the 310,311 and 312 folders to see if those are there, it checks
REM this far ahead to ensure good lib folder compatibility in the future.
REM this far ahead to ensure good lib folder compatiblity in the future.
set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe
if EXIST %PYTHON% (
goto detect_python_done

View File

@@ -1,502 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -5,6 +5,5 @@ BSD-2-Clause BSD-2-Clause-license.txt https://spdx.org/licenses/BS
BSD-3-Clause BSD-3-Clause-license.txt https://spdx.org/licenses/BSD-3-Clause.html
GPL-2.0-or-later GPL-license.txt https://spdx.org/licenses/GPL-2.0-or-later.html
GPL-3.0-or-later GPL3-license.txt https://spdx.org/licenses/GPL-3.0-or-later.html
LGPL-2.1-or-later LGPL2.1-license.txt https://spdx.org/licenses/LGPL-2.1-or-later.html
MIT MIT-license.txt https://spdx.org/licenses/MIT.html
Zlib Zlib-license.txt https://spdx.org/licenses/Zlib.html

View File

@@ -16,6 +16,7 @@ and <output-filename> is where to write the generated man page.
import argparse
import os
import subprocess
import sys
import time
from typing import (

View File

@@ -41,7 +41,7 @@ class MATERIAL_UL_matslots_example(bpy.types.UIList):
else:
layout.label(text="", translate=False, icon_value=icon)
# 'GRID' layout type should be as compact as possible (typically a single icon!).
elif self.layout_type == 'GRID':
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)

View File

@@ -73,7 +73,7 @@ class MESH_UL_vgroups_slow(bpy.types.UIList):
layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
elif self.layout_type == 'GRID':
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
if flt_flag & self.VGROUP_EMPTY:
layout.enabled = False

View File

@@ -1,27 +0,0 @@
Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

View File

@@ -1,8 +0,0 @@
Project: {fmt}
URL: https://github.com/fmtlib/fmt
License: MIT
Upstream version: 8.1.1 (b6f4cea)
Local modifications:
- Took only files needed for Blender: LICENSE, README and include/fmt
folder's core.h, format-inl.h, format.h

View File

@@ -1,528 +0,0 @@
{fmt}
=====
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
:alt: fmt is continuously fuzzed at oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
Summary&q=proj%3Dfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: https://stackoverflow.com/questions/tagged/fmt
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to the BYSOL
Foundation that helps victims of political repressions in Belarus:
https://bysol.org/en/bs/general/.
`Documentation <https://fmt.dev>`__
Q&A: ask questions on `StackOverflow with the tag fmt
<https://stackoverflow.com/questions/tagged/fmt>`_.
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
Features
--------
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
for localization
* Implementation of `C++20 std::format
<https://en.cppreference.com/w/cpp/utility/format>`__
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
round-trip guarantees
* Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
extension for positional arguments
* Extensibility: `support for user-defined types
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_
* High performance: faster than common standard library implementations of
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
and `Converting a hundred million integers to strings per second
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
* Small code size both in terms of source code with the minimum configuration
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
and compiled code; see `Compile time and code bloat`_
* Reliability: the library has an extensive set of `tests
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed
<https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20
Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_
* Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow
errors
* Ease of use: small self-contained code base, no external dependencies,
permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers
* Clean warning-free codebase even on high warning levels such as
``-Wall -Wextra -pedantic``
* Locale-independence by default
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
See the `documentation <https://fmt.dev>`_ for more details.
Examples
--------
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
.. code:: c++
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
}
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
.. code:: c++
std::string s = fmt::format("The answer is {}.", 42);
// s == "The answer is 42."
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
.. code:: c++
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
.. code:: c++
#include <fmt/chrono.h>
int main() {
using namespace std::literals::chrono_literals;
fmt::print("Default format: {} {}\n", 42s, 100ms);
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}
Output::
Default format: 42s 100ms
strftime-like format: 03:15:30
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_)
.. code:: c++
#include <vector>
#include <fmt/ranges.h>
int main() {
std::vector<int> v = {1, 2, 3};
fmt::print("{}\n", v);
}
Output::
[1, 2, 3]
**Check a format string at compile time**
.. code:: c++
std::string s = fmt::format("{:d}", "I am not a number");
This gives a compile-time error in C++20 because ``d`` is an invalid format
specifier for a string.
**Write a file from a single thread**
.. code:: c++
#include <fmt/os.h>
int main() {
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
}
This can be `5 to 9 times faster than fprintf
<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_.
**Print with colors and text styles**
.. code:: c++
#include <fmt/color.h>
int main() {
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
"Hello, {}!\n", "world");
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
fmt::emphasis::underline, "Hello, {}!\n", "мир");
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
"Hello, {}!\n", "世界");
}
Output on a modern terminal:
.. image:: https://user-images.githubusercontent.com/
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
Benchmarks
----------
Speed tests
~~~~~~~~~~~
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
`ryu <https://github.com/ulfjack/ryu>`_:
.. image:: https://user-images.githubusercontent.com/576385/
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
:target: https://fmt.dev/unknown_mac64_clang12.0.html
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
size compared to iostreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
**Non-optimized build**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.2 33 30
printf+string 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
Boost Format 54.1 365 303
Folly Format 79.9 445 430
============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ https://fmt.dev/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
Then you can run the speed test::
$ make speed-test
or the bloat test::
$ make bloat-test
Migrating code
--------------
`clang-tidy-fmt <https://github.com/mikecrowe/clang-tidy-fmt>`_ provides clang
tidy checks for converting occurrences of ``printf`` and ``fprintf`` to
``fmt::print``.
Projects using this library
---------------------------
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
real-time strategy game
* `2GIS <https://2gis.ru/>`_: free business listings with a city map
* `AMPL/MP <https://github.com/ampl/mp>`_:
an open-source library for mathematical programming
* `Aseprite <https://github.com/aseprite/aseprite>`_:
animated sprite editor & pixel art tool
* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft
operations suite
* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform
* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space
* `Ceph <https://ceph.com/>`_: a scalable distributed storage system
* `ccache <https://ccache.dev/>`_: a compiler cache
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
management system
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
(Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `fmtlog <https://github.com/MengRao/fmtlog>`_: a performant fmtlib-style
logging library with latency in nanoseconds
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
* `Grand Mountain Adventure
<https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
A beautiful open-world ski & snowboarding game
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server
engine
* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
research programming language for concurrent ownership
* `MongoDB <https://mongodb.com/>`_: distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to
generate randomized datasets
* `OpenSpace <https://openspaceproject.com/>`_: an open-source
astrovisualization framework
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
an MMO server, compatible with most Ultima Online clients
* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine
learning library
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
associative database
* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library
* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify
navigation, and executing complex multi-line terminal command sequences
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster
proxy
* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement
for mission critical systems written in C++
* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client
library
* `Salesforce Analytics Cloud
<https://www.salesforce.com/analytics-cloud/overview/>`_:
business intelligence software
* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store
that can handle 1 million transactions per second on a single server
* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++
framework for high-performance server applications on modern hardware
* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library
* `Stellar <https://www.stellar.org/>`_: financial platform
* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
MMORPG framework
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
terminal
`More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
`issue <https://github.com/fmtlib/fmt/issues>`_.
Motivation
----------
So why yet another formatting library?
There are plenty of methods for doing this task, from standard ones like
the printf family of function and iostreams to Boost Format and FastFormat
libraries. The reason for creating a new library is that every existing
solution that I found either had serious issues or didn't provide
all the features I needed.
printf
~~~~~~
The good thing about ``printf`` is that it is pretty fast and readily available
being a part of the C standard library. The main drawback is that it
doesn't support user-defined types. ``printf`` also has safety issues although
they are somewhat mitigated with `__attribute__ ((format (printf, ...))
<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
There is a POSIX extension that adds positional arguments required for
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
to ``printf`` but it is not a part of C99 and may not be available on some
platforms.
iostreams
~~~~~~~~~
The main issue with iostreams is best illustrated with an example:
.. code:: c++
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
which is a lot of typing compared to printf:
.. code:: c++
printf("%.2f\n", 1.23456);
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
don't support positional arguments by design.
The good part is that iostreams support user-defined types and are safe although
error handling is awkward.
Boost Format
~~~~~~~~~~~~
This is a very powerful library which supports both ``printf``-like format
strings and positional arguments. Its main drawback is performance. According to
various benchmarks, it is much slower than other methods considered here. Boost
Format also has excessive build times and severe code bloat issues (see
`Benchmarks`_).
FastFormat
~~~~~~~~~~
This is an interesting library which is fast, safe and has positional arguments.
However, it has significant limitations, citing its author:
Three features that have no hope of being accommodated within the
current design are:
* Leading zeros (or any other non-space padding)
* Octal/hexadecimal encoding
* Runtime width/alignment specification
It is also quite big and has a heavy dependency, STLSoft, which might be too
restrictive for using it in some projects.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
This is not really a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing verbatim text
with arguments. The library is pretty fast, but slower on integer formatting
than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
see `Converting a hundred million integers to strings per second
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
License
-------
{fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
Documentation License
---------------------
The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_.
For this reason the documentation is distributed under the Python Software
Foundation license available in `doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of {fmt}.
Maintainers
-----------
The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
<https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and
we'll make it right.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -423,7 +423,6 @@ TEST(atomic, atomic_fetch_and_and_uint32)
/** \} */
/* -------------------------------------------------------------------- */
/** \name 32 bit signed int atomics
* \{ */
@@ -560,7 +559,6 @@ TEST(atomic, atomic_fetch_and_and_int32)
/** \} */
/* -------------------------------------------------------------------- */
/** \name 16 bit signed int atomics
* \{ */
@@ -594,9 +592,6 @@ TEST(atomic, atomic_fetch_and_and_int16)
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name 8 bit unsigned int atomics
* \{ */
@@ -643,7 +638,6 @@ TEST(atomic, atomic_fetch_and_and_int8)
/** \} */
/* -------------------------------------------------------------------- */
/** \name char aliases
* \{ */
@@ -667,7 +661,6 @@ TEST(atomic, atomic_fetch_and_and_char)
/** \} */
/* -------------------------------------------------------------------- */
/** \name size_t aliases
* \{ */
@@ -779,7 +772,6 @@ TEST(atomic, atomic_fetch_and_update_max_z)
/** \} */
/* -------------------------------------------------------------------- */
/** \name unsigned int aliases
* \{ */
@@ -875,7 +867,6 @@ TEST(atomic, atomic_cas_u)
/** \} */
/* -------------------------------------------------------------------- */
/** \name pointer aliases
* \{ */
@@ -894,7 +885,6 @@ TEST(atomic, atomic_cas_ptr)
/** \} */
/* -------------------------------------------------------------------- */
/** \name floating point atomics
* \{ */

View File

@@ -2,12 +2,8 @@
# Copyright 2011-2022 Blender Foundation
# Standalone or with Blender
if(NOT WITH_BLENDER)
if(WITH_CYCLES_STANDALONE OR NOT WITH_CYCLES_HYDRA_RENDER_DELEGATE)
set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX})
else()
set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/hdCycles/resources)
endif()
if(NOT WITH_BLENDER AND WITH_CYCLES_STANDALONE)
set(CYCLES_INSTALL_PATH ${CMAKE_INSTALL_PREFIX})
else()
set(WITH_CYCLES_BLENDER ON)
# WINDOWS_PYTHON_DEBUG needs to write into the user addons folder since it will
@@ -339,11 +335,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang")
unset(_has_no_error_unused_macros)
endif()
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE AND NOT WITH_USD)
message(STATUS "USD not found, disabling WITH_CYCLES_HYDRA_RENDER_DELEGATE")
set(WITH_CYCLES_HYDRA_RENDER_DELEGATE OFF)
endif()
if(WITH_CYCLES_CUDA_BINARIES AND (NOT WITH_CYCLES_CUBIN_COMPILER))
if(MSVC)
set(MAX_MSVC 1800)
@@ -404,10 +395,6 @@ if(WITH_GTESTS)
add_subdirectory(test)
endif()
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE)
add_subdirectory(hydra)
endif()
if(NOT WITH_BLENDER)
if(NOT WITH_BLENDER AND WITH_CYCLES_STANDALONE)
delayed_do_install(${CMAKE_BINARY_DIR}/bin)
endif()

View File

@@ -22,16 +22,6 @@ set(LIBRARIES
cycles_util
)
if(WITH_ALEMBIC)
add_definitions(-DWITH_ALEMBIC)
list(APPEND INC_SYS
${ALEMBIC_INCLUDE_DIRS}
)
list(APPEND LIB
${ALEMBIC_LIBRARIES}
)
endif()
if(WITH_CYCLES_OSL)
list(APPEND LIBRARIES cycles_kernel_osl)
endif()

View File

@@ -9,7 +9,6 @@
#include "graph/node_xml.h"
#include "scene/alembic.h"
#include "scene/background.h"
#include "scene/camera.h"
#include "scene/film.h"
@@ -193,31 +192,6 @@ static void xml_read_camera(XMLReadState &state, xml_node node)
cam->update(state.scene);
}
/* Alembic */
#ifdef WITH_ALEMBIC
static void xml_read_alembic(XMLReadState &state, xml_node graph_node)
{
AlembicProcedural *proc = state.scene->create_node<AlembicProcedural>();
xml_read_node(state, proc, graph_node);
for (xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
if (string_iequals(node.name(), "object")) {
string path;
if (xml_read_string(&path, node, "path")) {
ustring object_path(path, 0);
AlembicObject *object = static_cast<AlembicObject *>(
proc->get_or_create_object(object_path));
array<Node *> used_shaders = object->get_used_shaders();
used_shaders.push_back_slow(state.shader);
object->set_used_shaders(used_shaders);
}
}
}
}
#endif
/* Shader */
static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node graph_node)
@@ -673,11 +647,6 @@ static void xml_read_scene(XMLReadState &state, xml_node scene_node)
if (xml_read_string(&src, node, "src"))
xml_read_include(state, src);
}
#ifdef WITH_ALEMBIC
else if (string_iequals(node.name(), "alembic")) {
xml_read_alembic(state, node);
}
#endif
else
fprintf(stderr, "Unknown node \"%s\".\n", node.name());
}

View File

@@ -83,17 +83,6 @@ class CyclesRender(bpy.types.RenderEngine):
# viewport render
def view_update(self, context, depsgraph):
if not self.session:
# When starting a new render session in viewport (by switching
# viewport to Rendered shading) unpause the render. The way to think
# of it is: artist requests render, so we start to render.
# Do it for both original and evaluated scene so that Cycles
# immediately reacts to un-paused render.
cscene = context.scene.cycles
cscene_eval = depsgraph.scene_eval.cycles
if cscene.preview_pause or cscene_eval.preview_pause:
cscene.preview_pause = False
cscene_eval.preview_pause = False
engine.create(self, context.blend_data,
context.region, context.space_data, context.region_data)

View File

@@ -228,10 +228,6 @@ def list_render_passes(scene, srl):
else:
yield (aov.name, "RGB", 'COLOR')
# Light groups.
for lightgroup in srl.lightgroups:
yield ("Combined_%s" % lightgroup.name, "RGB", 'COLOR')
def register_passes(engine, scene, view_layer):
for name, channelids, channeltype in list_render_passes(scene, view_layer):

View File

@@ -216,6 +216,7 @@ enum_direct_light_sampling_type = (
)
def update_render_passes(self, context):
scene = context.scene
view_layer = context.view_layer
view_layer.update_render_passes()
@@ -348,8 +349,8 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
scrambling_distance: FloatProperty(
name="Scrambling Distance",
default=1.0,
min=0.0, soft_max=1.0,
description="Reduce randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts if set too low",
min=0.0, max=1.0,
description="Reduce randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts if set too low. Only works when not using adaptive sampling",
)
preview_scrambling_distance: BoolProperty(
name="Scrambling Distance viewport",
@@ -360,7 +361,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
auto_scrambling_distance: BoolProperty(
name="Automatic Scrambling Distance",
default=False,
description="Automatically reduce the randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts",
description="Automatically reduce the randomization between pixels to improve GPU rendering performance, at the cost of possible rendering artifacts. Only works when not using adaptive sampling",
)
use_layer_samples: EnumProperty(
@@ -1012,12 +1013,6 @@ class CyclesLightSettings(bpy.types.PropertyGroup):
"note that this will make the light invisible",
default=False,
)
is_caustics_light: BoolProperty(
name="Shadow Caustics",
description="Generate approximate caustics in shadows of refractive surfaces. "
"Lights, caster and receiver objects must have shadow caustics options set to enable this",
default=False,
)
@classmethod
def register(cls):
@@ -1034,12 +1029,6 @@ class CyclesLightSettings(bpy.types.PropertyGroup):
class CyclesWorldSettings(bpy.types.PropertyGroup):
is_caustics_light: BoolProperty(
name="Shadow Caustics",
description="Generate approximate caustics in shadows of refractive surfaces. "
"Lights, caster and receiver objects must have shadow caustics options set to enable this",
default=False,
)
sampling_method: EnumProperty(
name="Sampling Method",
description="How to sample the background light",
@@ -1238,21 +1227,6 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
subtype='DISTANCE',
)
is_caustics_caster: BoolProperty(
name="Cast Shadow Caustics",
description="With refractive materials, generate approximate caustics in shadows of this object. "
"Up to 10 bounces inside this object are taken into account. Lights, caster and receiver objects "
"must have shadow caustics options set to enable this",
default=False,
)
is_caustics_receiver: BoolProperty(
name="Receive Shadow Caustics",
description="Receive approximate caustics from refractive materials in shadows on this object. "
"Lights, caster and receiver objects must have shadow caustics options set to enable this",
default=False,
)
@classmethod
def register(cls):
bpy.types.Object.cycles = PointerProperty(
@@ -1379,7 +1353,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
items=CyclesPreferences.get_device_types,
)
devices: CollectionProperty(type=CyclesDeviceSettings)
devices: bpy.props.CollectionProperty(type=CyclesDeviceSettings)
peer_memory: BoolProperty(
name="Distribute memory across devices",
@@ -1507,7 +1481,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
col.label(text="and NVIDIA driver version 470 or newer", icon='BLANK1')
elif device_type == 'HIP':
import sys
col.label(text="Requires discrete AMD GPU with Vega architecture", icon='BLANK1')
col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1')
if sys.platform[:3] == "win":
col.label(text="and AMD Radeon Pro 21.Q4 driver or newer", icon='BLANK1')
elif device_type == 'METAL':

View File

@@ -11,8 +11,7 @@ from bl_ui.utils import PresetPanel
from bpy.types import Panel
from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel
from bl_ui.properties_render import draw_hair_settings
from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel, ViewLayerLightgroupsPanel
from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel
class CyclesPresetPanel(PresetPanel, Panel):
COMPAT_ENGINES = {'CYCLES'}
@@ -289,8 +288,11 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
layout.separator()
heading = layout.column(align=True, heading="Scrambling Distance")
heading.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling)
heading.prop(cscene, "auto_scrambling_distance", text="Automatic")
heading.prop(cscene, "preview_scrambling_distance", text="Viewport")
sub = heading.row()
sub.active = not cscene.use_preview_adaptive_sampling
sub.prop(cscene, "preview_scrambling_distance", text="Viewport")
heading.prop(cscene, "scrambling_distance", text="Multiplier")
layout.separator()
@@ -353,13 +355,6 @@ class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel):
if ccscene.shape == 'RIBBONS':
col.prop(ccscene, "subdivisions", text="Curve Subdivisions")
class CYCLES_RENDER_PT_hair_viewport_display(CyclesButtonsPanel, Panel):
bl_label = "Viewport Display"
bl_parent_id = "CYCLES_RENDER_PT_hair"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
draw_hair_settings(self, context)
class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel):
bl_label = "Volumes"
@@ -883,12 +878,6 @@ class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, ViewLayerAOVPanel):
bl_parent_id = "CYCLES_RENDER_PT_passes"
class CYCLES_RENDER_PT_passes_lightgroups(CyclesButtonsPanel, ViewLayerLightgroupsPanel):
bl_label = "Light Groups"
bl_context = "view_layer"
bl_parent_id = "CYCLES_RENDER_PT_passes"
class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel):
bl_label = "Post Processing"
bl_options = {'DEFAULT_CLOSED'}
@@ -1034,7 +1023,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
def poll(cls, context):
ob = context.object
if CyclesButtonsPanel.poll(context) and ob:
if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'CURVES', 'POINTCLOUD'}:
if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'HAIR', 'POINTCLOUD'}:
return True
if ob.instance_type == 'COLLECTION' and ob.instance_collection:
return True
@@ -1073,7 +1062,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
def has_geometry_visibility(ob):
return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT', 'VOLUME', 'POINTCLOUD', 'CURVES'}) or
return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT', 'VOLUME', 'POINTCLOUD', 'HAIR'}) or
(ob.instance_type == 'COLLECTION' and ob.instance_collection))
@@ -1099,10 +1088,6 @@ class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel):
bl_parent_id = "CYCLES_OBJECT_PT_shading"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.object.type != 'LIGHT'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@@ -1120,10 +1105,6 @@ class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel):
bl_parent_id = "CYCLES_OBJECT_PT_shading"
bl_context = "object"
@classmethod
def poll(cls, context):
return context.object.type != 'LIGHT'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@@ -1139,45 +1120,6 @@ class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel):
col.prop(cob, "ao_distance")
class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel):
bl_label = "Caustics"
bl_parent_id = "CYCLES_OBJECT_PT_shading"
bl_context = "object"
@classmethod
def poll(cls, context):
return CyclesButtonsPanel.poll(context) and not use_metal(context) and context.object.type != 'LIGHT'
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
ob = context.object
cob = ob.cycles
col.prop(cob, "is_caustics_caster")
col.prop(cob, "is_caustics_receiver")
class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel):
bl_label = "Light Group"
bl_parent_id = "CYCLES_OBJECT_PT_shading"
bl_context = "object"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
ob = context.object
view_layer = context.view_layer
col = layout.column(align=True)
col.prop_search(ob, "lightgroup", view_layer, "lightgroups", text="Light Group")
class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel):
bl_label = "Visibility"
bl_context = "object"
@@ -1353,8 +1295,6 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
sub.active = not (light.type == 'AREA' and clamp.is_portal)
sub.prop(clamp, "cast_shadow")
sub.prop(clamp, "use_multiple_importance_sampling", text="Multiple Importance")
if not use_metal(context):
sub.prop(clamp, "is_caustics_light", text="Shadow Caustics")
if light.type == 'AREA':
col.prop(clamp, "is_portal", text="Portal")
@@ -1430,14 +1370,10 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel):
layout.use_property_split = True
world = context.world
view_layer = context.view_layer
if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'):
layout.prop(world, "color")
col = layout.column(align=True)
col.prop_search(world, "lightgroup", view_layer, "lightgroups", text="Light Group")
class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel):
bl_label = "Volume"
@@ -1555,8 +1491,6 @@ class CYCLES_WORLD_PT_settings_surface(CyclesButtonsPanel, Panel):
subsub.active = cworld.sampling_method == 'MANUAL'
subsub.prop(cworld, "sample_map_resolution")
sub.prop(cworld, "max_bounces")
sub.prop(cworld, "is_caustics_light", text="Shadow Caustics")
class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel):
@@ -2219,7 +2153,6 @@ classes = (
CYCLES_RENDER_PT_volumes,
CYCLES_RENDER_PT_subdivision,
CYCLES_RENDER_PT_hair,
CYCLES_RENDER_PT_hair_viewport_display,
CYCLES_RENDER_PT_simplify,
CYCLES_RENDER_PT_simplify_viewport,
CYCLES_RENDER_PT_simplify_render,
@@ -2244,7 +2177,6 @@ classes = (
CYCLES_RENDER_PT_passes_light,
CYCLES_RENDER_PT_passes_crypto,
CYCLES_RENDER_PT_passes_aov,
CYCLES_RENDER_PT_passes_lightgroups,
CYCLES_RENDER_PT_filter,
CYCLES_RENDER_PT_override,
CYCLES_PT_post_processing,
@@ -2255,8 +2187,6 @@ classes = (
CYCLES_OBJECT_PT_shading,
CYCLES_OBJECT_PT_shading_shadow_terminator,
CYCLES_OBJECT_PT_shading_gi_approximation,
CYCLES_OBJECT_PT_shading_caustics,
CYCLES_OBJECT_PT_lightgroup,
CYCLES_OBJECT_PT_visibility,
CYCLES_OBJECT_PT_visibility_ray_visibility,
CYCLES_OBJECT_PT_visibility_culling,

View File

@@ -5,6 +5,7 @@
from __future__ import annotations
import bpy
import math
from bpy.app.handlers import persistent

View File

@@ -114,9 +114,6 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_cast_shadow(get_boolean(clight, "cast_shadow"));
light->set_use_mis(get_boolean(clight, "use_multiple_importance_sampling"));
/* caustics light */
light->set_use_caustics(get_boolean(clight, "is_caustics_light"));
light->set_max_bounces(get_int(clight, "max_bounces"));
if (b_ob_info.real_object != b_ob_info.iter_object) {
@@ -143,9 +140,6 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_use_scatter((visibility & PATH_RAY_VOLUME_SCATTER) != 0);
light->set_is_shadow_catcher(b_ob_info.real_object.is_shadow_catcher());
/* lightgroup */
light->set_lightgroup(ustring(b_ob_info.real_object.lightgroup()));
/* tag */
light->tag_update(scene);
}
@@ -182,9 +176,6 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
/* force enable light again when world is resynced */
light->set_is_enabled(true);
/* caustic light */
light->set_use_caustics(get_boolean(cworld, "is_caustics_light"));
light->tag_update(scene);
light_map.set_recalc(b_world);
}

View File

@@ -298,12 +298,6 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
object->set_ao_distance(ao_distance);
bool is_caustics_caster = get_boolean(cobject, "is_caustics_caster");
object->set_is_caustics_caster(is_caustics_caster);
bool is_caustics_receiver = get_boolean(cobject, "is_caustics_receiver");
object->set_is_caustics_receiver(is_caustics_receiver);
/* sync the asset name for Cryptomatte */
BL::Object parent = b_ob.parent();
ustring parent_name;
@@ -325,9 +319,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
(object->get_geometry() && object->get_geometry()->is_modified())) {
object->name = b_ob.name().c_str();
object->set_pass_id(b_ob.pass_index());
const BL::Array<float, 4> object_color = b_ob.color();
object->set_color(get_float3(object_color));
object->set_alpha(object_color[3]);
object->set_color(get_float3(b_ob.color()));
object->set_tfm(tfm);
/* dupli texture coordinates and random_id */
@@ -343,9 +335,6 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
object->set_random_id(hash_uint2(hash_string(object->name.c_str()), 0));
}
/* lightgroup */
object->set_lightgroup(ustring(b_ob.lightgroup()));
object->tag_update(scene);
}
@@ -431,7 +420,7 @@ static float4 lookup_instance_property(BL::DepsgraphObjectInstance &b_instance,
return value;
}
return zero_float4();
return make_float4(0.0f);
}
bool BlenderSync::sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object)
@@ -630,8 +619,10 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
bool has_subdivision_modifier = false;
BL::MeshSequenceCacheModifier b_mesh_cache(PointerRNA_NULL);
/* Experimental as Blender does not have good support for procedurals at the moment. */
if (experimental) {
/* Experimental as Blender does not have good support for procedurals at the moment, also
* only available in preview renders since currently do not have a good cache policy, the
* data being loaded at once for all the frames. */
if (experimental && b_v3d) {
b_mesh_cache = object_mesh_cache_find(b_ob, &has_subdivision_modifier);
use_procedural = b_mesh_cache && b_mesh_cache.cache_file().use_render_procedural();
}

View File

@@ -271,7 +271,6 @@ static ShaderNode *add_node(Scene *scene,
curves->set_min_x(min_x);
curves->set_max_x(max_x);
curves->set_curves(curve_mapping_curves);
curves->set_extrapolate(mapping.extend() == mapping.extend_EXTRAPOLATED);
node = curves;
}
if (b_node.is_a(&RNA_ShaderNodeVectorCurve)) {
@@ -285,7 +284,6 @@ static ShaderNode *add_node(Scene *scene,
curves->set_min_x(min_x);
curves->set_max_x(max_x);
curves->set_curves(curve_mapping_curves);
curves->set_extrapolate(mapping.extend() == mapping.extend_EXTRAPOLATED);
node = curves;
}
else if (b_node.is_a(&RNA_ShaderNodeFloatCurve)) {
@@ -299,7 +297,6 @@ static ShaderNode *add_node(Scene *scene,
curve->set_min_x(min_x);
curve->set_max_x(max_x);
curve->set_curve(curve_mapping_curve);
curve->set_extrapolate(mapping.extend() == mapping.extend_EXTRAPOLATED);
node = curve;
}
else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) {
@@ -1532,8 +1529,6 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
background->set_use_shader(view_layer.use_background_shader ||
viewport_parameters.use_custom_shader());
background->set_lightgroup(ustring(b_world ? b_world.lightgroup() : ""));
background->tag_update(scene);
}

View File

@@ -346,48 +346,31 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL);
integrator->set_sampling_pattern(sampling_pattern);
int samples = 1;
bool use_adaptive_sampling = false;
if (preview) {
samples = get_int(cscene, "preview_samples");
use_adaptive_sampling = RNA_boolean_get(&cscene, "use_preview_adaptive_sampling");
integrator->set_use_adaptive_sampling(use_adaptive_sampling);
integrator->set_adaptive_threshold(get_float(cscene, "preview_adaptive_threshold"));
integrator->set_adaptive_min_samples(get_int(cscene, "preview_adaptive_min_samples"));
}
else {
samples = get_int(cscene, "samples");
use_adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling");
integrator->set_use_adaptive_sampling(use_adaptive_sampling);
integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold"));
integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples"));
}
int samples = get_int(cscene, "samples");
float scrambling_distance = get_float(cscene, "scrambling_distance");
bool auto_scrambling_distance = get_boolean(cscene, "auto_scrambling_distance");
if (auto_scrambling_distance) {
if (samples == 0) {
/* If samples is 0, then viewport rendering is set to render infinitely. In that case we
* override the samples value with 4096 so the Automatic Scrambling Distance algorithm
* picks a Scrambling Distance value with a good balance of performance and correlation
* artifacts when rendering to high sample counts. */
samples = 4096;
}
if (use_adaptive_sampling) {
/* If Adaptive Sampling is enabled, use "min_samples" in the Automatic Scrambling Distance
* algorithm to avoid artifacts common with Adaptive Sampling + Scrambling Distance. */
const AdaptiveSampling adaptive_sampling = integrator->get_adaptive_sampling();
samples = min(samples, adaptive_sampling.min_samples);
}
scrambling_distance *= 4.0f / sqrtf(samples);
}
/* Only use scrambling distance in the viewport if user wants to. */
/* only use scrambling distance in the viewport if user wants to and disable with AS */
bool preview_scrambling_distance = get_boolean(cscene, "preview_scrambling_distance");
if (preview && !preview_scrambling_distance) {
if ((preview && !preview_scrambling_distance) || use_adaptive_sampling)
scrambling_distance = 1.0f;
}
if (scrambling_distance != 1.0f) {
VLOG(3) << "Using scrambling distance: " << scrambling_distance;
@@ -745,20 +728,6 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
}
}
/* Light Group passes. */
BL::ViewLayer::lightgroups_iterator b_lightgroup_iter;
for (b_view_layer.lightgroups.begin(b_lightgroup_iter);
b_lightgroup_iter != b_view_layer.lightgroups.end();
++b_lightgroup_iter) {
BL::Lightgroup b_lightgroup(*b_lightgroup_iter);
string name = string_printf("Combined_%s", b_lightgroup.name().c_str());
b_engine.add_pass(name.c_str(), 3, "RGB", b_view_layer.name().c_str());
Pass *pass = pass_add(scene, PASS_COMBINED, name.c_str(), PassMode::NOISY);
pass->set_lightgroup(ustring(b_lightgroup.name()));
}
scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold());
}

View File

@@ -203,7 +203,7 @@ BVHObjectBinning::BVHObjectBinning(const BVHRange &job,
bestSAH = min(sah, bestSAH);
}
int4 mask = float3_to_float4(cent_bounds_.size()) <= zero_float4();
int4 mask = float3_to_float4(cent_bounds_.size()) <= make_float4(0.0f);
bestSAH = insert<3>(select(mask, make_float4(FLT_MAX), bestSAH), FLT_MAX);
/* find best dimension */

View File

@@ -101,7 +101,6 @@ macro(cycles_target_link_libraries target)
${PNG_LIBRARIES}
${JPEG_LIBRARIES}
${TIFF_LIBRARY}
${WEBP_LIBRARIES}
${OPENJPEG_LIBRARIES}
${OPENEXR_LIBRARIES}
${OPENEXR_LIBRARIES} # For circular dependencies between libs.

View File

@@ -197,8 +197,7 @@ cycles_add_library(cycles_device "${LIB}" ${SRC})
source_group("cpu" FILES ${SRC_CPU})
source_group("cuda" FILES ${SRC_CUDA})
source_group("dummy" FILES ${SRC_DUMMY})
source_group("hip" FILES ${SRC_HIP})
source_group("multi" FILES ${SRC_MULTI})
source_group("metal" FILES ${SRC_METAL})
source_group("optix" FILES ${SRC_OPTIX})
source_group("common" FILES ${SRC_BASE} ${SRC_HEADERS})
source_group("common" FILES ${SRC} ${SRC_HEADERS})

View File

@@ -51,7 +51,7 @@ static inline bool hipSupportsDevice(const int hipDevId)
hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId);
hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId);
return (major >= 9);
return (major > 10) || (major == 10 && minor >= 1);
}
CCL_NAMESPACE_END

View File

@@ -33,191 +33,6 @@
CCL_NAMESPACE_BEGIN
// A minimal copy of functionality `optix_denoiser_tiling.h` which allows to fix integer overflow
// issues without bumping SDK or driver requirement.
//
// The original code is Copyright NVIDIA Corporation, BSD-3-Clause.
namespace {
static OptixResult optixUtilDenoiserSplitImage(const OptixImage2D &input,
const OptixImage2D &output,
unsigned int overlapWindowSizeInPixels,
unsigned int tileWidth,
unsigned int tileHeight,
std::vector<OptixUtilDenoiserImageTile> &tiles)
{
if (tileWidth == 0 || tileHeight == 0)
return OPTIX_ERROR_INVALID_VALUE;
unsigned int inPixelStride = optixUtilGetPixelStride(input);
unsigned int outPixelStride = optixUtilGetPixelStride(output);
int inp_w = std::min(tileWidth + 2 * overlapWindowSizeInPixels, input.width);
int inp_h = std::min(tileHeight + 2 * overlapWindowSizeInPixels, input.height);
int inp_y = 0, copied_y = 0;
do {
int inputOffsetY = inp_y == 0 ? 0 :
std::max((int)overlapWindowSizeInPixels,
inp_h - ((int)input.height - inp_y));
int copy_y = inp_y == 0 ? std::min(input.height, tileHeight + overlapWindowSizeInPixels) :
std::min(tileHeight, input.height - copied_y);
int inp_x = 0, copied_x = 0;
do {
int inputOffsetX = inp_x == 0 ? 0 :
std::max((int)overlapWindowSizeInPixels,
inp_w - ((int)input.width - inp_x));
int copy_x = inp_x == 0 ? std::min(input.width, tileWidth + overlapWindowSizeInPixels) :
std::min(tileWidth, input.width - copied_x);
OptixUtilDenoiserImageTile tile;
tile.input.data = input.data + (size_t)(inp_y - inputOffsetY) * input.rowStrideInBytes +
+(size_t)(inp_x - inputOffsetX) * inPixelStride;
tile.input.width = inp_w;
tile.input.height = inp_h;
tile.input.rowStrideInBytes = input.rowStrideInBytes;
tile.input.pixelStrideInBytes = input.pixelStrideInBytes;
tile.input.format = input.format;
tile.output.data = output.data + (size_t)inp_y * output.rowStrideInBytes +
(size_t)inp_x * outPixelStride;
tile.output.width = copy_x;
tile.output.height = copy_y;
tile.output.rowStrideInBytes = output.rowStrideInBytes;
tile.output.pixelStrideInBytes = output.pixelStrideInBytes;
tile.output.format = output.format;
tile.inputOffsetX = inputOffsetX;
tile.inputOffsetY = inputOffsetY;
tiles.push_back(tile);
inp_x += inp_x == 0 ? tileWidth + overlapWindowSizeInPixels : tileWidth;
copied_x += copy_x;
} while (inp_x < static_cast<int>(input.width));
inp_y += inp_y == 0 ? tileHeight + overlapWindowSizeInPixels : tileHeight;
copied_y += copy_y;
} while (inp_y < static_cast<int>(input.height));
return OPTIX_SUCCESS;
}
static OptixResult optixUtilDenoiserInvokeTiled(OptixDenoiser denoiser,
CUstream stream,
const OptixDenoiserParams *params,
CUdeviceptr denoiserState,
size_t denoiserStateSizeInBytes,
const OptixDenoiserGuideLayer *guideLayer,
const OptixDenoiserLayer *layers,
unsigned int numLayers,
CUdeviceptr scratch,
size_t scratchSizeInBytes,
unsigned int overlapWindowSizeInPixels,
unsigned int tileWidth,
unsigned int tileHeight)
{
if (!guideLayer || !layers)
return OPTIX_ERROR_INVALID_VALUE;
std::vector<std::vector<OptixUtilDenoiserImageTile>> tiles(numLayers);
std::vector<std::vector<OptixUtilDenoiserImageTile>> prevTiles(numLayers);
for (unsigned int l = 0; l < numLayers; l++) {
if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(layers[l].input,
layers[l].output,
overlapWindowSizeInPixels,
tileWidth,
tileHeight,
tiles[l]))
return res;
if (layers[l].previousOutput.data) {
OptixImage2D dummyOutput = layers[l].previousOutput;
if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(layers[l].previousOutput,
dummyOutput,
overlapWindowSizeInPixels,
tileWidth,
tileHeight,
prevTiles[l]))
return res;
}
}
std::vector<OptixUtilDenoiserImageTile> albedoTiles;
if (guideLayer->albedo.data) {
OptixImage2D dummyOutput = guideLayer->albedo;
if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(guideLayer->albedo,
dummyOutput,
overlapWindowSizeInPixels,
tileWidth,
tileHeight,
albedoTiles))
return res;
}
std::vector<OptixUtilDenoiserImageTile> normalTiles;
if (guideLayer->normal.data) {
OptixImage2D dummyOutput = guideLayer->normal;
if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(guideLayer->normal,
dummyOutput,
overlapWindowSizeInPixels,
tileWidth,
tileHeight,
normalTiles))
return res;
}
std::vector<OptixUtilDenoiserImageTile> flowTiles;
if (guideLayer->flow.data) {
OptixImage2D dummyOutput = guideLayer->flow;
if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(guideLayer->flow,
dummyOutput,
overlapWindowSizeInPixels,
tileWidth,
tileHeight,
flowTiles))
return res;
}
for (size_t t = 0; t < tiles[0].size(); t++) {
std::vector<OptixDenoiserLayer> tlayers;
for (unsigned int l = 0; l < numLayers; l++) {
OptixDenoiserLayer layer = {};
layer.input = (tiles[l])[t].input;
layer.output = (tiles[l])[t].output;
if (layers[l].previousOutput.data)
layer.previousOutput = (prevTiles[l])[t].input;
tlayers.push_back(layer);
}
OptixDenoiserGuideLayer gl = {};
if (guideLayer->albedo.data)
gl.albedo = albedoTiles[t].input;
if (guideLayer->normal.data)
gl.normal = normalTiles[t].input;
if (guideLayer->flow.data)
gl.flow = flowTiles[t].input;
if (const OptixResult res = optixDenoiserInvoke(denoiser,
stream,
params,
denoiserState,
denoiserStateSizeInBytes,
&gl,
&tlayers[0],
numLayers,
(tiles[0])[t].inputOffsetX,
(tiles[0])[t].inputOffsetY,
scratch,
scratchSizeInBytes))
return res;
}
return OPTIX_SUCCESS;
}
} // namespace
OptiXDevice::Denoiser::Denoiser(OptiXDevice *device)
: device(device), queue(device), state(device, "__denoiser_state", true)
{
@@ -1260,20 +1075,20 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass)
/* Finally run denoising. */
OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */
optix_assert(ccl::optixUtilDenoiserInvokeTiled(denoiser_.optix_denoiser,
denoiser_.queue.stream(),
&params,
denoiser_.state.device_pointer,
denoiser_.sizes.stateSizeInBytes,
&guide_layers,
&image_layers,
1,
denoiser_.state.device_pointer +
denoiser_.sizes.stateSizeInBytes,
denoiser_.sizes.withOverlapScratchSizeInBytes,
denoiser_.sizes.overlapWindowSizeInPixels,
denoiser_.configured_size.x,
denoiser_.configured_size.y));
optix_assert(optixUtilDenoiserInvokeTiled(denoiser_.optix_denoiser,
denoiser_.queue.stream(),
&params,
denoiser_.state.device_pointer,
denoiser_.sizes.stateSizeInBytes,
&guide_layers,
&image_layers,
1,
denoiser_.state.device_pointer +
denoiser_.sizes.stateSizeInBytes,
denoiser_.sizes.withOverlapScratchSizeInBytes,
denoiser_.sizes.overlapWindowSizeInPixels,
denoiser_.configured_size.x,
denoiser_.configured_size.y));
return true;
}

View File

@@ -100,7 +100,7 @@ class DeviceQueue {
* based on number of cores and/or available memory. */
virtual int num_concurrent_states(const size_t state_size) const = 0;
/* Number of states which keeps the device occupied with work without losing performance.
/* Number of states which keeps the device occupied with work without loosing performance.
* The renderer will add more work (when available) when number of active paths falls below this
* value. */
virtual int num_concurrent_busy_states() const = 0;

View File

@@ -1,174 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2022 Blender Foundation
#####################################################################
# Cycles Hydra render delegate
#####################################################################
set(INC
..
)
set(INC_SYS
${USD_INCLUDE_DIRS}
${GLEW_INCLUDE_DIR}
)
set(INC_HD_CYCLES
attribute.h
camera.h
config.h
curves.h
display_driver.h
field.h
geometry.h
geometry.inl
instancer.h
light.h
material.h
mesh.h
node_util.h
output_driver.h
pointcloud.h
render_buffer.h
render_delegate.h
render_pass.h
session.h
volume.h
)
set(SRC_HD_CYCLES
attribute.cpp
curves.cpp
camera.cpp
display_driver.cpp
field.cpp
instancer.cpp
light.cpp
material.cpp
mesh.cpp
node_util.cpp
output_driver.cpp
pointcloud.cpp
render_buffer.cpp
render_delegate.cpp
render_pass.cpp
session.cpp
volume.cpp
)
add_definitions(${GL_DEFINITIONS})
if(WITH_OPENVDB)
add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS}
)
list(APPEND LIB
${OPENVDB_LIBRARIES}
)
endif()
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
add_library(hdCyclesStatic STATIC
${SRC_HD_CYCLES}
${INC_HD_CYCLES}
)
target_compile_options(hdCyclesStatic
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/wd4003 /wd4244 /wd4506>
$<$<CXX_COMPILER_ID:GNU>:-Wno-float-conversion -Wno-double-promotion -Wno-deprecated>
)
target_compile_definitions(hdCyclesStatic
PRIVATE
GLOG_NO_ABBREVIATED_SEVERITIES=1
OSL_DEBUG=$<CONFIG:DEBUG>
TBB_USE_DEBUG=$<CONFIG:DEBUG>
$<$<CXX_COMPILER_ID:MSVC>:NOMINMAX=1>
)
target_link_libraries(hdCyclesStatic
PRIVATE
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hd${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}plug${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}tf${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}trace${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}vt${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}work${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}sdf${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}cameraUtil${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hf${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}pxOsd${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}gf${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}arch${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hgi${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}glf${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}hdx${CMAKE_LINK_LIBRARY_SUFFIX}
${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}usdGeom${CMAKE_LINK_LIBRARY_SUFFIX}
cycles_scene
cycles_session
cycles_graph
)
if(USD_PYTHON_LIBRARIES)
target_link_libraries(hdCyclesStatic
PRIVATE
${USD_PYTHON_LIBRARIES}
)
endif()
set(HdCyclesPluginName hdCycles)
add_library(${HdCyclesPluginName} SHARED
plugin.h
plugin.cpp
)
set_target_properties(${HdCyclesPluginName}
PROPERTIES PREFIX ""
)
target_compile_definitions(${HdCyclesPluginName}
PRIVATE
MFB_PACKAGE_NAME=${HdCyclesPluginName}
MFB_ALT_PACKAGE_NAME=${HdCyclesPluginName}
GLOG_NO_ABBREVIATED_SEVERITIES=1
OSL_DEBUG=$<CONFIG:DEBUG>
TBB_USE_DEBUG=$<CONFIG:DEBUG>
$<$<CXX_COMPILER_ID:MSVC>:NOMINMAX=1>
)
target_link_libraries(${HdCyclesPluginName}
hdCyclesStatic
)
target_link_directories(${HdCyclesPluginName}
BEFORE
PRIVATE
${USD_LIBRARY_DIR}
)
cycles_target_link_libraries(${HdCyclesPluginName})
if(WITH_CYCLES_BLENDER)
set(CYCLES_HYDRA_INSTALL_PATH "../")
else()
set(CYCLES_HYDRA_INSTALL_PATH ${CMAKE_INSTALL_PREFIX})
# Put the root plugInfo.json one level up
delayed_install("${CMAKE_CURRENT_SOURCE_DIR}" "plugInfo.json" ${CMAKE_INSTALL_PREFIX})
endif()
delayed_install("" $<TARGET_FILE:${HdCyclesPluginName}> ${CYCLES_HYDRA_INSTALL_PATH})
set(PLUG_INFO_ROOT "..")
set(PLUG_INFO_LIBRARY_PATH "../${HdCyclesPluginName}${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(PLUG_INFO_RESOURCE_PATH "resources")
configure_file(resources/plugInfo.json
${CMAKE_CURRENT_BINARY_DIR}/resources/plugInfo.json
@ONLY
)
delayed_install("${CMAKE_CURRENT_BINARY_DIR}/resources" "plugInfo.json" "${CYCLES_HYDRA_INSTALL_PATH}/${HdCyclesPluginName}/resources")

View File

@@ -1,71 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/attribute.h"
#include "scene/attribute.h"
#include "scene/geometry.h"
#include "scene/scene.h"
#include <pxr/base/gf/vec2f.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/base/gf/vec4f.h>
#include <pxr/base/vt/array.h>
#include <pxr/imaging/hd/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
void ApplyPrimvars(AttributeSet &attributes,
const ustring &name,
VtValue value,
AttributeElement elem,
AttributeStandard std)
{
const void *data = HdGetValueData(value);
size_t size = value.GetArraySize();
const HdType valueType = HdGetValueTupleType(value).type;
TypeDesc attrType = CCL_NS::TypeUnknown;
switch (valueType) {
case HdTypeFloat:
attrType = CCL_NS::TypeFloat;
size *= sizeof(float);
break;
case HdTypeFloatVec2:
attrType = CCL_NS::TypeFloat2;
size *= sizeof(float2);
static_assert(sizeof(GfVec2f) == sizeof(float2));
break;
case HdTypeFloatVec3: {
attrType = CCL_NS::TypeVector;
size *= sizeof(float3);
// The Cycles "float3" data type is padded to "float4", so need to convert the array
const auto &valueData = value.Get<VtVec3fArray>();
VtArray<float3> valueConverted;
valueConverted.reserve(valueData.size());
for (const GfVec3f &vec : valueData) {
valueConverted.push_back(make_float3(vec[0], vec[1], vec[2]));
}
data = valueConverted.data();
value = std::move(valueConverted);
break;
}
case HdTypeFloatVec4:
attrType = CCL_NS::TypeFloat4;
size *= sizeof(float4);
static_assert(sizeof(GfVec4f) == sizeof(float4));
break;
default:
TF_WARN("Unsupported attribute type %d", static_cast<int>(valueType));
return;
}
Attribute *const attr = attributes.add(name, attrType, elem);
attr->std = std;
assert(size == attr->buffer.size());
std::memcpy(attr->data(), data, size);
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,21 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "scene/attribute.h"
#include <pxr/base/vt/value.h>
#include <pxr/imaging/hd/types.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
void ApplyPrimvars(CCL_NS::AttributeSet &attributes,
const CCL_NS::ustring &name,
PXR_NS::VtValue value,
CCL_NS::AttributeElement elem,
CCL_NS::AttributeStandard std);
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,297 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/camera.h"
#include "scene/camera.h"
#include <pxr/base/gf/frustum.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/usd/usdGeom/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
HdCyclesCamera::HdCyclesCamera(const SdfPath &sprimId) : HdCamera(sprimId)
{
#if PXR_VERSION >= 2102
// Synchronize default values
_horizontalAperture = _data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT;
_verticalAperture = _data.GetVerticalAperture() * GfCamera::APERTURE_UNIT;
_horizontalApertureOffset = _data.GetHorizontalApertureOffset() * GfCamera::APERTURE_UNIT;
_verticalApertureOffset = _data.GetVerticalApertureOffset() * GfCamera::APERTURE_UNIT;
_focalLength = _data.GetFocalLength() * GfCamera::FOCAL_LENGTH_UNIT;
_clippingRange = _data.GetClippingRange();
_fStop = _data.GetFStop();
_focusDistance = _data.GetFocusDistance();
#endif
}
HdCyclesCamera::~HdCyclesCamera()
{
}
HdDirtyBits HdCyclesCamera::GetInitialDirtyBitsMask() const
{
return DirtyBits::AllDirty;
}
void HdCyclesCamera::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
if (*dirtyBits == DirtyBits::Clean) {
return;
}
VtValue value;
const SdfPath &id = GetId();
#if PXR_VERSION >= 2102
if (*dirtyBits & DirtyBits::DirtyTransform) {
sceneDelegate->SampleTransform(id, &_transformSamples);
for (size_t i = 0; i < _transformSamples.count; ++i) {
if (_transformSamples.times[i] == 0.0f) {
_transform = _transformSamples.values[i];
_data.SetTransform(_transform);
break;
}
}
}
#else
if (*dirtyBits & DirtyBits::DirtyViewMatrix) {
sceneDelegate->SampleTransform(id, &_transformSamples);
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->worldToViewMatrix);
if (!value.IsEmpty()) {
_worldToViewMatrix = value.Get<GfMatrix4d>();
_worldToViewInverseMatrix = _worldToViewMatrix.GetInverse();
_data.SetTransform(_worldToViewInverseMatrix);
}
}
#endif
if (*dirtyBits & DirtyBits::DirtyProjMatrix) {
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->projectionMatrix);
if (!value.IsEmpty()) {
_projectionMatrix = value.Get<GfMatrix4d>();
const float focalLength = _data.GetFocalLength(); // Get default focal length
#if PXR_VERSION >= 2102
_data.SetFromViewAndProjectionMatrix(GetViewMatrix(), _projectionMatrix, focalLength);
#else
if (_projectionMatrix[2][3] < -0.5) {
_data.SetProjection(GfCamera::Perspective);
const float horizontalAperture = (2.0 * focalLength) / _projectionMatrix[0][0];
_data.SetHorizontalAperture(horizontalAperture);
_data.SetHorizontalApertureOffset(0.5 * horizontalAperture * _projectionMatrix[2][0]);
const float verticalAperture = (2.0 * focalLength) / _projectionMatrix[1][1];
_data.SetVerticalAperture(verticalAperture);
_data.SetVerticalApertureOffset(0.5 * verticalAperture * _projectionMatrix[2][1]);
_data.SetClippingRange(
GfRange1f(_projectionMatrix[3][2] / (_projectionMatrix[2][2] - 1.0),
_projectionMatrix[3][2] / (_projectionMatrix[2][2] + 1.0)));
}
else {
_data.SetProjection(GfCamera::Orthographic);
const float horizontalAperture = (2.0 / GfCamera::APERTURE_UNIT) / _projectionMatrix[0][0];
_data.SetHorizontalAperture(horizontalAperture);
_data.SetHorizontalApertureOffset(-0.5 * horizontalAperture * _projectionMatrix[3][0]);
const float verticalAperture = (2.0 / GfCamera::APERTURE_UNIT) / _projectionMatrix[1][1];
_data.SetVerticalAperture(verticalAperture);
_data.SetVerticalApertureOffset(-0.5 * verticalAperture * _projectionMatrix[3][1]);
const double nearMinusFarHalf = 1.0 / _projectionMatrix[2][2];
const double nearPlusFarHalf = nearMinusFarHalf * _projectionMatrix[3][2];
_data.SetClippingRange(
GfRange1f(nearPlusFarHalf + nearMinusFarHalf, nearPlusFarHalf - nearMinusFarHalf));
}
#endif
}
}
if (*dirtyBits & DirtyBits::DirtyWindowPolicy) {
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->windowPolicy);
if (!value.IsEmpty()) {
_windowPolicy = value.Get<CameraUtilConformWindowPolicy>();
}
}
if (*dirtyBits & DirtyBits::DirtyClipPlanes) {
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->clipPlanes);
if (!value.IsEmpty()) {
_clipPlanes = value.Get<std::vector<GfVec4d>>();
}
}
if (*dirtyBits & DirtyBits::DirtyParams) {
#if PXR_VERSION >= 2102
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->projection);
if (!value.IsEmpty()) {
_projection = value.Get<Projection>();
_data.SetProjection(_projection != Orthographic ? GfCamera::Perspective :
GfCamera::Orthographic);
}
#else
value = sceneDelegate->GetCameraParamValue(id, UsdGeomTokens->projection);
if (!value.IsEmpty()) {
_data.SetProjection(value.Get<TfToken>() != UsdGeomTokens->orthographic ?
GfCamera::Perspective :
GfCamera::Orthographic);
}
#endif
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->horizontalAperture);
if (!value.IsEmpty()) {
const auto horizontalAperture = value.Get<float>();
#if PXR_VERSION >= 2102
_horizontalAperture = horizontalAperture;
#endif
_data.SetHorizontalAperture(horizontalAperture / GfCamera::APERTURE_UNIT);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->verticalAperture);
if (!value.IsEmpty()) {
const auto verticalAperture = value.Get<float>();
#if PXR_VERSION >= 2102
_verticalAperture = verticalAperture;
#endif
_data.SetVerticalAperture(verticalAperture / GfCamera::APERTURE_UNIT);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->horizontalApertureOffset);
if (!value.IsEmpty()) {
const auto horizontalApertureOffset = value.Get<float>();
#if PXR_VERSION >= 2102
_horizontalApertureOffset = horizontalApertureOffset;
#endif
_data.SetHorizontalApertureOffset(horizontalApertureOffset / GfCamera::APERTURE_UNIT);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->verticalApertureOffset);
if (!value.IsEmpty()) {
const auto verticalApertureOffset = value.Get<float>();
#if PXR_VERSION >= 2102
_verticalApertureOffset = verticalApertureOffset;
#endif
_data.SetVerticalApertureOffset(verticalApertureOffset / GfCamera::APERTURE_UNIT);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->focalLength);
if (!value.IsEmpty()) {
const auto focalLength = value.Get<float>();
#if PXR_VERSION >= 2102
_focalLength = focalLength;
#endif
_data.SetFocalLength(focalLength / GfCamera::FOCAL_LENGTH_UNIT);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->clippingRange);
if (!value.IsEmpty()) {
const auto clippingRange = value.Get<GfRange1f>();
#if PXR_VERSION >= 2102
_clippingRange = clippingRange;
#endif
_data.SetClippingRange(clippingRange);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->fStop);
if (!value.IsEmpty()) {
const auto fStop = value.Get<float>();
#if PXR_VERSION >= 2102
_fStop = fStop;
#endif
_data.SetFStop(fStop);
}
value = sceneDelegate->GetCameraParamValue(id, HdCameraTokens->focusDistance);
if (!value.IsEmpty()) {
const auto focusDistance = value.Get<float>();
#if PXR_VERSION >= 2102
_focusDistance = focusDistance;
#endif
_data.SetFocusDistance(focusDistance);
}
}
*dirtyBits = DirtyBits::Clean;
}
void HdCyclesCamera::Finalize(HdRenderParam *renderParam)
{
HdCamera::Finalize(renderParam);
}
void HdCyclesCamera::ApplyCameraSettings(Camera *cam) const
{
ApplyCameraSettings(_data, _windowPolicy, cam);
array<Transform> motion(_transformSamples.count);
for (size_t i = 0; i < _transformSamples.count; ++i)
motion[i] = convert_transform(_transformSamples.values[i]) *
transform_scale(1.0f, 1.0f, -1.0f);
cam->set_motion(motion);
}
void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow,
CameraUtilConformWindowPolicy windowPolicy,
Camera *cam)
{
const float width = cam->get_full_width();
const float height = cam->get_full_height();
auto data = dataUnconformedWindow;
CameraUtilConformWindow(&data, windowPolicy, width / height);
static_assert(GfCamera::Perspective == CAMERA_PERSPECTIVE &&
GfCamera::Orthographic == CAMERA_ORTHOGRAPHIC);
cam->set_camera_type(static_cast<CameraType>(data.GetProjection()));
auto viewplane = data.GetFrustum().GetWindow();
auto focalLength = 1.0f;
if (data.GetProjection() == GfCamera::Perspective) {
viewplane *= 2.0 / viewplane.GetSize()[1]; // Normalize viewplane
focalLength = data.GetFocalLength() * 1e-3f;
cam->set_fov(GfDegreesToRadians(data.GetFieldOfView(GfCamera::FOVVertical)));
}
cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT);
cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT);
cam->set_nearclip(data.GetClippingRange().GetMin());
cam->set_farclip(data.GetClippingRange().GetMax());
cam->set_viewplane_left(viewplane.GetMin()[0]);
cam->set_viewplane_right(viewplane.GetMax()[0]);
cam->set_viewplane_bottom(viewplane.GetMin()[1]);
cam->set_viewplane_top(viewplane.GetMax()[1]);
if (data.GetFStop() != 0.0f) {
cam->set_focaldistance(data.GetFocusDistance());
cam->set_aperturesize(focalLength / (2.0f * data.GetFStop()));
}
cam->set_matrix(convert_transform(data.GetTransform()) * transform_scale(1.0f, 1.0f, -1.0f));
}
void HdCyclesCamera::ApplyCameraSettings(const GfMatrix4d &worldToViewMatrix,
const GfMatrix4d &projectionMatrix,
const std::vector<GfVec4d> &clipPlanes,
Camera *cam)
{
#if PXR_VERSION >= 2102
GfCamera data;
data.SetFromViewAndProjectionMatrix(worldToViewMatrix, projectionMatrix);
ApplyCameraSettings(data, CameraUtilFit, cam);
#else
TF_CODING_ERROR("Not implemented");
#endif
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,43 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/base/gf/camera.h>
#include <pxr/imaging/hd/camera.h>
#include <pxr/imaging/hd/timeSampleArray.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesCamera final : public PXR_NS::HdCamera {
public:
HdCyclesCamera(const PXR_NS::SdfPath &sprimId);
~HdCyclesCamera() override;
void ApplyCameraSettings(CCL_NS::Camera *targetCamera) const;
static void ApplyCameraSettings(const PXR_NS::GfCamera &cameraData,
PXR_NS::CameraUtilConformWindowPolicy windowPolicy,
CCL_NS::Camera *targetCamera);
static void ApplyCameraSettings(const PXR_NS::GfMatrix4d &worldToViewMatrix,
const PXR_NS::GfMatrix4d &projectionMatrix,
const std::vector<PXR_NS::GfVec4d> &clipPlanes,
CCL_NS::Camera *targetCamera);
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
private:
PXR_NS::GfCamera _data;
PXR_NS::HdTimeSampleArray<PXR_NS::GfMatrix4d, 2> _transformSamples;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,44 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include <pxr/pxr.h>
#define CCL_NS ccl
#define CCL_NAMESPACE_USING_DIRECTIVE using namespace CCL_NS;
#define HD_CYCLES_NS HdCycles
#define HDCYCLES_NAMESPACE_OPEN_SCOPE \
namespace HD_CYCLES_NS { \
CCL_NAMESPACE_USING_DIRECTIVE; \
PXR_NAMESPACE_USING_DIRECTIVE;
#define HDCYCLES_NAMESPACE_CLOSE_SCOPE }
namespace HD_CYCLES_NS {
class HdCyclesCamera;
class HdCyclesDelegate;
class HdCyclesSession;
class HdCyclesRenderBuffer;
} // namespace HD_CYCLES_NS
namespace CCL_NS {
class AttributeSet;
class BufferParams;
class Camera;
class Geometry;
class Hair;
class Light;
class Mesh;
class Object;
class ParticleSystem;
class Pass;
class PointCloud;
class Scene;
class Session;
class SessionParams;
class Shader;
class ShaderGraph;
class Volume;
} // namespace CCL_NS

View File

@@ -1,210 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/curves.h"
#include "hydra/geometry.inl"
#include "scene/hair.h"
#include <pxr/imaging/hd/extComputationUtils.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesCurves::HdCyclesCurves(const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
: HdCyclesGeometry(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
)
{
}
HdCyclesCurves::~HdCyclesCurves()
{
}
HdDirtyBits HdCyclesCurves::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths |
HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyTopology;
return bits;
}
HdDirtyBits HdCyclesCurves::_PropagateDirtyBits(HdDirtyBits bits) const
{
if (bits & (HdChangeTracker::DirtyTopology)) {
// Changing topology clears the geometry, so need to populate everything again
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths |
HdChangeTracker::DirtyPrimvar;
}
return bits;
}
void HdCyclesCurves::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
if (HdChangeTracker::IsTopologyDirty(dirtyBits, GetId())) {
PopulateTopology(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyPoints) {
PopulatePoints(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyWidths) {
PopulateWidths(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyPrimvar) {
PopulatePrimvars(sceneDelegate);
}
rebuild = (_geom->curve_keys_is_modified()) || (_geom->curve_radius_is_modified());
}
void HdCyclesCurves::PopulatePoints(HdSceneDelegate *sceneDelegate)
{
VtValue value;
for (const HdExtComputationPrimvarDescriptor &desc :
sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) {
if (desc.name == HdTokens->points) {
auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate);
const auto valueStoreIt = valueStore.find(desc.name);
if (valueStoreIt != valueStore.end()) {
value = std::move(valueStoreIt->second);
}
break;
}
}
if (value.IsEmpty()) {
value = GetPrimvar(sceneDelegate, HdTokens->points);
}
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid points data for %s", GetId().GetText());
return;
}
const auto &points = value.UncheckedGet<VtVec3fArray>();
array<float3> pointsDataCycles;
pointsDataCycles.reserve(points.size());
for (const GfVec3f &point : points) {
pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2]));
}
_geom->set_curve_keys(pointsDataCycles);
}
void HdCyclesCurves::PopulateWidths(HdSceneDelegate *sceneDelegate)
{
VtValue value = GetPrimvar(sceneDelegate, HdTokens->widths);
const HdInterpolation interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->widths);
if (!value.IsHolding<VtFloatArray>()) {
TF_WARN("Invalid widths data for %s", GetId().GetText());
return;
}
const auto &widths = value.UncheckedGet<VtFloatArray>();
array<float> radiusDataCycles;
radiusDataCycles.reserve(widths.size());
if (interpolation == HdInterpolationConstant) {
TF_VERIFY(widths.size() == 1);
const float constantRadius = widths[0] * 0.5f;
for (size_t i = 0; i < _geom->num_keys(); ++i) {
radiusDataCycles.push_back_reserved(constantRadius);
}
}
else if (interpolation == HdInterpolationVertex) {
TF_VERIFY(widths.size() == _geom->num_keys());
for (size_t i = 0; i < _geom->num_keys(); ++i) {
radiusDataCycles.push_back_reserved(widths[i] * 0.5f);
}
}
_geom->set_curve_radius(radiusDataCycles);
}
void HdCyclesCurves::PopulatePrimvars(HdSceneDelegate *sceneDelegate)
{
Scene *const scene = (Scene *)_geom->get_owner();
const std::pair<HdInterpolation, AttributeElement> interpolations[] = {
std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_CURVE_KEY),
std::make_pair(HdInterpolationVarying, ATTR_ELEMENT_CURVE_KEY),
std::make_pair(HdInterpolationUniform, ATTR_ELEMENT_CURVE),
std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT),
};
for (const auto &interpolation : interpolations) {
for (const HdPrimvarDescriptor &desc :
GetPrimvarDescriptors(sceneDelegate, interpolation.first)) {
// Skip special primvars that are handled separately
if (desc.name == HdTokens->points || desc.name == HdTokens->widths) {
continue;
}
VtValue value = GetPrimvar(sceneDelegate, desc.name);
if (value.IsEmpty()) {
continue;
}
const ustring name(desc.name.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (desc.role == HdPrimvarRoleTokens->textureCoordinate) {
std = ATTR_STD_UV;
}
else if (desc.name == HdTokens->displayColor &&
interpolation.first == HdInterpolationConstant) {
if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) {
const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0];
_instances[0]->set_color(make_float3(color[0], color[1], color[2]));
}
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name)) {
ApplyPrimvars(_geom->attributes, name, value, interpolation.second, std);
}
}
}
}
void HdCyclesCurves::PopulateTopology(HdSceneDelegate *sceneDelegate)
{
// Clear geometry before populating it again with updated topology
_geom->clear(true);
HdBasisCurvesTopology topology = GetBasisCurvesTopology(sceneDelegate);
_geom->reserve_curves(topology.GetNumCurves(), topology.CalculateNeededNumberOfControlPoints());
const VtIntArray vertCounts = topology.GetCurveVertexCounts();
for (int curve = 0, key = 0; curve < topology.GetNumCurves(); ++curve) {
// Always reference shader at index zero, which is the primitive material
_geom->add_curve(key, 0);
key += vertCounts[curve];
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,42 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/basisCurves.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesCurves final : public HdCyclesGeometry<PXR_NS::HdBasisCurves, CCL_NS::Hair> {
public:
HdCyclesCurves(
const PXR_NS::SdfPath &rprimId
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId = {}
#endif
);
~HdCyclesCurves() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
private:
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateWidths(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateTopology(PXR_NS::HdSceneDelegate *sceneDelegate);
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,240 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#ifdef _WIN32
// Include first to avoid "NOGDI" definition set in Cycles headers
# include <Windows.h>
#endif
#include "hydra/display_driver.h"
#include "hydra/render_buffer.h"
#include "hydra/session.h"
#include <GL/glew.h>
#include <pxr/imaging/hgiGL/texture.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesDisplayDriver::HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi)
: _renderParam(renderParam), _hgi(hgi)
{
#ifdef _WIN32
hdc_ = GetDC(CreateWindowA("STATIC",
"HdCycles",
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,
0,
64,
64,
NULL,
NULL,
GetModuleHandle(NULL),
NULL));
int pixelFormat = GetPixelFormat(wglGetCurrentDC());
PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd)};
DescribePixelFormat((HDC)hdc_, pixelFormat, sizeof(pfd), &pfd);
SetPixelFormat((HDC)hdc_, pixelFormat, &pfd);
TF_VERIFY(gl_context_ = wglCreateContext((HDC)hdc_));
TF_VERIFY(wglShareLists(wglGetCurrentContext(), (HGLRC)gl_context_));
#endif
glewInit();
glGenBuffers(1, &gl_pbo_id_);
}
HdCyclesDisplayDriver::~HdCyclesDisplayDriver()
{
if (texture_) {
_hgi->DestroyTexture(&texture_);
}
glDeleteBuffers(1, &gl_pbo_id_);
#ifdef _WIN32
TF_VERIFY(wglDeleteContext((HGLRC)gl_context_));
DestroyWindow(WindowFromDC((HDC)hdc_));
#endif
}
void HdCyclesDisplayDriver::next_tile_begin()
{
}
bool HdCyclesDisplayDriver::update_begin(const Params &params,
int texture_width,
int texture_height)
{
#ifdef _WIN32
if (!hdc_ || !gl_context_) {
return false;
}
#endif
graphics_interop_activate();
if (gl_render_sync_) {
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
}
if (pbo_size_.x != params.full_size.x || pbo_size_.y != params.full_size.y) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
glBufferData(GL_PIXEL_UNPACK_BUFFER,
sizeof(half4) * params.full_size.x * params.full_size.y,
0,
GL_DYNAMIC_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo_size_ = params.full_size;
}
need_update_ = true;
return true;
}
void HdCyclesDisplayDriver::update_end()
{
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
graphics_interop_deactivate();
}
void HdCyclesDisplayDriver::flush()
{
graphics_interop_activate();
if (gl_upload_sync_) {
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
}
if (gl_render_sync_) {
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
}
graphics_interop_deactivate();
}
half4 *HdCyclesDisplayDriver::map_texture_buffer()
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
const auto mapped_rgba_pixels = static_cast<half4 *>(
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
if (need_clear_ && mapped_rgba_pixels) {
memset(mapped_rgba_pixels, 0, sizeof(half4) * pbo_size_.x * pbo_size_.y);
need_clear_ = false;
}
return mapped_rgba_pixels;
}
void HdCyclesDisplayDriver::unmap_texture_buffer()
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
DisplayDriver::GraphicsInterop HdCyclesDisplayDriver::graphics_interop_get()
{
GraphicsInterop interop_dst;
interop_dst.buffer_width = pbo_size_.x;
interop_dst.buffer_height = pbo_size_.y;
interop_dst.opengl_pbo_id = gl_pbo_id_;
interop_dst.need_clear = need_clear_;
need_clear_ = false;
return interop_dst;
}
void HdCyclesDisplayDriver::graphics_interop_activate()
{
mutex_.lock();
#ifdef _WIN32
// Do not change context if this is called in the main thread
if (wglGetCurrentContext() == nullptr) {
TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_));
}
#endif
}
void HdCyclesDisplayDriver::graphics_interop_deactivate()
{
#ifdef _WIN32
if (wglGetCurrentContext() == gl_context_) {
TF_VERIFY(wglMakeCurrent(nullptr, nullptr));
}
#endif
mutex_.unlock();
}
void HdCyclesDisplayDriver::clear()
{
need_clear_ = true;
}
void HdCyclesDisplayDriver::draw(const Params &params)
{
const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(
_renderParam->GetDisplayAovBinding().renderBuffer);
if (!renderBuffer || // Ensure this render buffer matches the texture dimensions
(renderBuffer->GetWidth() != params.size.x || renderBuffer->GetHeight() != params.size.y)) {
return;
}
// Cycles 'DisplayDriver' only supports 'half4' format
TF_VERIFY(renderBuffer->GetFormat() == HdFormatFloat16Vec4);
const thread_scoped_lock lock(mutex_);
const GfVec3i dimensions(params.size.x, params.size.y, 1);
if (!texture_ || texture_->GetDescriptor().dimensions != dimensions) {
if (texture_) {
_hgi->DestroyTexture(&texture_);
}
HgiTextureDesc texDesc;
texDesc.usage = 0;
texDesc.format = HgiFormatFloat16Vec4;
texDesc.type = HgiTextureType2D;
texDesc.dimensions = dimensions;
texDesc.sampleCount = HgiSampleCount1;
texture_ = _hgi->CreateTexture(texDesc);
renderBuffer->SetResource(VtValue(texture_));
}
HgiGLTexture *const texture = dynamic_cast<HgiGLTexture *>(texture_.Get());
if (!texture || !need_update_ || pbo_size_.x != params.size.x || pbo_size_.y != params.size.y) {
return;
}
if (gl_upload_sync_) {
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
}
glBindTexture(GL_TEXTURE_2D, texture->GetTextureId());
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pbo_size_.x, pbo_size_.y, GL_RGBA, GL_HALF_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
need_update_ = false;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,61 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "session/display_driver.h"
#include "util/thread.h"
#include <pxr/imaging/hgi/hgi.h>
#include <pxr/imaging/hgi/texture.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver {
public:
HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi);
~HdCyclesDisplayDriver();
private:
void next_tile_begin() override;
bool update_begin(const Params &params, int texture_width, int texture_height) override;
void update_end() override;
void flush() override;
CCL_NS::half4 *map_texture_buffer() override;
void unmap_texture_buffer() override;
GraphicsInterop graphics_interop_get() override;
void graphics_interop_activate() override;
void graphics_interop_deactivate() override;
void clear() override;
void draw(const Params &params) override;
HdCyclesSession *const _renderParam;
Hgi *const _hgi;
#ifdef _WIN32
void *hdc_ = nullptr;
void *gl_context_ = nullptr;
#endif
CCL_NS::thread_mutex mutex_;
PXR_NS::HgiTextureHandle texture_;
unsigned int gl_pbo_id_ = 0;
CCL_NS::int2 pbo_size_ = CCL_NS::make_int2(0, 0);
bool need_update_ = false;
std::atomic_bool need_clear_ = false;
void *gl_render_sync_ = nullptr;
void *gl_upload_sync_ = nullptr;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,88 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/field.h"
#include "hydra/session.h"
#include "scene/image_vdb.h"
#include "scene/scene.h"
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/usd/sdf/assetPath.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(fieldName)
);
// clang-format on
#ifdef WITH_OPENVDB
class HdCyclesVolumeLoader : public VDBImageLoader {
public:
HdCyclesVolumeLoader(const std::string &filePath, const std::string &gridName)
: VDBImageLoader(gridName)
{
openvdb::io::File file(filePath);
file.setCopyMaxBytes(0);
if (file.open()) {
grid = file.readGrid(gridName);
}
}
};
#endif
HdCyclesField::HdCyclesField(const SdfPath &bprimId, const TfToken &typeId) : HdField(bprimId)
{
}
HdCyclesField::~HdCyclesField()
{
}
HdDirtyBits HdCyclesField::GetInitialDirtyBitsMask() const
{
return DirtyBits::DirtyParams;
}
void HdCyclesField::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
#ifdef WITH_OPENVDB
VtValue value;
const SdfPath &id = GetId();
if (*dirtyBits & DirtyBits::DirtyParams) {
value = sceneDelegate->Get(id, HdFieldTokens->filePath);
if (value.IsHolding<SdfAssetPath>()) {
std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath();
if (filename.empty()) {
filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath();
}
# if PXR_VERSION >= 2108
value = sceneDelegate->Get(id, HdFieldTokens->fieldName);
# else
value = sceneDelegate->Get(id, _tokens->fieldName);
# endif
if (value.IsHolding<TfToken>()) {
ImageLoader *const loader = new HdCyclesVolumeLoader(
filename, value.UncheckedGet<TfToken>().GetString());
const SceneLock lock(renderParam);
ImageParams params;
params.frame = 0.0f;
_handle = lock.scene->image_manager->add_image(loader, params, false);
}
}
}
#endif
*dirtyBits = DirtyBits::Clean;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,34 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "scene/image.h"
#include <pxr/imaging/hd/field.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesField final : public PXR_NS::HdField {
public:
HdCyclesField(const PXR_NS::SdfPath &bprimId, const PXR_NS::TfToken &typeId);
~HdCyclesField() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
CCL_NS::ImageHandle GetImageHandle() const
{
return _handle;
}
private:
CCL_NS::ImageHandle _handle;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,54 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/rprim.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
template<typename Base, typename CyclesBase> class HdCyclesGeometry : public Base {
public:
HdCyclesGeometry(const PXR_NS::SdfPath &rprimId
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId
#endif
);
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits,
const PXR_NS::TfToken &reprToken) override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
virtual void Finalize(PXR_NS::HdRenderParam *renderParam) override;
protected:
void _InitRepr(const PXR_NS::TfToken &reprToken, PXR_NS::HdDirtyBits *dirtyBits) override;
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
virtual void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) = 0;
PXR_NS::HdInterpolation GetPrimvarInterpolation(PXR_NS::HdSceneDelegate *sceneDelegate,
const PXR_NS::TfToken &name) const;
CyclesBase *_geom = nullptr;
std::vector<CCL_NS::Object *> _instances;
private:
void Initialize(PXR_NS::HdRenderParam *renderParam);
void InitializeInstance(int index);
PXR_NS::GfMatrix4d _geomTransform;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,247 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/attribute.h"
#include "hydra/geometry.h"
#include "hydra/instancer.h"
#include "hydra/material.h"
#include "hydra/session.h"
#include "scene/geometry.h"
#include "scene/object.h"
#include "scene/scene.h"
#include "util/hash.h"
#include <pxr/imaging/hd/sceneDelegate.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
template<typename Base, typename CyclesBase>
HdCyclesGeometry<Base, CyclesBase>::HdCyclesGeometry(const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
: Base(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
),
_geomTransform(1.0)
{
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::_InitRepr(const TfToken &reprToken,
HdDirtyBits *dirtyBits)
{
TF_UNUSED(reprToken);
TF_UNUSED(dirtyBits);
}
template<typename Base, typename CyclesBase>
HdDirtyBits HdCyclesGeometry<Base, CyclesBase>::GetInitialDirtyBitsMask() const
{
return HdChangeTracker::DirtyPrimID | HdChangeTracker::DirtyTransform |
HdChangeTracker::DirtyMaterialId | HdChangeTracker::DirtyVisibility |
HdChangeTracker::DirtyInstancer;
}
template<typename Base, typename CyclesBase>
HdDirtyBits HdCyclesGeometry<Base, CyclesBase>::_PropagateDirtyBits(HdDirtyBits bits) const
{
return bits;
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits,
const TfToken &reprToken)
{
TF_UNUSED(reprToken);
if (*dirtyBits == HdChangeTracker::Clean) {
return;
}
Initialize(renderParam);
#if PXR_VERSION >= 2102
Base::_UpdateInstancer(sceneDelegate, dirtyBits);
HdInstancer::_SyncInstancerAndParents(sceneDelegate->GetRenderIndex(), Base::GetInstancerId());
#endif
Base::_UpdateVisibility(sceneDelegate, dirtyBits);
const SceneLock lock(renderParam);
if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
#if HD_API_VERSION >= 37 && PXR_VERSION >= 2105
Base::SetMaterialId(sceneDelegate->GetMaterialId(Base::GetId()));
#else
Base::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
sceneDelegate->GetMaterialId(Base::GetId()));
#endif
const auto material = static_cast<const HdCyclesMaterial *>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material,
Base::GetMaterialId()));
array<Node *> usedShaders(1);
if (material && material->GetCyclesShader()) {
usedShaders[0] = material->GetCyclesShader();
}
else {
usedShaders[0] = lock.scene->default_surface;
}
for (Node *shader : usedShaders) {
static_cast<Shader *>(shader)->tag_used(lock.scene);
}
_geom->set_used_shaders(usedShaders);
}
const SdfPath &id = Base::GetId();
if (HdChangeTracker::IsPrimIdDirty(*dirtyBits, id)) {
// This needs to be corrected in the AOV
_instances[0]->set_pass_id(Base::GetPrimId() + 1);
}
if (HdChangeTracker::IsTransformDirty(*dirtyBits, id)) {
_geomTransform = sceneDelegate->GetTransform(id);
}
if (HdChangeTracker::IsTransformDirty(*dirtyBits, id) ||
HdChangeTracker::IsInstancerDirty(*dirtyBits, id)) {
const auto instancer = static_cast<HdCyclesInstancer *>(
sceneDelegate->GetRenderIndex().GetInstancer(Base::GetInstancerId()));
// Make sure the first object attribute is the instanceId
assert(_instances[0]->attributes.size() >= 1 &&
_instances[0]->attributes.front().name() == HdAovTokens->instanceId.GetString());
VtMatrix4dArray transforms;
if (instancer) {
transforms = instancer->ComputeInstanceTransforms(id);
_instances[0]->attributes.front() = ParamValue(HdAovTokens->instanceId.GetString(), +0.0f);
}
else {
// Default to a single instance with an identity transform
transforms.push_back(GfMatrix4d(1.0));
_instances[0]->attributes.front() = ParamValue(HdAovTokens->instanceId.GetString(), -1.0f);
}
const size_t oldSize = _instances.size();
const size_t newSize = transforms.size();
// Resize instance list
for (size_t i = newSize; i < oldSize; ++i) {
lock.scene->delete_node(_instances[i]);
}
_instances.resize(newSize);
for (size_t i = oldSize; i < newSize; ++i) {
_instances[i] = lock.scene->create_node<Object>();
InitializeInstance(static_cast<int>(i));
}
// Update transforms of all instances
for (size_t i = 0; i < transforms.size(); ++i) {
const Transform tfm = convert_transform(_geomTransform * transforms[i]);
_instances[i]->set_tfm(tfm);
}
}
if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
for (Object *instance : _instances) {
instance->set_visibility(Base::IsVisible() ? ~0 : 0);
}
}
// Must happen after material ID update, so that attribute decisions can be made
// based on it (e.g. check whether an attribute is actually needed)
bool rebuild = false;
Populate(sceneDelegate, *dirtyBits, rebuild);
if (_geom->is_modified() || rebuild) {
_geom->tag_update(lock.scene, rebuild);
}
for (Object *instance : _instances) {
instance->tag_update(lock.scene);
}
*dirtyBits = HdChangeTracker::Clean;
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::Finalize(HdRenderParam *renderParam)
{
if (!_geom && _instances.empty()) {
return;
}
const SceneLock lock(renderParam);
lock.scene->delete_node(_geom);
_geom = nullptr;
lock.scene->delete_nodes(set<Object *>(_instances.begin(), _instances.end()));
_instances.clear();
_instances.shrink_to_fit();
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::Initialize(HdRenderParam *renderParam)
{
if (_geom) {
return;
}
const SceneLock lock(renderParam);
// Create geometry
_geom = lock.scene->create_node<CyclesBase>();
_geom->name = Base::GetId().GetString();
// Create default instance
_instances.push_back(lock.scene->create_node<Object>());
InitializeInstance(0);
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::InitializeInstance(int index)
{
Object *instance = _instances[index];
instance->set_geometry(_geom);
instance->attributes.emplace_back(HdAovTokens->instanceId.GetString(),
_instances.size() == 1 ? -1.0f : static_cast<float>(index));
instance->set_color(make_float3(0.8f, 0.8f, 0.8f));
instance->set_random_id(hash_uint2(hash_string(_geom->name.c_str()), index));
}
template<typename Base, typename CyclesBase>
HdInterpolation HdCyclesGeometry<Base, CyclesBase>::GetPrimvarInterpolation(
HdSceneDelegate *sceneDelegate, const TfToken &name) const
{
for (int i = 0; i < HdInterpolationCount; ++i) {
for (const HdPrimvarDescriptor &desc :
Base::GetPrimvarDescriptors(sceneDelegate, static_cast<HdInterpolation>(i))) {
if (desc.name == name) {
return static_cast<HdInterpolation>(i);
}
}
}
return HdInterpolationCount;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,138 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/instancer.h"
#include <pxr/base/gf/quatd.h>
#include <pxr/imaging/hd/sceneDelegate.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesInstancer::HdCyclesInstancer(HdSceneDelegate *delegate,
const SdfPath &instancerId
#if PXR_VERSION <= 2011
,
const SdfPath &parentId
#endif
)
: HdInstancer(delegate,
instancerId
#if PXR_VERSION <= 2011
,
parentId
#endif
)
{
}
HdCyclesInstancer::~HdCyclesInstancer()
{
}
#if PXR_VERSION > 2011
void HdCyclesInstancer::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
_UpdateInstancer(sceneDelegate, dirtyBits);
if (HdChangeTracker::IsAnyPrimvarDirty(*dirtyBits, GetId())) {
SyncPrimvars();
}
}
#endif
void HdCyclesInstancer::SyncPrimvars()
{
HdSceneDelegate *const sceneDelegate = GetDelegate();
const HdDirtyBits dirtyBits =
sceneDelegate->GetRenderIndex().GetChangeTracker().GetInstancerDirtyBits(GetId());
for (const HdPrimvarDescriptor &desc :
sceneDelegate->GetPrimvarDescriptors(GetId(), HdInterpolationInstance)) {
if (!HdChangeTracker::IsPrimvarDirty(dirtyBits, GetId(), desc.name)) {
continue;
}
const VtValue value = sceneDelegate->Get(GetId(), desc.name);
if (value.IsEmpty()) {
continue;
}
if (desc.name == HdInstancerTokens->translate) {
_translate = value.Get<VtVec3fArray>();
}
else if (desc.name == HdInstancerTokens->rotate) {
_rotate = value.Get<VtVec4fArray>();
}
else if (desc.name == HdInstancerTokens->scale) {
_scale = value.Get<VtVec3fArray>();
}
else if (desc.name == HdInstancerTokens->instanceTransform) {
_instanceTransform = value.Get<VtMatrix4dArray>();
}
}
sceneDelegate->GetRenderIndex().GetChangeTracker().MarkInstancerClean(GetId());
}
VtMatrix4dArray HdCyclesInstancer::ComputeInstanceTransforms(const SdfPath &prototypeId)
{
#if PXR_VERSION <= 2011
SyncPrimvars();
#endif
const VtIntArray instanceIndices = GetDelegate()->GetInstanceIndices(GetId(), prototypeId);
const GfMatrix4d instanceTransform = GetDelegate()->GetInstancerTransform(GetId());
VtMatrix4dArray transforms;
transforms.reserve(instanceIndices.size());
for (int index : instanceIndices) {
GfMatrix4d transform = instanceTransform;
if (index < _translate.size()) {
GfMatrix4d translateMat(1);
translateMat.SetTranslate(_translate[index]);
transform *= translateMat;
}
if (index < _rotate.size()) {
GfMatrix4d rotateMat(1);
const GfVec4f &quat = _rotate[index];
rotateMat.SetRotate(GfQuatd(quat[0], quat[1], quat[2], quat[3]));
transform *= rotateMat;
}
if (index < _scale.size()) {
GfMatrix4d scaleMat(1);
scaleMat.SetScale(_scale[index]);
transform *= scaleMat;
}
if (index < _instanceTransform.size()) {
transform *= _instanceTransform[index];
}
transforms.push_back(transform);
}
VtMatrix4dArray resultTransforms;
if (const auto instancer = static_cast<HdCyclesInstancer *>(
GetDelegate()->GetRenderIndex().GetInstancer(GetParentId()))) {
for (const GfMatrix4d &parentTransform : instancer->ComputeInstanceTransforms(GetId())) {
for (const GfMatrix4d &localTransform : transforms) {
resultTransforms.push_back(parentTransform * localTransform);
}
}
}
else {
resultTransforms = std::move(transforms);
}
return resultTransforms;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,45 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/base/gf/vec4f.h>
#include <pxr/base/vt/array.h>
#include <pxr/imaging/hd/instancer.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesInstancer final : public PXR_NS::HdInstancer {
public:
HdCyclesInstancer(PXR_NS::HdSceneDelegate *delegate,
const PXR_NS::SdfPath &instancerId
#if PXR_VERSION <= 2011
,
const PXR_NS::SdfPath &parentId
#endif
);
~HdCyclesInstancer() override;
#if PXR_VERSION > 2011
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
#endif
PXR_NS::VtMatrix4dArray ComputeInstanceTransforms(const PXR_NS::SdfPath &prototypeId);
private:
void SyncPrimvars();
PXR_NS::VtVec3fArray _translate;
PXR_NS::VtVec4fArray _rotate;
PXR_NS::VtVec3fArray _scale;
PXR_NS::VtMatrix4dArray _instanceTransform;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,399 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/light.h"
#include "hydra/session.h"
#include "scene/light.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include "util/hash.h"
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/usd/sdf/assetPath.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(visibleInPrimaryRay)
);
// clang-format on
HdCyclesLight::HdCyclesLight(const SdfPath &sprimId, const TfToken &lightType)
: HdLight(sprimId), _lightType(lightType)
{
}
HdCyclesLight::~HdCyclesLight()
{
}
HdDirtyBits HdCyclesLight::GetInitialDirtyBitsMask() const
{
return DirtyBits::DirtyTransform | DirtyBits::DirtyParams;
}
void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
if (*dirtyBits == DirtyBits::Clean) {
return;
}
Initialize(renderParam);
const SceneLock lock(renderParam);
VtValue value;
const SdfPath &id = GetId();
if (*dirtyBits & DirtyBits::DirtyTransform) {
#if PXR_VERSION >= 2011
const Transform tfm = convert_transform(sceneDelegate->GetTransform(id));
#else
const Transform tfm = convert_transform(
sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get<GfMatrix4d>());
#endif
_light->set_tfm(tfm);
_light->set_co(transform_get_column(&tfm, 3));
_light->set_dir(-transform_get_column(&tfm, 2));
if (_lightType == HdPrimTypeTokens->diskLight || _lightType == HdPrimTypeTokens->rectLight) {
_light->set_axisu(transform_get_column(&tfm, 0));
_light->set_axisv(transform_get_column(&tfm, 1));
}
}
if (*dirtyBits & DirtyBits::DirtyParams) {
float3 strength = make_float3(1.0f, 1.0f, 1.0f);
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->color);
if (!value.IsEmpty()) {
const auto color = value.Get<GfVec3f>();
strength = make_float3(color[0], color[1], color[2]);
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->exposure);
if (!value.IsEmpty()) {
strength *= exp2(value.Get<float>());
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->intensity);
if (!value.IsEmpty()) {
strength *= value.Get<float>();
}
// Cycles lights are normalized by default, so need to scale intensity if Hydra light is not
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->normalize);
const bool normalize = value.IsHolding<bool>() && value.UncheckedGet<bool>();
value = sceneDelegate->GetLightParamValue(id, _tokens->visibleInPrimaryRay);
if (!value.IsEmpty()) {
_light->set_use_camera(value.Get<bool>());
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shadowEnable);
if (!value.IsEmpty()) {
_light->set_cast_shadow(value.Get<bool>());
}
if (_lightType == HdPrimTypeTokens->distantLight) {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->angle);
if (!value.IsEmpty()) {
_light->set_angle(GfDegreesToRadians(value.Get<float>()));
}
}
else if (_lightType == HdPrimTypeTokens->diskLight) {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius);
if (!value.IsEmpty()) {
const float size = value.Get<float>() * 2.0f;
_light->set_sizeu(size);
_light->set_sizev(size);
}
if (!normalize) {
const float radius = _light->get_sizeu() * 0.5f;
strength *= M_PI * radius * radius;
}
}
else if (_lightType == HdPrimTypeTokens->rectLight) {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->width);
if (!value.IsEmpty()) {
_light->set_sizeu(value.Get<float>());
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->height);
if (!value.IsEmpty()) {
_light->set_sizev(value.Get<float>());
}
if (!normalize) {
strength *= _light->get_sizeu() * _light->get_sizeu();
}
}
else if (_lightType == HdPrimTypeTokens->sphereLight) {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius);
if (!value.IsEmpty()) {
_light->set_size(value.Get<float>());
}
bool shaping = false;
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingConeAngle);
if (!value.IsEmpty()) {
_light->set_spot_angle(GfDegreesToRadians(value.Get<float>()) * 2.0f);
shaping = true;
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingConeSoftness);
if (!value.IsEmpty()) {
_light->set_spot_smooth(value.Get<float>());
shaping = true;
}
_light->set_light_type(shaping ? LIGHT_SPOT : LIGHT_POINT);
if (!normalize) {
const float radius = _light->get_size();
strength *= M_PI * radius * radius * 4.0f;
}
}
const bool visible = sceneDelegate->GetVisible(id);
// Disable invisible lights by zeroing the strength
// So 'LightManager::test_enabled_lights' updates the enabled flag correctly
if (!visible) {
strength = zero_float3();
}
_light->set_strength(strength);
_light->set_is_enabled(visible);
PopulateShaderGraph(sceneDelegate);
}
// Need to update shader graph when transform changes in case transform was baked into it
else if (_light->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight ||
_light->get_shader()->has_surface_spatial_varying)) {
PopulateShaderGraph(sceneDelegate);
}
if (_light->is_modified()) {
_light->tag_update(lock.scene);
}
*dirtyBits = DirtyBits::Clean;
}
void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate)
{
auto graph = new ShaderGraph();
ShaderNode *outputNode = nullptr;
if (_lightType == HdPrimTypeTokens->domeLight) {
BackgroundNode *bgNode = graph->create_node<BackgroundNode>();
// Bake strength into shader graph, since only the shader is used for background lights
bgNode->set_color(_light->get_strength());
graph->add(bgNode);
graph->connect(bgNode->output("Background"), graph->output()->input("Surface"));
outputNode = bgNode;
}
else {
EmissionNode *emissionNode = graph->create_node<EmissionNode>();
emissionNode->set_color(one_float3());
emissionNode->set_strength(1.0f);
graph->add(emissionNode);
graph->connect(emissionNode->output("Emission"), graph->output()->input("Surface"));
outputNode = emissionNode;
}
VtValue value;
const SdfPath &id = GetId();
bool hasSpatialVarying = false;
bool hasColorTemperature = false;
if (sceneDelegate != nullptr) {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->enableColorTemperature);
const bool enableColorTemperature = value.IsHolding<bool>() && value.UncheckedGet<bool>();
if (enableColorTemperature) {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->colorTemperature);
if (value.IsHolding<float>()) {
BlackbodyNode *blackbodyNode = graph->create_node<BlackbodyNode>();
blackbodyNode->set_temperature(value.UncheckedGet<float>());
graph->add(blackbodyNode);
if (_lightType == HdPrimTypeTokens->domeLight) {
VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
mathNode->set_vector2(_light->get_strength());
graph->add(mathNode);
graph->connect(blackbodyNode->output("Color"), mathNode->input("Vector1"));
graph->connect(mathNode->output("Vector"), outputNode->input("Color"));
}
else {
graph->connect(blackbodyNode->output("Color"), outputNode->input("Color"));
}
hasColorTemperature = true;
}
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingIesFile);
if (value.IsHolding<SdfAssetPath>()) {
std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath();
if (filename.empty()) {
filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath();
}
TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
coordNode->set_ob_tfm(_light->get_tfm());
coordNode->set_use_transform(true);
graph->add(coordNode);
IESLightNode *iesNode = graph->create_node<IESLightNode>();
iesNode->set_filename(ustring(filename));
graph->connect(coordNode->output("Normal"), iesNode->input("Vector"));
graph->connect(iesNode->output("Fac"), outputNode->input("Strength"));
hasSpatialVarying = true;
}
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->textureFile);
if (value.IsHolding<SdfAssetPath>()) {
std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath();
if (filename.empty()) {
filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath();
}
ImageSlotTextureNode *textureNode = nullptr;
if (_lightType == HdPrimTypeTokens->domeLight) {
Transform tfm = _light->get_tfm();
transform_set_column(&tfm, 3, zero_float3()); // Remove translation
TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
coordNode->set_ob_tfm(tfm);
coordNode->set_use_transform(true);
graph->add(coordNode);
textureNode = graph->create_node<EnvironmentTextureNode>();
static_cast<EnvironmentTextureNode *>(textureNode)->set_filename(ustring(filename));
graph->add(textureNode);
graph->connect(coordNode->output("Object"), textureNode->input("Vector"));
hasSpatialVarying = true;
}
else {
GeometryNode *coordNode = graph->create_node<GeometryNode>();
graph->add(coordNode);
textureNode = graph->create_node<ImageTextureNode>();
static_cast<ImageTextureNode *>(textureNode)->set_filename(ustring(filename));
graph->add(textureNode);
graph->connect(coordNode->output("Parametric"), textureNode->input("Vector"));
}
if (hasColorTemperature) {
VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
graph->add(mathNode);
graph->connect(textureNode->output("Color"), mathNode->input("Vector1"));
ShaderInput *const outputNodeInput = outputNode->input("Color");
graph->connect(outputNodeInput->link, mathNode->input("Vector2"));
graph->disconnect(outputNodeInput);
graph->connect(mathNode->output("Vector"), outputNodeInput);
}
else if (_lightType == HdPrimTypeTokens->domeLight) {
VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
mathNode->set_vector2(_light->get_strength());
graph->add(mathNode);
graph->connect(textureNode->output("Color"), mathNode->input("Vector1"));
graph->connect(mathNode->output("Vector"), outputNode->input("Color"));
}
else {
graph->connect(textureNode->output("Color"), outputNode->input("Color"));
}
}
}
Shader *const shader = _light->get_shader();
shader->set_graph(graph);
shader->tag_update((Scene *)_light->get_owner());
shader->has_surface_spatial_varying = hasSpatialVarying;
}
void HdCyclesLight::Finalize(HdRenderParam *renderParam)
{
if (!_light) {
return;
}
const SceneLock lock(renderParam);
lock.scene->delete_node(_light);
_light = nullptr;
}
void HdCyclesLight::Initialize(HdRenderParam *renderParam)
{
if (_light) {
return;
}
const SceneLock lock(renderParam);
_light = lock.scene->create_node<Light>();
_light->name = GetId().GetString();
_light->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0));
if (_lightType == HdPrimTypeTokens->domeLight) {
_light->set_light_type(LIGHT_BACKGROUND);
}
else if (_lightType == HdPrimTypeTokens->distantLight) {
_light->set_light_type(LIGHT_DISTANT);
}
else if (_lightType == HdPrimTypeTokens->diskLight) {
_light->set_light_type(LIGHT_AREA);
_light->set_round(true);
_light->set_size(1.0f);
}
else if (_lightType == HdPrimTypeTokens->rectLight) {
_light->set_light_type(LIGHT_AREA);
_light->set_round(false);
_light->set_size(1.0f);
}
else if (_lightType == HdPrimTypeTokens->sphereLight) {
_light->set_light_type(LIGHT_POINT);
_light->set_size(1.0f);
}
_light->set_use_mis(true);
_light->set_use_camera(false);
Shader *const shader = lock.scene->create_node<Shader>();
_light->set_shader(shader);
// Create default shader graph
PopulateShaderGraph(nullptr);
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,35 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/light.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesLight final : public PXR_NS::HdLight {
public:
HdCyclesLight(const PXR_NS::SdfPath &sprimId, const PXR_NS::TfToken &lightType);
~HdCyclesLight() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
private:
void Initialize(PXR_NS::HdRenderParam *renderParam);
void PopulateShaderGraph(PXR_NS::HdSceneDelegate *sceneDelegate);
CCL_NS::Light *_light = nullptr;
PXR_NS::TfToken _lightType;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,589 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/material.h"
#include "hydra/node_util.h"
#include "hydra/session.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include <pxr/imaging/hd/sceneDelegate.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(CyclesMaterialTokens,
((cyclesSurface, "cycles:surface"))
((cyclesDisplacement, "cycles:displacement"))
((cyclesVolume, "cycles:volume"))
(UsdPreviewSurface)
(UsdUVTexture)
(UsdPrimvarReader_float)
(UsdPrimvarReader_float2)
(UsdPrimvarReader_float3)
(UsdPrimvarReader_float4)
(UsdPrimvarReader_int)
(UsdTransform2d)
(a)
(rgb)
(r)
(g)
(b)
(result)
(st)
(wrapS)
(wrapT)
(periodic)
);
// clang-format on
namespace {
// Simple class to handle remapping of USDPreviewSurface nodes and parameters to Cycles equivalents
class UsdToCyclesMapping {
using ParamMap = std::unordered_map<TfToken, ustring, TfToken::HashFunctor>;
public:
UsdToCyclesMapping(const char *nodeType, ParamMap paramMap)
: _nodeType(nodeType), _paramMap(std::move(paramMap))
{
}
ustring nodeType() const
{
return _nodeType;
}
virtual std::string parameterName(const TfToken &name,
const ShaderInput *inputConnection,
VtValue *value = nullptr) const
{
// UsdNode.name -> Node.input
// These all follow a simple pattern that we can just remap
// based on the name or 'Node.input' type
if (inputConnection) {
if (name == CyclesMaterialTokens->a) {
return "alpha";
}
if (name == CyclesMaterialTokens->rgb) {
return "color";
}
// TODO: Is there a better mapping than 'color'?
if (name == CyclesMaterialTokens->r || name == CyclesMaterialTokens->g ||
name == CyclesMaterialTokens->b) {
return "color";
}
if (name == CyclesMaterialTokens->result) {
switch (inputConnection->socket_type.type) {
case SocketType::BOOLEAN:
case SocketType::FLOAT:
case SocketType::INT:
case SocketType::UINT:
return "alpha";
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
default:
return "color";
}
}
}
// Simple mapping case
const auto it = _paramMap.find(name);
return it != _paramMap.end() ? it->second.string() : name.GetString();
}
private:
const ustring _nodeType;
ParamMap _paramMap;
};
class UsdToCyclesTexture : public UsdToCyclesMapping {
public:
using UsdToCyclesMapping::UsdToCyclesMapping;
std::string parameterName(const TfToken &name,
const ShaderInput *inputConnection,
VtValue *value) const override
{
if (value) {
// Remap UsdUVTexture.wrapS and UsdUVTexture.wrapT to cycles_image_texture.extension
if (name == CyclesMaterialTokens->wrapS || name == CyclesMaterialTokens->wrapT) {
std::string valueString = VtValue::Cast<std::string>(*value).Get<std::string>();
// A value of 'repeat' in USD is equivalent to 'periodic' in Cycles
if (valueString == "repeat") {
*value = VtValue(CyclesMaterialTokens->periodic);
}
return "extension";
}
}
return UsdToCyclesMapping::parameterName(name, inputConnection, value);
}
};
class UsdToCycles {
const UsdToCyclesMapping UsdPreviewSurface = {
"principled_bsdf",
{
{TfToken("diffuseColor"), ustring("base_color")},
{TfToken("emissiveColor"), ustring("emission")},
{TfToken("specularColor"), ustring("specular")},
{TfToken("clearcoatRoughness"), ustring("clearcoat_roughness")},
{TfToken("opacity"), ustring("alpha")},
// opacityThreshold
// occlusion
// displacement
}};
const UsdToCyclesTexture UsdUVTexture = {
"image_texture",
{
{CyclesMaterialTokens->st, ustring("vector")},
{CyclesMaterialTokens->wrapS, ustring("extension")},
{CyclesMaterialTokens->wrapT, ustring("extension")},
{TfToken("file"), ustring("filename")},
{TfToken("sourceColorSpace"), ustring("colorspace")},
}};
const UsdToCyclesMapping UsdPrimvarReader = {"attribute",
{{TfToken("varname"), ustring("attribute")}}};
public:
const UsdToCyclesMapping *findUsd(const TfToken &usdNodeType)
{
if (usdNodeType == CyclesMaterialTokens->UsdPreviewSurface) {
return &UsdPreviewSurface;
}
if (usdNodeType == CyclesMaterialTokens->UsdUVTexture) {
return &UsdUVTexture;
}
if (usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float2 ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float3 ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float4 ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_int) {
return &UsdPrimvarReader;
}
return nullptr;
}
const UsdToCyclesMapping *findCycles(const ustring &cyclesNodeType)
{
return nullptr;
}
};
TfStaticData<UsdToCycles> sUsdToCyles;
} // namespace
struct HdCyclesMaterial::NodeDesc {
ShaderNode *node;
const UsdToCyclesMapping *mapping;
};
HdCyclesMaterial::HdCyclesMaterial(const SdfPath &sprimId) : HdMaterial(sprimId)
{
}
HdCyclesMaterial::~HdCyclesMaterial()
{
}
HdDirtyBits HdCyclesMaterial::GetInitialDirtyBitsMask() const
{
return DirtyBits::DirtyResource | DirtyBits::DirtyParams;
}
void HdCyclesMaterial::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
if (*dirtyBits == DirtyBits::Clean) {
return;
}
Initialize(renderParam);
const SceneLock lock(renderParam);
const bool dirtyParams = (*dirtyBits & DirtyBits::DirtyParams);
const bool dirtyResource = (*dirtyBits & DirtyBits::DirtyResource);
VtValue value;
const SdfPath &id = GetId();
if (dirtyResource || dirtyParams) {
value = sceneDelegate->GetMaterialResource(id);
#if 1
const HdMaterialNetwork2 *network = nullptr;
std::unique_ptr<HdMaterialNetwork2> networkConverted;
if (value.IsHolding<HdMaterialNetwork2>()) {
network = &value.UncheckedGet<HdMaterialNetwork2>();
}
else if (value.IsHolding<HdMaterialNetworkMap>()) {
const auto &networkOld = value.UncheckedGet<HdMaterialNetworkMap>();
// In the case of only parameter updates, there is no need to waste time converting to a
// HdMaterialNetwork2, as supporting HdMaterialNetworkMap for parameters only is trivial.
if (!_nodes.empty() && !dirtyResource) {
for (const auto &networkEntry : networkOld.map) {
UpdateParameters(networkEntry.second);
}
_shader->tag_modified();
}
else {
networkConverted = std::make_unique<HdMaterialNetwork2>();
HdMaterialNetwork2ConvertFromHdMaterialNetworkMap(networkOld, networkConverted.get());
network = networkConverted.get();
}
}
else {
TF_RUNTIME_ERROR("Could not get a HdMaterialNetwork2.");
}
if (network) {
if (!_nodes.empty() && !dirtyResource) {
UpdateParameters(*network);
_shader->tag_modified();
}
else {
PopulateShaderGraph(*network);
}
}
#endif
}
if (_shader->is_modified()) {
_shader->tag_update(lock.scene);
}
*dirtyBits = DirtyBits::Clean;
}
void HdCyclesMaterial::UpdateParameters(NodeDesc &nodeDesc,
const std::map<TfToken, VtValue> &parameters,
const SdfPath &nodePath)
{
for (const std::pair<TfToken, VtValue> &param : parameters) {
VtValue value = param.second;
// See if the parameter name is in USDPreviewSurface terms, and needs to be converted
const UsdToCyclesMapping *inputMapping = nodeDesc.mapping;
const std::string inputName = inputMapping ?
inputMapping->parameterName(param.first, nullptr, &value) :
param.first.GetString();
// Find the input to write the parameter value to
const SocketType *input = nullptr;
for (const SocketType &socket : nodeDesc.node->type->inputs) {
if (string_iequals(socket.name.string(), inputName) || socket.ui_name == inputName) {
input = &socket;
break;
}
}
if (!input) {
TF_WARN("Could not find parameter '%s' on node '%s' ('%s')",
param.first.GetText(),
nodePath.GetText(),
nodeDesc.node->name.c_str());
continue;
}
SetNodeValue(nodeDesc.node, *input, value);
}
}
void HdCyclesMaterial::UpdateParameters(const HdMaterialNetwork &network)
{
for (const HdMaterialNode &nodeEntry : network.nodes) {
const SdfPath &nodePath = nodeEntry.path;
const auto nodeIt = _nodes.find(nodePath);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not update parameters on missing node '%s'", nodePath.GetText());
continue;
}
UpdateParameters(nodeIt->second, nodeEntry.parameters, nodePath);
}
}
void HdCyclesMaterial::UpdateParameters(const HdMaterialNetwork2 &network)
{
for (const std::pair<SdfPath, HdMaterialNode2> &nodeEntry : network.nodes) {
const SdfPath &nodePath = nodeEntry.first;
const auto nodeIt = _nodes.find(nodePath);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not update parameters on missing node '%s'", nodePath.GetText());
continue;
}
UpdateParameters(nodeIt->second, nodeEntry.second.parameters, nodePath);
}
}
void HdCyclesMaterial::UpdateConnections(NodeDesc &nodeDesc,
const HdMaterialNode2 &matNode,
const SdfPath &nodePath,
ShaderGraph *shaderGraph)
{
for (const std::pair<TfToken, std::vector<HdMaterialConnection2>> &connection :
matNode.inputConnections) {
const TfToken &dstSocketName = connection.first;
const UsdToCyclesMapping *inputMapping = nodeDesc.mapping;
const std::string inputName = inputMapping ?
inputMapping->parameterName(dstSocketName, nullptr) :
dstSocketName.GetString();
// Find the input to connect to on the passed in node
ShaderInput *input = nullptr;
for (ShaderInput *in : nodeDesc.node->inputs) {
if (string_iequals(in->socket_type.name.string(), inputName)) {
input = in;
break;
}
}
if (!input) {
TF_WARN("Ignoring connection on '%s.%s', input '%s' was not found",
nodePath.GetText(),
dstSocketName.GetText(),
dstSocketName.GetText());
continue;
}
// Now find the output to connect from
const auto &connectedNodes = connection.second;
if (connectedNodes.empty()) {
continue;
}
// TODO: Hydra allows multiple connections of the same input
// Unsure how to handle this in Cycles, so just use the first
if (connectedNodes.size() > 1) {
TF_WARN(
"Ignoring multiple connections to '%s.%s'", nodePath.GetText(), dstSocketName.GetText());
}
const SdfPath &upstreamNodePath = connectedNodes.front().upstreamNode;
const TfToken &upstreamOutputName = connectedNodes.front().upstreamOutputName;
const auto srcNodeIt = _nodes.find(upstreamNodePath);
if (srcNodeIt == _nodes.end()) {
TF_WARN("Ignoring connection from '%s.%s' to '%s.%s', node '%s' was not found",
upstreamNodePath.GetText(),
upstreamOutputName.GetText(),
nodePath.GetText(),
dstSocketName.GetText(),
upstreamNodePath.GetText());
continue;
}
const UsdToCyclesMapping *outputMapping = srcNodeIt->second.mapping;
const std::string outputName = outputMapping ?
outputMapping->parameterName(upstreamOutputName, input) :
upstreamOutputName.GetString();
ShaderOutput *output = nullptr;
for (ShaderOutput *out : srcNodeIt->second.node->outputs) {
if (string_iequals(out->socket_type.name.string(), outputName)) {
output = out;
break;
}
}
if (!output) {
TF_WARN("Ignoring connection from '%s.%s' to '%s.%s', output '%s' was not found",
upstreamNodePath.GetText(),
upstreamOutputName.GetText(),
nodePath.GetText(),
dstSocketName.GetText(),
upstreamOutputName.GetText());
continue;
}
shaderGraph->connect(output, input);
}
}
void HdCyclesMaterial::PopulateShaderGraph(const HdMaterialNetwork2 &networkMap)
{
_nodes.clear();
auto graph = new ShaderGraph();
// Iterate all the nodes first and build a complete but unconnected graph with parameters set
for (const std::pair<SdfPath, HdMaterialNode2> &nodeEntry : networkMap.nodes) {
NodeDesc nodeDesc = {};
const SdfPath &nodePath = nodeEntry.first;
const auto nodeIt = _nodes.find(nodePath);
// Create new node only if it does not exist yet
if (nodeIt != _nodes.end()) {
nodeDesc = nodeIt->second;
}
else {
// E.g. cycles_principled_bsdf or UsdPreviewSurface
const std::string &nodeTypeId = nodeEntry.second.nodeTypeId.GetString();
ustring cyclesType(nodeTypeId);
// Interpret a node type ID prefixed with cycles_<type> or cycles:<type> as a node of <type>
if (nodeTypeId.rfind("cycles", 0) == 0) {
cyclesType = nodeTypeId.substr(7);
nodeDesc.mapping = sUsdToCyles->findCycles(cyclesType);
}
else {
// Check if any remapping is needed (e.g. for USDPreviewSurface to Cycles nodes)
nodeDesc.mapping = sUsdToCyles->findUsd(nodeEntry.second.nodeTypeId);
if (nodeDesc.mapping) {
cyclesType = nodeDesc.mapping->nodeType();
}
}
// If it's a native Cycles' node-type, just do the lookup now.
if (const NodeType *nodeType = NodeType::find(cyclesType)) {
nodeDesc.node = static_cast<ShaderNode *>(nodeType->create(nodeType));
nodeDesc.node->set_owner(graph);
graph->add(nodeDesc.node);
_nodes.emplace(nodePath, nodeDesc);
}
else {
TF_RUNTIME_ERROR("Could not create node '%s'", nodePath.GetText());
continue;
}
}
UpdateParameters(nodeDesc, nodeEntry.second.parameters, nodePath);
}
// Now that all nodes have been constructed, iterate the network again and build up any
// connections between nodes
for (const std::pair<SdfPath, HdMaterialNode2> &nodeEntry : networkMap.nodes) {
const SdfPath &nodePath = nodeEntry.first;
const auto nodeIt = _nodes.find(nodePath);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not find node '%s' to connect", nodePath.GetText());
continue;
}
UpdateConnections(nodeIt->second, nodeEntry.second, nodePath, graph);
}
// Finally connect the terminals to the graph output (Surface, Volume, Displacement)
for (const std::pair<TfToken, HdMaterialConnection2> &terminalEntry : networkMap.terminals) {
const TfToken &terminalName = terminalEntry.first;
const HdMaterialConnection2 &connection = terminalEntry.second;
const auto nodeIt = _nodes.find(connection.upstreamNode);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not find terminal node '%s'", connection.upstreamNode.GetText());
continue;
}
ShaderNode *const node = nodeIt->second.node;
const char *inputName = nullptr;
const char *outputName = nullptr;
if (terminalName == HdMaterialTerminalTokens->surface ||
terminalName == CyclesMaterialTokens->cyclesSurface) {
inputName = "Surface";
// Find default output name based on the node if none is provided
if (node->type->name == "add_closure" || node->type->name == "mix_closure") {
outputName = "Closure";
}
else if (node->type->name == "emission") {
outputName = "Emission";
}
else {
outputName = "BSDF";
}
}
else if (terminalName == HdMaterialTerminalTokens->displacement ||
terminalName == CyclesMaterialTokens->cyclesDisplacement) {
inputName = outputName = "Displacement";
}
else if (terminalName == HdMaterialTerminalTokens->volume ||
terminalName == CyclesMaterialTokens->cyclesVolume) {
inputName = outputName = "Volume";
}
if (!connection.upstreamOutputName.IsEmpty()) {
outputName = connection.upstreamOutputName.GetText();
}
ShaderInput *const input = inputName ? graph->output()->input(inputName) : nullptr;
if (!input) {
TF_RUNTIME_ERROR("Could not find terminal input '%s.%s'",
connection.upstreamNode.GetText(),
inputName ? inputName : "<null>");
continue;
}
ShaderOutput *const output = outputName ? node->output(outputName) : nullptr;
if (!output) {
TF_RUNTIME_ERROR("Could not find terminal output '%s.%s'",
connection.upstreamNode.GetText(),
outputName ? outputName : "<null>");
continue;
}
graph->connect(output, input);
}
// Create the instanceId AOV output
{
const ustring instanceId(HdAovTokens->instanceId.GetString());
OutputAOVNode *aovNode = graph->create_node<OutputAOVNode>();
aovNode->set_name(instanceId);
graph->add(aovNode);
AttributeNode *instanceIdNode = graph->create_node<AttributeNode>();
instanceIdNode->set_attribute(instanceId);
graph->add(instanceIdNode);
graph->connect(instanceIdNode->output("Fac"), aovNode->input("Value"));
}
_shader->set_graph(graph);
}
void HdCyclesMaterial::Finalize(HdRenderParam *renderParam)
{
if (!_shader) {
return;
}
const SceneLock lock(renderParam);
_nodes.clear();
lock.scene->delete_node(_shader);
_shader = nullptr;
}
void HdCyclesMaterial::Initialize(HdRenderParam *renderParam)
{
if (_shader) {
return;
}
const SceneLock lock(renderParam);
_shader = lock.scene->create_node<Shader>();
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,60 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/material.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesMaterial final : public PXR_NS::HdMaterial {
public:
HdCyclesMaterial(const PXR_NS::SdfPath &sprimId);
~HdCyclesMaterial() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
#if PXR_VERSION < 2011
void Reload() override
{
}
#endif
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
CCL_NS::Shader *GetCyclesShader() const
{
return _shader;
}
struct NodeDesc;
private:
void Initialize(PXR_NS::HdRenderParam *renderParam);
void UpdateParameters(NodeDesc &nodeDesc,
const std::map<PXR_NS::TfToken, PXR_NS::VtValue> &parameters,
const PXR_NS::SdfPath &nodePath);
void UpdateParameters(const PXR_NS::HdMaterialNetwork &network);
void UpdateParameters(const PXR_NS::HdMaterialNetwork2 &network);
void UpdateConnections(NodeDesc &nodeDesc,
const PXR_NS::HdMaterialNode2 &matNode,
const PXR_NS::SdfPath &nodePath,
CCL_NS::ShaderGraph *shaderGraph);
void PopulateShaderGraph(const PXR_NS::HdMaterialNetwork2 &network);
CCL_NS::Shader *_shader = nullptr;
std::unordered_map<PXR_NS::SdfPath, NodeDesc, PXR_NS::SdfPath::Hash> _nodes;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,524 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/mesh.h"
#include "hydra/geometry.inl"
#include "scene/mesh.h"
#include <pxr/base/gf/vec2f.h>
#include <pxr/imaging/hd/extComputationUtils.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
namespace {
template<typename T>
VtValue ComputeTriangulatedUniformPrimvar(VtValue value, const VtIntArray &primitiveParams)
{
T output;
output.reserve(primitiveParams.size());
const T &input = value.Get<T>();
for (size_t i = 0; i < primitiveParams.size(); ++i) {
const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(primitiveParams[i]);
output.push_back(input[faceIndex]);
}
return VtValue(output);
}
VtValue ComputeTriangulatedUniformPrimvar(VtValue value,
const HdType valueType,
const VtIntArray &primitiveParams)
{
switch (valueType) {
case HdTypeFloat:
return ComputeTriangulatedUniformPrimvar<VtFloatArray>(value, primitiveParams);
case HdTypeFloatVec2:
return ComputeTriangulatedUniformPrimvar<VtVec2fArray>(value, primitiveParams);
case HdTypeFloatVec3:
return ComputeTriangulatedUniformPrimvar<VtVec3fArray>(value, primitiveParams);
case HdTypeFloatVec4:
return ComputeTriangulatedUniformPrimvar<VtVec4fArray>(value, primitiveParams);
default:
TF_RUNTIME_ERROR("Unsupported attribute type %d", static_cast<int>(valueType));
return VtValue();
}
}
VtValue ComputeTriangulatedFaceVaryingPrimvar(VtValue value,
const HdType valueType,
HdMeshUtil &meshUtil)
{
if (meshUtil.ComputeTriangulatedFaceVaryingPrimvar(
HdGetValueData(value), value.GetArraySize(), valueType, &value)) {
return value;
}
return VtValue();
}
} // namespace
Transform convert_transform(const GfMatrix4d &matrix)
{
return make_transform(matrix[0][0],
matrix[1][0],
matrix[2][0],
matrix[3][0],
matrix[0][1],
matrix[1][1],
matrix[2][1],
matrix[3][1],
matrix[0][2],
matrix[1][2],
matrix[2][2],
matrix[3][2]);
}
HdCyclesMesh::HdCyclesMesh(const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
: HdCyclesGeometry(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
),
_util(&_topology, rprimId)
{
}
HdCyclesMesh::~HdCyclesMesh()
{
}
HdDirtyBits HdCyclesMesh::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals |
HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyTopology |
HdChangeTracker::DirtyDisplayStyle | HdChangeTracker::DirtySubdivTags;
return bits;
}
HdDirtyBits HdCyclesMesh::_PropagateDirtyBits(HdDirtyBits bits) const
{
if (bits & (HdChangeTracker::DirtyMaterialId)) {
// Update used shaders from geometry subsets if any exist in the topology
bits |= HdChangeTracker::DirtyTopology;
}
if (bits & (HdChangeTracker::DirtyTopology | HdChangeTracker::DirtyDisplayStyle |
HdChangeTracker::DirtySubdivTags)) {
// Do full topology update when display style or subdivision changes
bits |= HdChangeTracker::DirtyTopology | HdChangeTracker::DirtyDisplayStyle |
HdChangeTracker::DirtySubdivTags;
}
if (bits & (HdChangeTracker::DirtyTopology)) {
// Changing topology clears the geometry, so need to populate everything again
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals |
HdChangeTracker::DirtyPrimvar;
}
return bits;
}
void HdCyclesMesh::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
if (HdChangeTracker::IsTopologyDirty(dirtyBits, GetId())) {
PopulateTopology(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyPoints) {
PopulatePoints(sceneDelegate);
}
// Must happen after topology update, so that normals attribute size can be calculated
if (dirtyBits & HdChangeTracker::DirtyNormals) {
PopulateNormals(sceneDelegate);
}
// Must happen after topology update, so that appropriate attribute set can be selected
if (dirtyBits & HdChangeTracker::DirtyPrimvar) {
PopulatePrimvars(sceneDelegate);
}
rebuild = (_geom->triangles_is_modified()) || (_geom->subd_start_corner_is_modified()) ||
(_geom->subd_num_corners_is_modified()) || (_geom->subd_shader_is_modified()) ||
(_geom->subd_smooth_is_modified()) || (_geom->subd_ptex_offset_is_modified()) ||
(_geom->subd_face_corners_is_modified());
}
void HdCyclesMesh::PopulatePoints(HdSceneDelegate *sceneDelegate)
{
VtValue value;
for (const HdExtComputationPrimvarDescriptor &desc :
sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) {
if (desc.name == HdTokens->points) {
auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate);
const auto valueStoreIt = valueStore.find(desc.name);
if (valueStoreIt != valueStore.end()) {
value = std::move(valueStoreIt->second);
}
break;
}
}
if (value.IsEmpty()) {
value = GetPoints(sceneDelegate);
}
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid points data for %s", GetId().GetText());
return;
}
const auto &points = value.UncheckedGet<VtVec3fArray>();
TF_VERIFY(points.size() >= static_cast<size_t>(_topology.GetNumPoints()));
array<float3> pointsDataCycles;
pointsDataCycles.reserve(points.size());
for (const GfVec3f &point : points) {
pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2]));
}
_geom->set_verts(pointsDataCycles);
}
void HdCyclesMesh::PopulateNormals(HdSceneDelegate *sceneDelegate)
{
_geom->attributes.remove(ATTR_STD_FACE_NORMAL);
_geom->attributes.remove(ATTR_STD_VERTEX_NORMAL);
// Authored normals should only exist on triangle meshes
if (_geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE) {
return;
}
VtValue value;
HdInterpolation interpolation = HdInterpolationCount;
for (int i = 0; i < HdInterpolationCount && interpolation == HdInterpolationCount; ++i) {
for (const HdExtComputationPrimvarDescriptor &desc :
sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(),
static_cast<HdInterpolation>(i))) {
if (desc.name == HdTokens->normals) {
auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate);
const auto valueStoreIt = valueStore.find(desc.name);
if (valueStoreIt != valueStore.end()) {
value = std::move(valueStoreIt->second);
interpolation = static_cast<HdInterpolation>(i);
}
break;
}
}
}
if (value.IsEmpty()) {
interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->normals);
if (interpolation == HdInterpolationCount) {
return; // Ignore missing normals
}
value = GetNormals(sceneDelegate);
}
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid normals data for %s", GetId().GetText());
return;
}
const auto &normals = value.UncheckedGet<VtVec3fArray>();
if (interpolation == HdInterpolationConstant) {
TF_VERIFY(normals.size() == 1);
const GfVec3f constantNormal = normals[0];
float3 *const N = _geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3();
for (size_t i = 0; i < _geom->get_verts().size(); ++i) {
N[i] = make_float3(constantNormal[0], constantNormal[1], constantNormal[2]);
}
}
else if (interpolation == HdInterpolationUniform) {
TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumFaces()));
float3 *const N = _geom->attributes.add(ATTR_STD_FACE_NORMAL)->data_float3();
for (size_t i = 0; i < _geom->num_triangles(); ++i) {
const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]);
N[i] = make_float3(normals[faceIndex][0], normals[faceIndex][1], normals[faceIndex][2]);
}
}
else if (interpolation == HdInterpolationVertex || interpolation == HdInterpolationVarying) {
TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumPoints()) &&
static_cast<size_t>(_topology.GetNumPoints()) == _geom->get_verts().size());
float3 *const N = _geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3();
for (size_t i = 0; i < _geom->get_verts().size(); ++i) {
N[i] = make_float3(normals[i][0], normals[i][1], normals[i][2]);
}
}
else if (interpolation == HdInterpolationFaceVarying) {
TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumFaceVaryings()));
if (!_util.ComputeTriangulatedFaceVaryingPrimvar(
normals.data(), normals.size(), HdTypeFloatVec3, &value)) {
return;
}
const auto &normalsTriangulated = value.UncheckedGet<VtVec3fArray>();
// Cycles has no standard attribute for face-varying normals, so this is a lossy transformation
float3 *const N = _geom->attributes.add(ATTR_STD_FACE_NORMAL)->data_float3();
for (size_t i = 0; i < _geom->num_triangles(); ++i) {
GfVec3f averageNormal = normalsTriangulated[i * 3] + normalsTriangulated[i * 3 + 1] +
normalsTriangulated[i * 3 + 2];
GfNormalize(&averageNormal);
N[i] = make_float3(averageNormal[0], averageNormal[1], averageNormal[2]);
}
}
}
void HdCyclesMesh::PopulatePrimvars(HdSceneDelegate *sceneDelegate)
{
Scene *const scene = (Scene *)_geom->get_owner();
const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE;
AttributeSet &attributes = subdivision ? _geom->subd_attributes : _geom->attributes;
const std::pair<HdInterpolation, AttributeElement> interpolations[] = {
std::make_pair(HdInterpolationFaceVarying, ATTR_ELEMENT_CORNER),
std::make_pair(HdInterpolationUniform, ATTR_ELEMENT_FACE),
std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_VERTEX),
std::make_pair(HdInterpolationVarying, ATTR_ELEMENT_VERTEX),
std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT),
};
for (const auto &interpolation : interpolations) {
for (const HdPrimvarDescriptor &desc :
GetPrimvarDescriptors(sceneDelegate, interpolation.first)) {
// Skip special primvars that are handled separately
if (desc.name == HdTokens->points || desc.name == HdTokens->normals) {
continue;
}
VtValue value = GetPrimvar(sceneDelegate, desc.name);
if (value.IsEmpty()) {
continue;
}
const ustring name(desc.name.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (desc.role == HdPrimvarRoleTokens->textureCoordinate) {
std = ATTR_STD_UV;
}
else if (interpolation.first == HdInterpolationVertex) {
if (desc.name == HdTokens->displayColor || desc.role == HdPrimvarRoleTokens->color) {
std = ATTR_STD_VERTEX_COLOR;
}
else if (desc.name == HdTokens->normals) {
std = ATTR_STD_VERTEX_NORMAL;
}
}
else if (desc.name == HdTokens->displayColor &&
interpolation.first == HdInterpolationConstant) {
if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) {
const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0];
_instances[0]->set_color(make_float3(color[0], color[1], color[2]));
}
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name)) {
const HdType valueType = HdGetValueTupleType(value).type;
if (!subdivision) {
// Adjust attributes for polygons that were triangulated
if (interpolation.first == HdInterpolationUniform) {
value = ComputeTriangulatedUniformPrimvar(value, valueType, _primitiveParams);
if (value.IsEmpty()) {
continue;
}
}
else if (interpolation.first == HdInterpolationFaceVarying) {
value = ComputeTriangulatedFaceVaryingPrimvar(value, valueType, _util);
if (value.IsEmpty()) {
continue;
}
}
}
ApplyPrimvars(attributes, name, value, interpolation.second, std);
}
}
}
}
void HdCyclesMesh::PopulateTopology(HdSceneDelegate *sceneDelegate)
{
// Clear geometry before populating it again with updated topology
_geom->clear(true);
const HdDisplayStyle displayStyle = GetDisplayStyle(sceneDelegate);
_topology = HdMeshTopology(GetMeshTopology(sceneDelegate), displayStyle.refineLevel);
const TfToken subdivScheme = _topology.GetScheme();
if (subdivScheme == PxOsdOpenSubdivTokens->bilinear && _topology.GetRefineLevel() > 0) {
_geom->set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
}
else if (subdivScheme == PxOsdOpenSubdivTokens->catmullClark && _topology.GetRefineLevel() > 0) {
_geom->set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
}
else {
_geom->set_subdivision_type(Mesh::SUBDIVISION_NONE);
}
const bool smooth = !displayStyle.flatShadingEnabled;
const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE;
// Initialize lookup table from polygon face to material shader index
VtIntArray faceShaders(_topology.GetNumFaces(), 0);
HdGeomSubsets const &geomSubsets = _topology.GetGeomSubsets();
if (!geomSubsets.empty()) {
array<Node *> usedShaders = std::move(_geom->get_used_shaders());
// Remove any previous materials except for the material assigned to the prim
usedShaders.resize(1);
std::unordered_map<SdfPath, int, SdfPath::Hash> materials;
for (const HdGeomSubset &geomSubset : geomSubsets) {
TF_VERIFY(geomSubset.type == HdGeomSubset::TypeFaceSet);
int shader = 0;
const auto it = materials.find(geomSubset.materialId);
if (it != materials.end()) {
shader = it->second;
}
else {
const auto material = static_cast<const HdCyclesMaterial *>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material,
geomSubset.materialId));
if (material && material->GetCyclesShader()) {
shader = static_cast<int>(usedShaders.size());
usedShaders.push_back_slow(material->GetCyclesShader());
materials.emplace(geomSubset.materialId, shader);
}
}
for (int face : geomSubset.indices) {
faceShaders[face] = shader;
}
}
_geom->set_used_shaders(usedShaders);
}
const VtIntArray vertIndx = _topology.GetFaceVertexIndices();
const VtIntArray vertCounts = _topology.GetFaceVertexCounts();
if (!subdivision) {
VtVec3iArray triangles;
_util.ComputeTriangleIndices(&triangles, &_primitiveParams);
_geom->reserve_mesh(_topology.GetNumPoints(), triangles.size());
for (size_t i = 0; i < _primitiveParams.size(); ++i) {
const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]);
const GfVec3i triangle = triangles[i];
_geom->add_triangle(triangle[0], triangle[1], triangle[2], faceShaders[faceIndex], smooth);
}
}
else {
PxOsdSubdivTags subdivTags = GetSubdivTags(sceneDelegate);
_topology.SetSubdivTags(subdivTags);
size_t numNgons = 0;
size_t numCorners = 0;
for (int vertCount : vertCounts) {
numNgons += (vertCount == 4) ? 0 : 1;
numCorners += vertCount;
}
_geom->reserve_subd_faces(_topology.GetNumFaces(), numNgons, numCorners);
// TODO: Handle hole indices
size_t faceIndex = 0;
size_t indexOffset = 0;
for (int vertCount : vertCounts) {
_geom->add_subd_face(&vertIndx[indexOffset], vertCount, faceShaders[faceIndex], smooth);
faceIndex++;
indexOffset += vertCount;
}
const VtIntArray creaseLengths = subdivTags.GetCreaseLengths();
if (!creaseLengths.empty()) {
size_t numCreases = 0;
for (int creaseLength : creaseLengths) {
numCreases += creaseLength - 1;
}
_geom->reserve_subd_creases(numCreases);
const VtIntArray creaseIndices = subdivTags.GetCreaseIndices();
const VtFloatArray creaseWeights = subdivTags.GetCreaseWeights();
indexOffset = 0;
size_t creaseLengthOffset = 0;
size_t createWeightOffset = 0;
for (int creaseLength : creaseLengths) {
for (int j = 0; j < creaseLength - 1; ++j, ++createWeightOffset) {
const int v0 = creaseIndices[indexOffset + j];
const int v1 = creaseIndices[indexOffset + j + 1];
float weight = creaseWeights.size() == creaseLengths.size() ?
creaseWeights[creaseLengthOffset] :
creaseWeights[createWeightOffset];
_geom->add_edge_crease(v0, v1, weight);
}
indexOffset += creaseLength;
creaseLengthOffset++;
}
const VtIntArray cornerIndices = subdivTags.GetCornerIndices();
const VtFloatArray cornerWeights = subdivTags.GetCornerWeights();
for (size_t i = 0; i < cornerIndices.size(); ++i) {
_geom->add_vertex_crease(cornerIndices[i], cornerWeights[i]);
}
}
_geom->set_subd_dicing_rate(1.0f);
_geom->set_subd_max_level(_topology.GetRefineLevel());
_geom->set_subd_objecttoworld(_instances[0]->get_tfm());
}
}
void HdCyclesMesh::Finalize(PXR_NS::HdRenderParam *renderParam)
{
_topology = HdMeshTopology();
_primitiveParams.clear();
HdCyclesGeometry<PXR_NS::HdMesh, Mesh>::Finalize(renderParam);
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,49 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/mesh.h>
#include <pxr/imaging/hd/meshUtil.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesMesh final : public HdCyclesGeometry<PXR_NS::HdMesh, CCL_NS::Mesh> {
public:
HdCyclesMesh(
const PXR_NS::SdfPath &rprimId
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId = {}
#endif
);
~HdCyclesMesh() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
private:
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateNormals(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateTopology(PXR_NS::HdSceneDelegate *sceneDelegate);
PXR_NS::HdMeshUtil _util;
PXR_NS::HdMeshTopology _topology;
PXR_NS::VtIntArray _primitiveParams;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,561 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/node_util.h"
#include "util/transform.h"
#include <pxr/base/gf/matrix3d.h>
#include <pxr/base/gf/matrix3f.h>
#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/matrix4f.h>
#include <pxr/base/gf/vec2f.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/base/vt/array.h>
#include <pxr/usd/sdf/assetPath.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
namespace {
template<typename DstType> DstType convertToCycles(const VtValue &value)
{
if (value.IsHolding<DstType>()) {
return value.UncheckedGet<DstType>();
}
VtValue castedValue = VtValue::Cast<DstType>(value);
if (castedValue.IsHolding<DstType>()) {
return castedValue.UncheckedGet<DstType>();
}
TF_WARN("Could not convert VtValue to Cycles type");
return DstType(0);
}
template<> float2 convertToCycles<float2>(const VtValue &value)
{
const GfVec2f convertedValue = convertToCycles<GfVec2f>(value);
return make_float2(convertedValue[0], convertedValue[1]);
}
template<> float3 convertToCycles<float3>(const VtValue &value)
{
if (value.IsHolding<GfVec3f>()) {
const GfVec3f convertedValue = value.UncheckedGet<GfVec3f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
if (value.IsHolding<GfVec4f>()) {
const GfVec4f convertedValue = value.UncheckedGet<GfVec4f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
if (value.CanCast<GfVec3f>()) {
const GfVec3f convertedValue = VtValue::Cast<GfVec3f>(value).UncheckedGet<GfVec3f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
if (value.CanCast<GfVec4f>()) {
const GfVec4f convertedValue = VtValue::Cast<GfVec4f>(value).UncheckedGet<GfVec4f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
TF_WARN("Could not convert VtValue to float3");
return zero_float3();
}
template<> ustring convertToCycles<ustring>(const VtValue &value)
{
if (value.IsHolding<TfToken>()) {
return ustring(value.UncheckedGet<TfToken>().GetString());
}
if (value.IsHolding<std::string>()) {
return ustring(value.UncheckedGet<std::string>());
}
if (value.IsHolding<SdfAssetPath>()) {
const SdfAssetPath &path = value.UncheckedGet<SdfAssetPath>();
return ustring(path.GetResolvedPath());
}
if (value.CanCast<TfToken>()) {
return convertToCycles<ustring>(VtValue::Cast<TfToken>(value));
}
if (value.CanCast<std::string>()) {
return convertToCycles<ustring>(VtValue::Cast<std::string>(value));
}
if (value.CanCast<SdfAssetPath>()) {
return convertToCycles<ustring>(VtValue::Cast<SdfAssetPath>(value));
}
TF_WARN("Could not convert VtValue to ustring");
return ustring();
}
template<typename Matrix>
Transform convertMatrixToCycles(
const typename std::enable_if<Matrix::numRows == 3 && Matrix::numColumns == 3, Matrix>::type
&matrix)
{
return make_transform(matrix[0][0],
matrix[1][0],
matrix[2][0],
0,
matrix[0][1],
matrix[1][1],
matrix[2][1],
0,
matrix[0][2],
matrix[1][2],
matrix[2][2],
0);
}
template<typename Matrix>
Transform convertMatrixToCycles(
const typename std::enable_if<Matrix::numRows == 4 && Matrix::numColumns == 4, Matrix>::type
&matrix)
{
return make_transform(matrix[0][0],
matrix[1][0],
matrix[2][0],
matrix[3][0],
matrix[0][1],
matrix[1][1],
matrix[2][1],
matrix[3][1],
matrix[0][2],
matrix[1][2],
matrix[2][2],
matrix[3][2]);
}
template<> Transform convertToCycles<Transform>(const VtValue &value)
{
if (value.IsHolding<GfMatrix4f>()) {
return convertMatrixToCycles<GfMatrix4f>(value.UncheckedGet<GfMatrix4f>());
}
if (value.IsHolding<GfMatrix3f>()) {
return convertMatrixToCycles<GfMatrix3f>(value.UncheckedGet<GfMatrix3f>());
}
if (value.IsHolding<GfMatrix4d>()) {
return convertMatrixToCycles<GfMatrix4d>(value.UncheckedGet<GfMatrix4d>());
}
if (value.IsHolding<GfMatrix3d>()) {
return convertMatrixToCycles<GfMatrix3d>(value.UncheckedGet<GfMatrix3d>());
}
if (value.CanCast<GfMatrix4f>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix4f>(value));
}
if (value.CanCast<GfMatrix3f>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix3f>(value));
}
if (value.CanCast<GfMatrix4d>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix4d>(value));
}
if (value.CanCast<GfMatrix3d>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix3d>(value));
}
TF_WARN("Could not convert VtValue to Transform");
return transform_identity();
}
template<typename DstType, typename SrcType = DstType>
array<DstType> convertToCyclesArray(const VtValue &value)
{
static_assert(sizeof(DstType) == sizeof(SrcType),
"Size mismatch between VtArray and array base type");
using SrcArray = VtArray<SrcType>;
if (value.IsHolding<SrcArray>()) {
const auto &valueData = value.UncheckedGet<SrcArray>();
array<DstType> cyclesArray;
cyclesArray.resize(valueData.size());
std::memcpy(cyclesArray.data(), valueData.data(), valueData.size() * sizeof(DstType));
return cyclesArray;
}
if (value.CanCast<SrcArray>()) {
VtValue castedValue = VtValue::Cast<SrcArray>(value);
const auto &valueData = castedValue.UncheckedGet<SrcArray>();
array<DstType> cyclesArray;
cyclesArray.resize(valueData.size());
std::memcpy(cyclesArray.data(), valueData.data(), valueData.size() * sizeof(DstType));
return cyclesArray;
}
return array<DstType>();
}
template<> array<float3> convertToCyclesArray<float3, GfVec3f>(const VtValue &value)
{
if (value.IsHolding<VtVec3fArray>()) {
const auto &valueData = value.UncheckedGet<VtVec3fArray>();
array<float3> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const GfVec3f &vec : valueData) {
cyclesArray.push_back_reserved(make_float3(vec[0], vec[1], vec[2]));
}
return cyclesArray;
}
if (value.IsHolding<VtVec4fArray>()) {
const auto &valueData = value.UncheckedGet<VtVec4fArray>();
array<float3> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const GfVec4f &vec : valueData) {
cyclesArray.push_back_reserved(make_float3(vec[0], vec[1], vec[2]));
}
return cyclesArray;
}
if (value.CanCast<VtVec3fArray>()) {
return convertToCyclesArray<float3, GfVec3f>(VtValue::Cast<VtVec3fArray>(value));
}
if (value.CanCast<VtVec4fArray>()) {
return convertToCyclesArray<float3, GfVec3f>(VtValue::Cast<VtVec4fArray>(value));
}
return array<float3>();
}
template<> array<ustring> convertToCyclesArray<ustring, void>(const VtValue &value)
{
using SdfPathArray = VtArray<SdfAssetPath>;
if (value.IsHolding<VtStringArray>()) {
const auto &valueData = value.UncheckedGet<VtStringArray>();
array<ustring> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(ustring(element));
}
return cyclesArray;
}
if (value.IsHolding<VtTokenArray>()) {
const auto &valueData = value.UncheckedGet<VtTokenArray>();
array<ustring> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(ustring(element.GetString()));
}
return cyclesArray;
}
if (value.IsHolding<SdfPathArray>()) {
const auto &valueData = value.UncheckedGet<SdfPathArray>();
array<ustring> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(ustring(element.GetResolvedPath()));
}
return cyclesArray;
}
if (value.CanCast<VtStringArray>()) {
return convertToCyclesArray<ustring, void>(VtValue::Cast<VtStringArray>(value));
}
if (value.CanCast<VtTokenArray>()) {
return convertToCyclesArray<ustring, void>(VtValue::Cast<VtTokenArray>(value));
}
if (value.CanCast<SdfPathArray>()) {
return convertToCyclesArray<ustring, void>(VtValue::Cast<SdfPathArray>(value));
}
TF_WARN("Could not convert VtValue to array<ustring>");
return array<ustring>();
}
template<typename MatrixArray> array<Transform> convertToCyclesTransformArray(const VtValue &value)
{
assert(value.IsHolding<MatrixArray>());
const auto &valueData = value.UncheckedGet<MatrixArray>();
array<Transform> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(convertMatrixToCycles<MatrixArray::value_type>(element));
}
return cyclesArray;
}
template<> array<Transform> convertToCyclesArray<Transform, void>(const VtValue &value)
{
if (value.IsHolding<VtMatrix4fArray>()) {
return convertToCyclesTransformArray<VtMatrix4fArray>(value);
}
if (value.IsHolding<VtMatrix3fArray>()) {
return convertToCyclesTransformArray<VtMatrix3fArray>(value);
}
if (value.IsHolding<VtMatrix4dArray>()) {
return convertToCyclesTransformArray<VtMatrix4dArray>(value);
}
if (value.IsHolding<VtMatrix3dArray>()) {
return convertToCyclesTransformArray<VtMatrix3dArray>(value);
}
if (value.CanCast<VtMatrix4fArray>()) {
return convertToCyclesTransformArray<VtMatrix4fArray>(VtValue::Cast<VtMatrix4fArray>(value));
}
if (value.CanCast<VtMatrix3fArray>()) {
return convertToCyclesTransformArray<VtMatrix3fArray>(VtValue::Cast<VtMatrix3fArray>(value));
}
if (value.CanCast<VtMatrix4dArray>()) {
return convertToCyclesTransformArray<VtMatrix4dArray>(VtValue::Cast<VtMatrix4dArray>(value));
}
if (value.CanCast<VtMatrix3dArray>()) {
return convertToCyclesTransformArray<VtMatrix3dArray>(VtValue::Cast<VtMatrix3dArray>(value));
}
TF_WARN("Could not convert VtValue to array<Transform>");
return array<Transform>();
}
template<typename SrcType> VtValue convertFromCycles(const SrcType &value)
{
return VtValue(value);
}
template<> VtValue convertFromCycles<float2>(const float2 &value)
{
const GfVec2f convertedValue(value.x, value.y);
return VtValue(convertedValue);
}
template<> VtValue convertFromCycles<float3>(const float3 &value)
{
const GfVec3f convertedValue(value.x, value.y, value.z);
return VtValue(convertedValue);
}
template<> VtValue convertFromCycles<ustring>(const ustring &value)
{
return VtValue(value.string());
}
GfMatrix4f convertMatrixFromCycles(const Transform &matrix)
{
return GfMatrix4f(matrix[0][0],
matrix[1][0],
matrix[2][0],
0.0f,
matrix[0][1],
matrix[1][1],
matrix[2][1],
0.0f,
matrix[0][2],
matrix[1][2],
matrix[2][2],
0.0f,
0.0f,
0.0f,
0.0f,
1.0f);
}
template<> VtValue convertFromCycles<Transform>(const Transform &value)
{
return VtValue(convertMatrixFromCycles(value));
}
template<typename SrcType, typename DstType = SrcType>
VtValue convertFromCyclesArray(const array<SrcType> &value)
{
static_assert(sizeof(DstType) == sizeof(SrcType),
"Size mismatch between VtArray and array base type");
VtArray<DstType> convertedValue;
convertedValue.resize(value.size());
std::memcpy(convertedValue.data(), value.data(), value.size() * sizeof(SrcType));
return VtValue(convertedValue);
}
template<> VtValue convertFromCyclesArray<float3, GfVec3f>(const array<float3> &value)
{
VtVec3fArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(GfVec3f(element.x, element.y, element.z));
}
return VtValue(convertedValue);
}
template<> VtValue convertFromCyclesArray<ustring, void>(const array<ustring> &value)
{
VtStringArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(element.string());
}
return VtValue(convertedValue);
}
template<> VtValue convertFromCyclesArray<Transform, void>(const array<Transform> &value)
{
VtMatrix4fArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(convertMatrixFromCycles(element));
}
return VtValue(convertedValue);
}
} // namespace
void SetNodeValue(Node *node, const SocketType &socket, const VtValue &value)
{
switch (socket.type) {
default:
case SocketType::UNDEFINED:
TF_RUNTIME_ERROR("Unexpected conversion: SocketType::UNDEFINED");
break;
case SocketType::BOOLEAN:
node->set(socket, convertToCycles<bool>(value));
break;
case SocketType::FLOAT:
node->set(socket, convertToCycles<float>(value));
break;
case SocketType::INT:
node->set(socket, convertToCycles<int>(value));
break;
case SocketType::UINT:
node->set(socket, convertToCycles<unsigned int>(value));
break;
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
node->set(socket, convertToCycles<float3>(value));
break;
case SocketType::POINT2:
node->set(socket, convertToCycles<float2>(value));
break;
case SocketType::CLOSURE:
// Handled by node connections
break;
case SocketType::STRING:
node->set(socket, convertToCycles<ustring>(value));
break;
case SocketType::ENUM:
// Enum's can accept a string or an int
if (value.IsHolding<TfToken>() || value.IsHolding<std::string>()) {
node->set(socket, convertToCycles<ustring>(value));
}
else {
node->set(socket, convertToCycles<int>(value));
}
break;
case SocketType::TRANSFORM:
node->set(socket, convertToCycles<Transform>(value));
break;
case SocketType::NODE:
// TODO: renderIndex->GetRprim()->cycles_node ?
TF_WARN("Unimplemented conversion: SocketType::NODE");
break;
case SocketType::BOOLEAN_ARRAY: {
auto cyclesArray = convertToCyclesArray<bool>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::FLOAT_ARRAY: {
auto cyclesArray = convertToCyclesArray<float>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::INT_ARRAY: {
auto cyclesArray = convertToCyclesArray<int>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY: {
auto cyclesArray = convertToCyclesArray<float3, GfVec3f>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::POINT2_ARRAY: {
auto cyclesArray = convertToCyclesArray<float2, GfVec2f>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::STRING_ARRAY: {
auto cyclesArray = convertToCyclesArray<ustring, void>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::TRANSFORM_ARRAY: {
auto cyclesArray = convertToCyclesArray<Transform, void>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::NODE_ARRAY: {
// TODO: renderIndex->GetRprim()->cycles_node ?
TF_WARN("Unimplemented conversion: SocketType::NODE_ARRAY");
break;
}
}
}
VtValue GetNodeValue(const Node *node, const SocketType &socket)
{
switch (socket.type) {
default:
case SocketType::UNDEFINED:
TF_RUNTIME_ERROR("Unexpected conversion: SocketType::UNDEFINED");
return VtValue();
case SocketType::BOOLEAN:
return convertFromCycles(node->get_bool(socket));
case SocketType::FLOAT:
return convertFromCycles(node->get_float(socket));
case SocketType::INT:
return convertFromCycles(node->get_int(socket));
case SocketType::UINT:
return convertFromCycles(node->get_uint(socket));
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
return convertFromCycles(node->get_float3(socket));
case SocketType::POINT2:
return convertFromCycles(node->get_float2(socket));
case SocketType::CLOSURE:
return VtValue();
case SocketType::STRING:
return convertFromCycles(node->get_string(socket));
case SocketType::ENUM:
return convertFromCycles(node->get_int(socket));
case SocketType::TRANSFORM:
return convertFromCycles(node->get_transform(socket));
case SocketType::NODE:
TF_WARN("Unimplemented conversion: SocketType::NODE");
return VtValue();
case SocketType::BOOLEAN_ARRAY:
return convertFromCyclesArray(node->get_bool_array(socket));
case SocketType::FLOAT_ARRAY:
return convertFromCyclesArray(node->get_float_array(socket));
case SocketType::INT_ARRAY:
return convertFromCyclesArray(node->get_int_array(socket));
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY:
return convertFromCyclesArray<float3, GfVec3f>(node->get_float3_array(socket));
case SocketType::POINT2_ARRAY:
return convertFromCyclesArray<float2, GfVec2f>(node->get_float2_array(socket));
case SocketType::STRING_ARRAY:
return convertFromCyclesArray<ustring, void>(node->get_string_array(socket));
case SocketType::TRANSFORM_ARRAY:
return convertFromCyclesArray<Transform, void>(node->get_transform_array(socket));
case SocketType::NODE_ARRAY: {
TF_WARN("Unimplemented conversion: SocketType::NODE_ARRAY");
return VtValue();
}
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,18 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "graph/node.h"
#include "hydra/config.h"
#include <pxr/base/vt/value.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
void SetNodeValue(CCL_NS::Node *node, const CCL_NS::SocketType &socket, const VtValue &value);
VtValue GetNodeValue(const CCL_NS::Node *node, const CCL_NS::SocketType &socket);
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,78 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/output_driver.h"
#include "hydra/render_buffer.h"
#include "hydra/session.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesOutputDriver::HdCyclesOutputDriver(HdCyclesSession *renderParam)
: _renderParam(renderParam)
{
}
void HdCyclesOutputDriver::write_render_tile(const Tile &tile)
{
update_render_tile(tile);
// Update convergence state of all render buffers
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
renderBuffer->SetConverged(true);
}
}
}
bool HdCyclesOutputDriver::update_render_tile(const Tile &tile)
{
std::vector<float> pixels;
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (aovBinding == _renderParam->GetDisplayAovBinding()) {
continue; // Display AOV binding is already updated by Cycles display driver
}
if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
const HdFormat format = renderBuffer->GetFormat();
if (format == HdFormatInvalid) {
continue; // Skip invalid AOV bindings
}
const size_t channels = HdGetComponentCount(format);
// Avoid extra copy by mapping render buffer directly when dimensions/format match the tile
if (tile.offset.x == 0 && tile.offset.y == 0 && tile.size.x == renderBuffer->GetWidth() &&
tile.size.y == renderBuffer->GetHeight() &&
(format >= HdFormatFloat32 && format <= HdFormatFloat32Vec4)) {
float *const data = static_cast<float *>(renderBuffer->Map());
TF_VERIFY(tile.get_pass_pixels(aovBinding.aovName.GetString(), channels, data));
renderBuffer->Unmap();
}
else {
pixels.resize(channels * tile.size.x * tile.size.y);
if (tile.get_pass_pixels(aovBinding.aovName.GetString(), channels, pixels.data())) {
const bool isId = aovBinding.aovName == HdAovTokens->primId ||
aovBinding.aovName == HdAovTokens->elementId ||
aovBinding.aovName == HdAovTokens->instanceId;
renderBuffer->WritePixels(pixels.data(),
GfVec2i(tile.offset.x, tile.offset.y),
GfVec2i(tile.size.x, tile.size.y),
channels,
isId);
}
else {
// Do not warn on missing elementId, which is a standard AOV but is not implememted
if (aovBinding.aovName != HdAovTokens->elementId) {
TF_RUNTIME_ERROR("Could not find pass for AOV '%s'", aovBinding.aovName.GetText());
}
}
}
}
}
return true;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,23 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "session/output_driver.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesOutputDriver final : public CCL_NS::OutputDriver {
public:
HdCyclesOutputDriver(HdCyclesSession *renderParam);
private:
void write_render_tile(const Tile &tile) override;
bool update_render_tile(const Tile &tile) override;
HdCyclesSession *const _renderParam;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,3 +0,0 @@
{
"Includes": [ "*/resources/" ]
}

View File

@@ -1,71 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/plugin.h"
#include "hydra/render_delegate.h"
#include "util/log.h"
#include "util/path.h"
#include <pxr/base/arch/filesystem.h>
#include <pxr/base/plug/plugin.h>
#include <pxr/base/plug/thisPlugin.h>
#include <pxr/base/tf/envSetting.h>
#include <pxr/imaging/hd/rendererPluginRegistry.h>
PXR_NAMESPACE_OPEN_SCOPE
#ifdef WITH_CYCLES_LOGGING
TF_DEFINE_ENV_SETTING(CYCLES_LOGGING, false, "Enable Cycles logging")
TF_DEFINE_ENV_SETTING(CYCLES_LOGGING_SEVERITY, 1, "Cycles logging verbosity")
#endif
HdCyclesPlugin::HdCyclesPlugin()
{
const PlugPluginPtr plugin = PLUG_THIS_PLUGIN;
// Initialize Cycles paths relative to the plugin resource path
std::string rootPath = PXR_NS::ArchAbsPath(plugin->GetResourcePath());
CCL_NS::path_init(std::move(rootPath));
#ifdef WITH_CYCLES_LOGGING
if (TfGetEnvSetting(CYCLES_LOGGING)) {
CCL_NS::util_logging_start();
CCL_NS::util_logging_verbosity_set(TfGetEnvSetting(CYCLES_LOGGING_SEVERITY));
}
#endif
}
HdCyclesPlugin::~HdCyclesPlugin()
{
}
bool HdCyclesPlugin::IsSupported() const
{
return true;
}
HdRenderDelegate *HdCyclesPlugin::CreateRenderDelegate()
{
return CreateRenderDelegate({});
}
HdRenderDelegate *HdCyclesPlugin::CreateRenderDelegate(const HdRenderSettingsMap &settingsMap)
{
return new HD_CYCLES_NS::HdCyclesDelegate(settingsMap);
}
void HdCyclesPlugin::DeleteRenderDelegate(HdRenderDelegate *renderDelegate)
{
delete renderDelegate;
}
// USD's type system accounts for namespace, so we'd have to register our name as
// HdCycles::HdCyclesPlugin in plugInfo.json, which isn't all that bad for JSON,
// but those colons may cause issues for any USD specific tooling. So just put our
// plugin class in the pxr namespace (which USD's type system will elide).
TF_REGISTRY_FUNCTION(TfType)
{
HdRendererPluginRegistry::Define<PXR_NS::HdCyclesPlugin>();
}
PXR_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,25 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/rendererPlugin.h>
PXR_NAMESPACE_OPEN_SCOPE
class HdCyclesPlugin final : public PXR_NS::HdRendererPlugin {
public:
HdCyclesPlugin();
~HdCyclesPlugin() override;
bool IsSupported() const override;
PXR_NS::HdRenderDelegate *CreateRenderDelegate() override;
PXR_NS::HdRenderDelegate *CreateRenderDelegate(const PXR_NS::HdRenderSettingsMap &) override;
void DeleteRenderDelegate(PXR_NS::HdRenderDelegate *) override;
};
PXR_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,199 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/pointcloud.h"
#include "hydra/geometry.inl"
#include "scene/pointcloud.h"
#include <pxr/imaging/hd/extComputationUtils.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesPoints::HdCyclesPoints(const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
: HdCyclesGeometry(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
)
{
}
HdCyclesPoints::~HdCyclesPoints()
{
}
HdDirtyBits HdCyclesPoints::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths |
HdChangeTracker::DirtyPrimvar;
return bits;
}
HdDirtyBits HdCyclesPoints::_PropagateDirtyBits(HdDirtyBits bits) const
{
// Points and widths always have to be updated together
if (bits & (HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths)) {
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths;
}
return bits;
}
void HdCyclesPoints::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
if (dirtyBits & (HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths)) {
const size_t numPoints = _geom->num_points();
PopulatePoints(sceneDelegate);
PopulateWidths(sceneDelegate);
rebuild = _geom->num_points() != numPoints;
array<int> shaders;
shaders.reserve(_geom->num_points());
for (size_t i = 0; i < _geom->num_points(); ++i) {
shaders.push_back_reserved(0);
}
_geom->set_shader(shaders);
}
if (dirtyBits & HdChangeTracker::DirtyPrimvar) {
PopulatePrimvars(sceneDelegate);
}
}
void HdCyclesPoints::PopulatePoints(HdSceneDelegate *sceneDelegate)
{
VtValue value;
for (const HdExtComputationPrimvarDescriptor &desc :
sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) {
if (desc.name == HdTokens->points) {
auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate);
const auto valueStoreIt = valueStore.find(desc.name);
if (valueStoreIt != valueStore.end()) {
value = std::move(valueStoreIt->second);
}
break;
}
}
if (value.IsEmpty()) {
value = GetPrimvar(sceneDelegate, HdTokens->points);
}
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid points data for %s", GetId().GetText());
return;
}
const auto &points = value.UncheckedGet<VtVec3fArray>();
array<float3> pointsDataCycles;
pointsDataCycles.reserve(points.size());
for (const GfVec3f &point : points) {
pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2]));
}
_geom->set_points(pointsDataCycles);
}
void HdCyclesPoints::PopulateWidths(HdSceneDelegate *sceneDelegate)
{
VtValue value = GetPrimvar(sceneDelegate, HdTokens->widths);
const HdInterpolation interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->widths);
if (!value.IsHolding<VtFloatArray>()) {
TF_WARN("Invalid widths data for %s", GetId().GetText());
return;
}
const auto &widths = value.UncheckedGet<VtFloatArray>();
array<float> radiusDataCycles;
radiusDataCycles.reserve(_geom->num_points());
if (interpolation == HdInterpolationConstant) {
TF_VERIFY(widths.size() == 1);
const float constantRadius = widths[0] * 0.5f;
for (size_t i = 0; i < _geom->num_points(); ++i) {
radiusDataCycles.push_back_reserved(constantRadius);
}
}
else if (interpolation == HdInterpolationVertex) {
TF_VERIFY(widths.size() == _geom->num_points());
for (size_t i = 0; i < _geom->num_points(); ++i) {
radiusDataCycles.push_back_reserved(widths[i] * 0.5f);
}
}
_geom->set_radius(radiusDataCycles);
}
void HdCyclesPoints::PopulatePrimvars(HdSceneDelegate *sceneDelegate)
{
Scene *const scene = (Scene *)_geom->get_owner();
const std::pair<HdInterpolation, AttributeElement> interpolations[] = {
std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_VERTEX),
std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT),
};
for (const auto &interpolation : interpolations) {
for (const HdPrimvarDescriptor &desc :
GetPrimvarDescriptors(sceneDelegate, interpolation.first)) {
// Skip special primvars that are handled separately
if (desc.name == HdTokens->points || desc.name == HdTokens->widths) {
continue;
}
VtValue value = GetPrimvar(sceneDelegate, desc.name);
if (value.IsEmpty()) {
continue;
}
const ustring name(desc.name.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (desc.role == HdPrimvarRoleTokens->textureCoordinate) {
std = ATTR_STD_UV;
}
else if (interpolation.first == HdInterpolationVertex) {
if (desc.name == HdTokens->displayColor || desc.role == HdPrimvarRoleTokens->color) {
std = ATTR_STD_VERTEX_COLOR;
}
else if (desc.name == HdTokens->normals) {
std = ATTR_STD_VERTEX_NORMAL;
}
}
else if (desc.name == HdTokens->displayColor &&
interpolation.first == HdInterpolationConstant) {
if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) {
const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0];
_instances[0]->set_color(make_float3(color[0], color[1], color[2]));
}
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name)) {
ApplyPrimvars(_geom->attributes, name, value, interpolation.second, std);
}
}
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,40 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/points.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesPoints final : public HdCyclesGeometry<PXR_NS::HdPoints, CCL_NS::PointCloud> {
public:
HdCyclesPoints(
const PXR_NS::SdfPath &rprimId
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId = {}
#endif
);
~HdCyclesPoints() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
private:
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateWidths(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate);
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,276 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/render_buffer.h"
#include "hydra/session.h"
#include "util/half.h"
#include <pxr/base/gf/vec3i.h>
#include <pxr/base/gf/vec4f.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesRenderBuffer::HdCyclesRenderBuffer(const SdfPath &bprimId) : HdRenderBuffer(bprimId)
{
}
HdCyclesRenderBuffer::~HdCyclesRenderBuffer()
{
}
void HdCyclesRenderBuffer::Finalize(HdRenderParam *renderParam)
{
// Remove this render buffer from AOV bindings
// This ensures that 'OutputDriver' does not attempt to write to it anymore
static_cast<HdCyclesSession *>(renderParam)->RemoveAovBinding(this);
HdRenderBuffer::Finalize(renderParam);
}
bool HdCyclesRenderBuffer::Allocate(const GfVec3i &dimensions, HdFormat format, bool multiSampled)
{
if (dimensions[2] != 1) {
TF_RUNTIME_ERROR("HdCyclesRenderBuffer::Allocate called with dimensions that are not 2D.");
return false;
}
const size_t oldSize = _data.size();
const size_t newSize = dimensions[0] * dimensions[1] * HdDataSizeOfFormat(format);
if (oldSize == newSize) {
return true;
}
if (IsMapped()) {
TF_RUNTIME_ERROR("HdCyclesRenderBuffer::Allocate called while buffer is mapped.");
return false;
}
_width = dimensions[0];
_height = dimensions[1];
_format = format;
_data.resize(newSize);
return true;
}
void HdCyclesRenderBuffer::_Deallocate()
{
_width = 0u;
_height = 0u;
_format = HdFormatInvalid;
_data.clear();
_data.shrink_to_fit();
_resource = VtValue();
}
void *HdCyclesRenderBuffer::Map()
{
// Mapping is not implemented when a resource is set
if (!_resource.IsEmpty()) {
return nullptr;
}
++_mapped;
return _data.data();
}
void HdCyclesRenderBuffer::Unmap()
{
--_mapped;
}
bool HdCyclesRenderBuffer::IsMapped() const
{
return _mapped != 0;
}
void HdCyclesRenderBuffer::Resolve()
{
}
bool HdCyclesRenderBuffer::IsConverged() const
{
return _converged;
}
void HdCyclesRenderBuffer::SetConverged(bool converged)
{
_converged = converged;
}
VtValue HdCyclesRenderBuffer::GetResource(bool multiSampled) const
{
TF_UNUSED(multiSampled);
return _resource;
}
void HdCyclesRenderBuffer::SetResource(const VtValue &resource)
{
_resource = resource;
}
namespace {
struct SimpleConversion {
static float convert(float value)
{
return value;
}
};
struct IdConversion {
static int32_t convert(float value)
{
return static_cast<int32_t>(value) - 1;
}
};
struct UInt8Conversion {
static uint8_t convert(float value)
{
return static_cast<uint8_t>(value * 255.f);
}
};
struct SInt8Conversion {
static int8_t convert(float value)
{
return static_cast<int8_t>(value * 127.f);
}
};
struct HalfConversion {
static half convert(float value)
{
return float_to_half_image(value);
}
};
template<typename SrcT, typename DstT, typename Convertor = SimpleConversion>
void writePixels(const SrcT *srcPtr,
const GfVec2i &srcSize,
int srcChannelCount,
DstT *dstPtr,
const GfVec2i &dstSize,
int dstChannelCount,
const Convertor &convertor = {})
{
const auto writeSize = GfVec2i(GfMin(srcSize[0], dstSize[0]), GfMin(srcSize[1], dstSize[1]));
const auto writeChannelCount = GfMin(srcChannelCount, dstChannelCount);
for (int y = 0; y < writeSize[1]; ++y) {
for (int x = 0; x < writeSize[0]; ++x) {
for (int c = 0; c < writeChannelCount; ++c) {
dstPtr[x * dstChannelCount + c] = convertor.convert(srcPtr[x * srcChannelCount + c]);
}
}
srcPtr += srcSize[0] * srcChannelCount;
dstPtr += dstSize[0] * dstChannelCount;
}
}
} // namespace
void HdCyclesRenderBuffer::WritePixels(const float *srcPixels,
const PXR_NS::GfVec2i &srcOffset,
const GfVec2i &srcDims,
int srcChannels,
bool isId)
{
uint8_t *dstPixels = _data.data();
const size_t formatSize = HdDataSizeOfFormat(_format);
dstPixels += srcOffset[1] * (formatSize * _width) + srcOffset[0] * formatSize;
switch (_format) {
case HdFormatUNorm8:
case HdFormatUNorm8Vec2:
case HdFormatUNorm8Vec3:
case HdFormatUNorm8Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
dstPixels,
GfVec2i(_width, _height),
1 + (_format - HdFormatUNorm8),
UInt8Conversion());
break;
case HdFormatSNorm8:
case HdFormatSNorm8Vec2:
case HdFormatSNorm8Vec3:
case HdFormatSNorm8Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
dstPixels,
GfVec2i(_width, _height),
1 + (_format - HdFormatSNorm8),
SInt8Conversion());
break;
case HdFormatFloat16:
case HdFormatFloat16Vec2:
case HdFormatFloat16Vec3:
case HdFormatFloat16Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<half *>(dstPixels),
GfVec2i(_width, _height),
1 + (_format - HdFormatFloat16),
HalfConversion());
break;
case HdFormatFloat32:
case HdFormatFloat32Vec2:
case HdFormatFloat32Vec3:
case HdFormatFloat32Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<float *>(dstPixels),
GfVec2i(_width, _height),
1 + (_format - HdFormatFloat32));
break;
case HdFormatInt32:
// Special case for ID AOVs (see 'HdCyclesMesh::Sync')
if (isId) {
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<int *>(dstPixels),
GfVec2i(_width, _height),
1,
IdConversion());
}
else {
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<int *>(dstPixels),
GfVec2i(_width, _height),
1);
}
break;
case HdFormatInt32Vec2:
case HdFormatInt32Vec3:
case HdFormatInt32Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<int *>(dstPixels),
GfVec2i(_width, _height),
1 + (_format - HdFormatInt32));
break;
default:
TF_RUNTIME_ERROR("HdCyclesRenderBuffer::WritePixels called with unsupported format.");
break;
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,85 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/renderBuffer.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesRenderBuffer final : public PXR_NS::HdRenderBuffer {
public:
HdCyclesRenderBuffer(const PXR_NS::SdfPath &bprimId);
~HdCyclesRenderBuffer() override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
bool Allocate(const PXR_NS::GfVec3i &dimensions,
PXR_NS::HdFormat format,
bool multiSampled) override;
unsigned int GetWidth() const override
{
return _width;
}
unsigned int GetHeight() const override
{
return _height;
}
unsigned int GetDepth() const override
{
return 1u;
}
PXR_NS::HdFormat GetFormat() const override
{
return _format;
}
bool IsMultiSampled() const override
{
return false;
}
void *Map() override;
void Unmap() override;
bool IsMapped() const override;
void Resolve() override;
bool IsConverged() const override;
void SetConverged(bool converged);
PXR_NS::VtValue GetResource(bool multiSampled = false) const override;
void SetResource(const PXR_NS::VtValue &resource);
void WritePixels(const float *pixels,
const PXR_NS::GfVec2i &offset,
const PXR_NS::GfVec2i &dims,
int channels,
bool isId = false);
private:
void _Deallocate() override;
unsigned int _width = 0u;
unsigned int _height = 0u;
PXR_NS::HdFormat _format = PXR_NS::HdFormatInvalid;
std::vector<uint8_t> _data;
PXR_NS::VtValue _resource;
std::atomic_int _mapped = 0;
std::atomic_bool _converged = false;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,514 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/render_delegate.h"
#include "hydra/camera.h"
#include "hydra/curves.h"
#include "hydra/field.h"
#include "hydra/instancer.h"
#include "hydra/light.h"
#include "hydra/material.h"
#include "hydra/mesh.h"
#include "hydra/node_util.h"
#include "hydra/pointcloud.h"
#include "hydra/render_buffer.h"
#include "hydra/render_pass.h"
#include "hydra/session.h"
#include "hydra/volume.h"
#include "scene/integrator.h"
#include "scene/scene.h"
#include "session/session.h"
#include <pxr/base/tf/getenv.h>
#include <pxr/imaging/hd/extComputation.h>
#include <pxr/imaging/hgi/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(cycles)
(openvdbAsset)
);
TF_DEFINE_PRIVATE_TOKENS(HdCyclesRenderSettingsTokens,
((device, "cycles:device"))
((threads, "cycles:threads"))
((time_limit, "cycles:time_limit"))
((samples, "cycles:samples"))
((sample_offset, "cycles:sample_offset"))
);
// clang-format on
namespace {
const TfTokenVector kSupportedRPrimTypes = {
HdPrimTypeTokens->basisCurves,
HdPrimTypeTokens->mesh,
HdPrimTypeTokens->points,
#ifdef WITH_OPENVDB
HdPrimTypeTokens->volume,
#endif
};
const TfTokenVector kSupportedSPrimTypes = {
HdPrimTypeTokens->camera,
HdPrimTypeTokens->material,
HdPrimTypeTokens->diskLight,
HdPrimTypeTokens->distantLight,
HdPrimTypeTokens->domeLight,
HdPrimTypeTokens->rectLight,
HdPrimTypeTokens->sphereLight,
HdPrimTypeTokens->extComputation,
};
const TfTokenVector kSupportedBPrimTypes = {
HdPrimTypeTokens->renderBuffer,
#ifdef WITH_OPENVDB
_tokens->openvdbAsset,
#endif
};
SessionParams GetSessionParams(const HdRenderSettingsMap &settings)
{
SessionParams params;
params.threads = 0;
params.background = false;
params.use_resolution_divider = false;
HdRenderSettingsMap::const_iterator it;
// Pull all setting that contribute to device creation first
it = settings.find(HdCyclesRenderSettingsTokens->threads);
if (it != settings.end()) {
params.threads = VtValue::Cast<int>(it->second).GetWithDefault(params.threads);
}
// Get the Cycles device from settings or environment, falling back to CPU
std::string deviceType = Device::string_from_type(DEVICE_CPU);
it = settings.find(HdCyclesRenderSettingsTokens->device);
if (it != settings.end()) {
deviceType = VtValue::Cast<std::string>(it->second).GetWithDefault(deviceType);
}
else {
const std::string deviceTypeEnv = TfGetenv("CYCLES_DEVICE");
if (!deviceTypeEnv.empty()) {
deviceType = deviceTypeEnv;
}
}
// Move to all uppercase for Device::type_from_string
std::transform(deviceType.begin(), deviceType.end(), deviceType.begin(), ::toupper);
vector<DeviceInfo> devices = Device::available_devices(
DEVICE_MASK(Device::type_from_string(deviceType.c_str())));
if (devices.empty()) {
devices = Device::available_devices(DEVICE_MASK_CPU);
if (!devices.empty()) {
params.device = devices.front();
}
}
else {
params.device = Device::get_multi_device(devices, params.threads, params.background);
}
return params;
}
} // namespace
HdCyclesDelegate::HdCyclesDelegate(const HdRenderSettingsMap &settingsMap, Session *session_)
: HdRenderDelegate()
{
_renderParam = session_ ? std::make_unique<HdCyclesSession>(session_) :
std::make_unique<HdCyclesSession>(GetSessionParams(settingsMap));
// If the delegate owns the session, pull any remaining settings
if (!session_) {
for (const auto &setting : settingsMap) {
// Skip over the settings known to be used for initialization only
if (setting.first == HdCyclesRenderSettingsTokens->device ||
setting.first == HdCyclesRenderSettingsTokens->threads) {
continue;
}
SetRenderSetting(setting.first, setting.second);
}
}
}
HdCyclesDelegate::~HdCyclesDelegate()
{
}
void HdCyclesDelegate::SetDrivers(const HdDriverVector &drivers)
{
for (HdDriver *hdDriver : drivers) {
if (hdDriver->name == HgiTokens->renderDriver && hdDriver->driver.IsHolding<Hgi *>()) {
_hgi = hdDriver->driver.UncheckedGet<Hgi *>();
break;
}
}
}
bool HdCyclesDelegate::IsDisplaySupported() const
{
#ifdef _WIN32
return _hgi && _hgi->GetAPIName() == HgiTokens->OpenGL;
#else
return false;
#endif
}
const TfTokenVector &HdCyclesDelegate::GetSupportedRprimTypes() const
{
return kSupportedRPrimTypes;
}
const TfTokenVector &HdCyclesDelegate::GetSupportedSprimTypes() const
{
return kSupportedSPrimTypes;
}
const TfTokenVector &HdCyclesDelegate::GetSupportedBprimTypes() const
{
return kSupportedBPrimTypes;
}
HdRenderParam *HdCyclesDelegate::GetRenderParam() const
{
return _renderParam.get();
}
HdResourceRegistrySharedPtr HdCyclesDelegate::GetResourceRegistry() const
{
return HdResourceRegistrySharedPtr();
}
bool HdCyclesDelegate::IsPauseSupported() const
{
return true;
}
bool HdCyclesDelegate::Pause()
{
_renderParam->session->set_pause(true);
return true;
}
bool HdCyclesDelegate::Resume()
{
_renderParam->session->set_pause(false);
return true;
}
HdRenderPassSharedPtr HdCyclesDelegate::CreateRenderPass(HdRenderIndex *index,
const HdRprimCollection &collection)
{
return HdRenderPassSharedPtr(new HdCyclesRenderPass(index, collection, _renderParam.get()));
}
HdInstancer *HdCyclesDelegate::CreateInstancer(HdSceneDelegate *delegate,
const SdfPath &instancerId
#if PXR_VERSION < 2102
,
const SdfPath &parentId
#endif
)
{
return new HdCyclesInstancer(delegate,
instancerId
#if PXR_VERSION < 2102
,
parentId
#endif
);
}
void HdCyclesDelegate::DestroyInstancer(HdInstancer *instancer)
{
delete instancer;
}
HdRprim *HdCyclesDelegate::CreateRprim(const TfToken &typeId,
const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
{
if (typeId == HdPrimTypeTokens->mesh) {
return new HdCyclesMesh(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
);
}
if (typeId == HdPrimTypeTokens->basisCurves) {
return new HdCyclesCurves(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
);
}
if (typeId == HdPrimTypeTokens->points) {
return new HdCyclesPoints(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
);
}
#ifdef WITH_OPENVDB
if (typeId == HdPrimTypeTokens->volume) {
return new HdCyclesVolume(rprimId
# if PXR_VERSION < 2102
,
instancerId
# endif
);
}
#endif
TF_CODING_ERROR("Unknown Rprim type %s", typeId.GetText());
return nullptr;
}
void HdCyclesDelegate::DestroyRprim(HdRprim *rPrim)
{
delete rPrim;
}
HdSprim *HdCyclesDelegate::CreateSprim(const TfToken &typeId, const SdfPath &sprimId)
{
if (typeId == HdPrimTypeTokens->camera) {
return new HdCyclesCamera(sprimId);
}
if (typeId == HdPrimTypeTokens->material) {
return new HdCyclesMaterial(sprimId);
}
if (typeId == HdPrimTypeTokens->diskLight || typeId == HdPrimTypeTokens->distantLight ||
typeId == HdPrimTypeTokens->domeLight || typeId == HdPrimTypeTokens->rectLight ||
typeId == HdPrimTypeTokens->sphereLight) {
return new HdCyclesLight(sprimId, typeId);
}
if (typeId == HdPrimTypeTokens->extComputation) {
return new HdExtComputation(sprimId);
}
TF_CODING_ERROR("Unknown Sprim type %s", typeId.GetText());
return nullptr;
}
HdSprim *HdCyclesDelegate::CreateFallbackSprim(const TfToken &typeId)
{
return CreateSprim(typeId, SdfPath::EmptyPath());
}
void HdCyclesDelegate::DestroySprim(HdSprim *sPrim)
{
delete sPrim;
}
HdBprim *HdCyclesDelegate::CreateBprim(const TfToken &typeId, const SdfPath &bprimId)
{
if (typeId == HdPrimTypeTokens->renderBuffer) {
return new HdCyclesRenderBuffer(bprimId);
}
#ifdef WITH_OPENVDB
if (typeId == _tokens->openvdbAsset) {
return new HdCyclesField(bprimId, typeId);
}
#endif
TF_RUNTIME_ERROR("Unknown Bprim type %s", typeId.GetText());
return nullptr;
}
HdBprim *HdCyclesDelegate::CreateFallbackBprim(const TfToken &typeId)
{
return CreateBprim(typeId, SdfPath::EmptyPath());
}
void HdCyclesDelegate::DestroyBprim(HdBprim *bPrim)
{
delete bPrim;
}
void HdCyclesDelegate::CommitResources(HdChangeTracker *tracker)
{
TF_UNUSED(tracker);
const SceneLock lock(_renderParam.get());
_renderParam->UpdateScene();
}
TfToken HdCyclesDelegate::GetMaterialBindingPurpose() const
{
return HdTokens->full;
}
#if HD_API_VERSION < 41
TfToken HdCyclesDelegate::GetMaterialNetworkSelector() const
{
return _tokens->cycles;
}
#else
TfTokenVector HdCyclesDelegate::GetMaterialRenderContexts() const
{
return {_tokens->cycles};
}
#endif
VtDictionary HdCyclesDelegate::GetRenderStats() const
{
const Stats &stats = _renderParam->session->stats;
const Progress &progress = _renderParam->session->progress;
double totalTime, renderTime;
progress.get_time(totalTime, renderTime);
double fractionDone = progress.get_progress();
std::string status, substatus;
progress.get_status(status, substatus);
if (!substatus.empty()) {
status += " | " + substatus;
}
return {{"rendererName", VtValue("Cycles")},
{"rendererVersion", VtValue(GfVec3i(0, 0, 0))},
{"percentDone", VtValue(floor_to_int(fractionDone * 100))},
{"fractionDone", VtValue(fractionDone)},
{"loadClockTime", VtValue(totalTime - renderTime)},
{"peakMemory", VtValue(stats.mem_peak)},
{"totalClockTime", VtValue(totalTime)},
{"totalMemory", VtValue(stats.mem_used)},
{"renderProgressAnnotation", VtValue(status)}};
}
HdAovDescriptor HdCyclesDelegate::GetDefaultAovDescriptor(const TfToken &name) const
{
if (name == HdAovTokens->color) {
HdFormat colorFormat = HdFormatFloat32Vec4;
if (IsDisplaySupported()) {
// Can use Cycles 'DisplayDriver' in OpenGL, but it only supports 'half4' format
colorFormat = HdFormatFloat16Vec4;
}
return HdAovDescriptor(colorFormat, false, VtValue(GfVec4f(0.0f)));
}
if (name == HdAovTokens->depth) {
return HdAovDescriptor(HdFormatFloat32, false, VtValue(1.0f));
}
if (name == HdAovTokens->normal) {
return HdAovDescriptor(HdFormatFloat32Vec3, false, VtValue(GfVec3f(0.0f)));
}
if (name == HdAovTokens->primId || name == HdAovTokens->instanceId ||
name == HdAovTokens->elementId) {
return HdAovDescriptor(HdFormatInt32, false, VtValue(-1));
}
return HdAovDescriptor();
}
HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() const
{
Scene *const scene = _renderParam->session->scene;
HdRenderSettingDescriptorList descriptors;
descriptors.push_back({
"Time Limit",
HdCyclesRenderSettingsTokens->time_limit,
VtValue(0.0),
});
descriptors.push_back({
"Sample Count",
HdCyclesRenderSettingsTokens->samples,
VtValue(1024),
});
descriptors.push_back({
"Sample Offset",
HdCyclesRenderSettingsTokens->sample_offset,
VtValue(0),
});
for (const SocketType &socket : scene->integrator->type->inputs) {
descriptors.push_back({socket.ui_name.string(),
TfToken("cycles:integrator:" + socket.name.string()),
GetNodeValue(scene->integrator, socket)});
}
return descriptors;
}
void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value)
{
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
if (key == HdCyclesRenderSettingsTokens->time_limit) {
session->set_time_limit(
VtValue::Cast<double>(value).GetWithDefault(session->params.time_limit));
}
else if (key == HdCyclesRenderSettingsTokens->samples) {
int samples = VtValue::Cast<int>(value).GetWithDefault(session->params.samples);
samples = std::min(std::max(1, samples), Integrator::MAX_SAMPLES);
session->set_samples(samples);
}
else if (key == HdCyclesRenderSettingsTokens->sample_offset) {
session->params.sample_offset = VtValue::Cast<int>(value).GetWithDefault(
session->params.sample_offset);
++_settingsVersion;
}
else {
const std::string &keyString = key.GetString();
if (keyString.rfind("cycles:integrator:", 0) == 0) {
ustring socketName(keyString, sizeof("cycles:integrator:") - 1);
if (const SocketType *socket = scene->integrator->type->find_input(socketName)) {
SetNodeValue(scene->integrator, *socket, value);
++_settingsVersion;
}
}
}
}
VtValue HdCyclesDelegate::GetRenderSetting(const TfToken &key) const
{
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
if (key == HdCyclesRenderSettingsTokens->device) {
return VtValue(TfToken(Device::string_from_type(session->params.device.type)));
}
else if (key == HdCyclesRenderSettingsTokens->threads) {
return VtValue(session->params.threads);
}
else if (key == HdCyclesRenderSettingsTokens->time_limit) {
return VtValue(session->params.time_limit);
}
else if (key == HdCyclesRenderSettingsTokens->samples) {
return VtValue(session->params.samples);
}
else if (key == HdCyclesRenderSettingsTokens->sample_offset) {
return VtValue(session->params.sample_offset);
}
else {
const std::string &keyString = key.GetString();
if (keyString.rfind("cycles:integrator:", 0) == 0) {
ustring socketName(keyString, sizeof("cycles:integrator:") - 1);
if (const SocketType *socket = scene->integrator->type->find_input(socketName)) {
return GetNodeValue(scene->integrator, *socket);
}
}
}
return VtValue();
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,98 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/renderDelegate.h>
#include <pxr/imaging/hgi/hgi.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesDelegate final : public PXR_NS::HdRenderDelegate {
public:
HdCyclesDelegate(const PXR_NS::HdRenderSettingsMap &settingsMap,
CCL_NS::Session *session_ = nullptr);
~HdCyclesDelegate() override;
void SetDrivers(const PXR_NS::HdDriverVector &drivers) override;
bool IsDisplaySupported() const;
PXR_NS::Hgi *GetHgi() const
{
return _hgi;
}
const PXR_NS::TfTokenVector &GetSupportedRprimTypes() const override;
const PXR_NS::TfTokenVector &GetSupportedSprimTypes() const override;
const PXR_NS::TfTokenVector &GetSupportedBprimTypes() const override;
PXR_NS::HdRenderParam *GetRenderParam() const override;
PXR_NS::HdResourceRegistrySharedPtr GetResourceRegistry() const override;
PXR_NS::HdRenderSettingDescriptorList GetRenderSettingDescriptors() const override;
bool IsPauseSupported() const override;
bool Pause() override;
bool Resume() override;
PXR_NS::HdRenderPassSharedPtr CreateRenderPass(
PXR_NS::HdRenderIndex *index, const PXR_NS::HdRprimCollection &collection) override;
PXR_NS::HdInstancer *CreateInstancer(PXR_NS::HdSceneDelegate *delegate,
const PXR_NS::SdfPath &id
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId
#endif
) override;
void DestroyInstancer(PXR_NS::HdInstancer *instancer) override;
PXR_NS::HdRprim *CreateRprim(const PXR_NS::TfToken &typeId,
const PXR_NS::SdfPath &rprimId
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId
#endif
) override;
void DestroyRprim(PXR_NS::HdRprim *rPrim) override;
PXR_NS::HdSprim *CreateSprim(const PXR_NS::TfToken &typeId,
const PXR_NS::SdfPath &sprimId) override;
PXR_NS::HdSprim *CreateFallbackSprim(const PXR_NS::TfToken &typeId) override;
void DestroySprim(PXR_NS::HdSprim *sPrim) override;
PXR_NS::HdBprim *CreateBprim(const PXR_NS::TfToken &typeId,
const PXR_NS::SdfPath &bprimId) override;
PXR_NS::HdBprim *CreateFallbackBprim(const PXR_NS::TfToken &typeId) override;
void DestroyBprim(PXR_NS::HdBprim *bPrim) override;
void CommitResources(PXR_NS::HdChangeTracker *tracker) override;
PXR_NS::TfToken GetMaterialBindingPurpose() const override;
#if HD_API_VERSION < 41
PXR_NS::TfToken GetMaterialNetworkSelector() const override;
#else
PXR_NS::TfTokenVector GetMaterialRenderContexts() const override;
#endif
PXR_NS::VtDictionary GetRenderStats() const override;
PXR_NS::HdAovDescriptor GetDefaultAovDescriptor(const PXR_NS::TfToken &name) const override;
void SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value) override;
PXR_NS::VtValue GetRenderSetting(const PXR_NS::TfToken &key) const override;
private:
PXR_NS::Hgi *_hgi = nullptr;
std::unique_ptr<HdCyclesSession> _renderParam;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,175 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/render_pass.h"
#include "hydra/camera.h"
#include "hydra/display_driver.h"
#include "hydra/output_driver.h"
#include "hydra/render_buffer.h"
#include "hydra/render_delegate.h"
#include "hydra/session.h"
#include "scene/camera.h"
#include "scene/integrator.h"
#include "scene/scene.h"
#include "session/session.h"
#include <pxr/imaging/hd/renderPassState.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesRenderPass::HdCyclesRenderPass(HdRenderIndex *index,
HdRprimCollection const &collection,
HdCyclesSession *renderParam)
: HdRenderPass(index, collection), _renderParam(renderParam)
{
Session *const session = _renderParam->session;
// Reset cancel state so session thread can continue rendering
session->progress.reset();
session->set_output_driver(make_unique<HdCyclesOutputDriver>(renderParam));
const auto renderDelegate = static_cast<const HdCyclesDelegate *>(
GetRenderIndex()->GetRenderDelegate());
if (renderDelegate->IsDisplaySupported()) {
session->set_display_driver(
make_unique<HdCyclesDisplayDriver>(renderParam, renderDelegate->GetHgi()));
}
}
HdCyclesRenderPass::~HdCyclesRenderPass()
{
Session *const session = _renderParam->session;
session->cancel(true);
}
bool HdCyclesRenderPass::IsConverged() const
{
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (aovBinding.renderBuffer && !aovBinding.renderBuffer->IsConverged()) {
return false;
}
}
return true;
}
void HdCyclesRenderPass::ResetConverged()
{
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (const auto renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
renderBuffer->SetConverged(false);
}
}
}
void HdCyclesRenderPass::_Execute(const HdRenderPassStateSharedPtr &renderPassState,
const TfTokenVector &renderTags)
{
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
if (session->progress.get_cancel()) {
return; // Something went wrong and cannot continue without recreating the session
}
if (scene->mutex.try_lock()) {
const auto renderDelegate = static_cast<HdCyclesDelegate *>(
GetRenderIndex()->GetRenderDelegate());
const unsigned int settingsVersion = renderDelegate->GetRenderSettingsVersion();
// Update requested AOV bindings
const HdRenderPassAovBindingVector &aovBindings = renderPassState->GetAovBindings();
if (_renderParam->GetAovBindings() != aovBindings ||
// Need to resync passes when denoising is enabled or disabled to update the pass mode
(settingsVersion != _lastSettingsVersion &&
scene->integrator->use_denoise_is_modified())) {
_renderParam->SyncAovBindings(aovBindings);
if (renderDelegate->IsDisplaySupported()) {
// Update display pass to the first requested color AOV
HdRenderPassAovBinding displayAovBinding = !aovBindings.empty() ? aovBindings.front() :
HdRenderPassAovBinding();
if (displayAovBinding.aovName == HdAovTokens->color && displayAovBinding.renderBuffer) {
_renderParam->SetDisplayAovBinding(displayAovBinding);
}
else {
_renderParam->SetDisplayAovBinding(HdRenderPassAovBinding());
}
}
}
// Update camera dimensions to the viewport size
#if PXR_VERSION >= 2102
CameraUtilFraming framing = renderPassState->GetFraming();
if (!framing.IsValid()) {
const GfVec4f vp = renderPassState->GetViewport();
framing = CameraUtilFraming(GfRect2i(GfVec2i(0), int(vp[2]), int(vp[3])));
}
scene->camera->set_full_width(framing.dataWindow.GetWidth());
scene->camera->set_full_height(framing.dataWindow.GetHeight());
#else
const GfVec4f vp = renderPassState->GetViewport();
scene->camera->set_full_width(int(vp[2]));
scene->camera->set_full_height(int(vp[3]));
#endif
if (const auto camera = static_cast<const HdCyclesCamera *>(renderPassState->GetCamera())) {
camera->ApplyCameraSettings(scene->camera);
}
else {
HdCyclesCamera::ApplyCameraSettings(renderPassState->GetWorldToViewMatrix(),
renderPassState->GetProjectionMatrix(),
renderPassState->GetClipPlanes(),
scene->camera);
}
// Reset session if the session, scene, camera or AOV bindings changed
if (scene->need_reset() || settingsVersion != _lastSettingsVersion) {
_lastSettingsVersion = settingsVersion;
// Reset convergence state of all render buffers
ResetConverged();
BufferParams buffer_params;
#if PXR_VERSION >= 2102
buffer_params.full_x = static_cast<int>(framing.displayWindow.GetMin()[0]);
buffer_params.full_y = static_cast<int>(framing.displayWindow.GetMin()[1]);
buffer_params.full_width = static_cast<int>(framing.displayWindow.GetSize()[0]);
buffer_params.full_height = static_cast<int>(framing.displayWindow.GetSize()[1]);
buffer_params.window_x = framing.dataWindow.GetMinX() - buffer_params.full_x;
buffer_params.window_y = framing.dataWindow.GetMinY() - buffer_params.full_y;
buffer_params.window_width = framing.dataWindow.GetWidth();
buffer_params.window_height = framing.dataWindow.GetHeight();
buffer_params.width = buffer_params.window_width;
buffer_params.height = buffer_params.window_height;
#else
buffer_params.width = static_cast<int>(vp[2]);
buffer_params.height = static_cast<int>(vp[3]);
buffer_params.full_width = buffer_params.width;
buffer_params.full_height = buffer_params.height;
buffer_params.window_width = buffer_params.width;
buffer_params.window_height = buffer_params.height;
#endif
session->reset(session->params, buffer_params);
}
scene->mutex.unlock();
// Start Cycles render thread if not already running
session->start();
}
session->draw();
}
void HdCyclesRenderPass::_MarkCollectionDirty()
{
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,34 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/renderPass.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesRenderPass final : public PXR_NS::HdRenderPass {
public:
HdCyclesRenderPass(PXR_NS::HdRenderIndex *index,
const PXR_NS::HdRprimCollection &collection,
HdCyclesSession *renderParam);
~HdCyclesRenderPass() override;
bool IsConverged() const override;
private:
void ResetConverged();
void _Execute(const PXR_NS::HdRenderPassStateSharedPtr &renderPassState,
const PXR_NS::TfTokenVector &renderTags) override;
void _MarkCollectionDirty() override;
HdCyclesSession *_renderParam;
unsigned int _lastSettingsVersion = 0;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,22 +0,0 @@
{
"Plugins": [
{
"Info": {
"Types": {
"HdCyclesPlugin": {
"bases": [
"HdRendererPlugin"
],
"displayName": "Cycles",
"priority": 0
}
}
},
"LibraryPath": "@PLUG_INFO_LIBRARY_PATH@",
"Name": "hdCycles",
"ResourcePath": "@PLUG_INFO_RESOURCE_PATH@",
"Root": "@PLUG_INFO_ROOT@",
"Type": "library"
}
]
}

View File

@@ -1,170 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/session.h"
#include "scene/shader.h"
// Have to include shader.h before background.h so that 'set_shader' uses the correct 'set'
// overload taking a 'Node *', rather than the one taking a 'bool'
#include "scene/background.h"
#include "scene/light.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include "session/session.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
namespace {
const std::unordered_map<TfToken, PassType, TfToken::HashFunctor> kAovToPass = {
{HdAovTokens->color, PASS_COMBINED},
{HdAovTokens->depth, PASS_DEPTH},
{HdAovTokens->normal, PASS_NORMAL},
{HdAovTokens->primId, PASS_OBJECT_ID},
{HdAovTokens->instanceId, PASS_AOV_VALUE},
};
} // namespace
SceneLock::SceneLock(const HdRenderParam *renderParam)
: scene(static_cast<const HdCyclesSession *>(renderParam)->session->scene),
sceneLock(scene->mutex)
{
}
SceneLock::~SceneLock()
{
}
HdCyclesSession::HdCyclesSession(Session *session_) : session(session_), _ownCyclesSession(false)
{
}
HdCyclesSession::HdCyclesSession(const SessionParams &params)
: session(new Session(params, SceneParams())), _ownCyclesSession(true)
{
Scene *const scene = session->scene;
// Create background with ambient light
{
ShaderGraph *graph = new ShaderGraph();
BackgroundNode *bgNode = graph->create_node<BackgroundNode>();
bgNode->set_color(one_float3());
graph->add(bgNode);
graph->connect(bgNode->output("Background"), graph->output()->input("Surface"));
scene->default_background->set_graph(graph);
scene->default_background->tag_update(scene);
}
// Wire up object color in default surface material
{
ShaderGraph *graph = new ShaderGraph();
ObjectInfoNode *objectNode = graph->create_node<ObjectInfoNode>();
graph->add(objectNode);
DiffuseBsdfNode *diffuseNode = graph->create_node<DiffuseBsdfNode>();
graph->add(diffuseNode);
graph->connect(objectNode->output("Color"), diffuseNode->input("Color"));
graph->connect(diffuseNode->output("BSDF"), graph->output()->input("Surface"));
#if 1
// Create the instanceId AOV output
const ustring instanceId(HdAovTokens->instanceId.GetString());
OutputAOVNode *aovNode = graph->create_node<OutputAOVNode>();
aovNode->set_name(instanceId);
graph->add(aovNode);
AttributeNode *instanceIdNode = graph->create_node<AttributeNode>();
instanceIdNode->set_attribute(instanceId);
graph->add(instanceIdNode);
graph->connect(instanceIdNode->output("Fac"), aovNode->input("Value"));
#endif
scene->default_surface->set_graph(graph);
scene->default_surface->tag_update(scene);
}
}
HdCyclesSession::~HdCyclesSession()
{
if (_ownCyclesSession) {
delete session;
}
}
void HdCyclesSession::UpdateScene()
{
Scene *const scene = session->scene;
// Update background depending on presence of a background light
if (scene->light_manager->need_update()) {
Light *background_light = nullptr;
for (Light *light : scene->lights) {
if (light->get_light_type() == LIGHT_BACKGROUND) {
background_light = light;
break;
}
}
if (!background_light) {
scene->background->set_shader(scene->default_background);
scene->background->set_transparent(true);
}
else {
scene->background->set_shader(background_light->get_shader());
scene->background->set_transparent(false);
}
scene->background->tag_update(scene);
}
}
void HdCyclesSession::SyncAovBindings(const HdRenderPassAovBindingVector &aovBindings)
{
Scene *const scene = session->scene;
// Delete all existing passes
scene->delete_nodes(set<Pass *>(scene->passes.begin(), scene->passes.end()));
// Update passes with requested AOV bindings
_aovBindings = aovBindings;
for (const HdRenderPassAovBinding &aovBinding : aovBindings) {
const auto cyclesAov = kAovToPass.find(aovBinding.aovName);
if (cyclesAov == kAovToPass.end()) {
// TODO: Use PASS_AOV_COLOR and PASS_AOV_VALUE for these?
TF_WARN("Unknown pass %s", aovBinding.aovName.GetText());
continue;
}
const PassType type = cyclesAov->second;
const PassMode mode = PassMode::DENOISED;
Pass *pass = scene->create_node<Pass>();
pass->set_type(type);
pass->set_mode(mode);
pass->set_name(ustring(aovBinding.aovName.GetString()));
}
}
void HdCyclesSession::RemoveAovBinding(HdRenderBuffer *renderBuffer)
{
for (HdRenderPassAovBinding &aovBinding : _aovBindings) {
if (renderBuffer == aovBinding.renderBuffer) {
aovBinding.renderBuffer = nullptr;
break;
}
}
if (renderBuffer == _displayAovBinding.renderBuffer) {
_displayAovBinding.renderBuffer = nullptr;
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,59 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "util/thread.h"
#include <pxr/imaging/hd/renderDelegate.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
struct SceneLock {
SceneLock(const PXR_NS::HdRenderParam *renderParam);
~SceneLock();
CCL_NS::Scene *scene;
private:
CCL_NS::thread_scoped_lock sceneLock;
};
class HdCyclesSession final : public PXR_NS::HdRenderParam {
public:
HdCyclesSession(CCL_NS::Session *session_);
HdCyclesSession(const CCL_NS::SessionParams &params);
~HdCyclesSession() override;
void UpdateScene();
PXR_NS::HdRenderPassAovBinding GetDisplayAovBinding() const
{
return _displayAovBinding;
}
void SetDisplayAovBinding(const PXR_NS::HdRenderPassAovBinding &aovBinding)
{
_displayAovBinding = aovBinding;
}
const PXR_NS::HdRenderPassAovBindingVector &GetAovBindings() const
{
return _aovBindings;
}
void SyncAovBindings(const PXR_NS::HdRenderPassAovBindingVector &aovBindings);
void RemoveAovBinding(PXR_NS::HdRenderBuffer *renderBuffer);
CCL_NS::Session *session;
private:
const bool _ownCyclesSession;
PXR_NS::HdRenderPassAovBindingVector _aovBindings;
PXR_NS::HdRenderPassAovBinding _displayAovBinding;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,91 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#include "hydra/volume.h"
#include "hydra/field.h"
#include "hydra/geometry.inl"
#include "scene/volume.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(openvdbAsset)
);
// clang-format on
HdCyclesVolume::HdCyclesVolume(const SdfPath &rprimId
#if PXR_VERSION < 2102
,
const SdfPath &instancerId
#endif
)
: HdCyclesGeometry(rprimId
#if PXR_VERSION < 2102
,
instancerId
#endif
)
{
}
HdCyclesVolume::~HdCyclesVolume()
{
}
HdDirtyBits HdCyclesVolume::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyVolumeField;
return bits;
}
void HdCyclesVolume::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
Scene *const scene = (Scene *)_geom->get_owner();
if (dirtyBits & HdChangeTracker::DirtyVolumeField) {
for (const HdVolumeFieldDescriptor &field :
sceneDelegate->GetVolumeFieldDescriptors(GetId())) {
if (const auto openvdbAsset = static_cast<HdCyclesField *>(
sceneDelegate->GetRenderIndex().GetBprim(_tokens->openvdbAsset, field.fieldId))) {
const ustring name(field.fieldName.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
std = ATTR_STD_VOLUME_DENSITY;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
std = ATTR_STD_VOLUME_COLOR;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
std = ATTR_STD_VOLUME_FLAME;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
std = ATTR_STD_VOLUME_HEAT;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
std = ATTR_STD_VOLUME_TEMPERATURE;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) {
std = ATTR_STD_VOLUME_VELOCITY;
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name)) {
Attribute *const attr = (std != ATTR_STD_NONE) ?
_geom->attributes.add(std) :
_geom->attributes.add(
name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
attr->data_voxel() = openvdbAsset->GetImageHandle();
}
}
}
rebuild = true;
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -1,33 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2022 NVIDIA Corporation
* Copyright 2022 Blender Foundation */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/volume.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesVolume final : public HdCyclesGeometry<PXR_NS::HdVolume, CCL_NS::Volume> {
public:
HdCyclesVolume(
const PXR_NS::SdfPath &rprimId
#if PXR_VERSION < 2102
,
const PXR_NS::SdfPath &instancerId = {}
#endif
);
~HdCyclesVolume() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
private:
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -80,8 +80,7 @@ static bool is_single_supported_device(Device *device, DenoiserType type)
if (!device->info.multi_devices.empty()) {
/* Some configurations will use multi_devices, but keep the type of an individual device.
* This does simplify checks for homogeneous setups, but here we really need a single device.
*/
* This does simplify checks for homogenous setups, but here we really need a single device. */
return false;
}

View File

@@ -18,11 +18,7 @@ CCL_NAMESPACE_BEGIN
*/
PassAccessor::PassAccessInfo::PassAccessInfo(const BufferPass &pass)
: type(pass.type),
mode(pass.mode),
include_albedo(pass.include_albedo),
is_lightgroup(!pass.lightgroup.empty()),
offset(pass.offset)
: type(pass.type), mode(pass.mode), include_albedo(pass.include_albedo), offset(pass.offset)
{
}
@@ -131,8 +127,7 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
const PassType type = pass_access_info_.type;
const PassMode mode = pass_access_info_.mode;
const PassInfo pass_info = Pass::get_info(
type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup);
const PassInfo pass_info = Pass::get_info(type, pass_access_info_.include_albedo);
int num_written_components = pass_info.num_components;
if (pass_info.num_components == 1) {
@@ -220,8 +215,8 @@ void PassAccessor::init_kernel_film_convert(KernelFilmConvert *kfilm_convert,
const Destination &destination) const
{
const PassMode mode = pass_access_info_.mode;
const PassInfo &pass_info = Pass::get_info(
pass_access_info_.type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup);
const PassInfo &pass_info = Pass::get_info(pass_access_info_.type,
pass_access_info_.include_albedo);
kfilm_convert->pass_offset = pass_access_info_.offset;
kfilm_convert->pass_stride = buffer_params.pass_stride;
@@ -284,8 +279,8 @@ bool PassAccessor::set_render_tile_pixels(RenderBuffers *render_buffers, const S
return false;
}
const PassInfo pass_info = Pass::get_info(
pass_access_info_.type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup);
const PassInfo pass_info = Pass::get_info(pass_access_info_.type,
pass_access_info_.include_albedo);
const BufferParams &buffer_params = render_buffers->params;

View File

@@ -28,7 +28,6 @@ class PassAccessor {
PassType type = PASS_NONE;
PassMode mode = PassMode::NOISY;
bool include_albedo = false;
bool is_lightgroup = false;
int offset = -1;
/* For the shadow catcher matte pass: whether to approximate shadow catcher pass into its

View File

@@ -466,7 +466,7 @@ void PathTrace::set_denoiser_params(const DenoiseParams &params)
denoiser_ = Denoiser::create(device_, params);
/* Only take into account the "immediate" cancel to have interactive rendering responding to
* navigation as quickly as possible, but allow to run denoiser after user hit Escape key while
* navigation as quickly as possible, but allow to run denoiser after user hit Esc button while
* doing offline rendering. */
denoiser_->is_cancelled_cb = [this]() { return render_cancel_.is_requested; };
}

View File

@@ -20,7 +20,7 @@ RenderScheduler::RenderScheduler(TileManager &tile_manager, const SessionParams
background_(params.background),
pixel_size_(params.pixel_size),
tile_manager_(tile_manager),
default_start_resolution_divider_(params.use_resolution_divider ? pixel_size_ * 8 : 0)
default_start_resolution_divider_(pixel_size_ * 8)
{
use_progressive_noise_floor_ = !background_;
}
@@ -119,7 +119,7 @@ void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples,
/* In background mode never do lower resolution render preview, as it is not really supported
* by the software. */
if (background_ || start_resolution_divider_ == 0) {
if (background_) {
state_.resolution_divider = 1;
}
else {
@@ -1050,10 +1050,6 @@ bool RenderScheduler::work_need_rebalance()
void RenderScheduler::update_start_resolution_divider()
{
if (default_start_resolution_divider_ == 0) {
return;
}
if (start_resolution_divider_ == 0) {
/* Resolution divider has never been calculated before: use default resolution, so that we have
* somewhat good initial behavior, giving a chance to collect real numbers. */

View File

@@ -50,6 +50,7 @@ set(SRC_KERNEL_DEVICE_GPU_HEADERS
device/gpu/kernel.h
device/gpu/parallel_active_index.h
device/gpu/parallel_prefix_sum.h
device/gpu/parallel_reduce.h
device/gpu/parallel_sorted_index.h
device/gpu/work_stealing.h
)
@@ -223,7 +224,6 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS
integrator/intersect_subsurface.h
integrator/intersect_volume_stack.h
integrator/megakernel.h
integrator/mnee.h
integrator/path_state.h
integrator/shade_background.h
integrator/shade_light.h

View File

@@ -29,8 +29,8 @@ ccl_device_inline
IntegratorShadowState state,
const uint visibility,
const uint max_hits,
ccl_private uint *r_num_recorded_hits,
ccl_private float *r_throughput)
ccl_private uint *num_recorded_hits,
ccl_private float *throughput)
{
/* todo:
* - likely and unlikely for if() statements
@@ -60,18 +60,15 @@ ccl_device_inline
* recorded hits is exceeded and we no longer need to find hits beyond the max
* distance found. */
float t_max_world = ray->t;
/* Current maximum distance to the intersection.
* Is calculated as a ray length, transformed to an object space when entering
* instance node. */
float t_max_current = ray->t;
/* Equal to t_max_world when traversing top level BVH, transformed into local
* space when entering instances. */
float t_max_current = t_max_world;
/* Conversion from world to local space for the current instance if any, 1.0
* otherwise. */
float t_world_to_instance = 1.0f;
*r_num_recorded_hits = 0;
*r_throughput = 1.0f;
*num_recorded_hits = 0;
*throughput = 1.0f;
/* traversal loop */
do {
@@ -240,10 +237,10 @@ ccl_device_inline
/* Always use baked shadow transparency for curves. */
if (isect.type & PRIMITIVE_CURVE) {
*r_throughput *= intersection_curve_shadow_transparency(
*throughput *= intersection_curve_shadow_transparency(
kg, isect.object, isect.prim, isect.u);
if (*r_throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
if (*throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
return true;
}
else {
@@ -252,39 +249,37 @@ ccl_device_inline
}
if (record_intersection) {
/* Test if we need to record this transparent intersection. */
/* Increase the number of hits, possibly beyond max_hits, we will
* simply not record those and only keep the max_hits closest. */
uint record_index = (*num_recorded_hits)++;
const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
if (*r_num_recorded_hits < max_record_hits || isect.t < t_max_world) {
/* If maximum number of hits was reached, replace the intersection with the
* highest distance. We want to find the N closest intersections. */
const uint num_recorded_hits = min(*r_num_recorded_hits, max_record_hits);
uint isect_index = num_recorded_hits;
if (num_recorded_hits + 1 >= max_record_hits) {
float max_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t);
uint max_recorded_hit = 0;
if (record_index >= max_record_hits - 1) {
/* If maximum number of hits reached, find the intersection with
* the largest distance to potentially replace when another hit
* is found. */
const int num_recorded_hits = min(max_record_hits, record_index);
float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t);
int max_recorded_hit = 0;
for (uint i = 1; i < num_recorded_hits; ++i) {
const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t);
if (isect_t > max_t) {
max_recorded_hit = i;
max_t = isect_t;
}
for (int i = 1; i < num_recorded_hits; i++) {
const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t);
if (isect_t > max_recorded_t) {
max_recorded_t = isect_t;
max_recorded_hit = i;
}
if (num_recorded_hits >= max_record_hits) {
isect_index = max_recorded_hit;
}
/* Limit the ray distance and stop counting hits beyond this. */
t_max_world = max(isect.t, max_t);
}
integrator_state_write_shadow_isect(state, &isect, isect_index);
if (record_index >= max_record_hits) {
record_index = max_recorded_hit;
}
/* Limit the ray distance and stop counting hits beyond this. */
t_max_world = max(max_recorded_t, isect.t);
t_max_current = t_max_world * t_world_to_instance;
}
/* Always increase the number of recorded hits, even beyond the maximum,
* so that we can detect this and trace another ray if needed. */
++(*r_num_recorded_hits);
integrator_state_write_shadow_isect(state, &isect, record_index);
}
}
}
@@ -323,7 +318,7 @@ ccl_device_inline
#endif
/* Restore world space ray length. */
t_max_current = ray->t;
t_max_current = t_max_world;
object = OBJECT_NONE;
t_world_to_instance = 1.0f;

View File

@@ -31,17 +31,7 @@ ccl_device_inline float frac(float x, int *ix)
return x - (float)i;
}
template<typename TexT, typename OutT = float4> struct TextureInterpolator {
static ccl_always_inline OutT zero()
{
if constexpr (std::is_same<OutT, float4>::value) {
return zero_float4();
}
else {
return 0.0f;
}
}
template<typename T> struct TextureInterpolator {
static ccl_always_inline float4 read(float4 r)
{
@@ -50,18 +40,21 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
static ccl_always_inline float4 read(uchar4 r)
{
const float f = 1.0f / 255.0f;
float f = 1.0f / 255.0f;
return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
}
static ccl_always_inline float read(uchar r)
static ccl_always_inline float4 read(uchar r)
{
return r * (1.0f / 255.0f);
float f = r * (1.0f / 255.0f);
return make_float4(f, f, f, 1.0f);
}
static ccl_always_inline float read(float r)
static ccl_always_inline float4 read(float r)
{
return r;
/* TODO(dingto): Optimize this, so interpolation
* happens on float instead of float4 */
return make_float4(r, r, r, 1.0f);
}
static ccl_always_inline float4 read(half4 r)
@@ -69,131 +62,37 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
return half4_to_float4_image(r);
}
static ccl_always_inline float read(half r)
static ccl_always_inline float4 read(half r)
{
return half_to_float_image(r);
float f = half_to_float_image(r);
return make_float4(f, f, f, 1.0f);
}
static ccl_always_inline float read(uint16_t r)
static ccl_always_inline float4 read(uint16_t r)
{
return r * (1.0f / 65535.0f);
float f = r * (1.0f / 65535.0f);
return make_float4(f, f, f, 1.0f);
}
static ccl_always_inline float4 read(ushort4 r)
{
const float f = 1.0f / 65535.0f;
float f = 1.0f / 65535.0f;
return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
}
/* Read 2D Texture Data
* Does not check if data request is in bounds. */
static ccl_always_inline OutT read(const TexT *data, int x, int y, int width, int height)
static ccl_always_inline float4 read(const T *data, int x, int y, int width, int height)
{
return read(data[y * width + x]);
}
/* Read 2D Texture Data Clip
* Returns transparent black if data request is out of bounds. */
static ccl_always_inline OutT read_clip(const TexT *data, int x, int y, int width, int height)
{
if (x < 0 || x >= width || y < 0 || y >= height) {
return zero();
if (x < 0 || y < 0 || x >= width || y >= height) {
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
return read(data[y * width + x]);
}
/* Read 3D Texture Data
* Does not check if data request is in bounds. */
static ccl_always_inline OutT
read(const TexT *data, int x, int y, int z, int width, int height, int depth)
{
return read(data[x + y * width + z * width * height]);
}
/* Read 3D Texture Data Clip
* Returns transparent black if data request is out of bounds. */
static ccl_always_inline OutT
read_clip(const TexT *data, int x, int y, int z, int width, int height, int depth)
{
if (x < 0 || x >= width || y < 0 || y >= height || z < 0 || z >= depth) {
return zero();
}
return read(data[x + y * width + z * width * height]);
}
/* Trilinear Interpolation */
static ccl_always_inline OutT
trilinear_lookup(const TexT *data,
float tx,
float ty,
float tz,
int ix,
int iy,
int iz,
int nix,
int niy,
int niz,
int width,
int height,
int depth,
OutT read(const TexT *, int, int, int, int, int, int))
{
OutT r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) *
read(data, ix, iy, iz, width, height, depth);
r += (1.0f - tz) * (1.0f - ty) * tx * read(data, nix, iy, iz, width, height, depth);
r += (1.0f - tz) * ty * (1.0f - tx) * read(data, ix, niy, iz, width, height, depth);
r += (1.0f - tz) * ty * tx * read(data, nix, niy, iz, width, height, depth);
r += tz * (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, niz, width, height, depth);
r += tz * (1.0f - ty) * tx * read(data, nix, iy, niz, width, height, depth);
r += tz * ty * (1.0f - tx) * read(data, ix, niy, niz, width, height, depth);
r += tz * ty * tx * read(data, nix, niy, niz, width, height, depth);
return r;
}
/** Tricubic Interpolation */
static ccl_always_inline OutT
tricubic_lookup(const TexT *data,
float tx,
float ty,
float tz,
const int xc[4],
const int yc[4],
const int zc[4],
int width,
int height,
int depth,
OutT read(const TexT *, int, int, int, int, int, int))
{
float u[4], v[4], w[4];
/* Some helper macros to keep code size reasonable.
* Lets the compiler inline all the matrix multiplications.
*/
#define DATA(x, y, z) (read(data, xc[x], yc[y], zc[z], width, height, depth))
#define COL_TERM(col, row) \
(v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \
u[3] * DATA(3, col, row)))
#define ROW_TERM(row) \
(w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row)))
SET_CUBIC_SPLINE_WEIGHTS(u, tx);
SET_CUBIC_SPLINE_WEIGHTS(v, ty);
SET_CUBIC_SPLINE_WEIGHTS(w, tz);
/* Actual interpolation. */
return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
#undef COL_TERM
#undef ROW_TERM
#undef DATA
}
static ccl_always_inline int wrap_periodic(int x, int width)
{
x %= width;
if (x < 0) {
if (x < 0)
x += width;
}
return x;
}
@@ -204,8 +103,9 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
/* ******** 2D interpolation ******** */
static ccl_always_inline OutT interp_closest(const TextureInfo &info, float x, float y)
static ccl_always_inline float4 interp_closest(const TextureInfo &info, float x, float y)
{
const T *data = (const T *)info.data;
const int width = info.width;
const int height = info.height;
int ix, iy;
@@ -217,134 +117,105 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
iy = wrap_periodic(iy, height);
break;
case EXTENSION_CLIP:
/* No samples are inside the clip region. */
if (ix < 0 || ix >= width || iy < 0 || iy >= height) {
return zero();
if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) {
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
break;
ATTR_FALLTHROUGH;
case EXTENSION_EXTEND:
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
break;
default:
kernel_assert(0);
return zero();
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
const TexT *data = (const TexT *)info.data;
return read((const TexT *)data, ix, iy, width, height);
return read(data[ix + iy * width]);
}
static ccl_always_inline OutT interp_linear(const TextureInfo &info, float x, float y)
static ccl_always_inline float4 interp_linear(const TextureInfo &info, float x, float y)
{
const T *data = (const T *)info.data;
const int width = info.width;
const int height = info.height;
/* A -0.5 offset is used to center the linear samples around the sample point. */
int ix, iy;
int nix, niy;
int ix, iy, nix, niy;
const float tx = frac(x * (float)width - 0.5f, &ix);
const float ty = frac(y * (float)height - 0.5f, &iy);
switch (info.extension) {
case EXTENSION_REPEAT:
ix = wrap_periodic(ix, width);
nix = wrap_periodic(ix + 1, width);
iy = wrap_periodic(iy, height);
nix = wrap_periodic(ix + 1, width);
niy = wrap_periodic(iy + 1, height);
break;
case EXTENSION_CLIP:
/* No linear samples are inside the clip region. */
if (ix < -1 || ix >= width || iy < -1 || iy >= height) {
return zero();
}
nix = ix + 1;
niy = iy + 1;
break;
case EXTENSION_EXTEND:
nix = wrap_clamp(ix + 1, width);
ix = wrap_clamp(ix, width);
niy = wrap_clamp(iy + 1, height);
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
break;
default:
kernel_assert(0);
return zero();
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
const TexT *data = (const TexT *)info.data;
return (1.0f - ty) * (1.0f - tx) * read_clip(data, ix, iy, width, height) +
(1.0f - ty) * tx * read_clip(data, nix, iy, width, height) +
ty * (1.0f - tx) * read_clip(data, ix, niy, width, height) +
ty * tx * read_clip(data, nix, niy, width, height);
return (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, width, height) +
(1.0f - ty) * tx * read(data, nix, iy, width, height) +
ty * (1.0f - tx) * read(data, ix, niy, width, height) +
ty * tx * read(data, nix, niy, width, height);
}
static ccl_always_inline OutT interp_cubic(const TextureInfo &info, float x, float y)
static ccl_always_inline float4 interp_cubic(const TextureInfo &info, float x, float y)
{
const T *data = (const T *)info.data;
const int width = info.width;
const int height = info.height;
/* A -0.5 offset is used to center the cubic samples around the sample point. */
int ix, iy;
int ix, iy, nix, niy;
const float tx = frac(x * (float)width - 0.5f, &ix);
const float ty = frac(y * (float)height - 0.5f, &iy);
int pix, piy;
int nix, niy;
int nnix, nniy;
int pix, piy, nnix, nniy;
switch (info.extension) {
case EXTENSION_REPEAT:
ix = wrap_periodic(ix, width);
pix = wrap_periodic(ix - 1, width);
nix = wrap_periodic(ix + 1, width);
nnix = wrap_periodic(ix + 2, width);
iy = wrap_periodic(iy, height);
pix = wrap_periodic(ix - 1, width);
piy = wrap_periodic(iy - 1, height);
nix = wrap_periodic(ix + 1, width);
niy = wrap_periodic(iy + 1, height);
nnix = wrap_periodic(ix + 2, width);
nniy = wrap_periodic(iy + 2, height);
break;
case EXTENSION_CLIP:
/* No cubic samples are inside the clip region. */
if (ix < -2 || ix > width || iy < -2 || iy > height) {
return zero();
}
pix = ix - 1;
nix = ix + 1;
nnix = ix + 2;
piy = iy - 1;
nix = ix + 1;
niy = iy + 1;
nnix = ix + 2;
nniy = iy + 2;
break;
case EXTENSION_EXTEND:
pix = wrap_clamp(ix - 1, width);
nix = wrap_clamp(ix + 1, width);
nnix = wrap_clamp(ix + 2, width);
ix = wrap_clamp(ix, width);
piy = wrap_clamp(iy - 1, height);
nix = wrap_clamp(ix + 1, width);
niy = wrap_clamp(iy + 1, height);
nnix = wrap_clamp(ix + 2, width);
nniy = wrap_clamp(iy + 2, height);
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
break;
default:
kernel_assert(0);
return zero();
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
const TexT *data = (const TexT *)info.data;
const int xc[4] = {pix, ix, nix, nnix};
const int yc[4] = {piy, iy, niy, nniy};
float u[4], v[4];
/* Some helper macros to keep code size reasonable.
* Lets the compiler inline all the matrix multiplications.
/* Some helper macro to keep code reasonable size,
* let compiler to inline all the matrix multiplications.
*/
#define DATA(x, y) (read_clip(data, xc[x], yc[y], width, height))
#define DATA(x, y) (read(data, xc[x], yc[y], width, height))
#define TERM(col) \
(v[col] * \
(u[0] * DATA(0, col) + u[1] * DATA(1, col) + u[2] * DATA(2, col) + u[3] * DATA(3, col)))
@@ -358,8 +229,11 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
#undef DATA
}
static ccl_always_inline OutT interp(const TextureInfo &info, float x, float y)
static ccl_always_inline float4 interp(const TextureInfo &info, float x, float y)
{
if (UNLIKELY(!info.data)) {
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
switch (info.interpolation) {
case INTERPOLATION_CLOSEST:
return interp_closest(info, x, y);
@@ -372,14 +246,14 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
/* ******** 3D interpolation ******** */
static ccl_always_inline OutT interp_3d_closest(const TextureInfo &info,
float x,
float y,
float z)
static ccl_always_inline float4 interp_3d_closest(const TextureInfo &info,
float x,
float y,
float z)
{
const int width = info.width;
const int height = info.height;
const int depth = info.depth;
int width = info.width;
int height = info.height;
int depth = info.depth;
int ix, iy, iz;
frac(x * (float)width, &ix);
@@ -393,11 +267,10 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
iz = wrap_periodic(iz, depth);
break;
case EXTENSION_CLIP:
/* No samples are inside the clip region. */
if (ix < 0 || ix >= width || iy < 0 || iy >= height || iz < 0 || iz >= depth) {
return zero();
if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
break;
ATTR_FALLTHROUGH;
case EXTENSION_EXTEND:
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
@@ -405,25 +278,24 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
break;
default:
kernel_assert(0);
return zero();
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
const TexT *data = (const TexT *)info.data;
return read(data, ix, iy, iz, width, height, depth);
const T *data = (const T *)info.data;
return read(data[ix + iy * width + iz * width * height]);
}
static ccl_always_inline OutT interp_3d_linear(const TextureInfo &info,
float x,
float y,
float z)
static ccl_always_inline float4 interp_3d_linear(const TextureInfo &info,
float x,
float y,
float z)
{
const int width = info.width;
const int height = info.height;
const int depth = info.depth;
int width = info.width;
int height = info.height;
int depth = info.depth;
int ix, iy, iz;
int nix, niy, niz;
/* A -0.5 offset is used to center the linear samples around the sample point. */
float tx = frac(x * (float)width - 0.5f, &ix);
float ty = frac(y * (float)height - 0.5f, &iy);
float tz = frac(z * (float)depth - 0.5f, &iz);
@@ -431,79 +303,50 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
switch (info.extension) {
case EXTENSION_REPEAT:
ix = wrap_periodic(ix, width);
nix = wrap_periodic(ix + 1, width);
iy = wrap_periodic(iy, height);
niy = wrap_periodic(iy + 1, height);
iz = wrap_periodic(iz, depth);
nix = wrap_periodic(ix + 1, width);
niy = wrap_periodic(iy + 1, height);
niz = wrap_periodic(iz + 1, depth);
break;
case EXTENSION_CLIP:
/* No linear samples are inside the clip region. */
if (ix < -1 || ix >= width || iy < -1 || iy >= height || iz < -1 || iz >= depth) {
return zero();
if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
nix = ix + 1;
niy = iy + 1;
niz = iz + 1;
/* All linear samples are inside the clip region. */
if (ix >= 0 && nix < width && iy >= 0 && niy < height && iz >= 0 && niz < depth) {
break;
}
/* The linear samples span the clip border.
* #read_clip is used to ensure proper interpolation across the clip border. */
return trilinear_lookup((const TexT *)info.data,
tx,
ty,
tz,
ix,
iy,
iz,
nix,
niy,
niz,
width,
height,
depth,
read_clip);
ATTR_FALLTHROUGH;
case EXTENSION_EXTEND:
nix = wrap_clamp(ix + 1, width);
ix = wrap_clamp(ix, width);
niy = wrap_clamp(iy + 1, height);
iy = wrap_clamp(iy, height);
niz = wrap_clamp(iz + 1, depth);
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
iz = wrap_clamp(iz, depth);
break;
default:
kernel_assert(0);
return zero();
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
return trilinear_lookup((const TexT *)info.data,
tx,
ty,
tz,
ix,
iy,
iz,
nix,
niy,
niz,
width,
height,
depth,
read);
const T *data = (const T *)info.data;
float4 r;
r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) *
read(data[ix + iy * width + iz * width * height]);
r += (1.0f - tz) * (1.0f - ty) * tx * read(data[nix + iy * width + iz * width * height]);
r += (1.0f - tz) * ty * (1.0f - tx) * read(data[ix + niy * width + iz * width * height]);
r += (1.0f - tz) * ty * tx * read(data[nix + niy * width + iz * width * height]);
r += tz * (1.0f - ty) * (1.0f - tx) * read(data[ix + iy * width + niz * width * height]);
r += tz * (1.0f - ty) * tx * read(data[nix + iy * width + niz * width * height]);
r += tz * ty * (1.0f - tx) * read(data[ix + niy * width + niz * width * height]);
r += tz * ty * tx * read(data[nix + niy * width + niz * width * height]);
return r;
}
/* Tricubic b-spline interpolation.
*
* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are
/* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are
* causing stack overflow issue in this function unless it is inlined.
*
* Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization
@@ -514,101 +357,100 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
#else
static ccl_never_inline
#endif
OutT
float4
interp_3d_cubic(const TextureInfo &info, float x, float y, float z)
{
int width = info.width;
int height = info.height;
int depth = info.depth;
int ix, iy, iz;
/* A -0.5 offset is used to center the cubic samples around the sample point. */
int nix, niy, niz;
/* Tricubic b-spline interpolation. */
const float tx = frac(x * (float)width - 0.5f, &ix);
const float ty = frac(y * (float)height - 0.5f, &iy);
const float tz = frac(z * (float)depth - 0.5f, &iz);
int pix, piy, piz;
int nix, niy, niz;
int nnix, nniy, nniz;
int pix, piy, piz, nnix, nniy, nniz;
switch (info.extension) {
case EXTENSION_REPEAT:
ix = wrap_periodic(ix, width);
pix = wrap_periodic(ix - 1, width);
nix = wrap_periodic(ix + 1, width);
nnix = wrap_periodic(ix + 2, width);
iy = wrap_periodic(iy, height);
niy = wrap_periodic(iy + 1, height);
piy = wrap_periodic(iy - 1, height);
nniy = wrap_periodic(iy + 2, height);
iz = wrap_periodic(iz, depth);
pix = wrap_periodic(ix - 1, width);
piy = wrap_periodic(iy - 1, height);
piz = wrap_periodic(iz - 1, depth);
nix = wrap_periodic(ix + 1, width);
niy = wrap_periodic(iy + 1, height);
niz = wrap_periodic(iz + 1, depth);
nnix = wrap_periodic(ix + 2, width);
nniy = wrap_periodic(iy + 2, height);
nniz = wrap_periodic(iz + 2, depth);
break;
case EXTENSION_CLIP: {
/* No cubic samples are inside the clip region. */
if (ix < -2 || ix > width || iy < -2 || iy > height || iz < -2 || iz > depth) {
return zero();
case EXTENSION_CLIP:
if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
pix = ix - 1;
nnix = ix + 2;
nix = ix + 1;
piy = iy - 1;
niy = iy + 1;
nniy = iy + 2;
piz = iz - 1;
niz = iz + 1;
nniz = iz + 2;
/* All cubic samples are inside the clip region. */
if (pix >= 0 && nnix < width && piy >= 0 && nniy < height && piz >= 0 && nniz < depth) {
break;
}
/* The Cubic samples span the clip border.
* read_clip is used to ensure proper interpolation across the clip border. */
const int xc[4] = {pix, ix, nix, nnix};
const int yc[4] = {piy, iy, niy, nniy};
const int zc[4] = {piz, iz, niz, nniz};
return tricubic_lookup(
(const TexT *)info.data, tx, ty, tz, xc, yc, zc, width, height, depth, read_clip);
}
ATTR_FALLTHROUGH;
case EXTENSION_EXTEND:
pix = wrap_clamp(ix - 1, width);
nix = wrap_clamp(ix + 1, width);
nnix = wrap_clamp(ix + 2, width);
ix = wrap_clamp(ix, width);
piy = wrap_clamp(iy - 1, height);
niy = wrap_clamp(iy + 1, height);
nniy = wrap_clamp(iy + 2, height);
iy = wrap_clamp(iy, height);
piz = wrap_clamp(iz - 1, depth);
nix = wrap_clamp(ix + 1, width);
niy = wrap_clamp(iy + 1, height);
niz = wrap_clamp(iz + 1, depth);
nnix = wrap_clamp(ix + 2, width);
nniy = wrap_clamp(iy + 2, height);
nniz = wrap_clamp(iz + 2, depth);
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
iz = wrap_clamp(iz, depth);
break;
default:
kernel_assert(0);
return zero();
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
const int xc[4] = {pix, ix, nix, nnix};
const int yc[4] = {piy, iy, niy, nniy};
const int zc[4] = {piz, iz, niz, nniz};
const TexT *data = (const TexT *)info.data;
return tricubic_lookup(data, tx, ty, tz, xc, yc, zc, width, height, depth, read);
const int yc[4] = {width * piy, width * iy, width * niy, width * nniy};
const int zc[4] = {
width * height * piz, width * height * iz, width * height * niz, width * height * nniz};
float u[4], v[4], w[4];
/* Some helper macro to keep code reasonable size,
* let compiler to inline all the matrix multiplications.
*/
#define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]]))
#define COL_TERM(col, row) \
(v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \
u[3] * DATA(3, col, row)))
#define ROW_TERM(row) \
(w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row)))
SET_CUBIC_SPLINE_WEIGHTS(u, tx);
SET_CUBIC_SPLINE_WEIGHTS(v, ty);
SET_CUBIC_SPLINE_WEIGHTS(w, tz);
/* Actual interpolation. */
const T *data = (const T *)info.data;
return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
#undef COL_TERM
#undef ROW_TERM
#undef DATA
}
static ccl_always_inline OutT
static ccl_always_inline float4
interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp)
{
if (UNLIKELY(!info.data))
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) {
case INTERPOLATION_CLOSEST:
return interp_3d_closest(info, x, y, z);
@@ -621,13 +463,13 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
};
#ifdef WITH_NANOVDB
template<typename TexT, typename OutT = float4> struct NanoVDBInterpolator {
template<typename T> struct NanoVDBInterpolator {
typedef typename nanovdb::NanoGrid<TexT>::AccessorType AccessorType;
typedef typename nanovdb::NanoGrid<T>::AccessorType AccessorType;
static ccl_always_inline float read(float r)
static ccl_always_inline float4 read(float r)
{
return r;
return make_float4(r, r, r, 1.0f);
}
static ccl_always_inline float4 read(nanovdb::Vec3f r)
@@ -635,43 +477,40 @@ template<typename TexT, typename OutT = float4> struct NanoVDBInterpolator {
return make_float4(r[0], r[1], r[2], 1.0f);
}
static ccl_always_inline OutT interp_3d_closest(const AccessorType &acc,
float x,
float y,
float z)
static ccl_always_inline float4 interp_3d_closest(const AccessorType &acc,
float x,
float y,
float z)
{
const nanovdb::Vec3f xyz(x, y, z);
return read(nanovdb::SampleFromVoxels<AccessorType, 0, false>(acc)(xyz));
}
static ccl_always_inline OutT interp_3d_linear(const AccessorType &acc,
float x,
float y,
float z)
static ccl_always_inline float4 interp_3d_linear(const AccessorType &acc,
float x,
float y,
float z)
{
const nanovdb::Vec3f xyz(x - 0.5f, y - 0.5f, z - 0.5f);
return read(nanovdb::SampleFromVoxels<AccessorType, 1, false>(acc)(xyz));
}
/* Tricubic b-spline interpolation. */
# if defined(__GNUC__) || defined(__clang__)
static ccl_always_inline
# else
static ccl_never_inline
# endif
OutT
float4
interp_3d_cubic(const AccessorType &acc, float x, float y, float z)
{
int ix, iy, iz;
int nix, niy, niz;
int pix, piy, piz;
int nnix, nniy, nniz;
/* A -0.5 offset is used to center the cubic samples around the sample point. */
/* Tricubic b-spline interpolation. */
const float tx = frac(x - 0.5f, &ix);
const float ty = frac(y - 0.5f, &iy);
const float tz = frac(z - 0.5f, &iz);
pix = ix - 1;
piy = iy - 1;
piz = iz - 1;
@@ -687,8 +526,8 @@ template<typename TexT, typename OutT = float4> struct NanoVDBInterpolator {
const int zc[4] = {piz, iz, niz, nniz};
float u[4], v[4], w[4];
/* Some helper macros to keep code size reasonable.
* Lets the compiler inline all the matrix multiplications.
/* Some helper macro to keep code reasonable size,
* let compiler to inline all the matrix multiplications.
*/
# define DATA(x, y, z) (read(acc.getValue(nanovdb::Coord(xc[x], yc[y], zc[z]))))
# define COL_TERM(col, row) \
@@ -709,12 +548,12 @@ template<typename TexT, typename OutT = float4> struct NanoVDBInterpolator {
# undef DATA
}
static ccl_always_inline OutT
static ccl_always_inline float4
interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp)
{
using namespace nanovdb;
NanoGrid<TexT> *const grid = (NanoGrid<TexT> *)info.data;
NanoGrid<T> *const grid = (NanoGrid<T> *)info.data;
AccessorType acc = grid->getAccessor();
switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) {
@@ -735,27 +574,15 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, flo
{
const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
if (UNLIKELY(!info.data)) {
return zero_float4();
}
switch (info.data_type) {
case IMAGE_DATA_TYPE_HALF: {
const float f = TextureInterpolator<half, float>::interp(info, x, y);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_BYTE: {
const float f = TextureInterpolator<uchar, float>::interp(info, x, y);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_USHORT: {
const float f = TextureInterpolator<uint16_t, float>::interp(info, x, y);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_FLOAT: {
const float f = TextureInterpolator<float, float>::interp(info, x, y);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_HALF:
return TextureInterpolator<half>::interp(info, x, y);
case IMAGE_DATA_TYPE_BYTE:
return TextureInterpolator<uchar>::interp(info, x, y);
case IMAGE_DATA_TYPE_USHORT:
return TextureInterpolator<uint16_t>::interp(info, x, y);
case IMAGE_DATA_TYPE_FLOAT:
return TextureInterpolator<float>::interp(info, x, y);
case IMAGE_DATA_TYPE_HALF4:
return TextureInterpolator<half4>::interp(info, x, y);
case IMAGE_DATA_TYPE_BYTE4:
@@ -778,30 +605,19 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg,
{
const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
if (UNLIKELY(!info.data)) {
return zero_float4();
}
if (info.use_transform_3d) {
P = transform_point(&info.transform_3d, P);
}
switch (info.data_type) {
case IMAGE_DATA_TYPE_HALF: {
const float f = TextureInterpolator<half, float>::interp_3d(info, P.x, P.y, P.z, interp);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_BYTE: {
const float f = TextureInterpolator<uchar, float>::interp_3d(info, P.x, P.y, P.z, interp);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_USHORT: {
const float f = TextureInterpolator<uint16_t, float>::interp_3d(info, P.x, P.y, P.z, interp);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_FLOAT: {
const float f = TextureInterpolator<float, float>::interp_3d(info, P.x, P.y, P.z, interp);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_HALF:
return TextureInterpolator<half>::interp_3d(info, P.x, P.y, P.z, interp);
case IMAGE_DATA_TYPE_BYTE:
return TextureInterpolator<uchar>::interp_3d(info, P.x, P.y, P.z, interp);
case IMAGE_DATA_TYPE_USHORT:
return TextureInterpolator<uint16_t>::interp_3d(info, P.x, P.y, P.z, interp);
case IMAGE_DATA_TYPE_FLOAT:
return TextureInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp);
case IMAGE_DATA_TYPE_HALF4:
return TextureInterpolator<half4>::interp_3d(info, P.x, P.y, P.z, interp);
case IMAGE_DATA_TYPE_BYTE4:
@@ -811,10 +627,8 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg,
case IMAGE_DATA_TYPE_FLOAT4:
return TextureInterpolator<float4>::interp_3d(info, P.x, P.y, P.z, interp);
#ifdef WITH_NANOVDB
case IMAGE_DATA_TYPE_NANOVDB_FLOAT: {
const float f = NanoVDBInterpolator<float, float>::interp_3d(info, P.x, P.y, P.z, interp);
return make_float4(f, f, f, 1.0f);
}
case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
return NanoVDBInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp);
case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp);
#endif

View File

@@ -72,15 +72,15 @@ typedef unsigned long long uint64_t;
#define ccl_gpu_syncthreads() __syncthreads()
#define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate)
#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla)
/* GPU texture objects */
typedef unsigned long long CUtexObject;
typedef CUtexObject ccl_gpu_tex_object_2D;
typedef CUtexObject ccl_gpu_tex_object_3D;
typedef CUtexObject ccl_gpu_tex_object;
template<typename T>
ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object_2D texobj,
ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj,
const float x,
const float y)
{
@@ -88,7 +88,7 @@ ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object_2D
}
template<typename T>
ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object_3D texobj,
ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj,
const float x,
const float y,
const float z)

View File

@@ -88,7 +88,6 @@
extern "C" __global__ void __launch_bounds__(block_num_threads)
#define ccl_gpu_kernel_signature(name, ...) kernel_gpu_##name(__VA_ARGS__)
#define ccl_gpu_kernel_postfix
#define ccl_gpu_kernel_call(x) x

View File

@@ -56,7 +56,7 @@ ccl_device_noinline T kernel_tex_image_interp_bicubic(ccl_global const TextureIn
float x,
float y)
{
ccl_gpu_tex_object_2D tex = (ccl_gpu_tex_object_2D)info.data;
ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data;
x = (x * info.width) - 0.5f;
y = (y * info.height) - 0.5f;
@@ -85,7 +85,7 @@ template<typename T>
ccl_device_noinline T
kernel_tex_image_interp_tricubic(ccl_global const TextureInfo &info, float x, float y, float z)
{
ccl_gpu_tex_object_3D tex = (ccl_gpu_tex_object_3D)info.data;
ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data;
x = (x * info.width) - 0.5f;
y = (y * info.height) - 0.5f;
@@ -186,11 +186,11 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, flo
const int texture_type = info.data_type;
if (texture_type == IMAGE_DATA_TYPE_FLOAT4 || texture_type == IMAGE_DATA_TYPE_BYTE4 ||
texture_type == IMAGE_DATA_TYPE_HALF4 || texture_type == IMAGE_DATA_TYPE_USHORT4) {
if (info.interpolation == INTERPOLATION_CUBIC || info.interpolation == INTERPOLATION_SMART) {
if (info.interpolation == INTERPOLATION_CUBIC) {
return kernel_tex_image_interp_bicubic<float4>(info, x, y);
}
else {
ccl_gpu_tex_object_2D tex = (ccl_gpu_tex_object_2D)info.data;
ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data;
return ccl_gpu_tex_object_read_2D<float4>(tex, x, y);
}
}
@@ -198,11 +198,11 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, flo
else {
float f;
if (info.interpolation == INTERPOLATION_CUBIC || info.interpolation == INTERPOLATION_SMART) {
if (info.interpolation == INTERPOLATION_CUBIC) {
f = kernel_tex_image_interp_bicubic<float>(info, x, y);
}
else {
ccl_gpu_tex_object_2D tex = (ccl_gpu_tex_object_2D)info.data;
ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data;
f = ccl_gpu_tex_object_read_2D<float>(tex, x, y);
}
@@ -241,22 +241,22 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg,
#endif
if (texture_type == IMAGE_DATA_TYPE_FLOAT4 || texture_type == IMAGE_DATA_TYPE_BYTE4 ||
texture_type == IMAGE_DATA_TYPE_HALF4 || texture_type == IMAGE_DATA_TYPE_USHORT4) {
if (interpolation == INTERPOLATION_CUBIC || interpolation == INTERPOLATION_SMART) {
if (interpolation == INTERPOLATION_CUBIC) {
return kernel_tex_image_interp_tricubic<float4>(info, x, y, z);
}
else {
ccl_gpu_tex_object_3D tex = (ccl_gpu_tex_object_3D)info.data;
ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data;
return ccl_gpu_tex_object_read_3D<float4>(tex, x, y, z);
}
}
else {
float f;
if (interpolation == INTERPOLATION_CUBIC || interpolation == INTERPOLATION_SMART) {
if (interpolation == INTERPOLATION_CUBIC) {
f = kernel_tex_image_interp_tricubic<float>(info, x, y, z);
}
else {
ccl_gpu_tex_object_3D tex = (ccl_gpu_tex_object_3D)info.data;
ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data;
f = ccl_gpu_tex_object_read_3D<float>(tex, x, y, z);
}

View File

@@ -58,7 +58,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0;
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_init_from_camera,
@@ -90,7 +89,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(
integrator_init_from_camera(nullptr, state, tile, render_buffer, x, y, sample));
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_init_from_bake,
@@ -122,7 +120,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(
integrator_init_from_bake(nullptr, state, tile, render_buffer, x, y, sample));
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_intersect_closest,
@@ -137,7 +134,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_intersect_closest(NULL, state, render_buffer));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_intersect_shadow,
@@ -151,7 +147,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_intersect_shadow(NULL, state));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_intersect_subsurface,
@@ -165,7 +160,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_intersect_subsurface(NULL, state));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_intersect_volume_stack,
@@ -179,7 +173,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_intersect_volume_stack(NULL, state));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_shade_background,
@@ -194,7 +187,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_shade_background(NULL, state, render_buffer));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_shade_light,
@@ -209,7 +201,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_shade_light(NULL, state, render_buffer));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_shade_shadow,
@@ -224,7 +215,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_shade_shadow(NULL, state, render_buffer));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_shade_surface,
@@ -239,7 +229,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_shade_surface(NULL, state, render_buffer));
}
}
ccl_gpu_kernel_postfix
#ifdef __KERNEL_METAL__
constant int __dummy_constant [[function_constant(0)]];
@@ -267,7 +256,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
#endif
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(integrator_shade_volume,
@@ -282,7 +270,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(integrator_shade_volume(NULL, state, render_buffer));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_queued_paths_array,
@@ -301,7 +288,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_queued_shadow_paths_array,
@@ -320,7 +306,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_active_paths_array,
@@ -336,7 +321,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_terminated_paths_array,
@@ -353,7 +337,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_terminated_shadow_paths_array,
@@ -370,7 +353,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_sorted_paths_array,
@@ -398,7 +380,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE)
key_prefix_sum,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_compact_paths_array,
@@ -418,7 +399,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_compact_states,
@@ -436,7 +416,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_call(integrator_state_move(NULL, to_state, from_state));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_compact_shadow_paths_array,
@@ -456,7 +435,6 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE)
num_indices,
ccl_gpu_kernel_lambda_pass);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_signature(integrator_compact_shadow_states,
@@ -474,14 +452,12 @@ ccl_gpu_kernel_threads(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE)
ccl_gpu_kernel_call(integrator_shadow_state_move(NULL, to_state, from_state));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel_threads(GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE) ccl_gpu_kernel_signature(
prefix_sum, ccl_global int *counter, ccl_global int *prefix_sum, int num_values)
{
gpu_parallel_prefix_sum(ccl_gpu_global_id_x(), counter, prefix_sum, num_values);
}
ccl_gpu_kernel_postfix
/* --------------------------------------------------------------------
* Adaptive sampling.
@@ -518,7 +494,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
atomic_fetch_and_add_uint32(num_active_pixels, popcount(num_active_pixels_mask));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(adaptive_sampling_filter_x,
@@ -537,7 +512,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
kernel_adaptive_sampling_filter_x(NULL, render_buffer, sy + y, sx, sw, offset, stride));
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(adaptive_sampling_filter_y,
@@ -556,7 +530,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
kernel_adaptive_sampling_filter_y(NULL, render_buffer, sx + x, sy, sh, offset, stride));
}
}
ccl_gpu_kernel_postfix
/* --------------------------------------------------------------------
* Cryptomatte.
@@ -573,7 +546,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(kernel_cryptomatte_post(nullptr, render_buffer, pixel_index));
}
}
ccl_gpu_kernel_postfix
/* --------------------------------------------------------------------
* Film.
@@ -655,7 +627,6 @@ ccl_device_inline void kernel_gpu_film_convert_half_write(ccl_global uchar4 *rgb
\
FILM_GET_PASS_PIXEL_F32(variant, input_channel_count); \
} \
ccl_gpu_kernel_postfix \
\
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) \
ccl_gpu_kernel_signature(film_convert_##variant##_half_rgba, \
@@ -695,8 +666,7 @@ ccl_device_inline void kernel_gpu_film_convert_half_write(ccl_global uchar4 *rgb
const half4 half_pixel = float4_to_half4_display( \
make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); \
kernel_gpu_film_convert_half_write(rgba, rgba_offset, rgba_stride, x, y, half_pixel); \
} \
ccl_gpu_kernel_postfix
}
/* 1 channel inputs */
KERNEL_FILM_CONVERT_VARIANT(depth, 1)
@@ -736,7 +706,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(kernel_displace_evaluate(NULL, input, output, offset + i));
}
}
ccl_gpu_kernel_postfix
/* Background */
@@ -752,7 +721,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_call(kernel_background_evaluate(NULL, input, output, offset + i));
}
}
ccl_gpu_kernel_postfix
/* Curve Shadow Transparency */
@@ -769,7 +737,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
kernel_curve_shadow_transparency_evaluate(NULL, input, output, offset + i));
}
}
ccl_gpu_kernel_postfix
/* --------------------------------------------------------------------
* Denoising.
@@ -803,7 +770,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
color_out[1] = clamp(color_out[1], 0.0f, 10000.0f);
color_out[2] = clamp(color_out[2], 0.0f, 10000.0f);
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(filter_guiding_preprocess,
@@ -883,7 +849,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
flow_out[1] = -motion_in[1] * pixel_scale;
}
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(filter_guiding_set_fake_albedo,
@@ -912,7 +877,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
albedo_out[1] = 0.5f;
albedo_out[2] = 0.5f;
}
ccl_gpu_kernel_postfix
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
ccl_gpu_kernel_signature(filter_color_postprocess,
@@ -972,7 +936,6 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
denoised_pixel[3] = 0;
}
}
ccl_gpu_kernel_postfix
/* --------------------------------------------------------------------
* Shadow catcher.
@@ -998,4 +961,3 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
atomic_fetch_and_add_uint32(num_possible_splits, popcount(can_split_mask));
}
}
ccl_gpu_kernel_postfix

View File

@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2021-2022 Blender Foundation */
#pragma once
CCL_NAMESPACE_BEGIN
/* Parallel sum of array input_data with size n into output_sum.
*
* Adapted from "Optimizing Parallel Reduction in GPU", Mark Harris.
*
* This version adds multiple elements per thread sequentially. This reduces
* the overall cost of the algorithm while keeping the work complexity O(n) and
* the step complexity O(log n). (Brent's Theorem optimization) */
#ifdef __HIP__
# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 1024
#else
# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512
#endif
template<uint blocksize, typename InputT, typename OutputT, typename ConvertOp>
__device__ void gpu_parallel_sum(
const InputT *input_data, const uint n, OutputT *output_sum, OutputT zero, ConvertOp convert)
{
extern ccl_gpu_shared OutputT shared_data[];
const uint tid = ccl_gpu_thread_idx_x;
const uint gridsize = blocksize * ccl_gpu_grid_dim_x();
OutputT sum = zero;
for (uint i = ccl_gpu_block_idx_x * blocksize + tid; i < n; i += gridsize) {
sum += convert(input_data[i]);
}
shared_data[tid] = sum;
ccl_gpu_syncthreads();
if (blocksize >= 512 && tid < 256) {
shared_data[tid] = sum = sum + shared_data[tid + 256];
}
ccl_gpu_syncthreads();
if (blocksize >= 256 && tid < 128) {
shared_data[tid] = sum = sum + shared_data[tid + 128];
}
ccl_gpu_syncthreads();
if (blocksize >= 128 && tid < 64) {
shared_data[tid] = sum = sum + shared_data[tid + 64];
}
ccl_gpu_syncthreads();
if (blocksize >= 64 && tid < 32) {
shared_data[tid] = sum = sum + shared_data[tid + 32];
}
ccl_gpu_syncthreads();
if (tid < 32) {
for (int offset = ccl_gpu_warp_size / 2; offset > 0; offset /= 2) {
sum += ccl_shfl_down_sync(0xFFFFFFFF, sum, offset);
}
}
if (tid == 0) {
output_sum[ccl_gpu_block_idx_x] = sum;
}
}
CCL_NAMESPACE_END

View File

@@ -62,7 +62,7 @@ typedef unsigned long long uint64_t;
#define ccl_gpu_block_idx_x (blockIdx.x)
#define ccl_gpu_grid_dim_x (gridDim.x)
#define ccl_gpu_warp_size (warpSize)
#define ccl_gpu_thread_mask(thread_warp) uint64_t(0xFFFFFFFFFFFFFFFF >> (64 - thread_warp))
#define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x)
#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x)
@@ -71,13 +71,13 @@ typedef unsigned long long uint64_t;
#define ccl_gpu_syncthreads() __syncthreads()
#define ccl_gpu_ballot(predicate) __ballot(predicate)
#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down(var, detla)
/* GPU texture objects */
typedef hipTextureObject_t ccl_gpu_tex_object_2D;
typedef hipTextureObject_t ccl_gpu_tex_object_3D;
typedef hipTextureObject_t ccl_gpu_tex_object;
template<typename T>
ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object_2D texobj,
ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj,
const float x,
const float y)
{
@@ -85,7 +85,7 @@ ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object_2D
}
template<typename T>
ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object_3D texobj,
ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj,
const float x,
const float y,
const float z)

View File

@@ -31,7 +31,6 @@
extern "C" __global__ void __launch_bounds__(block_num_threads)
#define ccl_gpu_kernel_signature(name, ...) kernel_gpu_##name(__VA_ARGS__)
#define ccl_gpu_kernel_postfix
#define ccl_gpu_kernel_call(x) x

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