Add Chromium-only Blender WebEngine parity work
This commit is contained in:
453
blender-5.2.0/intern/cycles/kernel/CMakeLists.txt
Normal file
453
blender-5.2.0/intern/cycles/kernel/CMakeLists.txt
Normal file
@@ -0,0 +1,453 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_GPU_HEADERS
|
||||
device/gpu/block_sizes.h
|
||||
device/gpu/image.h
|
||||
device/gpu/kernel.h
|
||||
device/gpu/parallel_active_index.h
|
||||
device/gpu/parallel_prefix_sum.h
|
||||
device/gpu/parallel_sorted_index.h
|
||||
device/gpu/work_stealing.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_CLOSURE_HEADERS
|
||||
closure/alloc.h
|
||||
closure/bsdf.h
|
||||
closure/bsdf_ashikhmin_velvet.h
|
||||
closure/bsdf_burley.h
|
||||
closure/bsdf_diffuse.h
|
||||
closure/bsdf_diffuse_ramp.h
|
||||
closure/bsdf_microfacet.h
|
||||
closure/bsdf_oren_nayar.h
|
||||
closure/bsdf_phong_ramp.h
|
||||
closure/bsdf_ray_portal.h
|
||||
closure/bsdf_sheen.h
|
||||
closure/bsdf_toon.h
|
||||
closure/bsdf_transparent.h
|
||||
closure/bsdf_util.h
|
||||
closure/bsdf_ashikhmin_shirley.h
|
||||
closure/bsdf_hair.h
|
||||
closure/bssrdf.h
|
||||
closure/emissive.h
|
||||
closure/volume.h
|
||||
closure/volume_util.h
|
||||
closure/volume_henyey_greenstein.h
|
||||
closure/volume_rayleigh.h
|
||||
closure/volume_fournier_forand.h
|
||||
closure/volume_draine.h
|
||||
closure/bsdf_principled_hair_chiang.h
|
||||
closure/bsdf_principled_hair_huang.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_SVM_HEADERS
|
||||
svm/svm.h
|
||||
svm/ao.h
|
||||
svm/aov.h
|
||||
svm/attribute.h
|
||||
svm/bevel.h
|
||||
svm/blackbody.h
|
||||
svm/bump.h
|
||||
svm/camera.h
|
||||
svm/clamp.h
|
||||
svm/closure.h
|
||||
svm/convert.h
|
||||
svm/checker.h
|
||||
svm/color_util.h
|
||||
svm/brick.h
|
||||
svm/displace.h
|
||||
svm/fresnel.h
|
||||
svm/wireframe.h
|
||||
svm/wavelength.h
|
||||
svm/gabor.h
|
||||
svm/gamma.h
|
||||
svm/brightness.h
|
||||
svm/geometry.h
|
||||
svm/gradient.h
|
||||
svm/hsv.h
|
||||
svm/ies.h
|
||||
svm/image.h
|
||||
svm/invert.h
|
||||
svm/light_path.h
|
||||
svm/magic.h
|
||||
svm/map_range.h
|
||||
svm/mapping.h
|
||||
svm/mapping_util.h
|
||||
svm/math.h
|
||||
svm/math_util.h
|
||||
svm/mix.h
|
||||
svm/node_types.h
|
||||
svm/node_types_template.h
|
||||
svm/noise.h
|
||||
svm/noisetex.h
|
||||
svm/normal.h
|
||||
svm/ramp.h
|
||||
svm/ramp_util.h
|
||||
svm/radial_tiling.h
|
||||
svm/radial_tiling_shared.h
|
||||
svm/raycast.h
|
||||
svm/scene_time.h
|
||||
svm/sepcomb_color.h
|
||||
svm/sepcomb_vector.h
|
||||
svm/sky.h
|
||||
svm/tex_coord.h
|
||||
svm/fractal_noise.h
|
||||
svm/types.h
|
||||
svm/util.h
|
||||
svm/value.h
|
||||
svm/vector_rotate.h
|
||||
svm/vector_transform.h
|
||||
svm/voronoi.h
|
||||
svm/wave.h
|
||||
svm/white_noise.h
|
||||
svm/vertex_color.h
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
set(SRC_KERNEL_OSL_HEADERS
|
||||
osl/osl.h
|
||||
osl/camera.h
|
||||
osl/closures_setup.h
|
||||
osl/closures_template.h
|
||||
osl/services_gpu.h
|
||||
osl/strings.h
|
||||
osl/types.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SRC_KERNEL_GEOM_HEADERS
|
||||
geom/attribute.h
|
||||
geom/curve.h
|
||||
geom/curve_intersect.h
|
||||
geom/geom_intersect.h
|
||||
geom/motion_curve.h
|
||||
geom/motion_point.h
|
||||
geom/motion_triangle.h
|
||||
geom/motion_triangle_intersect.h
|
||||
geom/motion_triangle_shader.h
|
||||
geom/object.h
|
||||
geom/point.h
|
||||
geom/point_intersect.h
|
||||
geom/primitive.h
|
||||
geom/shader_data.h
|
||||
geom/triangle.h
|
||||
geom/triangle_intersect.h
|
||||
geom/volume.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_BAKE_HEADERS
|
||||
bake/bake.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_BVH_HEADERS
|
||||
bvh/bvh.h
|
||||
bvh/intersect_filter.h
|
||||
bvh/nodes.h
|
||||
bvh/shadow_all.h
|
||||
bvh/local.h
|
||||
bvh/traversal.h
|
||||
bvh/types.h
|
||||
bvh/util.h
|
||||
bvh/volume.h
|
||||
bvh/volume_all.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_CAMERA_HEADERS
|
||||
camera/camera.h
|
||||
camera/projection.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_FILM_HEADERS
|
||||
film/adaptive_sampling.h
|
||||
film/aov_passes.h
|
||||
film/data_passes.h
|
||||
film/denoising_passes.h
|
||||
film/volume_guiding_denoise.h
|
||||
film/cryptomatte_passes.h
|
||||
film/light_passes.h
|
||||
film/read.h
|
||||
film/write.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_INTEGRATOR_HEADERS
|
||||
integrator/displacement_shader.h
|
||||
integrator/init_from_bake.h
|
||||
integrator/init_from_camera.h
|
||||
integrator/intersect_dedicated_light.h
|
||||
integrator/intersect_closest.h
|
||||
integrator/intersect_mnee.h
|
||||
integrator/intersect_shadow.h
|
||||
integrator/intersect_subsurface.h
|
||||
integrator/intersect_volume_stack.h
|
||||
integrator/guiding.h
|
||||
integrator/megakernel.h
|
||||
integrator/mnee.h
|
||||
integrator/path_state.h
|
||||
integrator/shade_background.h
|
||||
integrator/shade_light.h
|
||||
integrator/shade_shadow.h
|
||||
integrator/shade_surface.h
|
||||
integrator/shade_volume.h
|
||||
integrator/shade_dedicated_light.h
|
||||
integrator/shadow_catcher.h
|
||||
integrator/shadow_linking.h
|
||||
integrator/shadow_state_template.h
|
||||
integrator/state_flow.h
|
||||
integrator/state.h
|
||||
integrator/state_template.h
|
||||
integrator/state_util.h
|
||||
integrator/subsurface_disk.h
|
||||
integrator/subsurface.h
|
||||
integrator/subsurface_random_walk.h
|
||||
integrator/surface_shader.h
|
||||
integrator/volume_shader.h
|
||||
integrator/volume_stack.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_LIGHT_HEADERS
|
||||
light/area.h
|
||||
light/background.h
|
||||
light/common.h
|
||||
light/distribution.h
|
||||
light/light.h
|
||||
light/point.h
|
||||
light/sample.h
|
||||
light/spot.h
|
||||
light/sun.h
|
||||
light/tree.h
|
||||
light/triangle.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_SAMPLE_HEADERS
|
||||
sample/lcg.h
|
||||
sample/mapping.h
|
||||
sample/mis.h
|
||||
sample/pattern.h
|
||||
sample/sobol_burley.h
|
||||
sample/tabulated_sobol.h
|
||||
sample/util.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_UTIL_HEADERS
|
||||
util/colorspace.h
|
||||
util/differential.h
|
||||
util/ies.h
|
||||
util/image_2d.h
|
||||
util/image_3d.h
|
||||
util/lookup_table.h
|
||||
util/nanovdb.h
|
||||
util/profiler.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_TYPES_HEADERS
|
||||
data_arrays.h
|
||||
data_template.h
|
||||
features.h
|
||||
globals.h
|
||||
image.h
|
||||
tables.h
|
||||
types.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_HEADERS
|
||||
${SRC_KERNEL_BAKE_HEADERS}
|
||||
${SRC_KERNEL_BVH_HEADERS}
|
||||
${SRC_KERNEL_CAMERA_HEADERS}
|
||||
${SRC_KERNEL_CLOSURE_HEADERS}
|
||||
${SRC_KERNEL_FILM_HEADERS}
|
||||
${SRC_KERNEL_GEOM_HEADERS}
|
||||
${SRC_KERNEL_INTEGRATOR_HEADERS}
|
||||
${SRC_KERNEL_LIGHT_HEADERS}
|
||||
${SRC_KERNEL_OSL_HEADERS}
|
||||
${SRC_KERNEL_SAMPLE_HEADERS}
|
||||
${SRC_KERNEL_SVM_HEADERS}
|
||||
${SRC_KERNEL_TYPES_HEADERS}
|
||||
${SRC_KERNEL_UTIL_HEADERS}
|
||||
)
|
||||
|
||||
set(SRC_UTIL_HEADERS
|
||||
../util/atomic.h
|
||||
../util/color.h
|
||||
../util/defines.h
|
||||
../util/guiding.h
|
||||
../util/half.h
|
||||
../util/hash.h
|
||||
../util/math.h
|
||||
../util/math_base.h
|
||||
../util/math_fast.h
|
||||
../util/math_intersect.h
|
||||
../util/math_float2.h
|
||||
../util/math_float3.h
|
||||
../util/math_float4.h
|
||||
../util/math_float8.h
|
||||
../util/math_int2.h
|
||||
../util/math_int3.h
|
||||
../util/math_int4.h
|
||||
../util/math_int8.h
|
||||
../util/math_dual.h
|
||||
../util/projection.h
|
||||
../util/projection_inverse.h
|
||||
../util/rect.h
|
||||
../util/static_assert.h
|
||||
../util/transform.h
|
||||
../util/types.h
|
||||
../util/types_base.h
|
||||
../util/types_float2.h
|
||||
../util/types_float3.h
|
||||
../util/types_float4.h
|
||||
../util/types_float8.h
|
||||
../util/types_image.h
|
||||
../util/types_int2.h
|
||||
../util/types_int3.h
|
||||
../util/types_int4.h
|
||||
../util/types_int8.h
|
||||
../util/types_normal.h
|
||||
../util/types_spectrum.h
|
||||
../util/types_rgbe.h
|
||||
../util/types_uchar2.h
|
||||
../util/types_uchar3.h
|
||||
../util/types_uchar4.h
|
||||
../util/types_uint2.h
|
||||
../util/types_uint3.h
|
||||
../util/types_uint4.h
|
||||
../util/types_ushort4.h
|
||||
../util/types_dual.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
# `Zstd` compressor for kernels.
|
||||
add_executable(zstd_compress ../cmake/zstd_compress.cpp)
|
||||
|
||||
target_link_libraries(zstd_compress PRIVATE bf::dependencies::zstd)
|
||||
target_link_libraries(zstd_compress PRIVATE bf::dependencies::pthreads)
|
||||
|
||||
if(NOT WITH_BLENDER)
|
||||
# For the Cycles standalone put libraries next to the Cycles application.
|
||||
set(cycles_kernel_runtime_lib_target_path ${CYCLES_INSTALL_PATH})
|
||||
else()
|
||||
# For Blender put the libraries next to the Blender executable.
|
||||
#
|
||||
# Note that the installation path in the delayed_install is relative to the versioned folder,
|
||||
# which means we need to go one level up.
|
||||
set(cycles_kernel_runtime_lib_target_path "../")
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(cycles_kernel_runtime_lib_target_path ${cycles_kernel_runtime_lib_target_path}/lib)
|
||||
endif()
|
||||
|
||||
if(WITH_COMPILER_ASAN)
|
||||
if((CMAKE_C_COMPILER_ID STREQUAL "GNU") AND (NOT WITH_CYCLES_KERNEL_ASAN))
|
||||
# GCC hangs compiling the big kernel files with ASAN and release, so disable by default.
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-sanitize=all")
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-sanitize=vptr")
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
# With OSL, Cycles disables RTTI in some modules, which then breaks at linking
|
||||
# when trying to use `vptr` sanitizer (included into 'undefined' general option).
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-sanitize=vptr")
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-sanitize=vptr")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Create module with headers common for all compute backends.
|
||||
|
||||
add_library(cycles_kernel INTERFACE)
|
||||
target_sources(cycles_kernel INTERFACE
|
||||
${SRC_KERNEL_HEADERS}
|
||||
${SRC_KERNEL_DEVICE_GPU_HEADERS}
|
||||
${SRC_KERNEL_UTIL_HEADERS}
|
||||
)
|
||||
|
||||
source_group("bake" FILES ${SRC_KERNEL_BAKE_HEADERS})
|
||||
source_group("bvh" FILES ${SRC_KERNEL_BVH_HEADERS})
|
||||
source_group("camera" FILES ${SRC_KERNEL_CAMERA_HEADERS})
|
||||
source_group("closure" FILES ${SRC_KERNEL_CLOSURE_HEADERS})
|
||||
source_group("device\\gpu" FILES ${SRC_KERNEL_DEVICE_GPU_HEADERS})
|
||||
source_group("film" FILES ${SRC_KERNEL_FILM_HEADERS})
|
||||
source_group("geom" FILES ${SRC_KERNEL_GEOM_HEADERS})
|
||||
source_group("integrator" FILES ${SRC_KERNEL_INTEGRATOR_HEADERS})
|
||||
source_group("kernel" FILES ${SRC_KERNEL_TYPES_HEADERS})
|
||||
source_group("light" FILES ${SRC_KERNEL_LIGHT_HEADERS})
|
||||
source_group("osl" FILES ${SRC_KERNEL_OSL_HEADERS})
|
||||
source_group("sample" FILES ${SRC_KERNEL_SAMPLE_HEADERS})
|
||||
source_group("svm" FILES ${SRC_KERNEL_SVM_HEADERS})
|
||||
source_group("util" FILES ${SRC_KERNEL_UTIL_HEADERS})
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Install kernel source for runtime compilation.
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_BAKE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bake)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_CAMERA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/camera)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_FILM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/film)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_GEOM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/geom)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_INTEGRATOR_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/integrator)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_LIGHT_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/light)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_OSL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/osl)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_SAMPLE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/sample)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/svm)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_TYPES_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/util)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/util)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# OSL module.
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
list(APPEND LIB
|
||||
cycles_kernel_osl
|
||||
)
|
||||
add_subdirectory(osl)
|
||||
add_subdirectory(osl/shaders)
|
||||
endif()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Backend-specific rules.
|
||||
|
||||
add_subdirectory(device/cpu)
|
||||
add_subdirectory(device/cuda)
|
||||
add_subdirectory(device/hip)
|
||||
add_subdirectory(device/hiprt)
|
||||
add_subdirectory(device/metal)
|
||||
add_subdirectory(device/oneapi)
|
||||
add_subdirectory(device/optix)
|
||||
|
||||
target_link_libraries(cycles_kernel
|
||||
INTERFACE cycles_kernel_cpu
|
||||
)
|
||||
|
||||
if(NOT WITH_CYCLES_PARALLEL_DEVICE_KERNEL_BUILD)
|
||||
set(_cycles_prev_gpu_kernel_target "")
|
||||
# Modifies in parent scope:
|
||||
# - `_cycles_prev_gpu_kernel_target`: updated to current target for chaining.
|
||||
function(cycles_add_gpu_kernel_dependencies GPU_KERNEL_TARGET)
|
||||
if(TARGET ${GPU_KERNEL_TARGET})
|
||||
if(_cycles_prev_gpu_kernel_target)
|
||||
add_dependencies(${GPU_KERNEL_TARGET} ${_cycles_prev_gpu_kernel_target})
|
||||
endif()
|
||||
set(_cycles_prev_gpu_kernel_target ${GPU_KERNEL_TARGET} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
cycles_add_gpu_kernel_dependencies(cycles_kernel_cuda)
|
||||
cycles_add_gpu_kernel_dependencies(cycles_kernel_optix)
|
||||
cycles_add_gpu_kernel_dependencies(cycles_kernel_hip)
|
||||
cycles_add_gpu_kernel_dependencies(cycles_kernel_hiprt)
|
||||
cycles_add_gpu_kernel_dependencies(cycles_kernel_oneapi)
|
||||
cycles_add_gpu_kernel_dependencies(cycles_kernel_cpu)
|
||||
endif()
|
||||
236
blender-5.2.0/intern/cycles/kernel/bake/bake.h
Normal file
236
blender-5.2.0/intern/cycles/kernel/bake/bake.h
Normal file
@@ -0,0 +1,236 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
|
||||
#include "kernel/camera/projection.h"
|
||||
#include "kernel/integrator/displacement_shader.h"
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/integrator/surface_shader.h"
|
||||
#include "kernel/integrator/volume_shader.h"
|
||||
|
||||
#include "kernel/geom/object.h"
|
||||
#include "kernel/geom/shader_data.h"
|
||||
|
||||
#include "kernel/util/colorspace.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device void kernel_displace_evaluate(KernelGlobals kg,
|
||||
const ccl_global KernelShaderEvalInput *input,
|
||||
ccl_global float *output,
|
||||
ccl_global uint *cache_miss,
|
||||
const int offset)
|
||||
{
|
||||
/* Setup shader data. */
|
||||
const KernelShaderEvalInput in = input[offset];
|
||||
|
||||
ShaderData sd;
|
||||
shader_setup_from_displace(kg, &sd, in.object, in.prim, in.u, in.v);
|
||||
|
||||
/* Evaluate displacement shader. */
|
||||
ConstIntegratorBakeState state;
|
||||
const float3 P = sd.P;
|
||||
displacement_shader_eval(kg, state, &sd);
|
||||
float3 D = sd.P - P;
|
||||
if (sd.flag & SD_CACHE_MISS) {
|
||||
*cache_miss = true;
|
||||
}
|
||||
object_inverse_dir_transform(kg, &sd, &D);
|
||||
|
||||
#ifdef __KERNEL_DEBUG_NAN__
|
||||
if (!isfinite_safe(D)) {
|
||||
kernel_assert(!"Cycles displacement with non-finite value detected");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Ensure finite displacement, preventing BVH from becoming degenerate and avoiding possible
|
||||
* traversal issues caused by non-finite math. */
|
||||
D = ensure_finite(D);
|
||||
|
||||
/* Write output. */
|
||||
output[offset * 3 + 0] = D.x;
|
||||
output[offset * 3 + 1] = D.y;
|
||||
output[offset * 3 + 2] = D.z;
|
||||
}
|
||||
|
||||
ccl_device void kernel_background_evaluate(KernelGlobals kg,
|
||||
const ccl_global KernelShaderEvalInput *input,
|
||||
ccl_global float *output,
|
||||
ccl_global uint *cache_miss,
|
||||
const int offset)
|
||||
{
|
||||
/* Setup ray */
|
||||
const KernelShaderEvalInput in = input[offset];
|
||||
const float3 ray_P = zero_float3();
|
||||
const float3 ray_D = equirectangular_to_direction(in.u, in.v);
|
||||
const float ray_time = 0.5f;
|
||||
|
||||
/* Compute ray differential from resolution passed via object and prim fields. */
|
||||
const float du = 1.0f / in.object;
|
||||
const float dv = 1.0f / in.prim;
|
||||
const float3 ray_D_du = equirectangular_to_direction(in.u + du, in.v);
|
||||
const float3 ray_D_dv = equirectangular_to_direction(in.u, in.v + dv);
|
||||
const float ray_dD = 0.5f * (len(ray_D_du - ray_D) + len(ray_D_dv - ray_D));
|
||||
|
||||
/* Setup shader data. */
|
||||
ShaderData sd;
|
||||
shader_setup_from_background(kg, &sd, ray_P, ray_D, ray_dD, ray_time);
|
||||
|
||||
/* Evaluate shader.
|
||||
* This is being evaluated for all BSDFs, so path flag does not contain a specific type.
|
||||
* However, we want to flag the ray visibility to ignore the sun in the background map. */
|
||||
ConstIntegratorBakeState state;
|
||||
const uint32_t path_flag = PATH_RAY_EMISSION | PATH_RAY_IMPORTANCE_BAKE;
|
||||
surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT &
|
||||
~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>(
|
||||
kg, state, &sd, nullptr, PATH_RAY_VISIBILITY_NONE, path_flag);
|
||||
if (sd.flag & SD_CACHE_MISS) {
|
||||
*cache_miss = true;
|
||||
}
|
||||
|
||||
Spectrum color = surface_shader_background(&sd);
|
||||
|
||||
#ifdef __KERNEL_DEBUG_NAN__
|
||||
if (!isfinite_safe(color)) {
|
||||
kernel_assert(!"Cycles background with non-finite value detected");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Ensure finite color, avoiding possible numerical instabilities in the path tracing kernels. */
|
||||
color = ensure_finite(color);
|
||||
|
||||
const float3 color_rgb = spectrum_to_rgb(color);
|
||||
|
||||
/* Write output. */
|
||||
output[offset * 3 + 0] = color_rgb.x;
|
||||
output[offset * 3 + 1] = color_rgb.y;
|
||||
output[offset * 3 + 2] = color_rgb.z;
|
||||
}
|
||||
|
||||
ccl_device void kernel_curve_shadow_transparency_evaluate(
|
||||
KernelGlobals kg,
|
||||
const ccl_global KernelShaderEvalInput *input,
|
||||
ccl_global float *output,
|
||||
ccl_global uint *cache_miss,
|
||||
const int offset)
|
||||
{
|
||||
#ifdef __HAIR__
|
||||
/* Setup shader data. */
|
||||
const KernelShaderEvalInput in = input[offset];
|
||||
|
||||
ShaderData sd;
|
||||
shader_setup_from_curve(kg, &sd, in.object, in.prim, __float_as_int(in.v), in.u);
|
||||
|
||||
/* Evaluate transparency. */
|
||||
ConstIntegratorBakeState state;
|
||||
surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW &
|
||||
~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>(
|
||||
kg, state, &sd, nullptr, PATH_RAY_VISIBILITY_SHADOW, PATH_RAY_FLAG_NONE);
|
||||
|
||||
if (sd.flag & SD_CACHE_MISS) {
|
||||
*cache_miss = true;
|
||||
}
|
||||
|
||||
/* Write output. */
|
||||
output[offset] = clamp(average(surface_shader_transparency(&sd)), 0.0f, 1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device void kernel_volume_density_evaluate(KernelGlobals kg,
|
||||
ccl_global const KernelShaderEvalInput *input,
|
||||
ccl_global float *output,
|
||||
ccl_global uint *cache_miss,
|
||||
const int offset)
|
||||
{
|
||||
#ifdef __VOLUME__
|
||||
if (input[offset * 2 + 1].object == SHADER_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
KernelShaderEvalInput in = input[offset * 2];
|
||||
|
||||
/* Setup ray. */
|
||||
Ray ray;
|
||||
ray.P = make_float3(__int_as_float(in.prim), in.u, in.v);
|
||||
ray.D = zero_float3();
|
||||
ray.tmin = 0.0f;
|
||||
/* Motion blur is ignored when computing the extrema of the density, but we also don't expect the
|
||||
* value to change a lot in one frame. */
|
||||
ray.time = 0.5f;
|
||||
|
||||
/* Setup shader data. */
|
||||
ShaderData sd;
|
||||
shader_setup_from_volume(&sd, &ray, in.object);
|
||||
sd.flag = SD_IS_VOLUME_SHADER_EVAL;
|
||||
/* For stochastic texture sampling. */
|
||||
sd.lcg_state = lcg_state_init(offset, 0, 0, 0x15b4f88d);
|
||||
|
||||
/* Evaluate extinction and emission without allocating closures. */
|
||||
sd.num_closure_left = 0;
|
||||
/* Evaluate density for camera ray because it usually makes the most visual impact. For shaders
|
||||
* that depends on ray types, the extrema are estimated on the fly. */
|
||||
/* TODO(weizhen): Volume invisible to camera ray might appear noisy. We can at least build a
|
||||
* separate octree for shadow ray. */
|
||||
const PathRayVisibility path_visibility = PATH_RAY_VISIBILITY_CAMERA;
|
||||
const uint32_t path_flag = PATH_RAY_FLAG_NONE;
|
||||
|
||||
/* Setup volume stack entry. */
|
||||
in = input[offset * 2 + 1];
|
||||
const int shader = in.object;
|
||||
const VolumeStack entry = {sd.object, shader};
|
||||
|
||||
const float3 voxel_size = make_float3(__int_as_float(in.prim), in.u, in.v);
|
||||
Extrema<float> extrema = {FLT_MAX, -FLT_MAX};
|
||||
/* For heterogeneous volume, we take 16 samples per grid;
|
||||
* for homogeneous volume, only 1 sample is needed. */
|
||||
const int num_samples = volume_is_homogeneous(kg, entry) ? 1 : 16;
|
||||
|
||||
const bool need_transformation = !(kernel_data_fetch(object_flag, sd.object) &
|
||||
SD_OBJECT_TRANSFORM_APPLIED);
|
||||
const Transform tfm = need_transformation ?
|
||||
object_fetch_transform(kg, sd.object, OBJECT_TRANSFORM) :
|
||||
Transform();
|
||||
for (int sample = 0; sample < num_samples; sample++) {
|
||||
/* Blue noise indexing. The sequence length is the number of samples. */
|
||||
const uint3 index = make_uint3(sample + offset * num_samples, 0, 0xffffffff);
|
||||
|
||||
/* Sample a random position inside the voxel. */
|
||||
const float3 rand_p = sobol_burley_sample_3D(
|
||||
index.x, PRNG_BAKE_VOLUME_DENSITY_EVAL, index.y, index.z);
|
||||
sd.P = ray.P + rand_p * voxel_size;
|
||||
if (need_transformation) {
|
||||
/* Convert to world space. */
|
||||
sd.P = transform_point(&tfm, sd.P);
|
||||
}
|
||||
sd.closure_transparent_extinction = zero_float3();
|
||||
sd.closure_emission_background = zero_float3();
|
||||
|
||||
/* Evaluate volume coefficients. */
|
||||
ConstIntegratorBakeState state;
|
||||
volume_shader_eval_entry<false,
|
||||
KERNEL_FEATURE_NODE_MASK_VOLUME & ~KERNEL_FEATURE_NODE_LIGHT_PATH>(
|
||||
kg, state, &sd, entry, path_visibility, path_flag);
|
||||
|
||||
if (sd.flag & SD_CACHE_MISS) {
|
||||
/* Note we keep rendering other samples so we find all cache misses in one go. */
|
||||
*cache_miss = true;
|
||||
}
|
||||
|
||||
const float sigma = reduce_max(sd.closure_transparent_extinction);
|
||||
const float emission = reduce_max(sd.closure_emission_background);
|
||||
|
||||
extrema = merge(extrema, fmaxf(sigma, emission));
|
||||
}
|
||||
|
||||
/* Write output. */
|
||||
const float scale = object_volume_density(kg, sd.object);
|
||||
output[offset * 2 + 0] = extrema.min / scale;
|
||||
output[offset * 2 + 1] = extrema.max / scale;
|
||||
#endif
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
437
blender-5.2.0/intern/cycles/kernel/bvh/bvh.h
Normal file
437
blender-5.2.0/intern/cycles/kernel/bvh/bvh.h
Normal file
@@ -0,0 +1,437 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/bvh/intersect_filter.h"
|
||||
#include "kernel/bvh/nodes.h"
|
||||
#include "kernel/bvh/types.h"
|
||||
#include "kernel/bvh/util.h"
|
||||
|
||||
#include "kernel/geom/curve_intersect.h"
|
||||
#include "kernel/geom/motion_triangle_intersect.h"
|
||||
#include "kernel/geom/object.h"
|
||||
#include "kernel/geom/point_intersect.h"
|
||||
#include "kernel/geom/triangle_intersect.h"
|
||||
|
||||
/* Device specific acceleration structures for ray tracing. */
|
||||
|
||||
#if defined(__EMBREE__)
|
||||
# include "kernel/device/cpu/bvh.h"
|
||||
# define __BVH2__
|
||||
#elif defined(__KERNEL_METALRT__)
|
||||
# include "kernel/device/metal/bvh.h"
|
||||
#elif defined(__KERNEL_OPTIX__)
|
||||
# include "kernel/device/optix/bvh.h"
|
||||
#elif defined(__KERNEL_HIPRT__)
|
||||
# include "kernel/device/hiprt/bvh.h"
|
||||
#else
|
||||
# define __BVH2__
|
||||
#endif
|
||||
|
||||
#if defined(__KERNEL_ONEAPI__) && defined(WITH_EMBREE_GPU)
|
||||
/* bool is apparently not tested for specialization constants:
|
||||
* https://github.com/intel/llvm/blob/39d1c65272a786b2b13a6f094facfddf9408406d/sycl/test/basic_tests/SYCL-2020-spec-constants.cpp#L25-L27
|
||||
* Instead of adding one more bool specialization constant, we reuse existing embree_features one
|
||||
* and use RTC_FEATURE_FLAG_NONE as value to test for avoiding to call Embree on GPU.
|
||||
*/
|
||||
/* We set it to RTC_FEATURE_FLAG_NONE by default so AoT binaries contain MNE and ray-trace kernels
|
||||
* pre-compiled without Embree.
|
||||
* Changing this default value would require updating the logic in oneapi_load_kernels(). */
|
||||
static constexpr sycl::specialization_id<RTCFeatureFlags> oneapi_embree_features{
|
||||
RTC_FEATURE_FLAG_NONE};
|
||||
# define IF_USING_EMBREE \
|
||||
if (kernel_handler.get_specialization_constant<oneapi_embree_features>() != \
|
||||
RTC_FEATURE_FLAG_NONE)
|
||||
# define IF_NOT_USING_EMBREE \
|
||||
if (kernel_handler.get_specialization_constant<oneapi_embree_features>() == \
|
||||
RTC_FEATURE_FLAG_NONE)
|
||||
#else
|
||||
# define IF_USING_EMBREE
|
||||
# define IF_NOT_USING_EMBREE
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Transparent shadow BVH traversal, recording multiple intersections.
|
||||
*/
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
|
||||
# if defined(__BVH2__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all
|
||||
# define BVH_FUNCTION_FEATURES BVH_POINTCLOUD
|
||||
# include "kernel/bvh/shadow_all.h"
|
||||
|
||||
# if defined(__HAIR__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_POINTCLOUD
|
||||
# include "kernel/bvh/shadow_all.h"
|
||||
# endif
|
||||
|
||||
# if defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_POINTCLOUD
|
||||
# include "kernel/bvh/shadow_all.h"
|
||||
# endif
|
||||
|
||||
# if defined(__HAIR__) && defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION | BVH_POINTCLOUD
|
||||
# include "kernel/bvh/shadow_all.h"
|
||||
# endif
|
||||
|
||||
ccl_device_inline void scene_intersect_shadow_all_bvh2(
|
||||
KernelGlobals kg,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private BVHShadowAllPayload &ccl_restrict payload)
|
||||
{
|
||||
# ifdef __OBJECT_MOTION__
|
||||
if (kernel_data.bvh.have_motion) {
|
||||
# ifdef __HAIR__
|
||||
if (kernel_data.bvh.have_curves) {
|
||||
bvh_intersect_shadow_all_hair_motion(kg, ray, payload);
|
||||
return;
|
||||
}
|
||||
# endif /* __HAIR__ */
|
||||
bvh_intersect_shadow_all_motion(kg, ray, payload);
|
||||
return;
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
|
||||
# ifdef __HAIR__
|
||||
if (kernel_data.bvh.have_curves) {
|
||||
bvh_intersect_shadow_all_hair(kg, ray, payload);
|
||||
return;
|
||||
}
|
||||
# endif /* __HAIR__ */
|
||||
bvh_intersect_shadow_all(kg, ray, payload);
|
||||
}
|
||||
# endif /* __BVH2__ */
|
||||
|
||||
ccl_device_intersect void scene_intersect_shadow_all(KernelGlobals kg,
|
||||
IntegratorShadowState state,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility,
|
||||
const uint max_transparent_hits,
|
||||
ccl_private uint *num_recorded_hits,
|
||||
ccl_private float *throughput)
|
||||
{
|
||||
# if !defined(__KERNEL_OPTIX__)
|
||||
/* OptiX does not perform well with conditional trace calls, so it handles the validity of the
|
||||
* ray in the scene_intersect_shadow_all_optix(). */
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
*num_recorded_hits = 0;
|
||||
*throughput = 1.0f;
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
|
||||
BVHShadowAllPayload payload;
|
||||
|
||||
/* A bit of a tricky initialization:
|
||||
* - Some backends require extra ray information for custom motion blur intersection.
|
||||
* - Some backends utilize registers to pass commonly accessed data to the trace calls. */
|
||||
# if !defined(__KERNEL_OPTIX__)
|
||||
BVH_PAYLOAD_BASE(payload).ray_self = ray->self;
|
||||
BVH_PAYLOAD_BASE(payload).ray_visibility = visibility;
|
||||
# if defined(__KERNEL_HIPRT__)
|
||||
BVH_PAYLOAD_BASE(payload).ray_time = ray->time;
|
||||
# endif
|
||||
# endif
|
||||
|
||||
payload.state = state;
|
||||
payload.max_transparent_hits = max_transparent_hits;
|
||||
payload.max_record_isect_t = ray->tmax;
|
||||
|
||||
# ifdef __EMBREE__
|
||||
IF_USING_EMBREE
|
||||
{
|
||||
if (kernel_data.device_bvh) {
|
||||
kernel_embree_intersect_shadow_all(kg, ray, payload);
|
||||
*num_recorded_hits = payload.num_recorded_hits;
|
||||
*throughput = payload.throughput;
|
||||
return;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
IF_NOT_USING_EMBREE
|
||||
{
|
||||
# if defined(__BVH2__)
|
||||
scene_intersect_shadow_all_bvh2(kg, ray, payload);
|
||||
# elif defined(__KERNEL_HIPRT__)
|
||||
scene_intersect_shadow_all_hiprt(kg, ray, payload);
|
||||
# elif defined(__KERNEL_METALRT__)
|
||||
scene_intersect_shadow_all_metalrt(ray, payload);
|
||||
# elif defined(__KERNEL_OPTIX__)
|
||||
scene_intersect_shadow_all_optix(ray, visibility, payload);
|
||||
# endif
|
||||
|
||||
*num_recorded_hits = payload.num_recorded_hits;
|
||||
*throughput = payload.throughput;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
kernel_assert(false);
|
||||
}
|
||||
#endif /* __TRANSPARENT_SHADOWS__ */
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __BVH2__
|
||||
|
||||
/* BVH2
|
||||
*
|
||||
* Bounding volume hierarchy for ray tracing, when no native acceleration
|
||||
* structure is available for the device.
|
||||
*
|
||||
* We compile different variations of the same BVH traversal function for
|
||||
* faster rendering when some types of primitives are not needed, using #includes
|
||||
* to work around the lack of C++ templates in OpenCL.
|
||||
*
|
||||
* Originally based on "Understanding the Efficiency of Ray Traversal on GPUs",
|
||||
* the code has been extended and modified to support more primitives and work
|
||||
* with CPU and various GPU kernel languages. */
|
||||
|
||||
/* Regular BVH traversal */
|
||||
|
||||
# define BVH_FUNCTION_NAME bvh_intersect
|
||||
# define BVH_FUNCTION_FEATURES BVH_POINTCLOUD
|
||||
# include "kernel/bvh/traversal.h"
|
||||
|
||||
# if defined(__HAIR__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_hair
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_POINTCLOUD
|
||||
# include "kernel/bvh/traversal.h"
|
||||
# endif
|
||||
|
||||
# if defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_POINTCLOUD
|
||||
# include "kernel/bvh/traversal.h"
|
||||
# endif
|
||||
|
||||
# if defined(__HAIR__) && defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_hair_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION | BVH_POINTCLOUD
|
||||
# include "kernel/bvh/traversal.h"
|
||||
# endif
|
||||
|
||||
ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility,
|
||||
ccl_private Intersection *isect)
|
||||
{
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef __EMBREE__
|
||||
IF_USING_EMBREE
|
||||
{
|
||||
if (kernel_data.device_bvh) {
|
||||
return kernel_embree_intersect(kg, ray, visibility, isect);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
IF_NOT_USING_EMBREE
|
||||
{
|
||||
# ifdef __OBJECT_MOTION__
|
||||
if (kernel_data.bvh.have_motion) {
|
||||
# ifdef __HAIR__
|
||||
if (kernel_data.bvh.have_curves) {
|
||||
return bvh_intersect_hair_motion(kg, ray, isect, visibility);
|
||||
}
|
||||
# endif /* __HAIR__ */
|
||||
|
||||
return bvh_intersect_motion(kg, ray, isect, visibility);
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
|
||||
# ifdef __HAIR__
|
||||
if (kernel_data.bvh.have_curves) {
|
||||
return bvh_intersect_hair(kg, ray, isect, visibility);
|
||||
}
|
||||
# endif /* __HAIR__ */
|
||||
|
||||
return bvh_intersect(kg, ray, isect, visibility);
|
||||
}
|
||||
|
||||
kernel_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
ccl_device_intersect bool scene_intersect_shadow(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility)
|
||||
{
|
||||
Intersection isect;
|
||||
return scene_intersect(kg, ray, visibility, &isect);
|
||||
}
|
||||
|
||||
/* Single object BVH traversal, for SSS/AO/bevel. */
|
||||
|
||||
# ifdef __BVH_LOCAL__
|
||||
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_local
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR
|
||||
# include "kernel/bvh/local.h"
|
||||
|
||||
# if defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_local_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR
|
||||
# include "kernel/bvh/local.h"
|
||||
# endif
|
||||
|
||||
template<bool single_hit = false>
|
||||
ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
if (local_isect) {
|
||||
local_isect->num_hits = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef __EMBREE__
|
||||
IF_USING_EMBREE
|
||||
{
|
||||
if (kernel_data.device_bvh) {
|
||||
return kernel_embree_intersect_local(
|
||||
kg, ray, local_isect, local_object, lcg_state, max_hits);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
IF_NOT_USING_EMBREE
|
||||
{
|
||||
# ifdef __OBJECT_MOTION__
|
||||
if (kernel_data.bvh.have_motion) {
|
||||
return bvh_intersect_local_motion(kg, ray, local_isect, local_object, lcg_state, max_hits);
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
return bvh_intersect_local(kg, ray, local_isect, local_object, lcg_state, max_hits);
|
||||
}
|
||||
|
||||
kernel_assert(false);
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Volume BVH traversal, for initializing or updating the volume stack. */
|
||||
|
||||
# if defined(__VOLUME__) && !defined(__VOLUME_RECORD_ALL__)
|
||||
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_volume
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR
|
||||
# include "kernel/bvh/volume.h"
|
||||
|
||||
# if defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_volume_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR
|
||||
# include "kernel/bvh/volume.h"
|
||||
# endif
|
||||
|
||||
ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef __EMBREE__
|
||||
IF_USING_EMBREE
|
||||
{
|
||||
if (kernel_data.device_bvh) {
|
||||
return kernel_embree_intersect_volume(kg, ray, isect, visibility);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
IF_NOT_USING_EMBREE
|
||||
{
|
||||
# ifdef __OBJECT_MOTION__
|
||||
if (kernel_data.bvh.have_motion) {
|
||||
return bvh_intersect_volume_motion(kg, ray, isect, visibility);
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
|
||||
return bvh_intersect_volume(kg, ray, isect, visibility);
|
||||
}
|
||||
|
||||
kernel_assert(false);
|
||||
return false;
|
||||
}
|
||||
# endif /* defined(__VOLUME__) && !defined(__VOLUME_RECORD_ALL__) */
|
||||
|
||||
/* Volume BVH traversal, for initializing or updating the volume stack.
|
||||
* Variation that records multiple intersections at once. */
|
||||
|
||||
# if defined(__VOLUME__) && defined(__VOLUME_RECORD_ALL__)
|
||||
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_volume_all
|
||||
# define BVH_FUNCTION_FEATURES BVH_HAIR
|
||||
# include "kernel/bvh/volume_all.h"
|
||||
|
||||
# if defined(__OBJECT_MOTION__)
|
||||
# define BVH_FUNCTION_NAME bvh_intersect_volume_all_motion
|
||||
# define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR
|
||||
# include "kernel/bvh/volume_all.h"
|
||||
# endif
|
||||
|
||||
ccl_device_intersect uint scene_intersect_volume(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint max_hits,
|
||||
const uint visibility)
|
||||
{
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# ifdef __EMBREE__
|
||||
IF_USING_EMBREE
|
||||
{
|
||||
if (kernel_data.device_bvh) {
|
||||
return kernel_embree_intersect_volume(kg, ray, isect, max_hits, visibility);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
IF_NOT_USING_EMBREE
|
||||
{
|
||||
# ifdef __OBJECT_MOTION__
|
||||
if (kernel_data.bvh.have_motion) {
|
||||
return bvh_intersect_volume_all_motion(kg, ray, isect, max_hits, visibility);
|
||||
}
|
||||
# endif /* __OBJECT_MOTION__ */
|
||||
|
||||
return bvh_intersect_volume_all(kg, ray, isect, max_hits, visibility);
|
||||
}
|
||||
|
||||
kernel_assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
# endif /* defined(__VOLUME__) && defined(__VOLUME_RECORD_ALL__) */
|
||||
|
||||
# undef BVH_FEATURE
|
||||
# undef BVH_NAME_JOIN
|
||||
# undef BVH_NAME_EVAL
|
||||
# undef BVH_FUNCTION_FULL_NAME
|
||||
|
||||
#endif /* __BVH2__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
311
blender-5.2.0/intern/cycles/kernel/bvh/intersect_filter.h
Normal file
311
blender-5.2.0/intern/cycles/kernel/bvh/intersect_filter.h
Normal file
@@ -0,0 +1,311 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Intersection and filtering functions for hardware ray-trace style of API.
|
||||
*
|
||||
* Filter functions are invoked for an intersection to give BVH traversal hints whether
|
||||
* traversal is to continue. Returning true from filter functions means the intersection is
|
||||
* filtered (ignored) and the traversal is to continue.
|
||||
*
|
||||
* Note on the template parameters
|
||||
* ===============================
|
||||
*
|
||||
* perform_intersection_tests controls whether checks that are typically are performed during
|
||||
* intersection are to be done in the filter function. Intersection checks that are done by the
|
||||
* hardware do not perform self-intersection and shadow-linking checks: they are done in the
|
||||
* filter function instead. However, if the intersection check uses custom function it performs
|
||||
* these checks early on, so skipping them in the filter function will lead to a better
|
||||
* performance. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/bvh/util.h"
|
||||
#include "kernel/globals.h"
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/integrator/state_util.h"
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
enum IntersectionTest : uint {
|
||||
ISECT_TEST_NONE = 0,
|
||||
|
||||
ISECT_TEST_VISIBILITY_FLAG = (1 << 0),
|
||||
ISECT_TEST_SHADOW_LINKING = (1 << 1),
|
||||
ISECT_TEST_SELF_SHADOW = (1 << 2),
|
||||
|
||||
ISECT_TEST_ALL = (ISECT_TEST_VISIBILITY_FLAG | ISECT_TEST_SHADOW_LINKING |
|
||||
ISECT_TEST_SELF_SHADOW),
|
||||
};
|
||||
|
||||
/* Special tricks to subclass payload.
|
||||
* The issue here is Metal does not support subclassing, but HIP-RT had performance issues with
|
||||
* composition in the past (see !136823). */
|
||||
#if defined(__KERNEL_HIPRT__)
|
||||
# define BVH_PAYLOAD_SUBCLASS(cls, base_cls) struct cls : base_cls
|
||||
# define BVH_PAYLOAD_SUBCLASS_DEFINE(base_cls)
|
||||
# define BVH_PAYLOAD_BASE(obj) (obj)
|
||||
#else
|
||||
# define BVH_PAYLOAD_SUBCLASS(cls, base_cls) struct cls
|
||||
# define BVH_PAYLOAD_SUBCLASS_DEFINE(base_cls) base_cls base;
|
||||
# define BVH_PAYLOAD_BASE(obj) ((obj).base)
|
||||
#endif
|
||||
|
||||
struct BVHPayload {
|
||||
/* In OptiX, self-intersection information and ray visibility are passed via Ray's pointer as
|
||||
* extra payload data. */
|
||||
#if !defined(__KERNEL_OPTIX__)
|
||||
/* Primitives for the self-intersections. */
|
||||
RaySelfPrimitives ray_self;
|
||||
|
||||
/* Ray visibility flags and time. */
|
||||
uint ray_visibility;
|
||||
#endif
|
||||
|
||||
#if defined(__KERNEL_HIPRT__)
|
||||
float ray_time;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* OptiX passes various parameters via registers to the tracing calls. No need to store duplicate
|
||||
* data for OptiX. This will essentially make it so BVHPayload contains data which is strictly
|
||||
* needed for intersection recording and for tracking curve transparency. */
|
||||
#if defined(__KERNEL_OPTIX__)
|
||||
# define BVH_SHADOW_ALL_PAYLOAD_SUBCLASS(cls, base_cls) struct cls
|
||||
# define BVH_SHADOW_ALL_PAYLOAD_SUBCLASS_DEFINE(base_cls)
|
||||
#else
|
||||
# define BVH_SHADOW_ALL_PAYLOAD_SUBCLASS(cls, base_cls) BVH_PAYLOAD_SUBCLASS(cls, base_cls)
|
||||
# define BVH_SHADOW_ALL_PAYLOAD_SUBCLASS_DEFINE(base_cls) BVH_PAYLOAD_SUBCLASS_DEFINE(base_cls)
|
||||
#endif
|
||||
|
||||
BVH_SHADOW_ALL_PAYLOAD_SUBCLASS(BVHShadowAllPayload, BVHPayload)
|
||||
{
|
||||
BVH_SHADOW_ALL_PAYLOAD_SUBCLASS_DEFINE(BVHPayload);
|
||||
|
||||
/* Using uint16_t is slower on HIP, while it is similar performance but potentially lower memory
|
||||
* footprint on other backends. */
|
||||
#if defined(__KERNEL_HIPRT__)
|
||||
using UIntType = uint;
|
||||
#else
|
||||
using UIntType = uint16_t;
|
||||
#endif
|
||||
|
||||
IntegratorShadowState state;
|
||||
|
||||
/* The maximum number of transparent intersections to consider: if there are more intersections
|
||||
* than this value, all light is considered blocked. */
|
||||
UIntType max_transparent_hits;
|
||||
/* The number of transparent intersections tested during BVH traversal. It might be higher than
|
||||
* the number of recorded intersections. */
|
||||
UIntType num_transparent_hits = 0;
|
||||
|
||||
/* Maximum intersection distance t for intersections that are to be recorded.
|
||||
* If intersection's distance exceeds this value, it is not recoded. */
|
||||
float max_record_isect_t;
|
||||
|
||||
/* An index within the shadow_isect array at which the next intersection will be recorded. */
|
||||
UIntType record_isect_index = 0;
|
||||
|
||||
/* The number of intersections that has been attempted to be recorded.
|
||||
* It might be higher than the shadow_isect size, indicating that more invocations of the
|
||||
* intersection kernel are needed. It is different from the num_transparent_hits as it does not
|
||||
* include transparent curve intersections that are handled by accumulating throughput in the
|
||||
* filter function. */
|
||||
UIntType num_recorded_hits = 0;
|
||||
|
||||
/* Accumulated throughput of transparent curve intersections.
|
||||
* Curves are using special optimization by baking their transparency and handling it in the
|
||||
* filter function. */
|
||||
float throughput = 1.0f;
|
||||
};
|
||||
|
||||
/* Filter intersection with possibly transparent surface.
|
||||
*
|
||||
* Designed to be used from the any-hit type of traversal:
|
||||
* - If an opaque surface is hit, returns false, stopping traversal. The scene intersection
|
||||
* function will consider the shadow ray to be blocked.
|
||||
* - If a transparent surface is hit, the intersection is recorded into the shadow_isect array in
|
||||
* the state. The closest N intersections are recorded. */
|
||||
template<uint perform_intersection_tests, uint enabled_primitive_types = PRIMITIVE_ALL>
|
||||
ccl_device_forceinline bool bvh_shadow_all_anyhit_filter(
|
||||
KernelGlobals kg,
|
||||
IntegratorShadowState state,
|
||||
ccl_ray_data BVHShadowAllPayload &ccl_restrict payload,
|
||||
const ccl_ray_data RaySelfPrimitives &ccl_restrict ray_self,
|
||||
const uint ray_visibility,
|
||||
const Intersection isect)
|
||||
|
||||
{
|
||||
#if defined(__VISIBILITY_FLAG__)
|
||||
if constexpr ((perform_intersection_tests & ISECT_TEST_VISIBILITY_FLAG) != 0) {
|
||||
if ((kernel_data_fetch(objects, isect.object).visibility & ray_visibility) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__SHADOW_LINKING__)
|
||||
if constexpr ((perform_intersection_tests & ISECT_TEST_SHADOW_LINKING) != 0) {
|
||||
if (intersection_skip_shadow_link(kg, ray_self, isect.object)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if constexpr ((perform_intersection_tests & ISECT_TEST_SELF_SHADOW) != 0) {
|
||||
if (intersection_skip_self_shadow(ray_self, isect.object, isect.prim)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__TRANSPARENT_SHADOWS__)
|
||||
/* No transparent shadows in the scene, all light is blocked and we can stop immediately. */
|
||||
payload.throughput = 0.0f;
|
||||
return false;
|
||||
#else
|
||||
/* Detect if this surface has a shader with transparent shadows. */
|
||||
/* TODO: optimize so primitive visibility flag indicates if the primitive has a transparent
|
||||
* shadow shader? */
|
||||
const int shader_flags = intersection_get_shader_flags(kg, isect.prim, isect.type);
|
||||
if ((shader_flags & SD_HAS_TRANSPARENT_SHADOW) == 0) {
|
||||
/* No transparent shadows for the shader, all light is blocked, and we can stop immediately. */
|
||||
payload.throughput = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fetch commonly accessed payload data, ensuring that it is used from either register to a
|
||||
* stack, without going to the global memory. */
|
||||
uint num_recorded_hits = payload.num_recorded_hits;
|
||||
|
||||
/* If the intersection is already recorded, ignore it completely: don't update throughput as it
|
||||
* has already been updated. But also don't count it for num_hits as that could result in a
|
||||
* situation when the same ray will be considered transparent when spatial split is off and be
|
||||
* opaque when spatial split is on. Since curves do not record intersections, there is
|
||||
* a possibility for optimization:
|
||||
* - Don't compile this code if the filter is only used for curve primitives.
|
||||
* - Don't run the check if the current intersection comes from the curve, as it will not match
|
||||
* any recorded intersection anyway.
|
||||
*
|
||||
* NOTE: Currently, spatial splits are not used with OptiX, so there is no need to check whether
|
||||
* the intersection has been already recorded. */
|
||||
# if !defined(__KERNEL_OPTIX__)
|
||||
if constexpr ((enabled_primitive_types & (PRIMITIVE_ALL & ~PRIMITIVE_CURVE)) != 0) {
|
||||
if ((isect.type & PRIMITIVE_CURVE) == 0) {
|
||||
if (intersection_skip_shadow_already_recoded(
|
||||
state, isect.object, isect.prim, num_recorded_hits))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Only count transparent bounces, volume bounds bounces are counted when shading. */
|
||||
payload.num_transparent_hits += !(shader_flags & SD_HAS_ONLY_VOLUME);
|
||||
if (payload.num_transparent_hits > payload.max_transparent_hits) {
|
||||
/* The maximum number of intersections has been reached, consider that all light has been
|
||||
* blocked. */
|
||||
payload.throughput = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
# if defined(__HAIR__)
|
||||
if constexpr ((enabled_primitive_types & PRIMITIVE_CURVE) != 0) {
|
||||
/* Always use baked shadow transparency for curves. */
|
||||
if (isect.type & PRIMITIVE_CURVE) {
|
||||
payload.throughput *= intersection_curve_shadow_transparency(
|
||||
kg, isect.object, isect.prim, isect.type, isect.u);
|
||||
|
||||
if (payload.throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
|
||||
/* Light attenuated too much through the curve intersections, assume all light is blocked
|
||||
* and do early output. */
|
||||
payload.throughput = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't record the intersection as the throughput has been already modified here.
|
||||
* Simply continue BVH traversal for other intersections. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/* If the filter function only handles curves, it is known for the fact that nothing is to be
|
||||
* recorded: curves accumulated baked transparency. Skip this code for a curve-only case. */
|
||||
if constexpr ((enabled_primitive_types & (PRIMITIVE_ALL & ~PRIMITIVE_CURVE)) != 0) {
|
||||
/* Always increase the number of recorded hits, even beyond the maximum, so that we can detect
|
||||
* this and trace another ray if needed. */
|
||||
num_recorded_hits += 1;
|
||||
payload.num_recorded_hits = num_recorded_hits;
|
||||
|
||||
constexpr uint max_record_hits = INTEGRATOR_SHADOW_ISECT_SIZE;
|
||||
if (num_recorded_hits <= max_record_hits || isect.t < payload.max_record_isect_t) {
|
||||
integrator_state_write_shadow_isect(state, &isect, payload.record_isect_index);
|
||||
|
||||
if (num_recorded_hits >= max_record_hits) {
|
||||
/* If the maximum number of hits is reached, find the furthest intersection to replace it
|
||||
* with the next closer one. We want the N closest intersections. */
|
||||
uint record_isect_index = 0;
|
||||
float tmax_hits = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t);
|
||||
for (uint i = 1; i < max_record_hits; ++i) {
|
||||
const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t);
|
||||
if (isect_t > tmax_hits) {
|
||||
record_isect_index = i;
|
||||
tmax_hits = isect_t;
|
||||
}
|
||||
}
|
||||
payload.max_record_isect_t = tmax_hits;
|
||||
payload.record_isect_index = record_isect_index;
|
||||
}
|
||||
else {
|
||||
payload.record_isect_index = num_recorded_hits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Filter intersection to intersections with only primitives with volume shader.
|
||||
*
|
||||
* Expected to be called only on a triangle primitive. The caller is to filter out intersections
|
||||
* with non-triangle primitives.
|
||||
*
|
||||
* Returns false if the primitive is not to be filtered out (accepted), true if the primitive is to
|
||||
* be ignored. */
|
||||
template<bool do_visibility_check = true>
|
||||
ccl_device_forceinline bool bvh_volume_anyhit_triangle_filter(
|
||||
KernelGlobals kg,
|
||||
const int object,
|
||||
const int prim,
|
||||
const ccl_ray_data RaySelfPrimitives &ccl_restrict ray_self,
|
||||
const uint ray_visibility)
|
||||
{
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
if constexpr (do_visibility_check) {
|
||||
if ((kernel_data_fetch(objects, object).visibility & ray_visibility) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((kernel_data_fetch(object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (intersection_skip_self(ray_self, object, prim)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int shader = kernel_data_fetch(tri_shader, prim);
|
||||
const int shader_flag = kernel_data_fetch(shaders, (shader & SHADER_MASK)).flags;
|
||||
if (!(shader_flag & SD_HAS_VOLUME)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
235
blender-5.2.0/intern/cycles/kernel/bvh/local.h
Normal file
235
blender-5.2.0/intern/cycles/kernel/bvh/local.h
Normal file
@@ -0,0 +1,235 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2009-2012 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted from code by Intel & NVIDIA. */
|
||||
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
# define NODE_INTERSECT bvh_node_intersect
|
||||
#else
|
||||
# define NODE_INTERSECT bvh_aligned_node_intersect
|
||||
#endif
|
||||
|
||||
/* This is a template BVH traversal function for finding local intersections
|
||||
* around the shading point, for subsurface scattering and bevel. We disable
|
||||
* various features for performance, and for instanced objects avoid traversing
|
||||
* other parts of the scene.
|
||||
*
|
||||
* BVH_MOTION: motion blur rendering
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
ccl_device
|
||||
#else
|
||||
ccl_device_inline
|
||||
#endif
|
||||
bool
|
||||
BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
/* todo:
|
||||
* - test if pushing distance on the stack helps (for non shadow rays)
|
||||
* - separate version for shadow rays
|
||||
* - likely and unlikely for if() statements
|
||||
* - test restrict attribute for pointers
|
||||
*/
|
||||
|
||||
/* traversal stack in CUDA thread-local memory */
|
||||
int traversal_stack[BVH_STACK_SIZE];
|
||||
traversal_stack[0] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
/* traversal variables in registers */
|
||||
int stack_ptr = 0;
|
||||
int node_addr = kernel_data_fetch(object_node, local_object);
|
||||
|
||||
/* ray parameters in registers */
|
||||
float3 P = ray->P;
|
||||
float3 dir = bvh_clamp_direction(ray->D);
|
||||
float3 idir = bvh_inverse_direction(dir);
|
||||
float tmin = ray->tmin;
|
||||
int object = OBJECT_NONE;
|
||||
float isect_t = ray->tmax;
|
||||
|
||||
if (local_isect != nullptr) {
|
||||
local_isect->num_hits = 0;
|
||||
}
|
||||
kernel_assert((local_isect == nullptr) == (max_hits == 0));
|
||||
|
||||
const uint object_flag = kernel_data_fetch(object_flag, local_object);
|
||||
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir);
|
||||
#else
|
||||
bvh_instance_push(kg, local_object, ray, &P, &dir, &idir);
|
||||
#endif
|
||||
object = local_object;
|
||||
}
|
||||
|
||||
/* traversal loop */
|
||||
do {
|
||||
do {
|
||||
/* traverse internal nodes */
|
||||
while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
|
||||
int node_addr_child1, traverse_mask;
|
||||
float dist[2];
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
|
||||
traverse_mask = NODE_INTERSECT(kg,
|
||||
P,
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
dir,
|
||||
#endif
|
||||
idir,
|
||||
tmin,
|
||||
isect_t,
|
||||
node_addr,
|
||||
PATH_RAY_VISIBILITY_ALL,
|
||||
dist);
|
||||
|
||||
node_addr = __float_as_int(cnodes.z);
|
||||
node_addr_child1 = __float_as_int(cnodes.w);
|
||||
|
||||
if (traverse_mask == 3) {
|
||||
/* Both children were intersected, push the farther one. */
|
||||
bool is_closest_child1 = (dist[1] < dist[0]);
|
||||
if (is_closest_child1) {
|
||||
int tmp = node_addr;
|
||||
node_addr = node_addr_child1;
|
||||
node_addr_child1 = tmp;
|
||||
}
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = node_addr_child1;
|
||||
}
|
||||
else {
|
||||
/* One child was intersected. */
|
||||
if (traverse_mask == 2) {
|
||||
node_addr = node_addr_child1;
|
||||
}
|
||||
else if (traverse_mask == 0) {
|
||||
/* Neither child was intersected. */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if node is leaf, fetch triangle list */
|
||||
if (node_addr < 0) {
|
||||
float4 leaf = kernel_data_fetch(bvh_leaf_nodes, (-node_addr - 1));
|
||||
int prim_addr = __float_as_int(leaf.x);
|
||||
|
||||
const int prim_addr2 = __float_as_int(leaf.y);
|
||||
const uint type = __float_as_int(leaf.w);
|
||||
|
||||
/* pop */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
|
||||
/* primitive intersection */
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
/* intersect ray against primitive */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
|
||||
/* Only intersect with matching object, for instanced objects we
|
||||
* already know we are only intersecting the right object. */
|
||||
if (object == OBJECT_NONE) {
|
||||
if (kernel_data_fetch(prim_object, prim_addr) != local_object) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip self intersection. */
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (intersection_skip_self_local(ray->self, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (triangle_intersect_local(kg,
|
||||
local_isect,
|
||||
P,
|
||||
dir,
|
||||
local_object,
|
||||
prim,
|
||||
tmin,
|
||||
isect_t,
|
||||
lcg_state,
|
||||
max_hits))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
/* intersect ray against primitive */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
|
||||
/* Only intersect with matching object, for instanced objects we
|
||||
* already know we are only intersecting the right object. */
|
||||
if (object == OBJECT_NONE) {
|
||||
if (kernel_data_fetch(prim_object, prim_addr) != local_object) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip self intersection. */
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (intersection_skip_self_local(ray->self, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (motion_triangle_intersect_local(kg,
|
||||
local_isect,
|
||||
P,
|
||||
dir,
|
||||
ray->time,
|
||||
local_object,
|
||||
prim,
|
||||
tmin,
|
||||
isect_t,
|
||||
lcg_state,
|
||||
max_hits))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, local_isect, local_object, lcg_state, max_hits);
|
||||
}
|
||||
|
||||
#undef BVH_FUNCTION_NAME
|
||||
#undef BVH_FUNCTION_FEATURES
|
||||
#undef NODE_INTERSECT
|
||||
149
blender-5.2.0/intern/cycles/kernel/bvh/nodes.h
Normal file
149
blender-5.2.0/intern/cycles/kernel/bvh/nodes.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "kernel/geom/object.h"
|
||||
#include "kernel/globals.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
// TODO(sergey): Look into avoid use of full Transform and use 3x3 matrix and
|
||||
// 3-vector which might be faster.
|
||||
ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals kg,
|
||||
const int node_addr,
|
||||
const int child)
|
||||
{
|
||||
Transform space;
|
||||
const int child_addr = node_addr + child * 3;
|
||||
space.x = kernel_data_fetch(bvh_nodes, child_addr + 1);
|
||||
space.y = kernel_data_fetch(bvh_nodes, child_addr + 2);
|
||||
space.z = kernel_data_fetch(bvh_nodes, child_addr + 3);
|
||||
return space;
|
||||
}
|
||||
|
||||
ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg,
|
||||
const float3 P,
|
||||
const float3 idir,
|
||||
const float tmin,
|
||||
const float tmax,
|
||||
const int node_addr,
|
||||
const uint visibility,
|
||||
float dist[2])
|
||||
{
|
||||
|
||||
/* fetch node data */
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
#endif
|
||||
float4 node0 = kernel_data_fetch(bvh_nodes, node_addr + 1);
|
||||
float4 node1 = kernel_data_fetch(bvh_nodes, node_addr + 2);
|
||||
float4 node2 = kernel_data_fetch(bvh_nodes, node_addr + 3);
|
||||
|
||||
/* intersect ray against child nodes */
|
||||
float c0lox = (node0.x - P.x) * idir.x;
|
||||
float c0hix = (node0.z - P.x) * idir.x;
|
||||
float c0loy = (node1.x - P.y) * idir.y;
|
||||
float c0hiy = (node1.z - P.y) * idir.y;
|
||||
float c0loz = (node2.x - P.z) * idir.z;
|
||||
float c0hiz = (node2.z - P.z) * idir.z;
|
||||
float c0min = max4(tmin, min(c0lox, c0hix), min(c0loy, c0hiy), min(c0loz, c0hiz));
|
||||
float c0max = min4(tmax, max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz));
|
||||
|
||||
float c1lox = (node0.y - P.x) * idir.x;
|
||||
float c1hix = (node0.w - P.x) * idir.x;
|
||||
float c1loy = (node1.y - P.y) * idir.y;
|
||||
float c1hiy = (node1.w - P.y) * idir.y;
|
||||
float c1loz = (node2.y - P.z) * idir.z;
|
||||
float c1hiz = (node2.w - P.z) * idir.z;
|
||||
float c1min = max4(tmin, min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz));
|
||||
float c1max = min4(tmax, max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz));
|
||||
|
||||
dist[0] = c0min;
|
||||
dist[1] = c1min;
|
||||
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
/* this visibility test gives a 5% performance hit, how to solve? */
|
||||
return (((c0max >= c0min) && (__float_as_uint(cnodes.x) & visibility)) ? 1 : 0) |
|
||||
(((c1max >= c1min) && (__float_as_uint(cnodes.y) & visibility)) ? 2 : 0);
|
||||
#else
|
||||
return ((c0max >= c0min) ? 1 : 0) | ((c1max >= c1min) ? 2 : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg,
|
||||
const float3 P,
|
||||
const float3 dir,
|
||||
const float tmin,
|
||||
const float tmax,
|
||||
const int node_addr,
|
||||
const int child,
|
||||
float dist[2])
|
||||
{
|
||||
Transform space = bvh_unaligned_node_fetch_space(kg, node_addr, child);
|
||||
float3 aligned_dir = transform_direction(&space, dir);
|
||||
float3 aligned_P = transform_point(&space, P);
|
||||
float3 nrdir = -bvh_inverse_direction(aligned_dir);
|
||||
float3 lower_xyz = aligned_P * nrdir;
|
||||
float3 upper_xyz = lower_xyz - nrdir;
|
||||
const float near_x = min(lower_xyz.x, upper_xyz.x);
|
||||
const float near_y = min(lower_xyz.y, upper_xyz.y);
|
||||
const float near_z = min(lower_xyz.z, upper_xyz.z);
|
||||
const float far_x = max(lower_xyz.x, upper_xyz.x);
|
||||
const float far_y = max(lower_xyz.y, upper_xyz.y);
|
||||
const float far_z = max(lower_xyz.z, upper_xyz.z);
|
||||
const float tnear = max4(tmin, near_x, near_y, near_z);
|
||||
const float tfar = min4(tmax, far_x, far_y, far_z);
|
||||
*dist = tnear;
|
||||
return tnear <= tfar;
|
||||
}
|
||||
|
||||
ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg,
|
||||
const float3 P,
|
||||
const float3 dir,
|
||||
const float tmin,
|
||||
const float tmax,
|
||||
const int node_addr,
|
||||
const uint visibility,
|
||||
float dist[2])
|
||||
{
|
||||
int mask = 0;
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
#endif
|
||||
if (bvh_unaligned_node_intersect_child(kg, P, dir, tmin, tmax, node_addr, 0, &dist[0])) {
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
if ((__float_as_uint(cnodes.x) & visibility))
|
||||
#endif
|
||||
{
|
||||
mask |= 1;
|
||||
}
|
||||
}
|
||||
if (bvh_unaligned_node_intersect_child(kg, P, dir, tmin, tmax, node_addr, 1, &dist[1])) {
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
if ((__float_as_uint(cnodes.y) & visibility))
|
||||
#endif
|
||||
{
|
||||
mask |= 2;
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
ccl_device_forceinline int bvh_node_intersect(KernelGlobals kg,
|
||||
const float3 P,
|
||||
const float3 dir,
|
||||
const float3 idir,
|
||||
const float tmin,
|
||||
const float tmax,
|
||||
const int node_addr,
|
||||
const uint visibility,
|
||||
float dist[2])
|
||||
{
|
||||
float4 node = kernel_data_fetch(bvh_nodes, node_addr);
|
||||
if (__float_as_uint(node.x) & PATH_RAY_VISIBILITY_NODE_UNALIGNED) {
|
||||
return bvh_unaligned_node_intersect(kg, P, dir, tmin, tmax, node_addr, visibility, dist);
|
||||
}
|
||||
return bvh_aligned_node_intersect(kg, P, idir, tmin, tmax, node_addr, visibility, dist);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
269
blender-5.2.0/intern/cycles/kernel/bvh/shadow_all.h
Normal file
269
blender-5.2.0/intern/cycles/kernel/bvh/shadow_all.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2009-2012 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
# define NODE_INTERSECT bvh_node_intersect
|
||||
#else
|
||||
# define NODE_INTERSECT bvh_aligned_node_intersect
|
||||
#endif
|
||||
|
||||
/* This is a template BVH traversal function, where various features can be
|
||||
* enabled/disabled. This way we can compile optimized versions for each case
|
||||
* without new features slowing things down.
|
||||
*
|
||||
* BVH_HAIR: hair curve rendering
|
||||
* BVH_POINTCLOUD: point cloud rendering
|
||||
* BVH_MOTION: motion blur rendering
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
ccl_device
|
||||
#else
|
||||
ccl_device_inline
|
||||
#endif
|
||||
void
|
||||
BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private BVHShadowAllPayload &ccl_restrict payload)
|
||||
{
|
||||
/* todo:
|
||||
* - likely and unlikely for if() statements
|
||||
* - test restrict attribute for pointers
|
||||
*/
|
||||
|
||||
/* traversal stack in CUDA thread-local memory */
|
||||
int traversal_stack[BVH_STACK_SIZE];
|
||||
traversal_stack[0] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
/* traversal variables in registers */
|
||||
int stack_ptr = 0;
|
||||
int node_addr = kernel_data.bvh.root;
|
||||
|
||||
/* ray parameters in registers */
|
||||
float3 P = ray->P;
|
||||
float3 dir = bvh_clamp_direction(ray->D);
|
||||
float3 idir = bvh_inverse_direction(dir);
|
||||
float tmin = ray->tmin;
|
||||
int object = OBJECT_NONE;
|
||||
|
||||
/* Max distance in world space. May be dynamically reduced when max number of recorded hits is
|
||||
* exceeded and we no longer need to find hits beyond the max distance found. */
|
||||
const float tmax = ray->tmax;
|
||||
|
||||
const uint visibility = payload.base.ray_visibility;
|
||||
|
||||
/* traversal loop */
|
||||
do {
|
||||
do {
|
||||
/* traverse internal nodes */
|
||||
while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
|
||||
int node_addr_child1, traverse_mask;
|
||||
float dist[2];
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
|
||||
traverse_mask = NODE_INTERSECT(kg,
|
||||
P,
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
dir,
|
||||
#endif
|
||||
idir,
|
||||
tmin,
|
||||
tmax,
|
||||
node_addr,
|
||||
visibility,
|
||||
dist);
|
||||
|
||||
node_addr = __float_as_int(cnodes.z);
|
||||
node_addr_child1 = __float_as_int(cnodes.w);
|
||||
|
||||
if (traverse_mask == 3) {
|
||||
/* Both children were intersected, push the farther one. */
|
||||
bool is_closest_child1 = (dist[1] < dist[0]);
|
||||
if (is_closest_child1) {
|
||||
int tmp = node_addr;
|
||||
node_addr = node_addr_child1;
|
||||
node_addr_child1 = tmp;
|
||||
}
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = node_addr_child1;
|
||||
}
|
||||
else {
|
||||
/* One child was intersected. */
|
||||
if (traverse_mask == 2) {
|
||||
node_addr = node_addr_child1;
|
||||
}
|
||||
else if (traverse_mask == 0) {
|
||||
/* Neither child was intersected. */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if node is leaf, fetch triangle list */
|
||||
if (node_addr < 0) {
|
||||
float4 leaf = kernel_data_fetch(bvh_leaf_nodes, (-node_addr - 1));
|
||||
int prim_addr = __float_as_int(leaf.x);
|
||||
|
||||
if (prim_addr >= 0) {
|
||||
const int prim_addr2 = __float_as_int(leaf.y);
|
||||
const uint type = __float_as_int(leaf.w);
|
||||
|
||||
/* pop */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
|
||||
/* primitive intersection */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
bool hit;
|
||||
|
||||
/* todo: specialized intersect functions which don't fill in
|
||||
* isect unless needed and check SD_HAS_TRANSPARENT_SHADOW?
|
||||
* might give a few % performance improvement */
|
||||
Intersection isect ccl_optional_struct_init;
|
||||
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_data_fetch(prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (intersection_skip_self_shadow(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, ray->self, prim_object)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
hit = triangle_intersect(
|
||||
kg, &isect, P, dir, tmin, tmax, visibility, prim_object, prim, prim_addr);
|
||||
break;
|
||||
}
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
hit = motion_triangle_intersect(kg,
|
||||
&isect,
|
||||
P,
|
||||
dir,
|
||||
tmin,
|
||||
tmax,
|
||||
ray->time,
|
||||
visibility,
|
||||
prim_object,
|
||||
prim,
|
||||
prim_addr);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if BVH_FEATURE(BVH_HAIR) && defined(__HAIR__)
|
||||
case PRIMITIVE_CURVE_THICK:
|
||||
case PRIMITIVE_MOTION_CURVE_THICK:
|
||||
case PRIMITIVE_CURVE_RIBBON:
|
||||
case PRIMITIVE_MOTION_CURVE_RIBBON:
|
||||
case PRIMITIVE_CURVE_THICK_LINEAR:
|
||||
case PRIMITIVE_MOTION_CURVE_THICK_LINEAR: {
|
||||
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_data_fetch(prim_time, prim_addr);
|
||||
if (ray->time < prim_time.x || ray->time > prim_time.y) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int curve_type = kernel_data_fetch(prim_type, prim_addr);
|
||||
hit = curve_intersect(
|
||||
kg, &isect, P, dir, tmin, tmax, prim_object, prim, ray->time, curve_type);
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if BVH_FEATURE(BVH_POINTCLOUD) && defined(__POINTCLOUD__)
|
||||
case PRIMITIVE_POINT:
|
||||
case PRIMITIVE_MOTION_POINT: {
|
||||
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_data_fetch(prim_time, prim_addr);
|
||||
if (ray->time < prim_time.x || ray->time > prim_time.y) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int point_type = kernel_data_fetch(prim_type, prim_addr);
|
||||
hit = point_intersect(
|
||||
kg, &isect, P, dir, tmin, tmax, prim_object, prim, ray->time, point_type);
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
|
||||
default: {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
if (!bvh_shadow_all_anyhit_filter<ISECT_TEST_NONE>(kg,
|
||||
payload.state,
|
||||
payload,
|
||||
payload.base.ray_self,
|
||||
payload.base.ray_visibility,
|
||||
isect))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* instance push */
|
||||
object = kernel_data_fetch(prim_object, -prim_addr - 1);
|
||||
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir);
|
||||
#else
|
||||
bvh_instance_push(kg, object, ray, &P, &dir, &idir);
|
||||
#endif
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
node_addr = kernel_data_fetch(object_node, object);
|
||||
}
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
if (stack_ptr >= 0) {
|
||||
kernel_assert(object != OBJECT_NONE);
|
||||
|
||||
/* Instance pop. */
|
||||
bvh_instance_pop(ray, &P, &dir, &idir);
|
||||
|
||||
object = OBJECT_NONE;
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
}
|
||||
|
||||
ccl_device_inline void BVH_FUNCTION_NAME(KernelGlobals kg,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private BVHShadowAllPayload &ccl_restrict payload)
|
||||
{
|
||||
BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, payload);
|
||||
}
|
||||
|
||||
#undef BVH_FUNCTION_NAME
|
||||
#undef BVH_FUNCTION_FEATURES
|
||||
#undef NODE_INTERSECT
|
||||
273
blender-5.2.0/intern/cycles/kernel/bvh/traversal.h
Normal file
273
blender-5.2.0/intern/cycles/kernel/bvh/traversal.h
Normal file
@@ -0,0 +1,273 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2009-2012 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
# define NODE_INTERSECT bvh_node_intersect
|
||||
#else
|
||||
# define NODE_INTERSECT bvh_aligned_node_intersect
|
||||
#endif
|
||||
|
||||
/* This is a template BVH traversal function, where various features can be
|
||||
* enabled/disabled. This way we can compile optimized versions for each case
|
||||
* without new features slowing things down.
|
||||
*
|
||||
* BVH_HAIR: hair curve rendering
|
||||
* BVH_POINTCLOUD: point cloud rendering
|
||||
* BVH_MOTION: motion blur rendering
|
||||
*/
|
||||
|
||||
ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
/* todo:
|
||||
* - test if pushing distance on the stack helps (for non shadow rays)
|
||||
* - separate version for shadow rays
|
||||
* - likely and unlikely for if() statements
|
||||
* - test restrict attribute for pointers
|
||||
*/
|
||||
|
||||
/* traversal stack in CUDA thread-local memory */
|
||||
int traversal_stack[BVH_STACK_SIZE];
|
||||
traversal_stack[0] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
/* traversal variables in registers */
|
||||
int stack_ptr = 0;
|
||||
int node_addr = kernel_data.bvh.root;
|
||||
|
||||
/* ray parameters in registers */
|
||||
float3 P = ray->P;
|
||||
float3 dir = bvh_clamp_direction(ray->D);
|
||||
float3 idir = bvh_inverse_direction(dir);
|
||||
const float tmin = ray->tmin;
|
||||
int object = OBJECT_NONE;
|
||||
|
||||
isect->t = ray->tmax;
|
||||
isect->u = 0.0f;
|
||||
isect->v = 0.0f;
|
||||
isect->prim = PRIM_NONE;
|
||||
isect->object = OBJECT_NONE;
|
||||
|
||||
/* traversal loop */
|
||||
do {
|
||||
do {
|
||||
/* traverse internal nodes */
|
||||
while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
|
||||
int node_addr_child1, traverse_mask;
|
||||
float dist[2];
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
|
||||
{
|
||||
traverse_mask = NODE_INTERSECT(kg,
|
||||
P,
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
dir,
|
||||
#endif
|
||||
idir,
|
||||
tmin,
|
||||
isect->t,
|
||||
node_addr,
|
||||
visibility,
|
||||
dist);
|
||||
}
|
||||
|
||||
node_addr = __float_as_int(cnodes.z);
|
||||
node_addr_child1 = __float_as_int(cnodes.w);
|
||||
|
||||
if (traverse_mask == 3) {
|
||||
/* Both children were intersected, push the farther one. */
|
||||
bool is_closest_child1 = (dist[1] < dist[0]);
|
||||
if (is_closest_child1) {
|
||||
int tmp = node_addr;
|
||||
node_addr = node_addr_child1;
|
||||
node_addr_child1 = tmp;
|
||||
}
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = node_addr_child1;
|
||||
}
|
||||
else {
|
||||
/* One child was intersected. */
|
||||
if (traverse_mask == 2) {
|
||||
node_addr = node_addr_child1;
|
||||
}
|
||||
else if (traverse_mask == 0) {
|
||||
/* Neither child was intersected. */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if node is leaf, fetch triangle list */
|
||||
if (node_addr < 0) {
|
||||
float4 leaf = kernel_data_fetch(bvh_leaf_nodes, (-node_addr - 1));
|
||||
int prim_addr = __float_as_int(leaf.x);
|
||||
|
||||
if (prim_addr >= 0) {
|
||||
const int prim_addr2 = __float_as_int(leaf.y);
|
||||
const uint type = __float_as_int(leaf.w);
|
||||
|
||||
/* pop */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
|
||||
/* primitive intersection */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_data_fetch(prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (intersection_skip_self_shadow(ray->self, prim_object, prim)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, ray->self, prim_object)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
if (triangle_intersect(kg,
|
||||
isect,
|
||||
P,
|
||||
dir,
|
||||
tmin,
|
||||
isect->t,
|
||||
visibility,
|
||||
prim_object,
|
||||
prim,
|
||||
prim_addr))
|
||||
{
|
||||
/* shadow ray early termination */
|
||||
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
if (motion_triangle_intersect(kg,
|
||||
isect,
|
||||
P,
|
||||
dir,
|
||||
tmin,
|
||||
isect->t,
|
||||
ray->time,
|
||||
visibility,
|
||||
prim_object,
|
||||
prim,
|
||||
prim_addr))
|
||||
{
|
||||
/* shadow ray early termination */
|
||||
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_MOTION) */
|
||||
#if BVH_FEATURE(BVH_HAIR) && defined(__HAIR__)
|
||||
case PRIMITIVE_CURVE_THICK:
|
||||
case PRIMITIVE_MOTION_CURVE_THICK:
|
||||
case PRIMITIVE_CURVE_RIBBON:
|
||||
case PRIMITIVE_MOTION_CURVE_RIBBON:
|
||||
case PRIMITIVE_CURVE_THICK_LINEAR:
|
||||
case PRIMITIVE_MOTION_CURVE_THICK_LINEAR: {
|
||||
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_data_fetch(prim_time, prim_addr);
|
||||
if (ray->time < prim_time.x || ray->time > prim_time.y) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int curve_type = kernel_data_fetch(prim_type, prim_addr);
|
||||
const bool hit = curve_intersect(
|
||||
kg, isect, P, dir, tmin, isect->t, prim_object, prim, ray->time, curve_type);
|
||||
if (hit) {
|
||||
/* shadow ray early termination */
|
||||
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_HAIR) */
|
||||
#if BVH_FEATURE(BVH_POINTCLOUD) && defined(__POINTCLOUD__)
|
||||
case PRIMITIVE_POINT:
|
||||
case PRIMITIVE_MOTION_POINT: {
|
||||
if ((type & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_data_fetch(prim_time, prim_addr);
|
||||
if (ray->time < prim_time.x || ray->time > prim_time.y) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int point_type = kernel_data_fetch(prim_type, prim_addr);
|
||||
const bool hit = point_intersect(
|
||||
kg, isect, P, dir, tmin, isect->t, prim_object, prim, ray->time, point_type);
|
||||
if (hit) {
|
||||
/* shadow ray early termination */
|
||||
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE)
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_FEATURE(BVH_POINTCLOUD) */
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* instance push */
|
||||
object = kernel_data_fetch(prim_object, -prim_addr - 1);
|
||||
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir);
|
||||
#else
|
||||
bvh_instance_push(kg, object, ray, &P, &dir, &idir);
|
||||
#endif
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
node_addr = kernel_data_fetch(object_node, object);
|
||||
}
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
if (stack_ptr >= 0) {
|
||||
kernel_assert(object != OBJECT_NONE);
|
||||
|
||||
/* instance pop */
|
||||
bvh_instance_pop(ray, &P, &dir, &idir);
|
||||
|
||||
object = OBJECT_NONE;
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
return (isect->prim != PRIM_NONE);
|
||||
}
|
||||
|
||||
ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility);
|
||||
}
|
||||
|
||||
#undef BVH_FUNCTION_NAME
|
||||
#undef BVH_FUNCTION_FEATURES
|
||||
#undef NODE_INTERSECT
|
||||
33
blender-5.2.0/intern/cycles/kernel/bvh/types.h
Normal file
33
blender-5.2.0/intern/cycles/kernel/bvh/types.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Don't inline intersect functions on GPU, this is faster */
|
||||
#ifdef __KERNEL_GPU__
|
||||
# define ccl_device_intersect ccl_device_forceinline
|
||||
#else
|
||||
# define ccl_device_intersect ccl_device_inline
|
||||
#endif
|
||||
|
||||
/* bottom-most stack entry, indicating the end of traversal */
|
||||
#define ENTRYPOINT_SENTINEL 0x76543210
|
||||
|
||||
/* 64 object BVH + 64 mesh BVH + 64 object node splitting */
|
||||
#define BVH_STACK_SIZE 192
|
||||
/* BVH intersection function variations */
|
||||
|
||||
#define BVH_MOTION 1
|
||||
#define BVH_HAIR 2
|
||||
#define BVH_POINTCLOUD 4
|
||||
|
||||
#define BVH_NAME_JOIN(x, y) x##_##y
|
||||
#define BVH_NAME_EVAL(x, y) BVH_NAME_JOIN(x, y)
|
||||
#define BVH_FUNCTION_FULL_NAME(prefix) BVH_NAME_EVAL(prefix, BVH_FUNCTION_NAME)
|
||||
|
||||
#define BVH_FEATURE(f) (((BVH_FUNCTION_FEATURES) & (f)) != 0)
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
313
blender-5.2.0/intern/cycles/kernel/bvh/util.h
Normal file
313
blender-5.2.0/intern/cycles/kernel/bvh/util.h
Normal file
@@ -0,0 +1,313 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device_inline bool intersection_ray_valid(const ccl_private Ray *ray)
|
||||
{
|
||||
/* NOTE: Due to some vectorization code non-finite origin point might
|
||||
* cause lots of false-positive intersections which will overflow traversal
|
||||
* stack.
|
||||
* This code is a quick way to perform early output, to avoid crashes in
|
||||
* such cases.
|
||||
* From production scenes so far it seems it's enough to test first element
|
||||
* only.
|
||||
* Scene intersection may also called with empty rays for conditional trace
|
||||
* calls that evaluate to false, so filter those out.
|
||||
*/
|
||||
return isfinite_safe(ray->P.x) && isfinite_safe(ray->D.x) && len_squared(ray->D) != 0.0f &&
|
||||
ray->tmin < FLT_MAX;
|
||||
}
|
||||
|
||||
/* Offset intersection distance by the smallest possible amount, to skip
|
||||
* intersections at this distance. This works in cases where the ray start
|
||||
* position is unchanged and only tmin is updated, since for self
|
||||
* intersection we'll be comparing against the exact same distances.
|
||||
*
|
||||
* Always returns normalized floating point value. */
|
||||
ccl_device_forceinline float intersection_t_offset(const float t)
|
||||
{
|
||||
/* This is a simplified version of `nextafterf(t, FLT_MAX)`, only dealing with
|
||||
* non-negative and finite t. */
|
||||
kernel_assert(t >= 0.0f && isfinite_safe(t));
|
||||
|
||||
/* Special handling of zero, which also includes handling of denormal values:
|
||||
* always return smallest normalized value. If a denormalized zero is returned
|
||||
* it will cause false-positive intersection detection with a distance of 0.
|
||||
*
|
||||
* The check relies on the fact that comparison of denormal values with zero
|
||||
* returns true. */
|
||||
if (t == 0.0f) {
|
||||
/* The exact bit value of this should be 0x1p-126, but hex floating point values notation is
|
||||
* not available in CUDA/OptiX. */
|
||||
return FLT_MIN;
|
||||
}
|
||||
|
||||
const uint32_t bits = __float_as_uint(t) + 1;
|
||||
const float result = __uint_as_float(bits);
|
||||
|
||||
/* Assert that the calculated value is indeed considered to be offset from the
|
||||
* original value. */
|
||||
kernel_assert(result > t);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Ray offset to avoid self intersection.
|
||||
*
|
||||
* This function can be used to compute a modified ray start position for rays
|
||||
* leaving from a surface. This is from:
|
||||
* "A Fast and Robust Method for Avoiding Self-Intersection"
|
||||
* Ray Tracing Gems, chapter 6.
|
||||
*/
|
||||
ccl_device_inline float3 ray_offset(const float3 P, const float3 Ng)
|
||||
{
|
||||
const float int_scale = 256.0f;
|
||||
const int3 of_i = make_int3(
|
||||
(int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z));
|
||||
|
||||
const float3 p_i = make_float3(
|
||||
__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)),
|
||||
__int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)),
|
||||
__int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z)));
|
||||
const float origin = 1.0f / 32.0f;
|
||||
const float float_scale = 1.0f / 65536.0f;
|
||||
return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x,
|
||||
fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y,
|
||||
fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z);
|
||||
}
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
ccl_device int intersections_compare(const void *a, const void *b)
|
||||
{
|
||||
const Intersection *isect_a = (const Intersection *)a;
|
||||
const Intersection *isect_b = (const Intersection *)b;
|
||||
|
||||
if (isect_a->t < isect_b->t) {
|
||||
return -1;
|
||||
}
|
||||
if (isect_a->t > isect_b->t) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* For subsurface scattering, only sorting a small amount of intersections
|
||||
* so bubble sort is fine for CPU and GPU. */
|
||||
ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection *hits,
|
||||
ccl_private float3 *Ng,
|
||||
uint num_hits)
|
||||
{
|
||||
bool swapped;
|
||||
do {
|
||||
swapped = false;
|
||||
for (uint j = 0; j < num_hits - 1; ++j) {
|
||||
if (hits[j].t > hits[j + 1].t) {
|
||||
Intersection tmp_hit = hits[j];
|
||||
float3 tmp_Ng = Ng[j];
|
||||
hits[j] = hits[j + 1];
|
||||
Ng[j] = Ng[j + 1];
|
||||
hits[j + 1] = tmp_hit;
|
||||
Ng[j + 1] = tmp_Ng;
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
--num_hits;
|
||||
} while (swapped);
|
||||
}
|
||||
|
||||
/* Utility to quickly get flags from an intersection. */
|
||||
|
||||
ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals kg,
|
||||
const int prim,
|
||||
const int type)
|
||||
{
|
||||
int shader = 0;
|
||||
|
||||
if (type & PRIMITIVE_TRIANGLE) {
|
||||
shader = kernel_data_fetch(tri_shader, prim);
|
||||
}
|
||||
#ifdef __POINTCLOUD__
|
||||
else if (type & PRIMITIVE_POINT) {
|
||||
shader = kernel_data_fetch(points_shader, prim);
|
||||
}
|
||||
#endif
|
||||
#ifdef __HAIR__
|
||||
else if (type & PRIMITIVE_CURVE) {
|
||||
shader = kernel_data_fetch(curves, prim).shader_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
return kernel_data_fetch(shaders, (shader & SHADER_MASK)).flags;
|
||||
}
|
||||
|
||||
ccl_device_forceinline int intersection_get_shader_from_isect_prim(KernelGlobals kg,
|
||||
const int prim,
|
||||
const int isect_type)
|
||||
{
|
||||
int shader = 0;
|
||||
|
||||
if (isect_type & PRIMITIVE_TRIANGLE) {
|
||||
shader = kernel_data_fetch(tri_shader, prim);
|
||||
}
|
||||
#ifdef __POINTCLOUD__
|
||||
else if (isect_type & PRIMITIVE_POINT) {
|
||||
shader = kernel_data_fetch(points_shader, prim);
|
||||
}
|
||||
#endif
|
||||
#ifdef __HAIR__
|
||||
else if (isect_type & PRIMITIVE_CURVE) {
|
||||
shader = kernel_data_fetch(curves, prim).shader_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
return shader & SHADER_MASK;
|
||||
}
|
||||
|
||||
ccl_device_forceinline int intersection_get_shader(
|
||||
KernelGlobals kg, const ccl_private Intersection *ccl_restrict isect)
|
||||
{
|
||||
return intersection_get_shader_from_isect_prim(kg, isect->prim, isect->type);
|
||||
}
|
||||
|
||||
ccl_device_forceinline uint
|
||||
intersection_get_object_flags(KernelGlobals kg, const ccl_private Intersection *ccl_restrict isect)
|
||||
{
|
||||
return kernel_data_fetch(object_flag, isect->object);
|
||||
}
|
||||
|
||||
/* TODO: find a better (faster) solution for this. Maybe store offset per object for
|
||||
* attributes needed in intersection? */
|
||||
ccl_device_inline int intersection_find_attribute(KernelGlobals kg,
|
||||
const int object,
|
||||
const uint id)
|
||||
{
|
||||
uint attr_offset = kernel_data_fetch(objects, object).attribute_map_offset;
|
||||
AttributeMap attr_map = kernel_data_fetch(attributes_map, attr_offset);
|
||||
|
||||
while (attr_map.id != id) {
|
||||
if (UNLIKELY(attr_map.id == ATTR_STD_NONE)) {
|
||||
if (UNLIKELY(attr_map.element == 0)) {
|
||||
return (int)ATTR_STD_NOT_FOUND;
|
||||
}
|
||||
/* Chain jump to a different part of the table. */
|
||||
attr_offset = attr_map.offset;
|
||||
}
|
||||
else {
|
||||
attr_offset += ATTR_PRIM_TYPES;
|
||||
}
|
||||
attr_map = kernel_data_fetch(attributes_map, attr_offset);
|
||||
}
|
||||
|
||||
/* return result */
|
||||
return (attr_map.element == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : attr_map.offset;
|
||||
}
|
||||
|
||||
/* Transparent Shadows */
|
||||
|
||||
/* Cut-off value to stop transparent shadow tracing when practically opaque. */
|
||||
#define CURVE_SHADOW_TRANSPARENCY_CUTOFF 0.001f
|
||||
|
||||
ccl_device_inline float intersection_curve_shadow_transparency(
|
||||
KernelGlobals kg, const int object, const int prim, const int type, const float u)
|
||||
{
|
||||
/* Find attribute. */
|
||||
const int offset = intersection_find_attribute(kg, object, ATTR_STD_SHADOW_TRANSPARENCY);
|
||||
if (offset == ATTR_STD_NOT_FOUND) {
|
||||
/* If no shadow transparency attribute, assume opaque. */
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Interpolate transparency between curve keys. */
|
||||
const KernelCurve kcurve = kernel_data_fetch(curves, prim);
|
||||
const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
|
||||
const int k1 = k0 + 1;
|
||||
|
||||
const float f0 = kernel_data_fetch(attributes_float, offset + k0);
|
||||
const float f1 = kernel_data_fetch(attributes_float, offset + k1);
|
||||
|
||||
return (1.0f - u) * f0 + u * f1;
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self(const ccl_ray_data RaySelfPrimitives &self,
|
||||
const int object,
|
||||
const int prim)
|
||||
{
|
||||
return (self.prim == prim) && (self.object == object);
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self_shadow(const ccl_ray_data RaySelfPrimitives &self,
|
||||
const int object,
|
||||
const int prim)
|
||||
{
|
||||
return ((self.prim == prim) && (self.object == object)) ||
|
||||
((self.light_prim == prim) && (self.light_object == object));
|
||||
}
|
||||
|
||||
ccl_device_inline bool intersection_skip_self_local(const ccl_ray_data RaySelfPrimitives &self,
|
||||
const int prim)
|
||||
{
|
||||
return (self.prim == prim);
|
||||
}
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
ccl_device_inline uint64_t
|
||||
ray_get_shadow_set_membership(KernelGlobals kg, const ccl_ray_data RaySelfPrimitives &self)
|
||||
{
|
||||
if (self.light_object != OBJECT_NONE) {
|
||||
return kernel_data_fetch(objects, self.light_object).shadow_set_membership;
|
||||
}
|
||||
|
||||
return LIGHT_LINK_MASK_ALL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ccl_device_inline bool intersection_skip_shadow_link(KernelGlobals kg,
|
||||
const ccl_ray_data RaySelfPrimitives &self,
|
||||
const int isect_object)
|
||||
{
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (!(kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t set_membership = ray_get_shadow_set_membership(kg, self);
|
||||
if (set_membership == LIGHT_LINK_MASK_ALL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint blocker_set = kernel_data_fetch(objects, isect_object).blocker_shadow_set;
|
||||
return ((uint64_t(1) << uint64_t(blocker_set)) & set_membership) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check whether an intersection denoted by its object and primitive is to be skipped due to it
|
||||
* being already recoded.
|
||||
* The situation when primitive is already recoded happens when BVH spatial splits are used. */
|
||||
ccl_device_forceinline bool intersection_skip_shadow_already_recoded(IntegratorShadowState state,
|
||||
const int object,
|
||||
const int prim,
|
||||
const uint num_hits)
|
||||
{
|
||||
const uint num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
|
||||
for (uint i = 0; i < num_recorded_hits; ++i) {
|
||||
const int isect_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, object);
|
||||
const int isect_prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, prim);
|
||||
if (object == isect_object && prim == isect_prim) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
235
blender-5.2.0/intern/cycles/kernel/bvh/volume.h
Normal file
235
blender-5.2.0/intern/cycles/kernel/bvh/volume.h
Normal file
@@ -0,0 +1,235 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2009-2012 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
# define NODE_INTERSECT bvh_node_intersect
|
||||
#else
|
||||
# define NODE_INTERSECT bvh_aligned_node_intersect
|
||||
#endif
|
||||
|
||||
/* This is a template BVH traversal function for volumes, where
|
||||
* various features can be enabled/disabled. This way we can compile optimized
|
||||
* versions for each case without new features slowing things down.
|
||||
*
|
||||
* BVH_MOTION: motion blur rendering
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
ccl_device
|
||||
#else
|
||||
ccl_device_inline
|
||||
#endif
|
||||
bool
|
||||
BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
/* todo:
|
||||
* - test if pushing distance on the stack helps (for non shadow rays)
|
||||
* - separate version for shadow rays
|
||||
* - likely and unlikely for if() statements
|
||||
* - test restrict attribute for pointers
|
||||
*/
|
||||
|
||||
/* traversal stack in CUDA thread-local memory */
|
||||
int traversal_stack[BVH_STACK_SIZE];
|
||||
traversal_stack[0] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
/* traversal variables in registers */
|
||||
int stack_ptr = 0;
|
||||
int node_addr = kernel_data.bvh.root;
|
||||
|
||||
/* ray parameters in registers */
|
||||
float3 P = ray->P;
|
||||
float3 dir = bvh_clamp_direction(ray->D);
|
||||
float3 idir = bvh_inverse_direction(dir);
|
||||
const float tmin = ray->tmin;
|
||||
int object = OBJECT_NONE;
|
||||
|
||||
isect->t = ray->tmax;
|
||||
isect->u = 0.0f;
|
||||
isect->v = 0.0f;
|
||||
isect->prim = PRIM_NONE;
|
||||
isect->object = OBJECT_NONE;
|
||||
|
||||
/* traversal loop */
|
||||
do {
|
||||
do {
|
||||
/* traverse internal nodes */
|
||||
while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
|
||||
int node_addr_child1, traverse_mask;
|
||||
float dist[2];
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
|
||||
traverse_mask = NODE_INTERSECT(kg,
|
||||
P,
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
dir,
|
||||
#endif
|
||||
idir,
|
||||
tmin,
|
||||
isect->t,
|
||||
node_addr,
|
||||
visibility,
|
||||
dist);
|
||||
|
||||
node_addr = __float_as_int(cnodes.z);
|
||||
node_addr_child1 = __float_as_int(cnodes.w);
|
||||
|
||||
if (traverse_mask == 3) {
|
||||
/* Both children were intersected, push the farther one. */
|
||||
bool is_closest_child1 = (dist[1] < dist[0]);
|
||||
if (is_closest_child1) {
|
||||
int tmp = node_addr;
|
||||
node_addr = node_addr_child1;
|
||||
node_addr_child1 = tmp;
|
||||
}
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = node_addr_child1;
|
||||
}
|
||||
else {
|
||||
/* One child was intersected. */
|
||||
if (traverse_mask == 2) {
|
||||
node_addr = node_addr_child1;
|
||||
}
|
||||
else if (traverse_mask == 0) {
|
||||
/* Neither child was intersected. */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if node is leaf, fetch triangle list */
|
||||
if (node_addr < 0) {
|
||||
float4 leaf = kernel_data_fetch(bvh_leaf_nodes, (-node_addr - 1));
|
||||
int prim_addr = __float_as_int(leaf.x);
|
||||
|
||||
if (prim_addr >= 0) {
|
||||
const int prim_addr2 = __float_as_int(leaf.y);
|
||||
const uint type = __float_as_int(leaf.w);
|
||||
|
||||
/* pop */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
|
||||
/* primitive intersection */
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
/* intersect ray against primitive */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
/* only primitives from volume object */
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_data_fetch(prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (bvh_volume_anyhit_triangle_filter<false>(
|
||||
kg, prim_object, prim, ray->self, visibility))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
triangle_intersect(
|
||||
kg, isect, P, dir, tmin, isect->t, visibility, prim_object, prim, prim_addr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
/* intersect ray against primitive */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
/* only primitives from volume object */
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_data_fetch(prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (bvh_volume_anyhit_triangle_filter<false>(
|
||||
kg, prim_object, prim, ray->self, visibility))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
motion_triangle_intersect(kg,
|
||||
isect,
|
||||
P,
|
||||
dir,
|
||||
tmin,
|
||||
isect->t,
|
||||
ray->time,
|
||||
visibility,
|
||||
prim_object,
|
||||
prim,
|
||||
prim_addr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* instance push */
|
||||
object = kernel_data_fetch(prim_object, -prim_addr - 1);
|
||||
uint object_flag = kernel_data_fetch(object_flag, object);
|
||||
if (object_flag & SD_OBJECT_HAS_VOLUME) {
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir);
|
||||
#else
|
||||
bvh_instance_push(kg, object, ray, &P, &dir, &idir);
|
||||
#endif
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
node_addr = kernel_data_fetch(object_node, object);
|
||||
}
|
||||
else {
|
||||
/* pop */
|
||||
object = OBJECT_NONE;
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
if (stack_ptr >= 0) {
|
||||
kernel_assert(object != OBJECT_NONE);
|
||||
|
||||
/* instance pop */
|
||||
bvh_instance_pop(ray, &P, &dir, &idir);
|
||||
|
||||
object = OBJECT_NONE;
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
return (isect->prim != PRIM_NONE);
|
||||
}
|
||||
|
||||
ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility);
|
||||
}
|
||||
|
||||
#undef BVH_FUNCTION_NAME
|
||||
#undef BVH_FUNCTION_FEATURES
|
||||
#undef NODE_INTERSECT
|
||||
263
blender-5.2.0/intern/cycles/kernel/bvh/volume_all.h
Normal file
263
blender-5.2.0/intern/cycles/kernel/bvh/volume_all.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2009-2012 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Adapted code from NVIDIA Corporation. */
|
||||
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
# define NODE_INTERSECT bvh_node_intersect
|
||||
#else
|
||||
# define NODE_INTERSECT bvh_aligned_node_intersect
|
||||
#endif
|
||||
|
||||
/* This is a template BVH traversal function for volumes, where
|
||||
* various features can be enabled/disabled. This way we can compile optimized
|
||||
* versions for each case without new features slowing things down.
|
||||
*
|
||||
* BVH_MOTION: motion blur rendering
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
ccl_device
|
||||
#else
|
||||
ccl_device_inline
|
||||
#endif
|
||||
uint
|
||||
BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
Intersection *isect_array,
|
||||
const uint max_hits,
|
||||
const uint visibility)
|
||||
{
|
||||
/* todo:
|
||||
* - test if pushing distance on the stack helps (for non shadow rays)
|
||||
* - separate version for shadow rays
|
||||
* - likely and unlikely for if() statements
|
||||
* - test restrict attribute for pointers
|
||||
*/
|
||||
|
||||
/* traversal stack in CUDA thread-local memory */
|
||||
int traversal_stack[BVH_STACK_SIZE];
|
||||
traversal_stack[0] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
/* traversal variables in registers */
|
||||
int stack_ptr = 0;
|
||||
int node_addr = kernel_data.bvh.root;
|
||||
|
||||
/* ray parameters in registers */
|
||||
float3 P = ray->P;
|
||||
float3 dir = bvh_clamp_direction(ray->D);
|
||||
float3 idir = bvh_inverse_direction(dir);
|
||||
const float tmin = ray->tmin;
|
||||
int object = OBJECT_NONE;
|
||||
float isect_t = ray->tmax;
|
||||
|
||||
uint num_hits = 0;
|
||||
isect_array->t = ray->tmax;
|
||||
|
||||
/* traversal loop */
|
||||
do {
|
||||
do {
|
||||
/* traverse internal nodes */
|
||||
while (node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) {
|
||||
int node_addr_child1, traverse_mask;
|
||||
float dist[2];
|
||||
float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0);
|
||||
|
||||
traverse_mask = NODE_INTERSECT(kg,
|
||||
P,
|
||||
#if BVH_FEATURE(BVH_HAIR)
|
||||
dir,
|
||||
#endif
|
||||
idir,
|
||||
tmin,
|
||||
isect_t,
|
||||
node_addr,
|
||||
visibility,
|
||||
dist);
|
||||
|
||||
node_addr = __float_as_int(cnodes.z);
|
||||
node_addr_child1 = __float_as_int(cnodes.w);
|
||||
|
||||
if (traverse_mask == 3) {
|
||||
/* Both children were intersected, push the farther one. */
|
||||
bool is_closest_child1 = (dist[1] < dist[0]);
|
||||
if (is_closest_child1) {
|
||||
int tmp = node_addr;
|
||||
node_addr = node_addr_child1;
|
||||
node_addr_child1 = tmp;
|
||||
}
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = node_addr_child1;
|
||||
}
|
||||
else {
|
||||
/* One child was intersected. */
|
||||
if (traverse_mask == 2) {
|
||||
node_addr = node_addr_child1;
|
||||
}
|
||||
else if (traverse_mask == 0) {
|
||||
/* Neither child was intersected. */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if node is leaf, fetch triangle list */
|
||||
if (node_addr < 0) {
|
||||
float4 leaf = kernel_data_fetch(bvh_leaf_nodes, (-node_addr - 1));
|
||||
int prim_addr = __float_as_int(leaf.x);
|
||||
|
||||
if (prim_addr >= 0) {
|
||||
const int prim_addr2 = __float_as_int(leaf.y);
|
||||
const uint type = __float_as_int(leaf.w);
|
||||
bool hit;
|
||||
|
||||
/* pop */
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
|
||||
/* primitive intersection */
|
||||
switch (type & PRIMITIVE_ALL) {
|
||||
case PRIMITIVE_TRIANGLE: {
|
||||
/* intersect ray against primitive */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
/* only primitives from volume object */
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_data_fetch(prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (bvh_volume_anyhit_triangle_filter<false>(
|
||||
kg, prim_object, prim, ray->self, visibility))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
hit = triangle_intersect(kg,
|
||||
isect_array,
|
||||
P,
|
||||
dir,
|
||||
tmin,
|
||||
isect_t,
|
||||
visibility,
|
||||
prim_object,
|
||||
prim,
|
||||
prim_addr);
|
||||
if (hit) {
|
||||
/* Move on to next entry in intersections array. */
|
||||
isect_array++;
|
||||
num_hits++;
|
||||
isect_array->t = isect_t;
|
||||
if (num_hits == max_hits) {
|
||||
return num_hits;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
case PRIMITIVE_MOTION_TRIANGLE: {
|
||||
/* intersect ray against primitive */
|
||||
for (; prim_addr < prim_addr2; prim_addr++) {
|
||||
kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) ==
|
||||
(type & PRIMITIVE_ALL));
|
||||
/* only primitives from volume object */
|
||||
const int prim_object = (object == OBJECT_NONE) ?
|
||||
kernel_data_fetch(prim_object, prim_addr) :
|
||||
object;
|
||||
const int prim = kernel_data_fetch(prim_index, prim_addr);
|
||||
if (bvh_volume_anyhit_triangle_filter<false>(
|
||||
kg, prim_object, prim, ray->self, visibility))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
hit = motion_triangle_intersect(kg,
|
||||
isect_array,
|
||||
P,
|
||||
dir,
|
||||
tmin,
|
||||
isect_t,
|
||||
ray->time,
|
||||
visibility,
|
||||
prim_object,
|
||||
prim,
|
||||
prim_addr);
|
||||
if (hit) {
|
||||
/* Move on to next entry in intersections array. */
|
||||
isect_array++;
|
||||
num_hits++;
|
||||
isect_array->t = isect_t;
|
||||
if (num_hits == max_hits) {
|
||||
return num_hits;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* BVH_MOTION */
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* instance push */
|
||||
object = kernel_data_fetch(prim_object, -prim_addr - 1);
|
||||
const uint object_flag = kernel_data_fetch(object_flag, object);
|
||||
if (object_flag & SD_OBJECT_HAS_VOLUME) {
|
||||
#if BVH_FEATURE(BVH_MOTION)
|
||||
bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir);
|
||||
#else
|
||||
bvh_instance_push(kg, object, ray, &P, &dir, &idir);
|
||||
#endif
|
||||
|
||||
isect_array->t = isect_t;
|
||||
|
||||
++stack_ptr;
|
||||
kernel_assert(stack_ptr < BVH_STACK_SIZE);
|
||||
traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL;
|
||||
|
||||
node_addr = kernel_data_fetch(object_node, object);
|
||||
}
|
||||
else {
|
||||
/* pop */
|
||||
object = OBJECT_NONE;
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
if (stack_ptr >= 0) {
|
||||
kernel_assert(object != OBJECT_NONE);
|
||||
|
||||
/* Instance pop. */
|
||||
bvh_instance_pop(ray, &P, &dir, &idir);
|
||||
|
||||
object = OBJECT_NONE;
|
||||
node_addr = traversal_stack[stack_ptr];
|
||||
--stack_ptr;
|
||||
}
|
||||
} while (node_addr != ENTRYPOINT_SENTINEL);
|
||||
|
||||
return num_hits;
|
||||
}
|
||||
|
||||
ccl_device_inline uint BVH_FUNCTION_NAME(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
Intersection *isect_array,
|
||||
const uint max_hits,
|
||||
const uint visibility)
|
||||
{
|
||||
return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, max_hits, visibility);
|
||||
}
|
||||
|
||||
#undef BVH_FUNCTION_NAME
|
||||
#undef BVH_FUNCTION_FEATURES
|
||||
#undef NODE_INTERSECT
|
||||
664
blender-5.2.0/intern/cycles/kernel/camera/camera.h
Normal file
664
blender-5.2.0/intern/cycles/kernel/camera/camera.h
Normal file
@@ -0,0 +1,664 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
|
||||
#include "kernel/camera/projection.h"
|
||||
#include "kernel/sample/mapping.h"
|
||||
#include "kernel/util/differential.h"
|
||||
#include "kernel/util/lookup_table.h"
|
||||
|
||||
#ifdef WITH_OSL
|
||||
# include "kernel/osl/camera.h"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Perspective Camera */
|
||||
|
||||
ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, const float2 rand)
|
||||
{
|
||||
const float blades = cam->blades;
|
||||
float2 bokeh;
|
||||
|
||||
if (blades == 0.0f) {
|
||||
/* sample disk */
|
||||
bokeh = sample_uniform_disk(rand);
|
||||
}
|
||||
else {
|
||||
/* sample polygon */
|
||||
const float rotation = cam->bladesrotation;
|
||||
bokeh = regular_polygon_sample(blades, rotation, rand);
|
||||
}
|
||||
|
||||
/* anamorphic lens bokeh */
|
||||
bokeh.x *= cam->inv_aperture_ratio;
|
||||
|
||||
return bokeh;
|
||||
}
|
||||
|
||||
ccl_device Spectrum camera_sample_perspective(KernelGlobals kg,
|
||||
const float2 raster_xy,
|
||||
const float2 rand_lens,
|
||||
ccl_private Ray *ray)
|
||||
{
|
||||
/* create ray form raster position */
|
||||
const ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera;
|
||||
const float3 raster = make_float3(raster_xy);
|
||||
float3 Pcamera = transform_perspective(&rastertocamera, raster);
|
||||
|
||||
if (kernel_data.cam.have_perspective_motion) {
|
||||
/* TODO(sergey): Currently we interpolate projected coordinate which
|
||||
* gives nice looking result and which is simple, but is in fact a bit
|
||||
* different comparing to constructing projective matrix from an
|
||||
* interpolated field of view.
|
||||
*/
|
||||
if (ray->time < 0.5f) {
|
||||
const ProjectionTransform rastertocamera_pre = kernel_data.cam.perspective_pre;
|
||||
const float3 Pcamera_pre = transform_perspective(&rastertocamera_pre, raster);
|
||||
Pcamera = interp(Pcamera_pre, Pcamera, ray->time * 2.0f);
|
||||
}
|
||||
else {
|
||||
const ProjectionTransform rastertocamera_post = kernel_data.cam.perspective_post;
|
||||
const float3 Pcamera_post = transform_perspective(&rastertocamera_post, raster);
|
||||
Pcamera = interp(Pcamera, Pcamera_post, (ray->time - 0.5f) * 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
float3 P = zero_float3();
|
||||
float3 D = Pcamera;
|
||||
|
||||
/* modify ray for depth of field */
|
||||
const float aperturesize = kernel_data.cam.aperturesize;
|
||||
|
||||
if (aperturesize > 0.0f) {
|
||||
/* sample point on aperture */
|
||||
const float2 lens_uv = camera_sample_aperture(&kernel_data.cam, rand_lens) * aperturesize;
|
||||
|
||||
/* compute point on plane of focus */
|
||||
const float ft = kernel_data.cam.focaldistance / D.z;
|
||||
const float3 Pfocus = D * ft;
|
||||
|
||||
/* update ray for effect of lens */
|
||||
P = make_float3(lens_uv);
|
||||
D = normalize(Pfocus - P);
|
||||
}
|
||||
|
||||
/* transform ray from camera to world */
|
||||
Transform cameratoworld = kernel_data.cam.cameratoworld;
|
||||
|
||||
if (kernel_data.cam.num_motion_steps) {
|
||||
transform_motion_array_interpolate(&cameratoworld,
|
||||
kernel_data_array(camera_motion),
|
||||
kernel_data.cam.num_motion_steps,
|
||||
ray->time);
|
||||
}
|
||||
|
||||
P = transform_point(&cameratoworld, P);
|
||||
D = normalize(transform_direction(&cameratoworld, D));
|
||||
|
||||
const bool use_stereo = kernel_data.cam.interocular_offset != 0.0f;
|
||||
if (!use_stereo) {
|
||||
/* No stereo */
|
||||
ray->P = P;
|
||||
ray->D = D;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
const float3 Dcenter = transform_direction(&cameratoworld, Pcamera);
|
||||
const float3 Dcenter_normalized = normalize(Dcenter);
|
||||
|
||||
/* TODO: can this be optimized to give compact differentials directly? */
|
||||
ray->dP = differential_zero_compact();
|
||||
differential3 dD;
|
||||
dD.dx = normalize(Dcenter + make_float3(kernel_data.cam.dx)) - Dcenter_normalized;
|
||||
dD.dy = normalize(Dcenter + make_float3(kernel_data.cam.dy)) - Dcenter_normalized;
|
||||
ray->dD = differential_make_compact(dD) * kernel_data.cam.differential_scale;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* Spherical stereo */
|
||||
spherical_stereo_transform(&kernel_data.cam, &P, &D);
|
||||
ray->P = P;
|
||||
ray->D = D;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* Ray differentials, computed from scratch using the raster coordinates
|
||||
* because we don't want to be affected by depth of field. We compute
|
||||
* ray origin and direction for the center and two neighboring pixels
|
||||
* and simply take their differences. */
|
||||
const float3 Pnostereo = transform_point(&cameratoworld, zero_float3());
|
||||
|
||||
float3 Pcenter = Pnostereo;
|
||||
float3 Dcenter = Pcamera;
|
||||
Dcenter = normalize(transform_direction(&cameratoworld, Dcenter));
|
||||
spherical_stereo_transform(&kernel_data.cam, &Pcenter, &Dcenter);
|
||||
|
||||
float3 Px = Pnostereo;
|
||||
float3 Dx = transform_perspective(&rastertocamera,
|
||||
make_float3(raster.x + 1.0f, raster.y, 0.0f));
|
||||
Dx = normalize(transform_direction(&cameratoworld, Dx));
|
||||
spherical_stereo_transform(&kernel_data.cam, &Px, &Dx);
|
||||
|
||||
differential3 dP;
|
||||
differential3 dD;
|
||||
|
||||
dP.dx = Px - Pcenter;
|
||||
dD.dx = Dx - Dcenter;
|
||||
|
||||
float3 Py = Pnostereo;
|
||||
float3 Dy = transform_perspective(&rastertocamera,
|
||||
make_float3(raster.x, raster.y + 1.0f, 0.0f));
|
||||
Dy = normalize(transform_direction(&cameratoworld, Dy));
|
||||
spherical_stereo_transform(&kernel_data.cam, &Py, &Dy);
|
||||
|
||||
dP.dy = Py - Pcenter;
|
||||
dD.dy = Dy - Dcenter;
|
||||
ray->dD = differential_make_compact(dD) * kernel_data.cam.differential_scale;
|
||||
ray->dP = differential_make_compact(dP) * kernel_data.cam.differential_scale;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* clipping */
|
||||
const float z_inv = 1.0f / normalize(Pcamera).z;
|
||||
const float nearclip = kernel_data.cam.nearclip * z_inv;
|
||||
ray->P += nearclip * ray->D;
|
||||
ray->dP += nearclip * ray->dD;
|
||||
ray->tmin = 0.0f;
|
||||
ray->tmax = kernel_data.cam.cliplength * z_inv;
|
||||
|
||||
return one_spectrum();
|
||||
}
|
||||
|
||||
/* Orthographic Camera */
|
||||
ccl_device Spectrum camera_sample_orthographic(KernelGlobals kg,
|
||||
const float2 raster_xy,
|
||||
const float2 rand_lens,
|
||||
ccl_private Ray *ray)
|
||||
{
|
||||
/* create ray form raster position */
|
||||
const ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera;
|
||||
const float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_xy));
|
||||
|
||||
float3 P;
|
||||
float3 D = make_float3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
/* modify ray for depth of field */
|
||||
const float aperturesize = kernel_data.cam.aperturesize;
|
||||
|
||||
if (aperturesize > 0.0f) {
|
||||
/* sample point on aperture */
|
||||
const float2 lens_uv = camera_sample_aperture(&kernel_data.cam, rand_lens) * aperturesize;
|
||||
|
||||
/* compute point on plane of focus */
|
||||
const float3 Pfocus = D * kernel_data.cam.focaldistance;
|
||||
|
||||
/* Update ray for effect of lens */
|
||||
const float3 lens_uvw = make_float3(lens_uv);
|
||||
|
||||
D = normalize(Pfocus - lens_uvw);
|
||||
/* Compute position the ray will be if it traveled until it intersected the near clip plane.
|
||||
* This allows for correct DOF while allowing near clipping. */
|
||||
P = Pcamera + lens_uvw + (D * (kernel_data.cam.nearclip / D.z));
|
||||
}
|
||||
else {
|
||||
P = Pcamera + make_float3(0.0f, 0.0f, kernel_data.cam.nearclip);
|
||||
}
|
||||
/* transform ray from camera to world */
|
||||
Transform cameratoworld = kernel_data.cam.cameratoworld;
|
||||
|
||||
if (kernel_data.cam.num_motion_steps) {
|
||||
transform_motion_array_interpolate(&cameratoworld,
|
||||
kernel_data_array(camera_motion),
|
||||
kernel_data.cam.num_motion_steps,
|
||||
ray->time);
|
||||
}
|
||||
|
||||
ray->P = transform_point(&cameratoworld, P);
|
||||
ray->D = normalize(transform_direction(&cameratoworld, D));
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* ray differential */
|
||||
differential3 dP;
|
||||
dP.dx = make_float3(kernel_data.cam.dx);
|
||||
dP.dy = make_float3(kernel_data.cam.dy);
|
||||
|
||||
ray->dP = differential_make_compact(dP) * kernel_data.cam.differential_scale;
|
||||
ray->dD = differential_zero_compact();
|
||||
#endif
|
||||
|
||||
/* clipping */
|
||||
ray->tmin = 0.0f;
|
||||
ray->tmax = kernel_data.cam.cliplength;
|
||||
|
||||
return one_spectrum();
|
||||
}
|
||||
|
||||
/* Custom Camera */
|
||||
|
||||
ccl_device_inline void camera_sample_to_ray(ccl_constant KernelCamera *cam,
|
||||
const ccl_global DecomposedTransform *cam_motion,
|
||||
float3 P,
|
||||
float3 D,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
float3 Pcenter,
|
||||
float3 Dcenter,
|
||||
float3 Px,
|
||||
float3 Dx,
|
||||
float3 Py,
|
||||
float3 Dy,
|
||||
#endif
|
||||
ccl_private Ray *ray)
|
||||
{
|
||||
/* Transform the ray from camera to world. */
|
||||
Transform cameratoworld = cam->cameratoworld;
|
||||
|
||||
if (cam->num_motion_steps) {
|
||||
transform_motion_array_interpolate(
|
||||
&cameratoworld, cam_motion, cam->num_motion_steps, ray->time);
|
||||
}
|
||||
|
||||
/* Stereo transform */
|
||||
const bool use_stereo = cam->interocular_offset != 0.0f;
|
||||
if (use_stereo) {
|
||||
spherical_stereo_transform(cam, &P, &D);
|
||||
}
|
||||
|
||||
P = transform_point(&cameratoworld, P);
|
||||
D = normalize(transform_direction(&cameratoworld, D));
|
||||
|
||||
ray->P = P;
|
||||
ray->D = D;
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
if (use_stereo) {
|
||||
spherical_stereo_transform(cam, &Pcenter, &Dcenter);
|
||||
spherical_stereo_transform(cam, &Px, &Dx);
|
||||
spherical_stereo_transform(cam, &Py, &Dy);
|
||||
|
||||
differential3 dP;
|
||||
Pcenter = transform_point(&cameratoworld, Pcenter);
|
||||
dP.dx = transform_point(&cameratoworld, Px) - Pcenter;
|
||||
dP.dy = transform_point(&cameratoworld, Py) - Pcenter;
|
||||
ray->dP = differential_make_compact(dP) * cam->differential_scale;
|
||||
}
|
||||
else {
|
||||
ray->dP = differential_zero_compact();
|
||||
}
|
||||
|
||||
differential3 dD;
|
||||
Dcenter = normalize(transform_direction(&cameratoworld, Dcenter));
|
||||
dD.dx = normalize(transform_direction(&cameratoworld, Dx)) - Dcenter;
|
||||
dD.dy = normalize(transform_direction(&cameratoworld, Dy)) - Dcenter;
|
||||
ray->dD = differential_make_compact(dD) * cam->differential_scale;
|
||||
#endif
|
||||
|
||||
/* clipping */
|
||||
const float nearclip = cam->nearclip;
|
||||
ray->P += nearclip * ray->D;
|
||||
ray->dP += nearclip * ray->dD;
|
||||
ray->tmin = 0.0f;
|
||||
ray->tmax = cam->cliplength;
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum camera_sample_custom(KernelGlobals kg,
|
||||
ccl_constant KernelCamera *cam,
|
||||
const ccl_global DecomposedTransform *cam_motion,
|
||||
const float2 raster,
|
||||
const float2 rand_lens,
|
||||
ccl_private Ray *ray,
|
||||
ccl_private int &r_cache_miss)
|
||||
{
|
||||
#ifdef WITH_OSL
|
||||
/* Transform raster position to camera space. */
|
||||
const ProjectionTransform rastertocamera = cam->rastertocamera;
|
||||
float3 sensor = transform_perspective(&rastertocamera, make_float3(raster.x, raster.y, 0.0f));
|
||||
float3 dSdx = transform_perspective_direction(&rastertocamera, make_float3(1.0f, 0.0f, 0.0f));
|
||||
float3 dSdy = transform_perspective_direction(&rastertocamera, make_float3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
ShaderDataTinyStorage sd_storage = {};
|
||||
ccl_private ShaderData *sd = AS_SHADER_DATA(&sd_storage);
|
||||
sd->object = OBJECT_NONE;
|
||||
sd->prim = PRIM_NONE;
|
||||
sd->shader = SHADER_NONE;
|
||||
sd->type = PRIMITIVE_NONE;
|
||||
sd->flag = 0;
|
||||
|
||||
/* Execute OSL shader to sample position, direction and transmission. */
|
||||
packed_float3 P, dPdx, dPdy, D, dDdx, dDdy, throughput;
|
||||
r_cache_miss = false;
|
||||
throughput = osl_eval_camera(
|
||||
kg, sd, sensor, dSdx, dSdy, rand_lens, P, dPdx, dPdy, D, dDdx, dDdy);
|
||||
if (sd->flag & SD_CACHE_MISS) {
|
||||
r_cache_miss = true;
|
||||
return zero_spectrum();
|
||||
}
|
||||
/* Zero throughput indicates failed sampling. */
|
||||
if (is_zero(throughput)) {
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
camera_sample_to_ray(cam,
|
||||
cam_motion,
|
||||
P,
|
||||
D,
|
||||
# ifdef __RAY_DIFFERENTIALS__
|
||||
P,
|
||||
D,
|
||||
P + dPdx,
|
||||
D + dDdx,
|
||||
P + dPdy,
|
||||
D + dDdy,
|
||||
# endif
|
||||
ray);
|
||||
|
||||
return throughput;
|
||||
#else
|
||||
(void)kg;
|
||||
(void)cam;
|
||||
(void)cam_motion;
|
||||
(void)raster;
|
||||
(void)rand_lens;
|
||||
(void)ray;
|
||||
(void)r_cache_miss;
|
||||
return zero_spectrum();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Panorama Camera */
|
||||
|
||||
ccl_device_inline float3 camera_panorama_direction(ccl_constant KernelCamera *cam,
|
||||
const float x,
|
||||
const float y)
|
||||
{
|
||||
const ProjectionTransform rastertocamera = cam->rastertocamera;
|
||||
const float3 Pcamera = transform_perspective(&rastertocamera, make_float3(x, y, 0.0f));
|
||||
return panorama_to_direction(cam, Pcamera.x, Pcamera.y);
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum camera_sample_panorama(ccl_constant KernelCamera *cam,
|
||||
const ccl_global DecomposedTransform *cam_motion,
|
||||
const float2 raster,
|
||||
const float2 rand_lens,
|
||||
ccl_private Ray *ray)
|
||||
{
|
||||
/* Create ray from raster position. */
|
||||
float3 P = zero_float3();
|
||||
float3 D = camera_panorama_direction(cam, raster.x, raster.y);
|
||||
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
/* Ray differentials, computed from scratch using the raster coordinates
|
||||
* because we don't want to be affected by depth of field. We compute
|
||||
* ray origin and direction for the center and two neighboring pixels
|
||||
* and simply take their differences. */
|
||||
float3 Dcenter = D;
|
||||
float3 Dx = camera_panorama_direction(cam, raster.x + 1.0f, raster.y);
|
||||
float3 Dy = camera_panorama_direction(cam, raster.x, raster.y + 1.0f);
|
||||
#endif
|
||||
|
||||
/* Here, zero indicates failed sampling, e.g. when the raster position is outside
|
||||
* the fisheye lens. */
|
||||
if (is_zero(D)) {
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
/* Perform depth-of-field sampling. */
|
||||
const float aperturesize = cam->aperturesize;
|
||||
|
||||
if (aperturesize > 0.0f) {
|
||||
/* Sample a point on the aperture. */
|
||||
const float2 lens_uv = camera_sample_aperture(cam, rand_lens) * aperturesize;
|
||||
|
||||
/* Compute the intersection of the original ray with the focal plane. */
|
||||
const float3 Dfocus = normalize(D);
|
||||
const float3 Pfocus = Dfocus * cam->focaldistance;
|
||||
|
||||
/* Calculate orthonormal coordinate system perpendicular to Dfocus. */
|
||||
const float3 U = normalize(make_float3(1.0f, 0.0f, 0.0f) - Dfocus.x * Dfocus);
|
||||
const float3 V = normalize(cross(Dfocus, U));
|
||||
|
||||
/* Compute new ray by shifting its origin (to account for aperture position) and
|
||||
* setting its direction to meet the original ray at the focal plane. */
|
||||
P = U * lens_uv.x + V * lens_uv.y;
|
||||
D = normalize(Pfocus - P);
|
||||
}
|
||||
|
||||
camera_sample_to_ray(cam,
|
||||
cam_motion,
|
||||
P,
|
||||
D,
|
||||
#ifdef __RAY_DIFFERENTIALS__
|
||||
zero_float3(),
|
||||
Dcenter,
|
||||
zero_float3(),
|
||||
Dx,
|
||||
zero_float3(),
|
||||
Dy,
|
||||
#endif
|
||||
ray);
|
||||
|
||||
return one_spectrum();
|
||||
}
|
||||
|
||||
/* Common */
|
||||
|
||||
/* Generates an outgoing camera ray for the given raster position and random inputs.
|
||||
* Returns camera sensitivity (used to initialize path throughput). */
|
||||
ccl_device_inline Spectrum camera_sample(KernelGlobals kg,
|
||||
const int x,
|
||||
const int y,
|
||||
const float2 filter_uv,
|
||||
const float time,
|
||||
const float2 lens_uv,
|
||||
ccl_private Ray *ray,
|
||||
ccl_private int &r_cache_miss)
|
||||
{
|
||||
float2 raster = make_float2(x, y);
|
||||
|
||||
/* pixel filter */
|
||||
if (kernel_data.integrator.pixel_jitter.x == FLT_MAX) {
|
||||
const int filter_table_offset = kernel_data.tables.filter_table_offset;
|
||||
raster.x += lookup_table_read(kg, filter_uv.x, filter_table_offset, FILTER_TABLE_SIZE);
|
||||
raster.y += lookup_table_read(kg, filter_uv.y, filter_table_offset, FILTER_TABLE_SIZE);
|
||||
}
|
||||
else {
|
||||
raster += -kernel_data.integrator.pixel_jitter;
|
||||
}
|
||||
|
||||
/* motion blur */
|
||||
if (kernel_data.cam.shuttertime == -1.0f) {
|
||||
ray->time = 0.5f;
|
||||
}
|
||||
else {
|
||||
/* TODO(sergey): Such lookup is unneeded when there's rolling shutter
|
||||
* effect in use but rolling shutter duration is set to 0.0.
|
||||
*/
|
||||
const int shutter_table_offset = kernel_data.cam.shutter_table_offset;
|
||||
ray->time = lookup_table_read(kg, time, shutter_table_offset, SHUTTER_TABLE_SIZE);
|
||||
/* TODO(sergey): Currently single rolling shutter effect type only
|
||||
* where scan-lines are acquired from top to bottom and whole scan-line
|
||||
* is acquired at once (no delay in acquisition happens between pixels
|
||||
* of single scan-line).
|
||||
*
|
||||
* Might want to support more models in the future.
|
||||
*/
|
||||
if (kernel_data.cam.rolling_shutter_type) {
|
||||
/* Time corresponding to a fully rolling shutter only effect:
|
||||
* top of the frame is time 0.0, bottom of the frame is time 1.0.
|
||||
*/
|
||||
const float time = 1.0f - (float)y / kernel_data.cam.height;
|
||||
const float duration = kernel_data.cam.rolling_shutter_duration;
|
||||
if (duration != 0.0f) {
|
||||
/* This isn't fully physical correct, but lets us to have simple
|
||||
* controls in the interface. The idea here is basically sort of
|
||||
* linear interpolation between how much rolling shutter effect
|
||||
* exist on the frame and how much of it is a motion blur effect.
|
||||
*/
|
||||
ray->time = (ray->time - 0.5f) * duration;
|
||||
ray->time += (time - 0.5f) * (1.0f - duration) + 0.5f;
|
||||
}
|
||||
else {
|
||||
ray->time = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sample */
|
||||
r_cache_miss = false;
|
||||
if (kernel_data.cam.type == CAMERA_PERSPECTIVE) {
|
||||
return camera_sample_perspective(kg, raster, lens_uv, ray);
|
||||
}
|
||||
if (kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) {
|
||||
return camera_sample_orthographic(kg, raster, lens_uv, ray);
|
||||
}
|
||||
if (kernel_data.cam.type == CAMERA_PANORAMA) {
|
||||
const ccl_global DecomposedTransform *cam_motion = kernel_data_array(camera_motion);
|
||||
return camera_sample_panorama(&kernel_data.cam, cam_motion, raster, lens_uv, ray);
|
||||
}
|
||||
if (kernel_data.cam.type == CAMERA_CUSTOM) {
|
||||
const ccl_global DecomposedTransform *cam_motion = kernel_data_array(camera_motion);
|
||||
return camera_sample_custom(
|
||||
kg, &kernel_data.cam, cam_motion, raster, lens_uv, ray, r_cache_miss);
|
||||
}
|
||||
kernel_assert(false);
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
ccl_device_inline float3 camera_position(KernelGlobals kg)
|
||||
{
|
||||
const Transform cameratoworld = kernel_data.cam.cameratoworld;
|
||||
return make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w);
|
||||
}
|
||||
|
||||
ccl_device_inline float camera_distance(KernelGlobals kg, const float3 P)
|
||||
{
|
||||
const Transform cameratoworld = kernel_data.cam.cameratoworld;
|
||||
const float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w);
|
||||
|
||||
if (kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) {
|
||||
const float3 camD = make_float3(cameratoworld.x.z, cameratoworld.y.z, cameratoworld.z.z);
|
||||
return fabsf(dot((P - camP), camD));
|
||||
}
|
||||
return len(P - camP);
|
||||
}
|
||||
|
||||
ccl_device_inline float camera_z_depth(KernelGlobals kg, const float3 P)
|
||||
{
|
||||
if (kernel_data.cam.type == CAMERA_PERSPECTIVE || kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) {
|
||||
const Transform worldtocamera = kernel_data.cam.worldtocamera;
|
||||
return transform_point(&worldtocamera, P).z;
|
||||
}
|
||||
const Transform cameratoworld = kernel_data.cam.cameratoworld;
|
||||
const float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w);
|
||||
return len(P - camP);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 camera_direction_from_point(KernelGlobals kg, const float3 P)
|
||||
{
|
||||
const Transform cameratoworld = kernel_data.cam.cameratoworld;
|
||||
|
||||
if (kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) {
|
||||
const float3 camD = make_float3(cameratoworld.x.z, cameratoworld.y.z, cameratoworld.z.z);
|
||||
return -camD;
|
||||
}
|
||||
const float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w);
|
||||
return normalize(camP - P);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 camera_world_to_ndc(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
float3 P)
|
||||
{
|
||||
if (kernel_data.cam.type == CAMERA_PERSPECTIVE || kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) {
|
||||
/* perspective / ortho */
|
||||
if (sd->object == PRIM_NONE && kernel_data.cam.type == CAMERA_PERSPECTIVE) {
|
||||
P += camera_position(kg);
|
||||
}
|
||||
|
||||
const ProjectionTransform tfm = kernel_data.cam.worldtondc;
|
||||
return transform_perspective(&tfm, P);
|
||||
}
|
||||
/* panorama or custom */
|
||||
const Transform tfm = kernel_data.cam.worldtocamera;
|
||||
|
||||
if (sd->object != OBJECT_NONE) {
|
||||
P = normalize(transform_point(&tfm, P));
|
||||
}
|
||||
else {
|
||||
P = normalize(transform_direction(&tfm, P));
|
||||
}
|
||||
|
||||
if (kernel_data.cam.type == CAMERA_PANORAMA) {
|
||||
return make_float3(direction_to_panorama(&kernel_data.cam, P));
|
||||
}
|
||||
/* TODO: Fall back to camera coordinates until we have inverse mappings for custom cameras. */
|
||||
return P;
|
||||
}
|
||||
|
||||
/* Motion vector for motion pass */
|
||||
|
||||
ccl_device_forceinline float4 camera_motion_vector_direction(KernelGlobals kg, const float3 D)
|
||||
{
|
||||
Transform tfm;
|
||||
float3 motion_center;
|
||||
float3 motion_pre;
|
||||
float3 motion_post;
|
||||
|
||||
/* Camera motion, for perspective/orthographic motion.pre/post will be a
|
||||
* world-to-raster matrix, for panorama it's world-to-camera, for custom
|
||||
* we fall back to the world position until we have inverse mapping for it */
|
||||
if (kernel_data.cam.type == CAMERA_CUSTOM) {
|
||||
/* TODO: Custom cameras don't have inverse mappings yet, so we fall back to
|
||||
* camera-space vectors here for now. */
|
||||
tfm = kernel_data.cam.worldtocamera;
|
||||
motion_center = normalize(transform_direction(&tfm, D));
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_pre;
|
||||
motion_pre = normalize(transform_direction(&tfm, D));
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_post;
|
||||
motion_post = normalize(transform_direction(&tfm, D));
|
||||
}
|
||||
else if (kernel_data.cam.type != CAMERA_PANORAMA) {
|
||||
/* Perspective and orthographics camera use the world-to-raster matrix. */
|
||||
ProjectionTransform projection = kernel_data.cam.worldtoraster;
|
||||
motion_center = transform_perspective_direction(&projection, D);
|
||||
|
||||
projection = kernel_data.cam.perspective_pre;
|
||||
motion_pre = transform_perspective_direction(&projection, D);
|
||||
|
||||
projection = kernel_data.cam.perspective_post;
|
||||
motion_post = transform_perspective_direction(&projection, D);
|
||||
}
|
||||
else {
|
||||
/* Panorama cameras have their own inverse mappings. */
|
||||
tfm = kernel_data.cam.worldtocamera;
|
||||
motion_center = normalize(transform_direction(&tfm, D));
|
||||
motion_center = make_float3(direction_to_panorama(&kernel_data.cam, motion_center));
|
||||
motion_center.x *= kernel_data.cam.width;
|
||||
motion_center.y *= kernel_data.cam.height;
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_pre;
|
||||
motion_pre = normalize(transform_direction(&tfm, D));
|
||||
motion_pre = make_float3(direction_to_panorama(&kernel_data.cam, motion_pre));
|
||||
motion_pre.x *= kernel_data.cam.width;
|
||||
motion_pre.y *= kernel_data.cam.height;
|
||||
|
||||
tfm = kernel_data.cam.motion_pass_post;
|
||||
motion_post = normalize(transform_direction(&tfm, D));
|
||||
motion_post = make_float3(direction_to_panorama(&kernel_data.cam, motion_post));
|
||||
motion_post.x *= kernel_data.cam.width;
|
||||
motion_post.y *= kernel_data.cam.height;
|
||||
}
|
||||
|
||||
motion_pre = motion_pre - motion_center;
|
||||
motion_post = motion_center - motion_post;
|
||||
|
||||
return make_float4(motion_pre.x, motion_pre.y, motion_post.x, motion_post.y);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
382
blender-5.2.0/intern/cycles/kernel/camera/projection.h
Normal file
382
blender-5.2.0/intern/cycles/kernel/camera/projection.h
Normal file
@@ -0,0 +1,382 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Equirectangular coordinates <-> Cartesian direction */
|
||||
|
||||
ccl_device float2 direction_to_equirectangular_range(const float3 dir, const float4 range)
|
||||
{
|
||||
if (is_zero(dir)) {
|
||||
return zero_float2();
|
||||
}
|
||||
|
||||
const float u = (atan2f(dir.y, dir.x) - range.y) / range.x;
|
||||
const float v = (acosf(dir.z / len(dir)) - range.w) / range.z;
|
||||
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
ccl_device dual2 direction_to_equirectangular_range(const dual3 dir, const float4 range)
|
||||
{
|
||||
if (is_zero(dir)) {
|
||||
return make_zero<dual2>();
|
||||
}
|
||||
|
||||
const dual1 u = (atan2(dir.y(), dir.x()) - range.y) / range.x;
|
||||
const dual1 v = (acos(dir.z() / len(dir)) - range.w) / range.z;
|
||||
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
ccl_device float3 equirectangular_range_to_direction(const float u,
|
||||
const float v,
|
||||
const float4 range)
|
||||
{
|
||||
const float phi = range.x * u + range.y;
|
||||
const float theta = range.z * v + range.w;
|
||||
return spherical_to_direction(theta, phi);
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_equirectangular(const float3 dir)
|
||||
{
|
||||
return direction_to_equirectangular_range(dir, make_float4(-M_2PI_F, M_PI_F, -M_PI_F, M_PI_F));
|
||||
}
|
||||
|
||||
ccl_device dual2 direction_to_equirectangular(const dual3 dir)
|
||||
{
|
||||
return direction_to_equirectangular_range(dir, make_float4(-M_2PI_F, M_PI_F, -M_PI_F, M_PI_F));
|
||||
}
|
||||
|
||||
ccl_device float3 equirectangular_to_direction(const float u, const float v)
|
||||
{
|
||||
return equirectangular_range_to_direction(u, v, make_float4(-M_2PI_F, M_PI_F, -M_PI_F, M_PI_F));
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_central_cylindrical(const float3 dir, const float4 range)
|
||||
{
|
||||
const float z = dir.z / len(make_float2(dir.x, dir.y));
|
||||
const float theta = atan2f(dir.y, dir.x);
|
||||
const float u = inverse_lerp(range.x, range.y, theta);
|
||||
const float v = inverse_lerp(range.z, range.w, z);
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
ccl_device float3 central_cylindrical_to_direction(const float u,
|
||||
const float v,
|
||||
const float4 range)
|
||||
{
|
||||
const float theta = mix(range.x, range.y, u);
|
||||
const float z = mix(range.z, range.w, v);
|
||||
return make_float3(cosf(theta), sinf(theta), z);
|
||||
}
|
||||
|
||||
/* Fisheye <-> Cartesian direction */
|
||||
|
||||
ccl_device_inline float3 fisheye_to_direction(const float theta,
|
||||
const float u,
|
||||
float v,
|
||||
const float r)
|
||||
{
|
||||
float phi = safe_acosf(safe_divide(u, r));
|
||||
if (v < 0.0f) {
|
||||
phi = -phi;
|
||||
}
|
||||
|
||||
return make_float3(cosf(theta), -cosf(phi) * sinf(theta), sinf(phi) * sinf(theta));
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_fisheye_equidistant(const float3 dir, const float fov)
|
||||
{
|
||||
const float r = atan2f(len(make_float2(dir.y, dir.z)), dir.x) / fov;
|
||||
const float2 uv = r * safe_normalize(make_float2(dir.y, dir.z));
|
||||
return make_float2(0.5f - uv.x, uv.y + 0.5f);
|
||||
}
|
||||
|
||||
ccl_device float3 fisheye_equidistant_to_direction(float u, float v, float fov)
|
||||
{
|
||||
u = (u - 0.5f) * 2.0f;
|
||||
v = (v - 0.5f) * 2.0f;
|
||||
|
||||
const float r = sqrtf(u * u + v * v);
|
||||
|
||||
if (r > 1.0f) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
const float theta = r * fov * 0.5f;
|
||||
|
||||
return fisheye_to_direction(theta, u, v, r);
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_fisheye_equisolid(const float3 dir,
|
||||
const float lens,
|
||||
const float width,
|
||||
const float height)
|
||||
{
|
||||
const float theta = safe_acosf(dir.x);
|
||||
const float r = 2.0f * lens * sinf(theta * 0.5f);
|
||||
|
||||
const float2 uv = r * safe_normalize(make_float2(dir.y, dir.z));
|
||||
return make_float2(0.5f - uv.x / width, uv.y / height + 0.5f);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 fisheye_equisolid_to_direction(
|
||||
float u, float v, float lens, const float fov, const float width, const float height)
|
||||
{
|
||||
u = (u - 0.5f) * width;
|
||||
v = (v - 0.5f) * height;
|
||||
|
||||
const float rmax = 2.0f * lens * sinf(fov * 0.25f);
|
||||
const float r = sqrtf(u * u + v * v);
|
||||
|
||||
if (r > rmax) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
const float theta = 2.0f * asinf(r / (2.0f * lens));
|
||||
|
||||
return fisheye_to_direction(theta, u, v, r);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 fisheye_lens_polynomial_to_direction(float u,
|
||||
float v,
|
||||
float coeff0,
|
||||
const float4 coeffs,
|
||||
const float fov,
|
||||
const float width,
|
||||
const float height)
|
||||
{
|
||||
u = (u - 0.5f) * width;
|
||||
v = (v - 0.5f) * height;
|
||||
|
||||
const float r = sqrtf(u * u + v * v);
|
||||
const float r2 = r * r;
|
||||
const float4 rr = make_float4(r, r2, r2 * r, r2 * r2);
|
||||
const float theta = -(coeff0 + dot(coeffs, rr));
|
||||
|
||||
if (fabsf(theta) > 0.5f * fov) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
return fisheye_to_direction(theta, u, v, r);
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_fisheye_lens_polynomial(
|
||||
float3 dir, const float coeff0, const float4 coeffs, const float width, const float height)
|
||||
{
|
||||
const float theta = -safe_acosf(dir.x);
|
||||
|
||||
/* Initialize r with the closed-form solution for the special case
|
||||
* coeffs.y = coeffs.z = coeffs.w = 0 */
|
||||
float r = (theta - coeff0) / coeffs.x;
|
||||
|
||||
const float4 diff_coeffs = make_float4(1.0f, 2.0f, 3.0f, 4.0f) * coeffs;
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
/** \name Newton's Method for Finding Roots
|
||||
*
|
||||
* Given is the result theta = distortion_model(r),
|
||||
* we need to find r.
|
||||
* Let F(r) := theta - distortion_model(r).
|
||||
* Then F(r) = 0 <=> distortion_model(r) = theta
|
||||
* Therefore we apply Newton's method for finding a root of F(r).
|
||||
* Newton step for the function F:
|
||||
* r_n+1 = r_n - F(r_n) / F'(r_n)
|
||||
* The addition in the implementation is due to canceling of signs.
|
||||
* \{ */
|
||||
const float old_r = r;
|
||||
const float r2 = r * r;
|
||||
const float F_r = theta - (coeff0 + dot(coeffs, make_float4(r, r2, r2 * r, r2 * r2)));
|
||||
const float dF_r = dot(diff_coeffs, make_float4(1.0f, r, r2, r2 * r));
|
||||
r += F_r / dF_r;
|
||||
|
||||
/* Early termination if the change is below the threshold */
|
||||
if (fabsf(r - old_r) < 1e-6f) {
|
||||
break;
|
||||
}
|
||||
/** \} */
|
||||
}
|
||||
|
||||
const float2 uv = r * safe_normalize(make_float2(dir.y, dir.z));
|
||||
return make_float2(0.5f - uv.x / width, uv.y / height + 0.5f);
|
||||
}
|
||||
|
||||
/* Mirror Ball <-> Cartesian direction. */
|
||||
|
||||
ccl_device float3 mirrorball_to_direction(const float u, const float v)
|
||||
{
|
||||
/* point on sphere */
|
||||
float3 dir;
|
||||
|
||||
dir.x = 2.0f * u - 1.0f;
|
||||
dir.z = 2.0f * v - 1.0f;
|
||||
|
||||
if (dir.x * dir.x + dir.z * dir.z > 1.0f) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
dir.y = -sqrtf(max(1.0f - dir.x * dir.x - dir.z * dir.z, 0.0f));
|
||||
|
||||
/* reflection */
|
||||
const float3 I = make_float3(0.0f, -1.0f, 0.0f);
|
||||
|
||||
return 2.0f * dot(dir, I) * dir - I;
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_mirrorball(float3 dir)
|
||||
{
|
||||
/* inverse of mirrorball_to_direction */
|
||||
dir.y -= 1.0f;
|
||||
|
||||
const float div = 2.0f * sqrtf(max(-0.5f * dir.y, 0.0f));
|
||||
if (div > 0.0f) {
|
||||
dir /= div;
|
||||
}
|
||||
|
||||
const float u = 0.5f * (dir.x + 1.0f);
|
||||
const float v = 0.5f * (dir.z + 1.0f);
|
||||
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
ccl_device dual2 direction_to_mirrorball(dual3 dir)
|
||||
{
|
||||
/* inverse of mirrorball_to_direction */
|
||||
dir.val.y -= 1.0f;
|
||||
|
||||
dir = dir * 0.5f * inversesqrt(-0.5f * dir.y());
|
||||
|
||||
const dual1 u = 0.5f * (dir.x() + 1.0f);
|
||||
const dual1 v = 0.5f * (dir.z() + 1.0f);
|
||||
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
/* Single face of a equiangular cube map projection as described in
|
||||
* https://blog.google/products/google-ar-vr/bringing-pixels-front-and-center-vr-video/ */
|
||||
ccl_device float3 equiangular_cubemap_face_to_direction(float u, float v)
|
||||
{
|
||||
u = tanf((0.5f - u) * M_PI_2_F);
|
||||
v = tanf((v - 0.5f) * M_PI_2_F);
|
||||
|
||||
return normalize(make_float3(1.0f, u, v));
|
||||
}
|
||||
|
||||
ccl_device float2 direction_to_equiangular_cubemap_face(const float3 dir)
|
||||
{
|
||||
const float u = 0.5f - atan2f(dir.y, dir.x) * 2.0f / M_PI_F;
|
||||
const float v = atan2f(dir.z, dir.x) * 2.0f / M_PI_F + 0.5f;
|
||||
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
ccl_device_inline float3 panorama_to_direction(ccl_constant KernelCamera *cam,
|
||||
const float u,
|
||||
float v)
|
||||
{
|
||||
switch (cam->panorama_type) {
|
||||
case PANORAMA_EQUIRECTANGULAR:
|
||||
return equirectangular_range_to_direction(u, v, cam->equirectangular_range);
|
||||
case PANORAMA_EQUIANGULAR_CUBEMAP_FACE:
|
||||
return equiangular_cubemap_face_to_direction(u, v);
|
||||
case PANORAMA_MIRRORBALL:
|
||||
return mirrorball_to_direction(u, v);
|
||||
case PANORAMA_FISHEYE_EQUIDISTANT:
|
||||
return fisheye_equidistant_to_direction(u, v, cam->fisheye_fov);
|
||||
case PANORAMA_FISHEYE_LENS_POLYNOMIAL:
|
||||
return fisheye_lens_polynomial_to_direction(u,
|
||||
v,
|
||||
cam->fisheye_lens_polynomial_bias,
|
||||
cam->fisheye_lens_polynomial_coefficients,
|
||||
cam->fisheye_fov,
|
||||
cam->sensorwidth,
|
||||
cam->sensorheight);
|
||||
case PANORAMA_CENTRAL_CYLINDRICAL:
|
||||
return central_cylindrical_to_direction(u, v, cam->central_cylindrical_range);
|
||||
case PANORAMA_FISHEYE_EQUISOLID:
|
||||
default:
|
||||
return fisheye_equisolid_to_direction(
|
||||
u, v, cam->fisheye_lens, cam->fisheye_fov, cam->sensorwidth, cam->sensorheight);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline float2 direction_to_panorama(ccl_constant KernelCamera *cam, const float3 dir)
|
||||
{
|
||||
switch (cam->panorama_type) {
|
||||
case PANORAMA_EQUIRECTANGULAR:
|
||||
return direction_to_equirectangular_range(dir, cam->equirectangular_range);
|
||||
case PANORAMA_EQUIANGULAR_CUBEMAP_FACE:
|
||||
return direction_to_equiangular_cubemap_face(dir);
|
||||
case PANORAMA_MIRRORBALL:
|
||||
return direction_to_mirrorball(dir);
|
||||
case PANORAMA_FISHEYE_EQUIDISTANT:
|
||||
return direction_to_fisheye_equidistant(dir, cam->fisheye_fov);
|
||||
case PANORAMA_FISHEYE_LENS_POLYNOMIAL:
|
||||
return direction_to_fisheye_lens_polynomial(dir,
|
||||
cam->fisheye_lens_polynomial_bias,
|
||||
cam->fisheye_lens_polynomial_coefficients,
|
||||
cam->sensorwidth,
|
||||
cam->sensorheight);
|
||||
case PANORAMA_CENTRAL_CYLINDRICAL:
|
||||
return direction_to_central_cylindrical(dir, cam->central_cylindrical_range);
|
||||
case PANORAMA_FISHEYE_EQUISOLID:
|
||||
default:
|
||||
return direction_to_fisheye_equisolid(
|
||||
dir, cam->fisheye_lens, cam->sensorwidth, cam->sensorheight);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline void spherical_stereo_transform(ccl_constant KernelCamera *cam,
|
||||
ccl_private float3 *P,
|
||||
ccl_private float3 *D)
|
||||
{
|
||||
float interocular_offset = cam->interocular_offset;
|
||||
|
||||
/* Interocular offset of zero means either non stereo, or stereo without
|
||||
* spherical stereo. */
|
||||
kernel_assert(interocular_offset != 0.0f);
|
||||
|
||||
if (cam->pole_merge_angle_to > 0.0f) {
|
||||
const float pole_merge_angle_from = cam->pole_merge_angle_from;
|
||||
const float pole_merge_angle_to = cam->pole_merge_angle_to;
|
||||
const float altitude = fabsf(safe_asinf((*D).z));
|
||||
if (altitude > pole_merge_angle_to) {
|
||||
interocular_offset = 0.0f;
|
||||
}
|
||||
else if (altitude > pole_merge_angle_from) {
|
||||
const float fac = (altitude - pole_merge_angle_from) /
|
||||
(pole_merge_angle_to - pole_merge_angle_from);
|
||||
const float fade = cosf(fac * M_PI_2_F);
|
||||
interocular_offset *= fade;
|
||||
}
|
||||
}
|
||||
|
||||
const float3 up = make_float3(0.0f, 0.0f, 1.0f);
|
||||
const float3 side = normalize(cross(*D, up));
|
||||
const float3 stereo_offset = side * interocular_offset;
|
||||
|
||||
*P += stereo_offset;
|
||||
|
||||
/* Convergence distance is FLT_MAX in the case of parallel convergence mode,
|
||||
* no need to modify direction in this case either. */
|
||||
const float convergence_distance = cam->convergence_distance;
|
||||
|
||||
if (convergence_distance != FLT_MAX) {
|
||||
const float3 screen_offset = convergence_distance * (*D);
|
||||
*D = normalize(screen_offset - stereo_offset);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
120
blender-5.2.0/intern/cycles/kernel/closure/alloc.h
Normal file
120
blender-5.2.0/intern/cycles/kernel/closure/alloc.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device ccl_private ShaderClosure *closure_alloc(ccl_private ShaderData *sd,
|
||||
const uint size,
|
||||
ClosureType type,
|
||||
Spectrum weight)
|
||||
{
|
||||
kernel_assert(size <= sizeof(ShaderClosure));
|
||||
(void)size;
|
||||
|
||||
if (sd->num_closure_left == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ccl_private ShaderClosure *sc = &sd->closure[sd->num_closure];
|
||||
|
||||
sc->type = type;
|
||||
sc->weight = weight;
|
||||
|
||||
sd->num_closure++;
|
||||
sd->num_closure_left--;
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
ccl_device ccl_private void *closure_alloc_extra(ccl_private ShaderData *sd, const int size)
|
||||
{
|
||||
/* Allocate extra space for closure that need more parameters. We allocate
|
||||
* in chunks of sizeof(ShaderClosure) starting from the end of the closure
|
||||
* array.
|
||||
*
|
||||
* This lets us keep the same fast array iteration over closures, as we
|
||||
* found linked list iteration and iteration with skipping to be slower. */
|
||||
const int num_extra = ((size + sizeof(ShaderClosure) - 1) / sizeof(ShaderClosure));
|
||||
|
||||
if (num_extra > sd->num_closure_left) {
|
||||
/* Remove previous closure if it was allocated. */
|
||||
sd->num_closure--;
|
||||
sd->num_closure_left++;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sd->num_closure_left -= num_extra;
|
||||
return (ccl_private void *)(sd->closure + sd->num_closure + sd->num_closure_left);
|
||||
}
|
||||
|
||||
ccl_device_inline float closure_sample_weight(const int flag, ccl_private Spectrum &weight)
|
||||
{
|
||||
kernel_assert(isfinite_safe(weight));
|
||||
|
||||
/* No negative weights allowed. */
|
||||
weight = max(weight, zero_float3());
|
||||
|
||||
const float sample_weight = fabsf(average(weight));
|
||||
|
||||
/* Do not perform weight cutoff for volume shaders, because large volume with low density could
|
||||
* still contribute significantly to the scene. It should be up to the volume shader to decide
|
||||
* the cutoff. */
|
||||
/* Use comparison this way to help dealing with non-finite weight: if the average is not finite
|
||||
* we will not allocate new closure. */
|
||||
if ((sample_weight >= CLOSURE_WEIGHT_CUTOFF) || (flag & SD_IS_VOLUME_SHADER_EVAL)) {
|
||||
return sample_weight;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
ccl_device_inline ccl_private ShaderClosure *bsdf_alloc(ccl_private ShaderData *sd,
|
||||
const int size,
|
||||
Spectrum weight)
|
||||
{
|
||||
const float sample_weight = closure_sample_weight(sd->flag, weight);
|
||||
if (!(sample_weight > 0.0f)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ccl_private ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
|
||||
if (!sc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sc->sample_weight = sample_weight;
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
/* Allocate BSDF closures that are possibly used for emission. */
|
||||
template<class Bsdf>
|
||||
ccl_device_inline ccl_private Bsdf *bsdf_alloc_maybe_emission(ccl_private ShaderData *sd,
|
||||
ccl_private Bsdf *bsdf,
|
||||
const uint32_t path_flag,
|
||||
Spectrum weight)
|
||||
{
|
||||
if (path_flag & PATH_RAY_EMISSION) {
|
||||
/* When evaluating emission we don't allocate closures, but we still need a valid closure to
|
||||
* compute the weight. */
|
||||
const float sample_weight = closure_sample_weight(sd->flag, weight);
|
||||
if (!(sample_weight > 0.0f)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bsdf->weight = weight;
|
||||
bsdf->sample_weight = sample_weight;
|
||||
}
|
||||
else {
|
||||
bsdf = (ccl_private Bsdf *)bsdf_alloc(sd, sizeof(Bsdf), weight);
|
||||
}
|
||||
|
||||
return bsdf;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
725
blender-5.2.0/intern/cycles/kernel/closure/bsdf.h
Normal file
725
blender-5.2.0/intern/cycles/kernel/closure/bsdf.h
Normal file
@@ -0,0 +1,725 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
// clang-format off
|
||||
#include "kernel/closure/bsdf_ashikhmin_velvet.h"
|
||||
#include "kernel/closure/bsdf_diffuse.h"
|
||||
#include "kernel/closure/bsdf_oren_nayar.h"
|
||||
#include "kernel/closure/bsdf_phong_ramp.h"
|
||||
#include "kernel/closure/bsdf_diffuse_ramp.h"
|
||||
#include "kernel/closure/bsdf_microfacet.h"
|
||||
#include "kernel/closure/bsdf_burley.h"
|
||||
#include "kernel/closure/bsdf_sheen.h"
|
||||
#include "kernel/closure/bsdf_transparent.h"
|
||||
#include "kernel/closure/bsdf_ray_portal.h"
|
||||
#include "kernel/closure/bsdf_ashikhmin_shirley.h"
|
||||
#include "kernel/closure/bsdf_toon.h"
|
||||
#include "kernel/closure/bsdf_hair.h"
|
||||
#include "kernel/closure/bsdf_principled_hair_chiang.h"
|
||||
#include "kernel/closure/bsdf_principled_hair_huang.h"
|
||||
// clang-format on
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Returns the square of the roughness of the closure if it has roughness,
|
||||
* 0 for singular closures and 1 otherwise. */
|
||||
ccl_device_inline float bsdf_get_specular_roughness_squared(const ccl_private ShaderClosure *sc)
|
||||
{
|
||||
if (CLOSURE_IS_BSDF_SINGULAR(sc->type)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (CLOSURE_IS_BSDF_MICROFACET(sc->type)) {
|
||||
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc;
|
||||
return bsdf->alpha_x * bsdf->alpha_y;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
ccl_device_inline float bsdf_get_roughness_pass_squared(const ccl_private ShaderClosure *sc)
|
||||
{
|
||||
if (sc->type == CLOSURE_BSDF_OREN_NAYAR_ID || sc->type == CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID) {
|
||||
ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)sc;
|
||||
return sqr(sqr(bsdf->param.roughness));
|
||||
}
|
||||
|
||||
/* For the Principled BSDF, we want the Roughness pass to return the value that
|
||||
* was set in the node. However, this value doesn't affect all closures (e.g.
|
||||
* diffuse), so skip those that don't really have a concept of roughness. */
|
||||
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
return bsdf_get_specular_roughness_squared(sc);
|
||||
}
|
||||
|
||||
/* Widen the compact ray differential dD after a non-specular bounce so that
|
||||
* texture mip selection on subsequent hits reflects the BSDF lobe's angular
|
||||
* spread. This significantly save memory, and is needed to make image cache
|
||||
* memory usage scale with render tile size rather than overall resolution.
|
||||
*
|
||||
* This must be done consistently between next event estimation and forward
|
||||
* sampling for both to converge to the same result for MIS. This is not just
|
||||
* a theoretical concern, but can otherwise lead to seams.
|
||||
*
|
||||
* To achieve that, the sampled roughness is computed as a MIS weighted
|
||||
* average. This makes it so directions with high contribution from sharp
|
||||
* BSDFs have a lower roughness, as they will have a high MIS weight. */
|
||||
ccl_device_forceinline float bsdf_widen_dD(const float prev_dD, const float avg_roughness_squared)
|
||||
{
|
||||
if (!(avg_roughness_squared > 0.0f)) {
|
||||
return prev_dD;
|
||||
}
|
||||
|
||||
return max(prev_dD, sqrtf(avg_roughness_squared));
|
||||
}
|
||||
|
||||
/* An additional term to smooth illumination on grazing angles when using bump mapping
|
||||
* based on "A Microfacet-Based Shadowing Function to Solve the Bump Terminator Problem"
|
||||
* by Alejandro Conty Estevez, Pascal Lecocq, and Clifford Stein. It preserves detail
|
||||
* close to the shadow terminator, and doesn't "wash out" intermediate bumps using a
|
||||
* Cook-Torrance GGX function for shading. */
|
||||
ccl_device_inline float bump_shadowing_term(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 I,
|
||||
const bool is_eval)
|
||||
{
|
||||
if (isequal(sc->N, sd->N)) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* Smoothing doesn't apply to curve geometry. */
|
||||
if (sd->type & PRIMITIVE_CURVE) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* In order to avoid artifacts at the shadow terminator when using smooth normals,
|
||||
* the BSDF evaluation functions allow for light leaking through the actual geometry
|
||||
* and only checks that the directions are in the correct hemisphere w.r.t. the
|
||||
* shading normal.
|
||||
* However, when using bump/normal mapping, this can lead to light leaking not just
|
||||
* "around" the shadow terminator, but to the rear side of supposedly opaque geometry.
|
||||
* In order to detect this case, we can ensure that the direction is also valid w.r.t.
|
||||
* the smoothed (but non-bump-mapped) normal `sd->N` (or `Ns` for short below).
|
||||
*
|
||||
* `dot(Ns, I) * dot(Ns, N)` tells us if I and N are on the same side of the smoothed geometry.
|
||||
* If incoming(I) and normal(N) are on the same side we reject refractions, `dot(N, I) < 0`.
|
||||
* If they are on different sides we reject reflections, `dot(N, I) > 0`. */
|
||||
const float cosNsI = dot(sd->N, I);
|
||||
const float cosNsN = dot(sd->N, sc->N);
|
||||
const float cosNI = dot(sc->N, I);
|
||||
const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type);
|
||||
if (cosNsI * cosNsN * cosNI < 0.0f && (is_eval || is_diffuse)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* The above test applies to all closures, but the softening only applies to diffuse ones. */
|
||||
if (!is_diffuse) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* When bump map correction is not used do skip the smoothing. */
|
||||
if ((sd->flag & SD_USE_BUMP_MAP_CORRECTION) == 0) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* Get absolute incoming and shader normal deviation from smoothed normal, then clamp. */
|
||||
const float cos_i = fabsf(cosNsI);
|
||||
const float cos_d = fabsf(cosNsN);
|
||||
if (cos_d >= 1.0f || cos_i >= 1.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (cos_i < 1e-6f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Get GGX shading values for final smoothing. */
|
||||
const float tan2_d = 1.0f / sqr(cos_d) - 1.0f;
|
||||
const float bump_alpha2 = saturatef(0.125f * tan2_d);
|
||||
|
||||
/* Return smoothed value to avoid discontinuity at perpendicular angle. */
|
||||
return bsdf_G<MicrofacetType::GGX>(bump_alpha2, cos_i);
|
||||
}
|
||||
|
||||
ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multiplier)
|
||||
{
|
||||
/* Shadow terminator workaround, taken from Appleseed.
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2019 Francois Beaune, The appleseedhq Organization */
|
||||
cos_in = min(cos_in, 1.0f);
|
||||
|
||||
const float angle = fast_acosf(cos_in);
|
||||
const float val = max(cosf(angle * frequency_multiplier), 0.0f) / cos_in;
|
||||
return val;
|
||||
}
|
||||
|
||||
ccl_device_inline bool bsdf_is_transmission(const ccl_private ShaderClosure *sc, const float3 wo)
|
||||
{
|
||||
return dot(sc->N, wo) < 0.0f;
|
||||
}
|
||||
|
||||
ccl_device_inline int bsdf_sample(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness,
|
||||
ccl_private float *eta)
|
||||
{
|
||||
/* For curves use the smooth normal, particularly for ribbons the geometric
|
||||
* normal gives too much darkening otherwise. */
|
||||
*eval = zero_spectrum();
|
||||
*pdf = 0.f;
|
||||
int label = LABEL_NONE;
|
||||
const float3 Ng = (sd->type & PRIMITIVE_CURVE) ? sc->N : sd->Ng;
|
||||
const float2 rand_xy = make_float2(rand);
|
||||
|
||||
switch (sc->type) {
|
||||
case CLOSURE_BSDF_DIFFUSE_ID:
|
||||
label = bsdf_diffuse_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
#if defined(__SVM__) || defined(__OSL__)
|
||||
case CLOSURE_BSDF_OREN_NAYAR_ID:
|
||||
label = bsdf_oren_nayar_sample(
|
||||
sc, Ng, sd->wi, rand_xy, eval, wo, pdf, sampled_roughness, eta);
|
||||
break;
|
||||
case CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID:
|
||||
label = bsdf_rough_translucent_sample(
|
||||
sc, Ng, sd->wi, rand_xy, eval, wo, pdf, sampled_roughness, eta);
|
||||
break;
|
||||
# ifdef __OSL__
|
||||
case CLOSURE_BSDF_BURLEY_ID:
|
||||
label = bsdf_burley_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_PHONG_RAMP_ID:
|
||||
label = bsdf_phong_ramp_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf, sampled_roughness);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
|
||||
label = bsdf_diffuse_ramp_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_TRANSLUCENT_ID:
|
||||
label = bsdf_translucent_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_TRANSPARENT_ID:
|
||||
label = bsdf_transparent_sample(sc, Ng, sd->wi, eval, wo, pdf);
|
||||
*sampled_roughness = zero_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_RAY_PORTAL_ID:
|
||||
/* ray portals are not handled by the BSDF code, we should never get here */
|
||||
kernel_assert(false);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
|
||||
label = bsdf_microfacet_ggx_sample(
|
||||
kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
|
||||
break;
|
||||
case CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID:
|
||||
label = bsdf_thin_glass_transmission_sample(
|
||||
kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
|
||||
label = bsdf_microfacet_beckmann_sample(
|
||||
kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta);
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
|
||||
label = bsdf_ashikhmin_shirley_sample(
|
||||
sc, Ng, sd->wi, rand_xy, eval, wo, pdf, sampled_roughness);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
|
||||
label = bsdf_ashikhmin_velvet_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
|
||||
label = bsdf_diffuse_toon_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_GLOSSY_TOON_ID:
|
||||
label = bsdf_glossy_toon_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
// double check if this is valid
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
|
||||
label = bsdf_hair_reflection_sample(
|
||||
sc, Ng, sd->wi, rand_xy, eval, wo, pdf, sampled_roughness);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
|
||||
label = bsdf_hair_transmission_sample(
|
||||
sc, Ng, sd->wi, rand_xy, eval, wo, pdf, sampled_roughness);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# ifdef __PRINCIPLED_HAIR__
|
||||
case CLOSURE_BSDF_HAIR_CHIANG_ID:
|
||||
label = bsdf_hair_chiang_sample(kg, sc, sd, rand, eval, wo, pdf, sampled_roughness);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_HUANG_ID:
|
||||
label = bsdf_hair_huang_sample(kg, sc, sd, rand, eval, wo, pdf, sampled_roughness);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_SHEEN_ID:
|
||||
label = bsdf_sheen_sample(sc, Ng, sd->wi, rand_xy, eval, wo, pdf);
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
label = LABEL_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test if BSDF sample should be treated as transparent for background. */
|
||||
if (label & LABEL_TRANSMIT) {
|
||||
const float threshold_squared = kernel_data.background.transparent_roughness_squared_threshold;
|
||||
|
||||
if (threshold_squared >= 0.0f && !(label & LABEL_DIFFUSE)) {
|
||||
if (bsdf_get_specular_roughness_squared(sc) <= threshold_squared) {
|
||||
label |= LABEL_TRANSMIT_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (label != LABEL_NONE) {
|
||||
/* Shadow terminator offset. */
|
||||
const float frequency_multiplier =
|
||||
kernel_data_fetch(objects, sd->object).shadow_terminator_shading_offset;
|
||||
if (frequency_multiplier > 1.0f) {
|
||||
const float cosNO = dot(*wo, sc->N);
|
||||
*eval *= shift_cos_in(cosNO, frequency_multiplier);
|
||||
}
|
||||
*eval *= bump_shadowing_term(sd, sc, *wo, false);
|
||||
}
|
||||
|
||||
#ifdef WITH_CYCLES_DEBUG
|
||||
kernel_assert(*pdf >= 0.0f);
|
||||
kernel_assert(eval->x >= 0.0f && eval->y >= 0.0f && eval->z >= 0.0f);
|
||||
#endif
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_roughness_eta(const ccl_private ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
ccl_private float2 *roughness,
|
||||
ccl_private float *eta)
|
||||
{
|
||||
#ifdef __SVM__
|
||||
float alpha = 1.0f;
|
||||
#endif
|
||||
switch (sc->type) {
|
||||
case CLOSURE_BSDF_DIFFUSE_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
#ifdef __SVM__
|
||||
case CLOSURE_BSDF_OREN_NAYAR_ID:
|
||||
case CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# ifdef __OSL__
|
||||
case CLOSURE_BSDF_BURLEY_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_PHONG_RAMP_ID:
|
||||
alpha = phong_ramp_exponent_to_roughness(((const ccl_private PhongRampBsdf *)sc)->exponent);
|
||||
*roughness = make_float2(alpha, alpha);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_TRANSLUCENT_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_TRANSPARENT_ID:
|
||||
case CLOSURE_BSDF_RAY_PORTAL_ID:
|
||||
*roughness = zero_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
|
||||
case CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: {
|
||||
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
||||
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
|
||||
*eta = (bsdf_is_transmission(sc, wo)) ? bsdf->ior : 1.0f;
|
||||
break;
|
||||
}
|
||||
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: {
|
||||
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
||||
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
}
|
||||
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_GLOSSY_TOON_ID:
|
||||
// double check if this is valid
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
|
||||
*roughness = make_float2(((ccl_private HairBsdf *)sc)->roughness1,
|
||||
((ccl_private HairBsdf *)sc)->roughness2);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
|
||||
*roughness = make_float2(((ccl_private HairBsdf *)sc)->roughness1,
|
||||
((ccl_private HairBsdf *)sc)->roughness2);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# ifdef __PRINCIPLED_HAIR__
|
||||
case CLOSURE_BSDF_HAIR_CHIANG_ID:
|
||||
alpha = ((ccl_private ChiangHairBSDF *)sc)->m0_roughness;
|
||||
*roughness = make_float2(alpha, alpha);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_HUANG_ID:
|
||||
alpha = ((ccl_private HuangHairBSDF *)sc)->roughness;
|
||||
*roughness = make_float2(alpha, alpha);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_SHEEN_ID:
|
||||
alpha = ((ccl_private SheenBsdf *)sc)->roughness;
|
||||
*roughness = make_float2(alpha, alpha);
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
*roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline int bsdf_label(const KernelGlobals kg,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wo)
|
||||
{
|
||||
/* For curves use the smooth normal, particularly for ribbons the geometric
|
||||
* normal gives too much darkening otherwise. */
|
||||
int label;
|
||||
switch (sc->type) {
|
||||
case CLOSURE_BSDF_DIFFUSE_ID:
|
||||
case CLOSURE_BSSRDF_BURLEY_ID:
|
||||
case CLOSURE_BSSRDF_RANDOM_WALK_ID:
|
||||
case CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID:
|
||||
case CLOSURE_BSSRDF_RANDOM_WALK_LEGACY_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
#ifdef __SVM__
|
||||
case CLOSURE_BSDF_OREN_NAYAR_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
# ifdef __OSL__
|
||||
case CLOSURE_BSDF_BURLEY_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
case CLOSURE_BSDF_PHONG_RAMP_ID:
|
||||
label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_TRANSLUCENT_ID:
|
||||
case CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID:
|
||||
label = LABEL_TRANSMIT | LABEL_DIFFUSE;
|
||||
break;
|
||||
case CLOSURE_BSDF_TRANSPARENT_ID:
|
||||
label = LABEL_TRANSMIT | LABEL_TRANSPARENT;
|
||||
break;
|
||||
case CLOSURE_BSDF_RAY_PORTAL_ID:
|
||||
label = LABEL_TRANSMIT | LABEL_RAY_PORTAL;
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: {
|
||||
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
||||
label = ((bsdf_is_transmission(sc, wo)) ? LABEL_TRANSMIT : LABEL_REFLECT) |
|
||||
((bsdf_microfacet_eval_flag(bsdf)) ? LABEL_GLOSSY : LABEL_SINGULAR);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID:
|
||||
label = LABEL_TRANSMIT | LABEL_GLOSSY;
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
|
||||
label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
case CLOSURE_BSDF_GLOSSY_TOON_ID:
|
||||
label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
|
||||
label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
|
||||
label = LABEL_TRANSMIT | LABEL_GLOSSY;
|
||||
break;
|
||||
# ifdef __PRINCIPLED_HAIR__
|
||||
case CLOSURE_BSDF_HAIR_CHIANG_ID:
|
||||
if (bsdf_is_transmission(sc, wo)) {
|
||||
label = LABEL_TRANSMIT | LABEL_GLOSSY;
|
||||
}
|
||||
else {
|
||||
label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
}
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_HUANG_ID:
|
||||
label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_SHEEN_ID:
|
||||
label = LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
label = LABEL_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Test if BSDF sample should be treated as transparent for background. */
|
||||
if (label & LABEL_TRANSMIT) {
|
||||
const float threshold_squared = kernel_data.background.transparent_roughness_squared_threshold;
|
||||
|
||||
if (threshold_squared >= 0.0f) {
|
||||
if (bsdf_get_specular_roughness_squared(sc) <= threshold_squared) {
|
||||
label |= LABEL_TRANSMIT_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
#ifndef __KERNEL_CUDA__
|
||||
ccl_device
|
||||
#else
|
||||
ccl_device_inline
|
||||
#endif
|
||||
Spectrum
|
||||
bsdf_eval(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
Spectrum eval = zero_spectrum();
|
||||
*pdf = 0.f;
|
||||
|
||||
const float bump_shadowing = bump_shadowing_term(sd, sc, wo, true);
|
||||
if (bump_shadowing == 0.0f) {
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
switch (sc->type) {
|
||||
case CLOSURE_BSDF_DIFFUSE_ID:
|
||||
eval = bsdf_diffuse_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
#if defined(__SVM__) || defined(__OSL__)
|
||||
case CLOSURE_BSDF_OREN_NAYAR_ID:
|
||||
eval = bsdf_oren_nayar_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID:
|
||||
eval = bsdf_rough_translucent_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
# ifdef __OSL__
|
||||
case CLOSURE_BSDF_BURLEY_ID:
|
||||
eval = bsdf_burley_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_PHONG_RAMP_ID:
|
||||
eval = bsdf_phong_ramp_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_RAMP_ID:
|
||||
eval = bsdf_diffuse_ramp_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_TRANSLUCENT_ID:
|
||||
eval = bsdf_translucent_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_TRANSPARENT_ID:
|
||||
eval = bsdf_transparent_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_RAY_PORTAL_ID:
|
||||
eval = bsdf_ray_portal_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
|
||||
eval = bsdf_microfacet_ggx_eval(kg, sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID:
|
||||
eval = bsdf_thin_glass_transmission_eval(kg, sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
|
||||
eval = bsdf_microfacet_beckmann_eval(kg, sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
|
||||
eval = bsdf_ashikhmin_shirley_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID:
|
||||
eval = bsdf_ashikhmin_velvet_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_DIFFUSE_TOON_ID:
|
||||
eval = bsdf_diffuse_toon_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_GLOSSY_TOON_ID:
|
||||
eval = bsdf_glossy_toon_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
# ifdef __PRINCIPLED_HAIR__
|
||||
case CLOSURE_BSDF_HAIR_CHIANG_ID:
|
||||
eval = bsdf_hair_chiang_eval(kg, sd, sc, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_HUANG_ID:
|
||||
eval = bsdf_hair_huang_eval(kg, sd, sc, wo, pdf);
|
||||
break;
|
||||
# endif
|
||||
case CLOSURE_BSDF_HAIR_REFLECTION_ID:
|
||||
eval = bsdf_hair_reflection_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_TRANSMISSION_ID:
|
||||
eval = bsdf_hair_transmission_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
case CLOSURE_BSDF_SHEEN_ID:
|
||||
eval = bsdf_sheen_eval(sc, sd->wi, wo, pdf);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
eval *= bump_shadowing;
|
||||
|
||||
/* Shadow terminator offset. */
|
||||
const float frequency_multiplier =
|
||||
kernel_data_fetch(objects, sd->object).shadow_terminator_shading_offset;
|
||||
if (frequency_multiplier > 1.0f) {
|
||||
const float cosNO = dot(wo, sc->N);
|
||||
if (cosNO >= 0.0f) {
|
||||
eval *= shift_cos_in(cosNO, frequency_multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_CYCLES_DEBUG
|
||||
kernel_assert(*pdf >= 0.0f);
|
||||
kernel_assert(eval.x >= 0.0f && eval.y >= 0.0f && eval.z >= 0.0f);
|
||||
#endif
|
||||
return eval;
|
||||
}
|
||||
|
||||
ccl_device void bsdf_blur(ccl_private ShaderClosure *sc, const float roughness)
|
||||
{
|
||||
/* TODO: do we want to blur volume closures? */
|
||||
#if defined(__SVM__) || defined(__OSL__)
|
||||
switch (sc->type) {
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID:
|
||||
case CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID:
|
||||
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID:
|
||||
/* TODO: Recompute energy preservation after blur? */
|
||||
bsdf_microfacet_blur(sc, roughness);
|
||||
break;
|
||||
case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID:
|
||||
bsdf_ashikhmin_shirley_blur(sc, roughness);
|
||||
break;
|
||||
# ifdef __PRINCIPLED_HAIR__
|
||||
case CLOSURE_BSDF_HAIR_CHIANG_ID:
|
||||
bsdf_hair_chiang_blur(sc, roughness);
|
||||
break;
|
||||
case CLOSURE_BSDF_HAIR_HUANG_ID:
|
||||
bsdf_hair_huang_blur(sc, roughness);
|
||||
break;
|
||||
# endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum bsdf_albedo(KernelGlobals kg,
|
||||
const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const bool reflection,
|
||||
const bool transmission)
|
||||
{
|
||||
Spectrum albedo = sc->weight;
|
||||
/* Some closures include additional components such as Fresnel terms that cause their albedo to
|
||||
* be below 1. The point of this function is to return a best-effort estimation of their albedo,
|
||||
* meaning the amount of reflected/refracted light that would be expected when illuminated by a
|
||||
* uniform white background.
|
||||
* This is used for the denoising albedo pass and diffuse/glossy/transmission color passes.
|
||||
* NOTE: This should always match the sample_weight of the closure - as in, if there's an albedo
|
||||
* adjustment in here, the sample_weight should also be reduced accordingly.
|
||||
* TODO(lukas): Consider calling this function to determine the sample_weight? Would be a bit of
|
||||
* extra overhead though. */
|
||||
#if defined(__SVM__) || defined(__OSL__)
|
||||
if (CLOSURE_IS_BSDF_MICROFACET(sc->type)) {
|
||||
albedo *= bsdf_microfacet_estimate_albedo(
|
||||
kg, sd->wi, (const ccl_private MicrofacetBsdf *)sc, reflection, transmission);
|
||||
}
|
||||
# ifdef __PRINCIPLED_HAIR__
|
||||
else if (sc->type == CLOSURE_BSDF_HAIR_CHIANG_ID) {
|
||||
/* TODO(lukas): Principled Hair could also be split into a glossy and a transmission component,
|
||||
* similar to Glass BSDFs. */
|
||||
albedo *= bsdf_hair_chiang_albedo(sd, sc);
|
||||
}
|
||||
else if (sc->type == CLOSURE_BSDF_HAIR_HUANG_ID) {
|
||||
albedo *= bsdf_hair_huang_albedo(sd, sc);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
return albedo;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,228 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/*
|
||||
* ASHIKHMIN SHIRLEY BSDF
|
||||
*
|
||||
* Implementation of
|
||||
* Michael Ashikhmin and Peter Shirley: "An Anisotropic Phong BRDF Model" (2000)
|
||||
*
|
||||
* The Fresnel factor is missing to get a separable bsdf (intensity*color), as is
|
||||
* the case with all other microfacet-based BSDF implementations in Cycles.
|
||||
*
|
||||
* Other than that, the implementation directly follows the paper.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/bsdf_microfacet.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device int bsdf_ashikhmin_shirley_setup(ccl_private MicrofacetBsdf *bsdf)
|
||||
{
|
||||
bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f);
|
||||
bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f);
|
||||
|
||||
bsdf->fresnel_type = MicrofacetFresnel::NONE;
|
||||
bsdf->type = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID;
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device void bsdf_ashikhmin_shirley_blur(ccl_private ShaderClosure *sc, const float roughness)
|
||||
{
|
||||
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc;
|
||||
|
||||
bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x);
|
||||
bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y);
|
||||
}
|
||||
|
||||
ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(const float roughness)
|
||||
{
|
||||
return 2.0f / (roughness * roughness) - 2.0f;
|
||||
}
|
||||
|
||||
ccl_device_forceinline Spectrum bsdf_ashikhmin_shirley_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
float NdotI = dot(N, wi);
|
||||
float NdotO = dot(N, wo);
|
||||
|
||||
float out = 0.0f;
|
||||
|
||||
if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f || (NdotI < 0.0f) || (NdotO < 0.0f)) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
NdotI = fmaxf(NdotI, 1e-6f);
|
||||
NdotO = fmaxf(NdotO, 1e-6f);
|
||||
const float3 H = normalize(wi + wo);
|
||||
const float HdotI = fmaxf(fabsf(dot(H, wi)), 1e-6f);
|
||||
const float HdotN = fmaxf(dot(H, N), 1e-6f);
|
||||
|
||||
/* pump from original paper
|
||||
* (first derivative disc., but cancels the HdotI in the pdf nicely) */
|
||||
const float pump = 1.0f / fmaxf(1e-6f, (HdotI * fmaxf(NdotI, NdotO)));
|
||||
/* `pump` from D-BRDF paper. */
|
||||
// float pump = 1.0f / fmaxf(1e-4f, ((NdotI + NdotO) * (NdotI * NdotO)));
|
||||
|
||||
const float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
|
||||
const float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
|
||||
|
||||
if (n_x == n_y) {
|
||||
/* isotropic */
|
||||
const float e = n_x;
|
||||
const float lobe = powf(HdotN, e);
|
||||
const float norm = (n_x + 1.0f) / (8.0f * M_PI_F);
|
||||
|
||||
out = NdotO * norm * lobe * pump;
|
||||
/* this is p_h / 4(H.I) (conversion from `wh measure` to `wi measure`, eq. 8 in paper). */
|
||||
*pdf = norm * lobe / HdotI;
|
||||
}
|
||||
else {
|
||||
/* anisotropic */
|
||||
float3 X;
|
||||
float3 Y;
|
||||
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
|
||||
|
||||
const float HdotX = dot(H, X);
|
||||
const float HdotY = dot(H, Y);
|
||||
float lobe;
|
||||
if (HdotN < 1.0f) {
|
||||
const float e = (n_x * HdotX * HdotX + n_y * HdotY * HdotY) / (1.0f - HdotN * HdotN);
|
||||
lobe = powf(HdotN, e);
|
||||
}
|
||||
else {
|
||||
lobe = 1.0f;
|
||||
}
|
||||
const float norm = sqrtf((n_x + 1.0f) * (n_y + 1.0f)) / (8.0f * M_PI_F);
|
||||
|
||||
out = NdotO * norm * lobe * pump;
|
||||
*pdf = norm * lobe / HdotI;
|
||||
}
|
||||
|
||||
return make_spectrum(out);
|
||||
}
|
||||
|
||||
ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x,
|
||||
const float n_y,
|
||||
const float2 rand,
|
||||
ccl_private float *phi,
|
||||
ccl_private float *cos_theta)
|
||||
{
|
||||
*phi = atanf(sqrtf((n_x + 1.0f) / (n_y + 1.0f)) * tanf(M_PI_2_F * rand.x));
|
||||
const float cos_phi = cosf(*phi);
|
||||
const float sin_phi = sinf(*phi);
|
||||
*cos_theta = powf(rand.y, 1.0f / (n_x * cos_phi * cos_phi + n_y * sin_phi * sin_phi + 1.0f));
|
||||
}
|
||||
|
||||
ccl_device int bsdf_ashikhmin_shirley_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 wi,
|
||||
float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness)
|
||||
{
|
||||
const ccl_private MicrofacetBsdf *bsdf = (const ccl_private MicrofacetBsdf *)sc;
|
||||
*sampled_roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
|
||||
const float3 N = bsdf->N;
|
||||
int label = LABEL_REFLECT | LABEL_GLOSSY;
|
||||
|
||||
const float NdotI = dot(N, wi);
|
||||
if (!(NdotI > 0.0f)) {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
const float n_x = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_x);
|
||||
const float n_y = bsdf_ashikhmin_shirley_roughness_to_exponent(bsdf->alpha_y);
|
||||
|
||||
/* get x,y basis on the surface for anisotropy */
|
||||
float3 X;
|
||||
float3 Y;
|
||||
|
||||
if (n_x == n_y) {
|
||||
make_orthonormals(N, &X, &Y);
|
||||
}
|
||||
else {
|
||||
make_orthonormals_tangent(N, bsdf->T, &X, &Y);
|
||||
}
|
||||
|
||||
/* sample spherical coords for h in tangent space */
|
||||
float phi;
|
||||
float cos_theta;
|
||||
if (n_x == n_y) {
|
||||
/* isotropic sampling */
|
||||
phi = M_2PI_F * rand.x;
|
||||
cos_theta = powf(rand.y, 1.0f / (n_x + 1.0f));
|
||||
}
|
||||
else {
|
||||
/* anisotropic sampling */
|
||||
if (rand.x < 0.25f) { /* first quadrant */
|
||||
rand.x *= 4.0f;
|
||||
bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, rand, &phi, &cos_theta);
|
||||
}
|
||||
else if (rand.x < 0.5f) { /* second quadrant */
|
||||
rand.x = 4.0f * (0.5f - rand.x);
|
||||
bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, rand, &phi, &cos_theta);
|
||||
phi = M_PI_F - phi;
|
||||
}
|
||||
else if (rand.x < 0.75f) { /* third quadrant */
|
||||
rand.x = 4.0f * (rand.x - 0.5f);
|
||||
bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, rand, &phi, &cos_theta);
|
||||
phi = M_PI_F + phi;
|
||||
}
|
||||
else { /* fourth quadrant */
|
||||
rand.x = 4.0f * (1.0f - rand.x);
|
||||
bsdf_ashikhmin_shirley_sample_first_quadrant(n_x, n_y, rand, &phi, &cos_theta);
|
||||
phi = 2.0f * M_PI_F - phi;
|
||||
}
|
||||
}
|
||||
|
||||
/* get half vector in tangent space */
|
||||
const float3 h = spherical_cos_to_direction(cos_theta, phi);
|
||||
|
||||
/* half vector to world space */
|
||||
float3 H = to_global(h, X, Y, N);
|
||||
const float HdotI = dot(H, wi);
|
||||
if (HdotI < 0.0f) {
|
||||
H = -H;
|
||||
}
|
||||
|
||||
/* reflect wi on H to get wo */
|
||||
*wo = -wi + (2.0f * HdotI) * H;
|
||||
|
||||
/* Check hemisphere. */
|
||||
if (dot(Ng, *wo) < 0.0f) {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) {
|
||||
/* Some high number for MIS. */
|
||||
*pdf = 1e6f;
|
||||
*eval = make_spectrum(1e6f);
|
||||
label = LABEL_REFLECT | LABEL_SINGULAR;
|
||||
}
|
||||
else {
|
||||
/* leave the rest to eval */
|
||||
*eval = bsdf_ashikhmin_shirley_eval(sc, wi, *wo, pdf);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,134 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/sample/mapping.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct VelvetBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
float sigma;
|
||||
float invsigma2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(VelvetBsdf), "VelvetBsdf is too large!");
|
||||
|
||||
ccl_device int bsdf_ashikhmin_velvet_setup(ccl_private VelvetBsdf *bsdf)
|
||||
{
|
||||
const float sigma = fmaxf(bsdf->sigma, 0.01f);
|
||||
bsdf->invsigma2 = 1.0f / (sigma * sigma);
|
||||
|
||||
bsdf->type = CLOSURE_BSDF_ASHIKHMIN_VELVET_ID;
|
||||
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_ashikhmin_velvet_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private VelvetBsdf *bsdf = (const ccl_private VelvetBsdf *)sc;
|
||||
const float m_invsigma2 = bsdf->invsigma2;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
const float cosNI = dot(N, wi);
|
||||
const float cosNO = dot(N, wo);
|
||||
if (!(cosNI > 0 && cosNO > 0)) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
const float3 H = normalize(wi + wo);
|
||||
|
||||
const float cosNH = dot(N, H);
|
||||
const float cosHI = fabsf(dot(wi, H));
|
||||
|
||||
if (!(fabsf(cosNH) < 1.0f - 1e-5f && cosHI > 1e-5f)) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
float cosNHdivHI = cosNH / cosHI;
|
||||
cosNHdivHI = fmaxf(cosNHdivHI, 1e-5f);
|
||||
|
||||
const float fac1 = 2 * fabsf(cosNHdivHI * cosNI);
|
||||
const float fac2 = 2 * fabsf(cosNHdivHI * cosNO);
|
||||
|
||||
const float sinNH2 = 1 - cosNH * cosNH;
|
||||
const float sinNH4 = sinNH2 * sinNH2;
|
||||
const float cotangent2 = (cosNH * cosNH) / sinNH2;
|
||||
|
||||
const float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * M_1_PI_F / sinNH4;
|
||||
const float G = fminf(1.0f, fminf(fac1, fac2)); // TODO: derive G from D analytically
|
||||
|
||||
const float out = 0.25f * (D * G) / cosNI;
|
||||
|
||||
*pdf = 0.5f * M_1_PI_F;
|
||||
return make_spectrum(out);
|
||||
}
|
||||
|
||||
ccl_device int bsdf_ashikhmin_velvet_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private VelvetBsdf *bsdf = (const ccl_private VelvetBsdf *)sc;
|
||||
const float m_invsigma2 = bsdf->invsigma2;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
// we are viewing the surface from above - send a ray out with uniform
|
||||
// distribution over the hemisphere
|
||||
sample_uniform_hemisphere(N, rand, wo, pdf);
|
||||
|
||||
if (!(dot(Ng, *wo) > 0)) {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
const float3 H = normalize(wi + *wo);
|
||||
|
||||
const float cosNI = dot(N, wi);
|
||||
const float cosNO = dot(N, *wo);
|
||||
const float cosHI = fabsf(dot(wi, H));
|
||||
const float cosNH = dot(N, H);
|
||||
|
||||
if (!(cosNI > 1e-5f && fabsf(cosNH) < 1.0f - 1e-5f && cosHI > 1e-5f)) {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
float cosNHdivHI = cosNH / cosHI;
|
||||
cosNHdivHI = fmaxf(cosNHdivHI, 1e-5f);
|
||||
|
||||
const float fac1 = 2 * fabsf(cosNHdivHI * cosNI);
|
||||
const float fac2 = 2 * fabsf(cosNHdivHI * cosNO);
|
||||
|
||||
const float sinNH2 = 1 - cosNH * cosNH;
|
||||
const float sinNH4 = sinNH2 * sinNH2;
|
||||
const float cotangent2 = (cosNH * cosNH) / sinNH2;
|
||||
|
||||
const float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * M_1_PI_F / sinNH4;
|
||||
const float G = fminf(1.0f, fminf(fac1, fac2)); // TODO: derive G from D analytically
|
||||
|
||||
const float power = 0.25f * (D * G) / cosNI;
|
||||
|
||||
*eval = make_spectrum(power);
|
||||
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
88
blender-5.2.0/intern/cycles/kernel/closure/bsdf_burley.h
Normal file
88
blender-5.2.0/intern/cycles/kernel/closure/bsdf_burley.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2025 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/closure/bsdf_util.h"
|
||||
#include "kernel/sample/mapping.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __OSL__
|
||||
|
||||
struct BurleyBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
float roughness;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(BurleyBsdf), "BurleyBsdf is too large!");
|
||||
|
||||
ccl_device Spectrum bsdf_burley_get_intensity(const float roughness,
|
||||
const float3 n,
|
||||
const float3 v,
|
||||
const float3 l)
|
||||
{
|
||||
const float NdotL = dot(n, l);
|
||||
const float NdotV = dot(n, v);
|
||||
const float fl = schlick_fresnel(NdotL);
|
||||
const float fv = schlick_fresnel(NdotV);
|
||||
const float LdotH = dot(l, normalize(l + v));
|
||||
const float F90 = 0.5f + (2.0f * roughness * LdotH * LdotH);
|
||||
return make_spectrum(M_1_PI_F * NdotL * mix(1.0f, F90, fl) * mix(1.0f, F90, fv));
|
||||
}
|
||||
|
||||
ccl_device int bsdf_burley_setup(ccl_private BurleyBsdf *bsdf, const float roughness)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_BURLEY_ID;
|
||||
bsdf->roughness = saturatef(roughness);
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_burley_eval(ccl_private const ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
ccl_private const BurleyBsdf *bsdf = (ccl_private const BurleyBsdf *)sc;
|
||||
|
||||
const float cosNO = dot(bsdf->N, wo);
|
||||
if (cosNO > 0.0f) {
|
||||
*pdf = cosNO * M_1_PI_F;
|
||||
return bsdf_burley_get_intensity(bsdf->roughness, bsdf->N, wi, wo);
|
||||
}
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device int bsdf_burley_sample(ccl_private const ShaderClosure *sc,
|
||||
float3 Ng,
|
||||
float3 wi,
|
||||
float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
ccl_private const BurleyBsdf *bsdf = (ccl_private const BurleyBsdf *)sc;
|
||||
float3 N = bsdf->N;
|
||||
|
||||
// distribution over the hemisphere
|
||||
sample_cos_hemisphere(N, rand, wo, pdf);
|
||||
|
||||
if (dot(Ng, *wo) > 0.0f) {
|
||||
*eval = bsdf_burley_get_intensity(bsdf->roughness, bsdf->N, wi, *wo);
|
||||
}
|
||||
else {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
}
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
#endif /* __OSL__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
127
blender-5.2.0/intern/cycles/kernel/closure/bsdf_diffuse.h
Normal file
127
blender-5.2.0/intern/cycles/kernel/closure/bsdf_diffuse.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/alloc.h"
|
||||
#include "kernel/sample/mapping.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct DiffuseBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(DiffuseBsdf), "DiffuseBsdf is too large!");
|
||||
|
||||
/* DIFFUSE */
|
||||
|
||||
ccl_device void bsdf_diffuse_setup(ccl_private ShaderData *sd,
|
||||
const float3 N,
|
||||
const Spectrum weight)
|
||||
{
|
||||
ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc(
|
||||
sd, sizeof(DiffuseBsdf), weight);
|
||||
if (bsdf) {
|
||||
bsdf->N = N;
|
||||
bsdf->type = CLOSURE_BSDF_DIFFUSE_ID;
|
||||
sd->flag |= (SD_BSDF | SD_BSDF_HAS_EVAL);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_diffuse_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*wi*/,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private DiffuseBsdf *bsdf = (const ccl_private DiffuseBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
const float cosNO = fmaxf(dot(N, wo), 0.0f) * M_1_PI_F;
|
||||
*pdf = cosNO;
|
||||
return make_spectrum(cosNO);
|
||||
}
|
||||
|
||||
ccl_device int bsdf_diffuse_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 /*wi*/,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private DiffuseBsdf *bsdf = (const ccl_private DiffuseBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
// distribution over the hemisphere
|
||||
sample_cos_hemisphere(N, rand, wo, pdf);
|
||||
|
||||
if (dot(Ng, *wo) > 0.0f) {
|
||||
*eval = make_spectrum(*pdf);
|
||||
}
|
||||
else {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
}
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
/* TRANSLUCENT */
|
||||
|
||||
ccl_device void bsdf_translucent_setup(ccl_private ShaderData *sd,
|
||||
const float3 N,
|
||||
const Spectrum weight)
|
||||
{
|
||||
ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc(
|
||||
sd, sizeof(DiffuseBsdf), weight);
|
||||
if (bsdf) {
|
||||
bsdf->N = N;
|
||||
bsdf->type = CLOSURE_BSDF_TRANSLUCENT_ID;
|
||||
sd->flag |= (SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_translucent_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*wi*/,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private DiffuseBsdf *bsdf = (const ccl_private DiffuseBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
const float cosNO = fmaxf(-dot(N, wo), 0.0f) * M_1_PI_F;
|
||||
*pdf = cosNO;
|
||||
return make_spectrum(cosNO);
|
||||
}
|
||||
|
||||
ccl_device int bsdf_translucent_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 /*wi*/,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private DiffuseBsdf *bsdf = (const ccl_private DiffuseBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
// we are viewing the surface from the right side - send a ray out with cosine
|
||||
// distribution over the hemisphere
|
||||
sample_cos_hemisphere(-N, rand, wo, pdf);
|
||||
if (dot(Ng, *wo) < 0) {
|
||||
*eval = make_spectrum(*pdf);
|
||||
}
|
||||
else {
|
||||
*pdf = 0;
|
||||
*eval = zero_spectrum();
|
||||
}
|
||||
return LABEL_TRANSMIT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/sample/mapping.h"
|
||||
#include "kernel/util/colorspace.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __OSL__
|
||||
|
||||
struct DiffuseRampBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
ccl_private float3 *colors;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(DiffuseRampBsdf), "DiffuseRampBsdf is too large!");
|
||||
|
||||
ccl_device float3 bsdf_diffuse_ramp_get_color(const float3 colors[8], float pos)
|
||||
{
|
||||
const int MAXCOLORS = 8;
|
||||
|
||||
const float npos = pos * (float)(MAXCOLORS - 1);
|
||||
const int ipos = float_to_int(npos);
|
||||
if (ipos < 0) {
|
||||
return colors[0];
|
||||
}
|
||||
if (ipos >= (MAXCOLORS - 1)) {
|
||||
return colors[MAXCOLORS - 1];
|
||||
}
|
||||
const float offset = npos - (float)ipos;
|
||||
return colors[ipos] * (1.0f - offset) + colors[ipos + 1] * offset;
|
||||
}
|
||||
|
||||
ccl_device int bsdf_diffuse_ramp_setup(DiffuseRampBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_DIFFUSE_RAMP_ID;
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device void bsdf_diffuse_ramp_blur(ccl_private ShaderClosure * /*sc*/,
|
||||
const float /*roughness*/)
|
||||
{
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_diffuse_ramp_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*wi*/,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
const float cosNO = fmaxf(dot(N, wo), 0.0f);
|
||||
if (cosNO >= 0.0f) {
|
||||
*pdf = cosNO * M_1_PI_F;
|
||||
return rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, cosNO) * M_1_PI_F);
|
||||
}
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device int bsdf_diffuse_ramp_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 /*wi*/,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
|
||||
// distribution over the hemisphere
|
||||
sample_cos_hemisphere(N, rand, wo, pdf);
|
||||
|
||||
if (dot(Ng, *wo) > 0.0f) {
|
||||
*eval = rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, *pdf * M_PI_F) * M_1_PI_F);
|
||||
}
|
||||
else {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
}
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
#endif /* __OSL__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
261
blender-5.2.0/intern/cycles/kernel/closure/bsdf_hair.h
Normal file
261
blender-5.2.0/intern/cycles/kernel/closure/bsdf_hair.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "util/math_fast.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct HairBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
float3 T;
|
||||
float roughness1;
|
||||
float roughness2;
|
||||
float offset;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(HairBsdf), "HairBsdf is too large!");
|
||||
|
||||
ccl_device int bsdf_hair_reflection_setup(ccl_private HairBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_HAIR_REFLECTION_ID;
|
||||
bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f);
|
||||
bsdf->roughness2 = clamp(bsdf->roughness2, 0.001f, 1.0f);
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID;
|
||||
bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f);
|
||||
bsdf->roughness2 = clamp(bsdf->roughness2, 0.001f, 1.0f);
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_hair_reflection_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private HairBsdf *bsdf = (const ccl_private HairBsdf *)sc;
|
||||
if (dot(bsdf->N, wo) < 0.0f) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
const float offset = bsdf->offset;
|
||||
const float3 Tg = bsdf->T;
|
||||
const float roughness1 = bsdf->roughness1;
|
||||
const float roughness2 = bsdf->roughness2;
|
||||
|
||||
const float Iz = dot(Tg, wi);
|
||||
const float3 locy = normalize(wi - Tg * Iz);
|
||||
|
||||
const float theta_r = M_PI_2_F - fast_acosf(Iz);
|
||||
|
||||
const float wo_z = dot(Tg, wo);
|
||||
const float3 wo_y = normalize(wo - Tg * wo_z);
|
||||
|
||||
const float theta_i = M_PI_2_F - fast_acosf(wo_z);
|
||||
const float cosphi_i = dot(wo_y, locy);
|
||||
|
||||
if (M_PI_2_F - fabsf(theta_i) < 0.001f || cosphi_i < 0.0f) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
const float roughness1_inv = 1.0f / roughness1;
|
||||
const float roughness2_inv = 1.0f / roughness2;
|
||||
float phi_i = fast_acosf(cosphi_i) * roughness2_inv;
|
||||
phi_i = fabsf(phi_i) < M_PI_F ? phi_i : M_PI_F;
|
||||
const float costheta_i = fast_cosf(theta_i);
|
||||
|
||||
const float a_R = fast_atan2f(((M_PI_2_F + theta_r) * 0.5f - offset) * roughness1_inv, 1.0f);
|
||||
const float b_R = fast_atan2f(((-M_PI_2_F + theta_r) * 0.5f - offset) * roughness1_inv, 1.0f);
|
||||
|
||||
const float theta_h = (theta_i + theta_r) * 0.5f;
|
||||
const float t = theta_h - offset;
|
||||
|
||||
const float phi_pdf = fast_cosf(phi_i * 0.5f) * 0.25f * roughness2_inv;
|
||||
const float theta_pdf = roughness1 /
|
||||
(2 * (t * t + roughness1 * roughness1) * (a_R - b_R) * costheta_i);
|
||||
*pdf = phi_pdf * theta_pdf;
|
||||
|
||||
return make_spectrum(*pdf);
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_hair_transmission_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private HairBsdf *bsdf = (const ccl_private HairBsdf *)sc;
|
||||
if (dot(bsdf->N, wo) >= 0.0f) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
const float offset = bsdf->offset;
|
||||
const float3 Tg = bsdf->T;
|
||||
const float roughness1 = bsdf->roughness1;
|
||||
const float roughness2 = bsdf->roughness2;
|
||||
const float Iz = dot(Tg, wi);
|
||||
const float3 locy = normalize(wi - Tg * Iz);
|
||||
|
||||
const float theta_r = M_PI_2_F - fast_acosf(Iz);
|
||||
|
||||
const float wo_z = dot(Tg, wo);
|
||||
const float3 wo_y = normalize(wo - Tg * wo_z);
|
||||
|
||||
const float theta_i = M_PI_2_F - fast_acosf(wo_z);
|
||||
const float phi_i = fast_acosf(dot(wo_y, locy));
|
||||
|
||||
if (M_PI_2_F - fabsf(theta_i) < 0.001f) {
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
const float costheta_i = fast_cosf(theta_i);
|
||||
|
||||
const float roughness1_inv = 1.0f / roughness1;
|
||||
const float a_TT = fast_atan2f(((M_PI_2_F + theta_r) / 2 - offset) * roughness1_inv, 1.0f);
|
||||
const float b_TT = fast_atan2f(((-M_PI_2_F + theta_r) / 2 - offset) * roughness1_inv, 1.0f);
|
||||
const float c_TT = 2 * fast_atan2f(M_PI_2_F / roughness2, 1.0f);
|
||||
|
||||
const float theta_h = (theta_i + theta_r) / 2;
|
||||
const float t = theta_h - offset;
|
||||
const float phi = fabsf(phi_i);
|
||||
|
||||
const float p = M_PI_F - phi;
|
||||
const float theta_pdf = roughness1 /
|
||||
(2 * (t * t + roughness1 * roughness1) * (a_TT - b_TT) * costheta_i);
|
||||
const float phi_pdf = roughness2 / (c_TT * (p * p + roughness2 * roughness2));
|
||||
|
||||
*pdf = phi_pdf * theta_pdf;
|
||||
return make_spectrum(*pdf);
|
||||
}
|
||||
|
||||
ccl_device int bsdf_hair_reflection_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*Ng*/,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness)
|
||||
{
|
||||
const ccl_private HairBsdf *bsdf = (const ccl_private HairBsdf *)sc;
|
||||
const float offset = bsdf->offset;
|
||||
const float3 Tg = bsdf->T;
|
||||
const float roughness1 = bsdf->roughness1;
|
||||
const float roughness2 = bsdf->roughness2;
|
||||
*sampled_roughness = make_float2(roughness1, roughness2);
|
||||
const float Iz = dot(Tg, wi);
|
||||
const float3 locy = normalize(wi - Tg * Iz);
|
||||
const float3 locx = cross(locy, Tg);
|
||||
const float theta_r = M_PI_2_F - fast_acosf(Iz);
|
||||
|
||||
const float roughness1_inv = 1.0f / roughness1;
|
||||
const float a_R = fast_atan2f(((M_PI_2_F + theta_r) * 0.5f - offset) * roughness1_inv, 1.0f);
|
||||
const float b_R = fast_atan2f(((-M_PI_2_F + theta_r) * 0.5f - offset) * roughness1_inv, 1.0f);
|
||||
|
||||
const float t = roughness1 * tanf(rand.x * (a_R - b_R) + b_R);
|
||||
|
||||
const float theta_h = t + offset;
|
||||
const float theta_i = 2 * theta_h - theta_r;
|
||||
|
||||
float costheta_i;
|
||||
float sintheta_i;
|
||||
fast_sincosf(theta_i, &sintheta_i, &costheta_i);
|
||||
|
||||
const float phi = 2 * safe_asinf(1 - 2 * rand.y) * roughness2;
|
||||
|
||||
const float phi_pdf = fast_cosf(phi * 0.5f) * 0.25f / roughness2;
|
||||
|
||||
const float theta_pdf = roughness1 /
|
||||
(2 * (t * t + roughness1 * roughness1) * (a_R - b_R) * costheta_i);
|
||||
|
||||
float sinphi;
|
||||
float cosphi;
|
||||
fast_sincosf(phi, &sinphi, &cosphi);
|
||||
*wo = (cosphi * costheta_i) * locy - (sinphi * costheta_i) * locx + (sintheta_i)*Tg;
|
||||
|
||||
*pdf = fabsf(phi_pdf * theta_pdf);
|
||||
if (M_PI_2_F - fabsf(theta_i) < 0.001f) {
|
||||
*pdf = 0.0f;
|
||||
}
|
||||
|
||||
*eval = make_spectrum(*pdf);
|
||||
|
||||
return LABEL_REFLECT | LABEL_GLOSSY;
|
||||
}
|
||||
|
||||
ccl_device int bsdf_hair_transmission_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*Ng*/,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness)
|
||||
{
|
||||
const ccl_private HairBsdf *bsdf = (const ccl_private HairBsdf *)sc;
|
||||
const float offset = bsdf->offset;
|
||||
const float3 Tg = bsdf->T;
|
||||
const float roughness1 = bsdf->roughness1;
|
||||
const float roughness2 = bsdf->roughness2;
|
||||
*sampled_roughness = make_float2(roughness1, roughness2);
|
||||
const float Iz = dot(Tg, wi);
|
||||
const float3 locy = normalize(wi - Tg * Iz);
|
||||
const float3 locx = cross(locy, Tg);
|
||||
const float theta_r = M_PI_2_F - fast_acosf(Iz);
|
||||
|
||||
const float roughness1_inv = 1.0f / roughness1;
|
||||
const float a_TT = fast_atan2f(((M_PI_2_F + theta_r) / 2 - offset) * roughness1_inv, 1.0f);
|
||||
const float b_TT = fast_atan2f(((-M_PI_2_F + theta_r) / 2 - offset) * roughness1_inv, 1.0f);
|
||||
const float c_TT = 2 * fast_atan2f(M_PI_2_F / roughness2, 1.0f);
|
||||
|
||||
const float t = roughness1 * tanf(rand.x * (a_TT - b_TT) + b_TT);
|
||||
|
||||
const float theta_h = t + offset;
|
||||
const float theta_i = 2 * theta_h - theta_r;
|
||||
|
||||
float costheta_i;
|
||||
float sintheta_i;
|
||||
fast_sincosf(theta_i, &sintheta_i, &costheta_i);
|
||||
|
||||
const float p = roughness2 * tanf(c_TT * (rand.y - 0.5f));
|
||||
const float phi = p + M_PI_F;
|
||||
const float theta_pdf = roughness1 /
|
||||
(2 * (t * t + roughness1 * roughness1) * (a_TT - b_TT) * costheta_i);
|
||||
const float phi_pdf = roughness2 / (c_TT * (p * p + roughness2 * roughness2));
|
||||
|
||||
float sinphi;
|
||||
float cosphi;
|
||||
fast_sincosf(phi, &sinphi, &cosphi);
|
||||
*wo = (cosphi * costheta_i) * locy - (sinphi * costheta_i) * locx + (sintheta_i)*Tg;
|
||||
|
||||
*pdf = fabsf(phi_pdf * theta_pdf);
|
||||
if (M_PI_2_F - fabsf(theta_i) < 0.001f) {
|
||||
*pdf = 0.0f;
|
||||
}
|
||||
|
||||
*eval = make_spectrum(*pdf);
|
||||
|
||||
/* TODO(sergey): Should always be negative, but seems some precision issue
|
||||
* is involved here.
|
||||
*/
|
||||
kernel_assert(dot(locy, *wo) < 1e-4f);
|
||||
|
||||
return LABEL_TRANSMIT | LABEL_GLOSSY;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
1403
blender-5.2.0/intern/cycles/kernel/closure/bsdf_microfacet.h
Normal file
1403
blender-5.2.0/intern/cycles/kernel/closure/bsdf_microfacet.h
Normal file
File diff suppressed because it is too large
Load Diff
243
blender-5.2.0/intern/cycles/kernel/closure/bsdf_oren_nayar.h
Normal file
243
blender-5.2.0/intern/cycles/kernel/closure/bsdf_oren_nayar.h
Normal file
@@ -0,0 +1,243 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/bsdf_diffuse.h"
|
||||
#include "kernel/sample/mapping.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct OrenNayarParam {
|
||||
float roughness;
|
||||
float a;
|
||||
float b;
|
||||
Spectrum multiscatter_term;
|
||||
};
|
||||
|
||||
struct OrenNayarBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
OrenNayarParam param;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(OrenNayarBsdf), "OrenNayarBsdf is too large!");
|
||||
|
||||
/* NOTE: This implements the improved Oren-Nayar model by Yasuhiro Fujii
|
||||
* (https://mimosa-pudica.net/improved-oren-nayar.html), plus an
|
||||
* energy-preserving multi-scattering term based on the OpenPBR specification
|
||||
* (https://academysoftwarefoundation.github.io/OpenPBR). */
|
||||
|
||||
/* Above certain roughness threshold we switch to Oren Nayar model. */
|
||||
ccl_device_forceinline bool diffuse_roughness_is_almost_zero(const float alpha)
|
||||
{
|
||||
return alpha < 1e-5f;
|
||||
}
|
||||
|
||||
ccl_device_inline float bsdf_oren_nayar_G(const float cosTheta)
|
||||
{
|
||||
if (cosTheta < 1e-6f) {
|
||||
/* The tan(theta) term starts to act up at low cosTheta, so fall back to Taylor expansion. */
|
||||
return (M_PI_2_F - 2.0f / 3.0f) - cosTheta;
|
||||
}
|
||||
const float sinTheta = sin_from_cos(cosTheta);
|
||||
const float theta = safe_acosf(cosTheta);
|
||||
return sinTheta * (theta - 2.0f / 3.0f - sinTheta * cosTheta) +
|
||||
2.0f / 3.0f * (sinTheta / cosTheta) * (1.0f - sqr(sinTheta) * sinTheta);
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_oren_nayar_get_intensity(const ccl_private OrenNayarBsdf *bsdf,
|
||||
const float3 n,
|
||||
const float3 v,
|
||||
const float3 l)
|
||||
{
|
||||
const OrenNayarParam param = bsdf->param;
|
||||
const float nl = max(dot(n, l), 0.0f);
|
||||
if (param.b <= 0.0f) {
|
||||
return make_spectrum(nl * M_1_PI_F);
|
||||
}
|
||||
const float nv = max(dot(n, v), 0.0f);
|
||||
float t = dot(l, v) - nl * nv;
|
||||
|
||||
if (t > 0.0f) {
|
||||
t /= max(nl, nv) + FLT_MIN;
|
||||
}
|
||||
|
||||
const float single_scatter = param.a + param.b * t;
|
||||
|
||||
const float El = param.a * M_PI_F + param.b * bsdf_oren_nayar_G(nl);
|
||||
const Spectrum multi_scatter = param.multiscatter_term * (1.0f - El);
|
||||
|
||||
return nl * (make_spectrum(single_scatter) + multi_scatter);
|
||||
}
|
||||
|
||||
ccl_device_inline OrenNayarParam bsdf_oren_nayar_param(const Spectrum color,
|
||||
const float nv,
|
||||
const float roughness)
|
||||
{
|
||||
const float sigma = saturatef(roughness);
|
||||
const float a = 1.0f / (M_PI_F + sigma * (M_PI_2_F - 2.0f / 3.0f));
|
||||
const float b = sigma * a;
|
||||
|
||||
/* Compute energy compensation term (except for (1.0f - El) factor since it depends on wo). */
|
||||
const Spectrum albedo = saturate(color);
|
||||
const float Eavg = a * M_PI_F + ((M_2PI_F - 5.6f) / 3.0f) * b;
|
||||
const Spectrum Ems = M_1_PI_F * sqr(albedo) * (Eavg / (1.0f - Eavg)) /
|
||||
(one_spectrum() - albedo * (1.0f - Eavg));
|
||||
const float Ev = a * M_PI_F + b * bsdf_oren_nayar_G(max(nv, 0.0f));
|
||||
|
||||
return {/* .roughness = */ roughness,
|
||||
/* .a = */ a,
|
||||
/* .b = */ b,
|
||||
/* .multiscatter_term = */ Ems * (1.0f - Ev)};
|
||||
}
|
||||
|
||||
ccl_device void bsdf_oren_nayar_setup(ccl_private ShaderData *sd,
|
||||
const float3 N,
|
||||
const Spectrum weight,
|
||||
const float roughness,
|
||||
const Spectrum color)
|
||||
{
|
||||
ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc(
|
||||
sd, sizeof(OrenNayarBsdf), weight);
|
||||
if (bsdf) {
|
||||
bsdf->N = N;
|
||||
bsdf->type = CLOSURE_BSDF_OREN_NAYAR_ID;
|
||||
bsdf->param = bsdf_oren_nayar_param(color, dot(bsdf->N, sd->wi), roughness);
|
||||
sd->flag |= SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_oren_nayar_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private OrenNayarBsdf *bsdf = (const ccl_private OrenNayarBsdf *)sc;
|
||||
const float cosNO = dot(bsdf->N, wo);
|
||||
if (cosNO > 0.0f) {
|
||||
*pdf = cosNO * M_1_PI_F;
|
||||
return bsdf_oren_nayar_get_intensity(bsdf, bsdf->N, wi, wo);
|
||||
}
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device int bsdf_oren_nayar_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness,
|
||||
ccl_private float *eta)
|
||||
{
|
||||
const ccl_private OrenNayarBsdf *bsdf = (const ccl_private OrenNayarBsdf *)sc;
|
||||
|
||||
sample_cos_hemisphere(bsdf->N, rand, wo, pdf);
|
||||
|
||||
if (dot(Ng, *wo) > 0.0f) {
|
||||
*eval = bsdf_oren_nayar_get_intensity(bsdf, bsdf->N, wi, *wo);
|
||||
}
|
||||
else {
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
}
|
||||
|
||||
*sampled_roughness = one_float2();
|
||||
*eta = 1.0f;
|
||||
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------- */
|
||||
/** \name Subsurface in thin-walled mode
|
||||
*
|
||||
* An infinitesimally thin sheet of dense scattering material, following OpenPBR spec
|
||||
* https://academysoftwarefoundation.github.io/OpenPBR/#model/thin-walledcase
|
||||
*
|
||||
* It is approximated by a diffuse lobe and a translucent lobe, the respective weights of both
|
||||
* lobes are given by subsurface anisotropy, with specifies the relative amount of backward and
|
||||
* forward scattering.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
ccl_device_inline void bsdf_thin_subsurface_setup(ccl_private ShaderData *sd,
|
||||
const float3 N,
|
||||
const Spectrum weight,
|
||||
const float anisotropy,
|
||||
const float roughness,
|
||||
const Spectrum color)
|
||||
{
|
||||
const Spectrum reflection_weight = saturatef(0.5f * (1.0f - anisotropy)) * weight;
|
||||
const Spectrum transmission_weight = saturatef(0.5f * (1.0f + anisotropy)) * weight;
|
||||
if (is_zero(reflection_weight) && is_zero(transmission_weight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sd->num_closure_left == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (diffuse_roughness_is_almost_zero(roughness)) {
|
||||
bsdf_diffuse_setup(sd, N, reflection_weight);
|
||||
bsdf_translucent_setup(sd, N, transmission_weight);
|
||||
return;
|
||||
}
|
||||
|
||||
const OrenNayarParam param = bsdf_oren_nayar_param(color, dot(N, sd->wi), roughness);
|
||||
|
||||
/* Reflection. */
|
||||
{
|
||||
ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc(
|
||||
sd, sizeof(OrenNayarBsdf), reflection_weight);
|
||||
if (bsdf) {
|
||||
bsdf->type = CLOSURE_BSDF_OREN_NAYAR_ID;
|
||||
bsdf->N = N;
|
||||
bsdf->param = param;
|
||||
sd->flag |= SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Transmission. */
|
||||
{
|
||||
ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc(
|
||||
sd, sizeof(OrenNayarBsdf), transmission_weight);
|
||||
if (bsdf) {
|
||||
bsdf->type = CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID;
|
||||
bsdf->N = -N;
|
||||
bsdf->param = param;
|
||||
sd->flag |= SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device int bsdf_rough_translucent_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness,
|
||||
ccl_private float *eta)
|
||||
{
|
||||
bsdf_oren_nayar_sample(sc, -Ng, reflect(wi, sc->N), rand, eval, wo, pdf, sampled_roughness, eta);
|
||||
return LABEL_TRANSMIT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_rough_translucent_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
return bsdf_oren_nayar_eval(sc, reflect(wi, sc->N), wo, pdf);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
127
blender-5.2.0/intern/cycles/kernel/closure/bsdf_phong_ramp.h
Normal file
127
blender-5.2.0/intern/cycles/kernel/closure/bsdf_phong_ramp.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/util/colorspace.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __OSL__
|
||||
|
||||
struct PhongRampBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
float exponent;
|
||||
ccl_private float3 *colors;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(PhongRampBsdf), "PhongRampBsdf is too large!");
|
||||
|
||||
ccl_device float3 bsdf_phong_ramp_get_color(const float3 colors[8], float pos)
|
||||
{
|
||||
const int MAXCOLORS = 8;
|
||||
|
||||
const float npos = pos * (float)(MAXCOLORS - 1);
|
||||
const int ipos = float_to_int(npos);
|
||||
if (ipos < 0) {
|
||||
return colors[0];
|
||||
}
|
||||
if (ipos >= (MAXCOLORS - 1)) {
|
||||
return colors[MAXCOLORS - 1];
|
||||
}
|
||||
const float offset = npos - (float)ipos;
|
||||
return colors[ipos] * (1.0f - offset) + colors[ipos + 1] * offset;
|
||||
}
|
||||
|
||||
ccl_device int bsdf_phong_ramp_setup(ccl_private PhongRampBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_PHONG_RAMP_ID;
|
||||
bsdf->exponent = max(bsdf->exponent, 0.0f);
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_phong_ramp_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private PhongRampBsdf *bsdf = (const ccl_private PhongRampBsdf *)sc;
|
||||
const float m_exponent = bsdf->exponent;
|
||||
const float cosNI = dot(bsdf->N, wi);
|
||||
const float cosNO = dot(bsdf->N, wo);
|
||||
|
||||
if (cosNI > 0 && cosNO > 0) {
|
||||
// reflect the view vector
|
||||
const float3 R = (2 * cosNI) * bsdf->N - wi;
|
||||
const float cosRO = dot(R, wo);
|
||||
if (cosRO > 0) {
|
||||
const float cosp = powf(cosRO, m_exponent);
|
||||
const float common = 0.5f * M_1_PI_F * cosp;
|
||||
const float out = cosNO * (m_exponent + 2) * common;
|
||||
*pdf = (m_exponent + 1) * common;
|
||||
return rgb_to_spectrum(bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out);
|
||||
}
|
||||
}
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device_inline float phong_ramp_exponent_to_roughness(const float exponent)
|
||||
{
|
||||
return sqrt(1.0f / ((exponent + 2.0f) / 2.0f));
|
||||
}
|
||||
|
||||
ccl_device int bsdf_phong_ramp_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness)
|
||||
{
|
||||
const ccl_private PhongRampBsdf *bsdf = (const ccl_private PhongRampBsdf *)sc;
|
||||
const float cosNI = dot(bsdf->N, wi);
|
||||
const float m_exponent = bsdf->exponent;
|
||||
const float m_roughness = phong_ramp_exponent_to_roughness(m_exponent);
|
||||
*sampled_roughness = make_float2(m_roughness, m_roughness);
|
||||
|
||||
if (cosNI > 0) {
|
||||
// reflect the view vector
|
||||
const float3 R = (2 * cosNI) * bsdf->N - wi;
|
||||
float3 T;
|
||||
float3 B;
|
||||
make_orthonormals(R, &T, &B);
|
||||
const float phi = M_2PI_F * rand.x;
|
||||
const float cosTheta = powf(rand.y, 1 / (m_exponent + 1));
|
||||
*wo = to_global(spherical_cos_to_direction(cosTheta, phi), T, B, R);
|
||||
if (dot(Ng, *wo) > 0.0f) {
|
||||
// common terms for pdf and eval
|
||||
const float cosNO = dot(bsdf->N, *wo);
|
||||
// make sure the direction we chose is still in the right hemisphere
|
||||
if (cosNO > 0) {
|
||||
const float cosp = powf(cosTheta, m_exponent);
|
||||
const float common = 0.5f * M_1_PI_F * cosp;
|
||||
*pdf = (m_exponent + 1) * common;
|
||||
const float out = cosNO * (m_exponent + 2) * common;
|
||||
*eval = rgb_to_spectrum(bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
*eval = zero_spectrum();
|
||||
*pdf = 0.0f;
|
||||
}
|
||||
return LABEL_REFLECT | LABEL_GLOSSY;
|
||||
}
|
||||
|
||||
#endif /* __OSL__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,481 @@
|
||||
/* SPDX-FileCopyrightText: 2018-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* This code implements the paper [A practical and controllable hair and fur model for production
|
||||
* path tracing](https://doi.org/10.1145/2775280.2792559) by Chiang, Matt Jen-Yuan, et al. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
# include <cfenv>
|
||||
#endif
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/bsdf_util.h"
|
||||
|
||||
#include "kernel/util/colorspace.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct ChiangHairBSDF {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
/* Absorption coefficient. */
|
||||
Spectrum sigma;
|
||||
/* Variance of the underlying logistic distribution. */
|
||||
float v;
|
||||
/* Scale factor of the underlying logistic distribution. */
|
||||
float s;
|
||||
/* Cuticle tilt angle. */
|
||||
float alpha;
|
||||
/* IOR. */
|
||||
float eta;
|
||||
/* Effective variance for the diffuse bounce only. */
|
||||
float m0_roughness;
|
||||
|
||||
/* Azimuthal offset. */
|
||||
float h;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(ChiangHairBSDF), "ChiangHairBSDF is too large!");
|
||||
|
||||
/* Gives the change in direction in the normal plane for the given angles and p-th-order
|
||||
* scattering. */
|
||||
ccl_device_inline float delta_phi(const int p, const float gamma_o, const float gamma_t)
|
||||
{
|
||||
return 2.0f * p * gamma_t - 2.0f * gamma_o + p * M_PI_F;
|
||||
}
|
||||
|
||||
/* Remaps the given angle to [-pi, pi]. */
|
||||
ccl_device_inline float wrap_angle(const float a)
|
||||
{
|
||||
return (a + M_PI_F) - M_2PI_F * floorf((a + M_PI_F) / M_2PI_F) - M_PI_F;
|
||||
}
|
||||
|
||||
/* Logistic distribution function. */
|
||||
ccl_device_inline float logistic(const float x, const float s)
|
||||
{
|
||||
const float v = expf(-fabsf(x) / s);
|
||||
return v / (s * sqr(1.0f + v));
|
||||
}
|
||||
|
||||
/* Logistic cumulative density function. */
|
||||
ccl_device_inline float logistic_cdf(const float x, const float s)
|
||||
{
|
||||
const float arg = -x / s;
|
||||
/* expf() overflows if arg >= 89.0. */
|
||||
if (arg > 88.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
return 1.0f / (1.0f + expf(arg));
|
||||
}
|
||||
|
||||
/* Numerical approximation to the Bessel function of the first kind. */
|
||||
ccl_device_inline float bessel_I0(float x)
|
||||
{
|
||||
x = sqr(x);
|
||||
float val = 1.0f + 0.25f * x;
|
||||
float pow_x_2i = sqr(x);
|
||||
uint64_t i_fac_2 = 1;
|
||||
int pow_4_i = 16;
|
||||
for (int i = 2; i < 10; i++) {
|
||||
i_fac_2 *= i * i;
|
||||
const float newval = val + pow_x_2i / (pow_4_i * i_fac_2);
|
||||
if (val == newval) {
|
||||
return val;
|
||||
}
|
||||
val = newval;
|
||||
pow_x_2i *= x;
|
||||
pow_4_i *= 4;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Logarithm of the Bessel function of the first kind. */
|
||||
ccl_device_inline float log_bessel_I0(const float x)
|
||||
{
|
||||
if (x > 12.0f) {
|
||||
/* log(1/x) == -log(x) if x > 0.
|
||||
* This is only used with positive cosines. */
|
||||
return x + 0.5f * (1.f / (8.0f * x) - M_LN_2PI_F - logf(x));
|
||||
}
|
||||
return logf(bessel_I0(x));
|
||||
}
|
||||
|
||||
/* Logistic distribution limited to the interval [-pi, pi]. */
|
||||
ccl_device_inline float trimmed_logistic(const float x, const float s)
|
||||
{
|
||||
/* The logistic distribution is symmetric and centered around zero,
|
||||
* so logistic_cdf(x, s) = 1 - logistic_cdf(-x, s).
|
||||
* Therefore, logistic_cdf(x, s)-logistic_cdf(-x, s) = 1 - 2*logistic_cdf(-x, s) */
|
||||
const float scaling_fac = 1.0f - 2.0f * logistic_cdf(-M_PI_F, s);
|
||||
const float val = logistic(x, s);
|
||||
return safe_divide(val, scaling_fac);
|
||||
}
|
||||
|
||||
/* Sampling function for the trimmed logistic function. */
|
||||
ccl_device_inline float sample_trimmed_logistic(const float u, const float s)
|
||||
{
|
||||
const float cdf_minuspi = logistic_cdf(-M_PI_F, s);
|
||||
const float x = -s * logf(1.0f / (u * (1.0f - 2.0f * cdf_minuspi) + cdf_minuspi) - 1.0f);
|
||||
return clamp(x, -M_PI_F, M_PI_F);
|
||||
}
|
||||
|
||||
/* Azimuthal scattering function Np. */
|
||||
ccl_device_inline float azimuthal_scattering(
|
||||
float phi, const int p, const float s, float gamma_o, const float gamma_t)
|
||||
{
|
||||
const float phi_o = wrap_angle(phi - delta_phi(p, gamma_o, gamma_t));
|
||||
const float val = trimmed_logistic(phi_o, s);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Longitudinal scattering function Mp. */
|
||||
ccl_device_inline float longitudinal_scattering(float sin_theta_i,
|
||||
const float cos_theta_i,
|
||||
const float sin_theta_o,
|
||||
const float cos_theta_o,
|
||||
const float v)
|
||||
{
|
||||
const float inv_v = 1.0f / v;
|
||||
const float cos_arg = cos_theta_i * cos_theta_o * inv_v;
|
||||
const float sin_arg = sin_theta_i * sin_theta_o * inv_v;
|
||||
if (v <= 0.1f) {
|
||||
const float i0 = log_bessel_I0(cos_arg);
|
||||
const float val = expf(i0 - sin_arg - inv_v + 0.6931f + logf(0.5f * inv_v));
|
||||
kernel_assert(isfinite_safe(val));
|
||||
return val;
|
||||
}
|
||||
const float i0 = bessel_I0(cos_arg);
|
||||
const float val = (expf(-sin_arg) * i0) / (sinhf(inv_v) * 2.0f * v);
|
||||
kernel_assert(isfinite_safe(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
#ifdef __HAIR__
|
||||
/* Set up the hair closure. */
|
||||
ccl_device int bsdf_hair_chiang_setup(ccl_private ShaderData *sd, ccl_private ChiangHairBSDF *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_HAIR_CHIANG_ID;
|
||||
bsdf->v = clamp(bsdf->v, 0.001f, 1.0f);
|
||||
bsdf->s = clamp(bsdf->s, 0.001f, 1.0f);
|
||||
/* Apply Primary Reflection Roughness modifier. */
|
||||
bsdf->m0_roughness = clamp(bsdf->m0_roughness * bsdf->v, 0.001f, 1.0f);
|
||||
|
||||
/* Map from roughness_u and roughness_v to variance and scale factor. */
|
||||
bsdf->v = sqr(0.726f * bsdf->v + 0.812f * sqr(bsdf->v) + 3.700f * pow20(bsdf->v));
|
||||
bsdf->s = (0.265f * bsdf->s + 1.194f * sqr(bsdf->s) + 5.372f * pow22(bsdf->s)) * M_SQRT_PI_8_F;
|
||||
bsdf->m0_roughness = sqr(0.726f * bsdf->m0_roughness + 0.812f * sqr(bsdf->m0_roughness) +
|
||||
3.700f * pow20(bsdf->m0_roughness));
|
||||
|
||||
/* Compute local frame, aligned to curve tangent and ray direction. */
|
||||
const float3 X = safe_normalize(sd->dPdu);
|
||||
const float3 Y = safe_normalize(cross(X, sd->wi));
|
||||
const float3 Z = safe_normalize(cross(X, Y));
|
||||
|
||||
/* h -1..0..1 means the rays goes from grazing the hair, to hitting it at
|
||||
* the center, to grazing the other edge. This is the sine of the angle
|
||||
* between sd->Ng and Z, as seen from the tangent X. */
|
||||
|
||||
/* TODO: we convert this value to a cosine later and discard the sign, so
|
||||
* we could probably save some operations. */
|
||||
bsdf->h = ((sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) ? -sd->v :
|
||||
dot(cross(sd->Ng, X), Z);
|
||||
|
||||
kernel_assert(fabsf(bsdf->h) < 1.0f + 1e-4f);
|
||||
kernel_assert(isfinite_safe(Y));
|
||||
kernel_assert(isfinite_safe(bsdf->h));
|
||||
|
||||
bsdf->N = Y;
|
||||
bsdf->alpha = -bsdf->alpha;
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
|
||||
}
|
||||
|
||||
#endif /* __HAIR__ */
|
||||
|
||||
/* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */
|
||||
ccl_device_inline void hair_attenuation(KernelGlobals kg,
|
||||
const float f,
|
||||
Spectrum T,
|
||||
ccl_private Spectrum *Ap,
|
||||
ccl_private float *Ap_energy)
|
||||
{
|
||||
/* Primary specular (R). */
|
||||
Ap[0] = make_spectrum(f);
|
||||
Ap_energy[0] = f;
|
||||
|
||||
/* Transmission (TT). */
|
||||
Spectrum col = sqr(1.0f - f) * T;
|
||||
Ap[1] = col;
|
||||
Ap_energy[1] = spectrum_to_gray(kg, col);
|
||||
|
||||
/* Secondary specular (TRT). */
|
||||
col *= T * f;
|
||||
Ap[2] = col;
|
||||
Ap_energy[2] = spectrum_to_gray(kg, col);
|
||||
|
||||
/* Residual component (TRRT+). */
|
||||
col *= safe_divide(T * f, one_spectrum() - T * f);
|
||||
Ap[3] = col;
|
||||
Ap_energy[3] = spectrum_to_gray(kg, col);
|
||||
|
||||
/* Normalize sampling weights. */
|
||||
const float totweight = Ap_energy[0] + Ap_energy[1] + Ap_energy[2] + Ap_energy[3];
|
||||
const float fac = safe_divide(1.0f, totweight);
|
||||
|
||||
Ap_energy[0] *= fac;
|
||||
Ap_energy[1] *= fac;
|
||||
Ap_energy[2] *= fac;
|
||||
Ap_energy[3] *= fac;
|
||||
}
|
||||
|
||||
/* Update sin_theta_o and cos_theta_o to account for scale tilt for each bounce. */
|
||||
ccl_device_inline void hair_alpha_angles(const float sin_theta_o,
|
||||
const float cos_theta_o,
|
||||
const float alpha,
|
||||
ccl_private float *angles)
|
||||
{
|
||||
const float sin_1alpha = sinf(alpha);
|
||||
const float cos_1alpha = cos_from_sin(sin_1alpha);
|
||||
const float sin_2alpha = 2.0f * sin_1alpha * cos_1alpha;
|
||||
const float cos_2alpha = sqr(cos_1alpha) - sqr(sin_1alpha);
|
||||
const float sin_4alpha = 2.0f * sin_2alpha * cos_2alpha;
|
||||
const float cos_4alpha = sqr(cos_2alpha) - sqr(sin_2alpha);
|
||||
|
||||
angles[0] = sin_theta_o * cos_2alpha - cos_theta_o * sin_2alpha;
|
||||
angles[1] = fabsf(cos_theta_o * cos_2alpha + sin_theta_o * sin_2alpha);
|
||||
angles[2] = sin_theta_o * cos_1alpha + cos_theta_o * sin_1alpha;
|
||||
angles[3] = fabsf(cos_theta_o * cos_1alpha - sin_theta_o * sin_1alpha);
|
||||
angles[4] = sin_theta_o * cos_4alpha + cos_theta_o * sin_4alpha;
|
||||
angles[5] = fabsf(cos_theta_o * cos_4alpha - sin_theta_o * sin_4alpha);
|
||||
}
|
||||
|
||||
/* Evaluation function for our shader. */
|
||||
ccl_device Spectrum bsdf_hair_chiang_eval(KernelGlobals kg,
|
||||
const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
kernel_assert(isfinite_safe(sd->P) && isfinite_safe(sd->ray_length));
|
||||
|
||||
const ccl_private ChiangHairBSDF *bsdf = (const ccl_private ChiangHairBSDF *)sc;
|
||||
const float3 Y = bsdf->N;
|
||||
|
||||
const float3 X = safe_normalize(sd->dPdu);
|
||||
kernel_assert(fabsf(dot(X, Y)) < 1e-3f);
|
||||
const float3 Z = safe_normalize(cross(X, Y));
|
||||
|
||||
/* local_I is the illumination direction. */
|
||||
const float3 local_O = to_local(sd->wi, X, Y, Z);
|
||||
const float3 local_I = to_local(wo, X, Y, Z);
|
||||
|
||||
const float sin_theta_o = local_O.x;
|
||||
const float cos_theta_o = cos_from_sin(sin_theta_o);
|
||||
const float phi_o = atan2f(local_O.z, local_O.y);
|
||||
|
||||
const float sin_theta_t = sin_theta_o / bsdf->eta;
|
||||
const float cos_theta_t = cos_from_sin(sin_theta_t);
|
||||
|
||||
const float sin_gamma_o = bsdf->h;
|
||||
const float cos_gamma_o = cos_from_sin(sin_gamma_o);
|
||||
const float gamma_o = safe_asinf(sin_gamma_o);
|
||||
|
||||
const float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o));
|
||||
const float cos_gamma_t = cos_from_sin(sin_gamma_t);
|
||||
const float gamma_t = safe_asinf(sin_gamma_t);
|
||||
|
||||
const Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t));
|
||||
Spectrum Ap[4];
|
||||
float Ap_energy[4];
|
||||
hair_attenuation(
|
||||
kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap, Ap_energy);
|
||||
|
||||
const float sin_theta_i = local_I.x;
|
||||
const float cos_theta_i = cos_from_sin(sin_theta_i);
|
||||
const float phi_i = atan2f(local_I.z, local_I.y);
|
||||
|
||||
const float phi = phi_i - phi_o;
|
||||
|
||||
float angles[6];
|
||||
hair_alpha_angles(sin_theta_o, cos_theta_o, bsdf->alpha, angles);
|
||||
|
||||
Spectrum F = zero_spectrum();
|
||||
float F_energy = 0.0f;
|
||||
|
||||
/* Primary specular (R), Transmission (TT) and Secondary Specular (TRT). */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const float Mp = longitudinal_scattering(sin_theta_i,
|
||||
cos_theta_i,
|
||||
angles[2 * i],
|
||||
angles[2 * i + 1],
|
||||
(i == 0) ? bsdf->m0_roughness :
|
||||
(i == 1) ? 0.25f * bsdf->v :
|
||||
4.0f * bsdf->v);
|
||||
const float Np = azimuthal_scattering(phi, i, bsdf->s, gamma_o, gamma_t);
|
||||
F += Ap[i] * Mp * Np;
|
||||
F_energy += Ap_energy[i] * Mp * Np;
|
||||
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
|
||||
}
|
||||
|
||||
/* Residual component (TRRT+). */
|
||||
{
|
||||
const float Mp = longitudinal_scattering(
|
||||
sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
|
||||
const float Np = M_1_2PI_F;
|
||||
F += Ap[3] * Mp * Np;
|
||||
F_energy += Ap_energy[3] * Mp * Np;
|
||||
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
|
||||
}
|
||||
|
||||
*pdf = F_energy;
|
||||
return F;
|
||||
}
|
||||
|
||||
/* Sampling function for the hair shader. */
|
||||
ccl_device int bsdf_hair_chiang_sample(KernelGlobals kg,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
ccl_private ShaderData *sd,
|
||||
float3 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness)
|
||||
{
|
||||
ccl_private ChiangHairBSDF *bsdf = (ccl_private ChiangHairBSDF *)sc;
|
||||
|
||||
*sampled_roughness = make_float2(bsdf->m0_roughness, bsdf->m0_roughness);
|
||||
|
||||
const float3 Y = bsdf->N;
|
||||
|
||||
const float3 X = safe_normalize(sd->dPdu);
|
||||
kernel_assert(fabsf(dot(X, Y)) < 1e-3f);
|
||||
const float3 Z = safe_normalize(cross(X, Y));
|
||||
|
||||
/* `wo` in PBRT. */
|
||||
const float3 local_O = to_local(sd->wi, X, Y, Z);
|
||||
|
||||
const float sin_theta_o = local_O.x;
|
||||
const float cos_theta_o = cos_from_sin(sin_theta_o);
|
||||
const float phi_o = atan2f(local_O.z, local_O.y);
|
||||
|
||||
const float sin_theta_t = sin_theta_o / bsdf->eta;
|
||||
const float cos_theta_t = cos_from_sin(sin_theta_t);
|
||||
|
||||
const float sin_gamma_o = bsdf->h;
|
||||
const float cos_gamma_o = cos_from_sin(sin_gamma_o);
|
||||
const float gamma_o = safe_asinf(sin_gamma_o);
|
||||
|
||||
const float sin_gamma_t = sin_gamma_o * cos_theta_o / sqrtf(sqr(bsdf->eta) - sqr(sin_theta_o));
|
||||
const float cos_gamma_t = cos_from_sin(sin_gamma_t);
|
||||
const float gamma_t = safe_asinf(sin_gamma_t);
|
||||
|
||||
const Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t));
|
||||
Spectrum Ap[4];
|
||||
float Ap_energy[4];
|
||||
hair_attenuation(
|
||||
kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap, Ap_energy);
|
||||
|
||||
int p = 0;
|
||||
for (; p < 3; p++) {
|
||||
if (rand.z < Ap_energy[p]) {
|
||||
break;
|
||||
}
|
||||
rand.z -= Ap_energy[p];
|
||||
}
|
||||
rand.z /= Ap_energy[p];
|
||||
|
||||
float v = bsdf->v;
|
||||
if (p == 1) {
|
||||
v *= 0.25f;
|
||||
}
|
||||
if (p >= 2) {
|
||||
v *= 4.0f;
|
||||
}
|
||||
|
||||
float angles[6];
|
||||
hair_alpha_angles(sin_theta_o, cos_theta_o, bsdf->alpha, angles);
|
||||
float sin_theta_o_tilted = sin_theta_o;
|
||||
float cos_theta_o_tilted = cos_theta_o;
|
||||
if (p < 3) {
|
||||
sin_theta_o_tilted = angles[2 * p];
|
||||
cos_theta_o_tilted = angles[2 * p + 1];
|
||||
}
|
||||
rand.z = max(rand.z, 1e-5f);
|
||||
const float fac = 1.0f + v * logf(rand.z + (1.0f - rand.z) * expf(-2.0f / v));
|
||||
const float sin_theta_i = -fac * sin_theta_o_tilted +
|
||||
sin_from_cos(fac) * cosf(M_2PI_F * rand.y) * cos_theta_o_tilted;
|
||||
const float cos_theta_i = cos_from_sin(sin_theta_i);
|
||||
|
||||
float phi;
|
||||
if (p < 3) {
|
||||
phi = delta_phi(p, gamma_o, gamma_t) + sample_trimmed_logistic(rand.x, bsdf->s);
|
||||
}
|
||||
else {
|
||||
phi = M_2PI_F * rand.x;
|
||||
}
|
||||
const float phi_i = phi_o + phi;
|
||||
|
||||
Spectrum F = zero_spectrum();
|
||||
float F_energy = 0.0f;
|
||||
|
||||
/* Primary specular (R), Transmission (TT) and Secondary Specular (TRT). */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const float Mp = longitudinal_scattering(sin_theta_i,
|
||||
cos_theta_i,
|
||||
angles[2 * i],
|
||||
angles[2 * i + 1],
|
||||
(i == 0) ? bsdf->m0_roughness :
|
||||
(i == 1) ? 0.25f * bsdf->v :
|
||||
4.0f * bsdf->v);
|
||||
const float Np = azimuthal_scattering(phi, i, bsdf->s, gamma_o, gamma_t);
|
||||
F += Ap[i] * Mp * Np;
|
||||
F_energy += Ap_energy[i] * Mp * Np;
|
||||
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
|
||||
}
|
||||
|
||||
/* Residual component (TRRT+). */
|
||||
{
|
||||
const float Mp = longitudinal_scattering(
|
||||
sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v);
|
||||
const float Np = M_1_2PI_F;
|
||||
F += Ap[3] * Mp * Np;
|
||||
F_energy += Ap_energy[3] * Mp * Np;
|
||||
kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy));
|
||||
}
|
||||
|
||||
*eval = F;
|
||||
*pdf = F_energy;
|
||||
*wo = to_global(spherical_cos_to_direction(sin_theta_i, phi_i), Y, Z, X);
|
||||
|
||||
return LABEL_GLOSSY | ((p == 0) ? LABEL_REFLECT : LABEL_TRANSMIT);
|
||||
}
|
||||
|
||||
/* Implements Filter Glossy by capping the effective roughness. */
|
||||
ccl_device void bsdf_hair_chiang_blur(ccl_private ShaderClosure *sc, const float roughness)
|
||||
{
|
||||
ccl_private ChiangHairBSDF *bsdf = (ccl_private ChiangHairBSDF *)sc;
|
||||
|
||||
bsdf->v = fmaxf(roughness, bsdf->v);
|
||||
bsdf->s = fmaxf(roughness, bsdf->s);
|
||||
bsdf->m0_roughness = fmaxf(roughness, bsdf->m0_roughness);
|
||||
}
|
||||
|
||||
/* Hair Albedo. */
|
||||
ccl_device Spectrum bsdf_hair_chiang_albedo(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc)
|
||||
{
|
||||
ccl_private ChiangHairBSDF *bsdf = (ccl_private ChiangHairBSDF *)sc;
|
||||
|
||||
const float cos_theta_o = cos_from_sin(dot(sd->wi, safe_normalize(sd->dPdu)));
|
||||
const float cos_gamma_o = cos_from_sin(bsdf->h);
|
||||
const float f = fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta);
|
||||
|
||||
const float roughness_scale = bsdf_principled_hair_albedo_roughness_scale(bsdf->v);
|
||||
/* TODO(lukas): Adding the Fresnel term here as a workaround until the proper refactor. */
|
||||
return exp(-sqrt(bsdf->sigma) * roughness_scale) + make_spectrum(f);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,935 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* This code implements the paper [A Microfacet-based Hair Scattering
|
||||
* Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by Weizhen Huang, Matthias B.
|
||||
* Hullin and Johannes Hanika. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/bsdf_microfacet.h"
|
||||
#include "kernel/closure/bsdf_principled_hair_chiang.h"
|
||||
#include "kernel/closure/bsdf_transparent.h"
|
||||
#include "kernel/closure/bsdf_util.h"
|
||||
#include "kernel/sample/lcg.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct HuangHairExtra {
|
||||
/* Optional modulation factors. */
|
||||
float R, TT, TRT;
|
||||
|
||||
/* Local coordinate system. X is stored as `bsdf->N`. */
|
||||
float3 Y, Z;
|
||||
|
||||
/* Incident direction in local coordinate system. */
|
||||
float3 wi;
|
||||
|
||||
/* Projected radius from the view direction. */
|
||||
float radius;
|
||||
|
||||
/* Squared Eccentricity. */
|
||||
float e2;
|
||||
|
||||
/* The projected width of half a pixel at `sd->P` in `h` space. */
|
||||
float pixel_coverage;
|
||||
|
||||
/* Valid integration interval, pre-divided by the radius so the range is [-1, 1]. */
|
||||
Interval<float> h;
|
||||
};
|
||||
|
||||
struct HuangHairBSDF {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
/* Absorption coefficient. */
|
||||
Spectrum sigma;
|
||||
|
||||
/* Microfacet distribution roughness. */
|
||||
float roughness;
|
||||
|
||||
/* Cuticle tilt angle. */
|
||||
float tilt;
|
||||
|
||||
/* Index of refraction. */
|
||||
float eta;
|
||||
|
||||
/* The ratio of the minor axis to the major axis. */
|
||||
float aspect_ratio;
|
||||
|
||||
/* Azimuthal offset. */
|
||||
float h;
|
||||
|
||||
/* Extra closure for optional modulation factors and local coordinate system. */
|
||||
ccl_private HuangHairExtra *extra;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(HuangHairBSDF), "HuangHairBSDF is too large!");
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(HuangHairExtra), "HuangHairExtra is too large!");
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Hair coordinate system utils.
|
||||
* \{ */
|
||||
|
||||
/* TODO(weizhen): add a namespace `huanghair` for the below utility functions. */
|
||||
|
||||
/* Returns `sin(theta)` of the given direction. */
|
||||
ccl_device_inline float sin_theta(const float3 w)
|
||||
{
|
||||
return w.y;
|
||||
}
|
||||
|
||||
/* Returns `cos(theta)` of the given direction. */
|
||||
ccl_device_inline float cos_theta(const float3 w)
|
||||
{
|
||||
return safe_sqrtf(sqr(w.x) + sqr(w.z));
|
||||
}
|
||||
|
||||
/* Returns `tan(theta)` of the given direction. */
|
||||
ccl_device_inline float tan_theta(const float3 w)
|
||||
{
|
||||
return sin_theta(w) / cos_theta(w);
|
||||
}
|
||||
|
||||
/* Returns `sin(phi)` and `cos(phi)` of the given direction. */
|
||||
ccl_device float sin_phi(const float3 w)
|
||||
{
|
||||
return w.x / cos_theta(w);
|
||||
}
|
||||
|
||||
/* Check whether the hair cross-section is circular. */
|
||||
ccl_device_inline bool is_circular(const float b)
|
||||
{
|
||||
return b == 1.0f;
|
||||
}
|
||||
|
||||
ccl_device float2 sincos_phi(const float3 w)
|
||||
{
|
||||
const float c = cos_theta(w);
|
||||
return make_float2(w.x / c, w.z / c);
|
||||
}
|
||||
|
||||
/* Extract the theta coordinate from the given direction.
|
||||
* -pi < theta < pi */
|
||||
ccl_device_inline float dir_theta(const float3 w)
|
||||
{
|
||||
return atan2f(sin_theta(w), cos_theta(w));
|
||||
}
|
||||
|
||||
/* Extract the phi coordinate from the given direction, assuming `phi(wi) == 0`.
|
||||
* -pi < phi < pi */
|
||||
ccl_device_inline float dir_phi(const float3 w)
|
||||
{
|
||||
return atan2f(w.x, w.z);
|
||||
}
|
||||
|
||||
/* Extract theta and phi coordinates from the given direction, assuming `phi(wi) == 0`.
|
||||
* -pi/2 < theta < pi/2, -pi < phi < pi */
|
||||
ccl_device_inline float2 dir_sph(const float3 w)
|
||||
{
|
||||
return make_float2(dir_theta(w), dir_phi(w));
|
||||
}
|
||||
|
||||
/* Conversion between `gamma` and `phi`. Notations see Figure 5 in the paper. */
|
||||
ccl_device_inline float to_phi(const float gamma, const float b)
|
||||
{
|
||||
if (is_circular(b)) {
|
||||
return gamma;
|
||||
}
|
||||
float sin_gamma;
|
||||
float cos_gamma;
|
||||
fast_sincosf(gamma, &sin_gamma, &cos_gamma);
|
||||
return atan2f(b * sin_gamma, cos_gamma);
|
||||
}
|
||||
|
||||
ccl_device_inline float to_gamma(const float phi, const float b)
|
||||
{
|
||||
if (is_circular(b)) {
|
||||
return phi;
|
||||
}
|
||||
float sin_phi;
|
||||
float cos_phi;
|
||||
fast_sincosf(phi, &sin_phi, &cos_phi);
|
||||
return atan2f(sin_phi, b * cos_phi);
|
||||
}
|
||||
|
||||
/* Intersecting `wi` with the ellipse defined by `x = sin_gamma, y = b * cos_gamma` results in
|
||||
* equation `h = -cos_phi_i * sin_gamma + b * sin_phi_i * cos_gamma`. */
|
||||
ccl_device_inline float phi_to_h(const float phi, const float b, const float3 wi)
|
||||
{
|
||||
if (is_circular(b)) {
|
||||
return -fast_sinf(phi);
|
||||
}
|
||||
|
||||
float sin_gamma, cos_gamma;
|
||||
fast_sincosf(to_gamma(phi, b), &sin_gamma, &cos_gamma);
|
||||
const float2 sin_cos_phi_i = sincos_phi(wi);
|
||||
return -sin_cos_phi_i.y * sin_gamma + b * sin_cos_phi_i.x * cos_gamma;
|
||||
}
|
||||
|
||||
/* Solve for `gamma` in equation `h = -cos_phi_i * sin_gamma + b * sin_phi_i * cos_gamma`.
|
||||
* Also, make use of `r = sqrt(sqr(cos_phi_i) + sqr(b * sin_phi_i))` to pre-map `h` to [-1, 1]. */
|
||||
ccl_device_inline float h_to_gamma(const float h_div_r, const float b, const float3 wi)
|
||||
{
|
||||
return is_circular(b) ? -asinf(h_div_r) : atan2f(wi.z, -b * wi.x) - acosf(-h_div_r);
|
||||
}
|
||||
|
||||
/* Jacobian |d_gamma/d_h|, used for changing variable in the integration. */
|
||||
ccl_device_inline float d_gamma_d_h(const float2 sincos_phi_i, const float gamma, const float b)
|
||||
{
|
||||
if (is_circular(b)) {
|
||||
return safe_divide(1.0f, fast_cosf(gamma));
|
||||
}
|
||||
|
||||
float sin_gamma, cos_gamma;
|
||||
fast_sincosf(gamma, &sin_gamma, &cos_gamma);
|
||||
return safe_divide(1.0f, sincos_phi_i.y * cos_gamma + b * sincos_phi_i.x * sin_gamma);
|
||||
}
|
||||
|
||||
/* Compute the coordinate on the ellipse, given `gamma` and the aspect ratio between the minor axis
|
||||
* and the major axis. */
|
||||
ccl_device_inline float2 to_point(const float gamma, const float b)
|
||||
{
|
||||
float sin_gamma;
|
||||
float cos_gamma;
|
||||
fast_sincosf(gamma, &sin_gamma, &cos_gamma);
|
||||
return make_float2(sin_gamma, b * cos_gamma);
|
||||
}
|
||||
|
||||
/* Compute the vector direction given by `theta` and `gamma`. */
|
||||
ccl_device_inline float3 sphg_dir(const float theta, const float gamma, const float b)
|
||||
{
|
||||
float sin_theta;
|
||||
float cos_theta;
|
||||
float sin_gamma;
|
||||
float cos_gamma;
|
||||
float sin_phi;
|
||||
float cos_phi;
|
||||
|
||||
fast_sincosf(theta, &sin_theta, &cos_theta);
|
||||
fast_sincosf(gamma, &sin_gamma, &cos_gamma);
|
||||
|
||||
if (is_circular(b) || fabsf(cos_gamma) < 1e-6f) {
|
||||
sin_phi = sin_gamma;
|
||||
cos_phi = cos_gamma;
|
||||
}
|
||||
else {
|
||||
const float tan_gamma = sin_gamma / cos_gamma;
|
||||
const float tan_phi = b * tan_gamma;
|
||||
cos_phi = signf(cos_gamma) * inversesqrtf(sqr(tan_phi) + 1.0f);
|
||||
sin_phi = cos_phi * tan_phi;
|
||||
}
|
||||
return make_float3(sin_phi * cos_theta, sin_theta, cos_phi * cos_theta);
|
||||
}
|
||||
|
||||
ccl_device_inline float arc_length(const float e2, const float gamma)
|
||||
{
|
||||
return e2 == 0 ? 1.0f : sqrtf(1.0f - e2 * sqr(sinf(gamma)));
|
||||
}
|
||||
|
||||
ccl_device_inline bool is_nearfield(const ccl_private HuangHairBSDF *bsdf)
|
||||
{
|
||||
return bsdf->extra->radius > bsdf->extra->pixel_coverage;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __HAIR__
|
||||
/* Set up the hair closure. */
|
||||
ccl_device int bsdf_hair_huang_setup(ccl_private ShaderData *sd,
|
||||
ccl_private HuangHairBSDF *bsdf,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_HAIR_HUANG_ID;
|
||||
|
||||
bsdf->roughness = clamp(bsdf->roughness, 0.001f, 1.0f);
|
||||
|
||||
/* Negate to keep it consistent with principled hair BSDF. */
|
||||
bsdf->tilt = -bsdf->tilt;
|
||||
|
||||
/* Compute local frame. The Y axis is aligned with the curve tangent; the X axis is perpendicular
|
||||
* to the ray direction for circular cross-sections, or aligned with the major axis for
|
||||
* elliptical cross-sections. */
|
||||
bsdf->extra->Y = safe_normalize(sd->dPdu);
|
||||
const float3 X = safe_normalize(cross(sd->dPdu, sd->wi));
|
||||
|
||||
/* h from -1..0..1 means the rays goes from grazing the hair, to hitting it at the center, to
|
||||
* grazing the other edge. This is the cosine of the angle between `sd->N` and `X`. */
|
||||
bsdf->h = ((sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) ? -sd->v : -dot(X, sd->N);
|
||||
|
||||
kernel_assert(fabsf(bsdf->h) < 1.0f + 1e-4f);
|
||||
kernel_assert(isfinite_safe(bsdf->h));
|
||||
|
||||
if (bsdf->aspect_ratio != 1.0f && (sd->type & PRIMITIVE_CURVE)) {
|
||||
/* Adjust `bsdf->N` to be orthogonal to `sd->dPdu`. */
|
||||
bsdf->N = safe_normalize(cross(sd->dPdu, safe_normalize(cross(bsdf->N, sd->dPdu))));
|
||||
/* Align local frame with the curve normal. */
|
||||
if (bsdf->aspect_ratio > 1.0f) {
|
||||
/* Switch major and minor axis. */
|
||||
bsdf->aspect_ratio = 1.0f / bsdf->aspect_ratio;
|
||||
const float3 minor_axis = safe_normalize(cross(sd->dPdu, bsdf->N));
|
||||
bsdf->N = safe_normalize(cross(minor_axis, sd->dPdu));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Align local frame with the ray direction so that `phi_i == 0`. */
|
||||
bsdf->N = X;
|
||||
}
|
||||
|
||||
/* Fill extra closure. */
|
||||
if (is_zero(bsdf->N) || !isfinite_safe(bsdf->N)) {
|
||||
/* Construct arbitrary local coordinate system. The implementation should ensure smooth
|
||||
* transition along the hair shaft. */
|
||||
make_orthonormals(bsdf->extra->Y, &bsdf->extra->Z, &bsdf->N);
|
||||
}
|
||||
else {
|
||||
bsdf->extra->Z = safe_normalize(cross(bsdf->N, sd->dPdu));
|
||||
}
|
||||
|
||||
const float3 I = to_local(sd->wi, bsdf->N, bsdf->extra->Y, bsdf->extra->Z);
|
||||
bsdf->extra->wi = I;
|
||||
bsdf->extra->e2 = 1.0f - sqr(bsdf->aspect_ratio);
|
||||
bsdf->extra->radius = bsdf->extra->e2 == 0 ?
|
||||
1.0f :
|
||||
sqrtf(1.0f - bsdf->extra->e2 * sqr(I.x) / (sqr(I.x) + sqr(I.z)));
|
||||
|
||||
/* Treat as transparent material if intersection lies outside of the projected radius. */
|
||||
if (fabsf(bsdf->h) >= bsdf->extra->radius) {
|
||||
/* Remove allocated closures. */
|
||||
sd->num_closure--;
|
||||
sd->num_closure_left += 2;
|
||||
/* Allocate transparent closure. */
|
||||
bsdf_transparent_setup(sd, bsdf->weight, path_flag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_HAS_TRANSMISSION;
|
||||
}
|
||||
|
||||
#endif /* __HAIR__ */
|
||||
|
||||
/* Albedo correction, treat as glass. `rough` has already applied square root. */
|
||||
ccl_device_forceinline float bsdf_hair_huang_energy_scale(KernelGlobals kg,
|
||||
const float mu,
|
||||
const float rough,
|
||||
const float ior)
|
||||
{
|
||||
const bool inv_table = (ior < 1.0f);
|
||||
const int ofs = inv_table ? kernel_data.tables.ggx_glass_inv_E : kernel_data.tables.ggx_glass_E;
|
||||
const float z = sqrtf(fabsf((ior - 1.0f) / (ior + 1.0f)));
|
||||
return 1.0f / lookup_table_read_3D(kg, rough, mu, z, ofs, 16, 16, 16);
|
||||
}
|
||||
|
||||
/* Sample microfacets from a tilted mesonormal. */
|
||||
ccl_device_inline float3 sample_wh(const float roughness,
|
||||
const float3 wi,
|
||||
const float3 wm,
|
||||
const float2 rand)
|
||||
{
|
||||
/* Coordinate transformation for microfacet sampling. */
|
||||
float3 s;
|
||||
float3 t;
|
||||
make_orthonormals(wm, &s, &t);
|
||||
|
||||
const float3 wi_wm = to_local(wi, s, t, wm);
|
||||
const float3 wh_wm = microfacet_ggx_sample_vndf(wi_wm, roughness, roughness, rand);
|
||||
const float3 wh = to_global(wh_wm, s, t, wm);
|
||||
return wh;
|
||||
}
|
||||
|
||||
/* Check micronormal/mesonormal direct visibility from direction `v`. */
|
||||
ccl_device_inline bool microfacet_visible(const float3 v, const float3 m, const float3 h)
|
||||
{
|
||||
return (dot(v, h) > 0.0f && dot(v, m) > 0.0f);
|
||||
}
|
||||
|
||||
/* Check micronormal/mesonormal direct visibility from directions `wi` and `wo`. */
|
||||
ccl_device_inline bool microfacet_visible(const float3 wi,
|
||||
const float3 wo,
|
||||
const float3 m,
|
||||
const float3 h)
|
||||
{
|
||||
return microfacet_visible(wi, m, h) && microfacet_visible(wo, m, h);
|
||||
}
|
||||
|
||||
/* Combined shadowing-masking term divided by the shadowing-masking in the incoming direction. */
|
||||
ccl_device_inline float bsdf_Go(const float alpha2, const float cos_NI, const float cos_NO)
|
||||
{
|
||||
const float lambdaI = bsdf_lambda<MicrofacetType::GGX>(alpha2, cos_NI);
|
||||
const float lambdaO = bsdf_lambda<MicrofacetType::GGX>(alpha2, cos_NO);
|
||||
return (1.0f + lambdaI) / (1.0f + lambdaI + lambdaO);
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_hair_huang_eval_r(KernelGlobals kg,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo)
|
||||
{
|
||||
ccl_private HuangHairBSDF *bsdf = (ccl_private HuangHairBSDF *)sc;
|
||||
|
||||
if (bsdf->extra->R <= 0.0f) {
|
||||
return zero_float3();
|
||||
}
|
||||
|
||||
/* Get minor axis, assuming major axis is 1. */
|
||||
const float b = bsdf->aspect_ratio;
|
||||
|
||||
const float3 wh = normalize(wi + wo);
|
||||
|
||||
const float roughness = bsdf->roughness;
|
||||
const float roughness2 = sqr(roughness);
|
||||
|
||||
const float2 sincos_phi_i = sincos_phi(wi);
|
||||
|
||||
/* Maximal sample resolution. */
|
||||
float res = roughness * 0.7f;
|
||||
|
||||
const float h_range = bsdf->extra->h.length();
|
||||
|
||||
/* Number of intervals should be even. */
|
||||
const size_t intervals = 2 * (size_t)ceilf(h_range / res * 0.5f);
|
||||
|
||||
/* Modified resolution based on numbers of intervals. */
|
||||
res = h_range / float(intervals);
|
||||
|
||||
/* Integrate using Composite Simpson's 1/3 rule. */
|
||||
float integral = 0.0f;
|
||||
for (size_t i = 0; i <= intervals; i++) {
|
||||
const float h = bsdf->extra->h.min + i * res;
|
||||
const float gamma_m = h_to_gamma(h, b, wi);
|
||||
const float3 wm = sphg_dir(bsdf->tilt, gamma_m, b);
|
||||
|
||||
if (microfacet_visible(wi, wo, make_float3(wm.x, 0.0f, wm.z), wh)) {
|
||||
const float jacobian = d_gamma_d_h(sincos_phi_i, gamma_m, b);
|
||||
const float weight = ((i == 0 || i == intervals) ? 0.5f : (i % 2 + 1)) * jacobian;
|
||||
const float cos_mi = dot(wm, wi);
|
||||
const float G = bsdf_G<MicrofacetType::GGX>(roughness2, cos_mi, dot(wm, wo));
|
||||
integral += weight * bsdf_D<MicrofacetType::GGX>(roughness2, dot(wm, wh)) * G *
|
||||
arc_length(bsdf->extra->e2, gamma_m) *
|
||||
bsdf_hair_huang_energy_scale(kg, cos_mi, sqrtf(roughness), bsdf->eta);
|
||||
}
|
||||
}
|
||||
|
||||
/* Simpson coefficient */
|
||||
integral *= (2.0f / 3.0f * res);
|
||||
|
||||
const float F = fresnel_dielectric_cos(dot(wi, wh), bsdf->eta);
|
||||
|
||||
return make_spectrum(bsdf->extra->R * 0.25f * F * integral);
|
||||
}
|
||||
|
||||
/* Approximate components beyond TRT (starting TRRT) by summing up a geometric series. Attenuations
|
||||
* are approximated from previous interactions. */
|
||||
ccl_device Spectrum bsdf_hair_huang_eval_trrt(const float T, const float R, const Spectrum A)
|
||||
{
|
||||
/* `T` could be zero due to total internal reflection. Clamp to avoid numerical issues. */
|
||||
const float T_avg = max(1.0f - R, 1e-5f);
|
||||
const Spectrum TRRT_avg = T * sqr(R) * T_avg * A * A * A;
|
||||
return TRRT_avg / (one_spectrum() - A * (1.0f - T_avg));
|
||||
}
|
||||
|
||||
/* Evaluate components beyond R using numerical integration. TT and TRT are computed via combined
|
||||
* Monte Carlo-Simpson integration; components beyond TRRT are integrated via Simpson's method. */
|
||||
ccl_device Spectrum bsdf_hair_huang_eval_residual(KernelGlobals kg,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private uint *rng_quadrature)
|
||||
{
|
||||
ccl_private HuangHairBSDF *bsdf = (ccl_private HuangHairBSDF *)sc;
|
||||
|
||||
if (bsdf->extra->TT <= 0.0f && bsdf->extra->TRT <= 0.0f) {
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
/* Get minor axis, assuming major axis is 1. */
|
||||
const float b = bsdf->aspect_ratio;
|
||||
|
||||
const Spectrum mu_a = bsdf->sigma;
|
||||
const float eta = bsdf->eta;
|
||||
const float inv_eta = 1.0f / eta;
|
||||
|
||||
const float roughness = bsdf->roughness;
|
||||
const float roughness2 = sqr(roughness);
|
||||
const float sqrt_roughness = sqrtf(roughness);
|
||||
|
||||
const float2 sincos_phi_i = sincos_phi(wi);
|
||||
|
||||
float res = roughness * 0.8f;
|
||||
const float h_range = bsdf->extra->h.length();
|
||||
const size_t intervals = 2 * (size_t)ceilf(h_range / res * 0.5f);
|
||||
res = h_range / intervals;
|
||||
|
||||
Spectrum S_tt = zero_spectrum();
|
||||
Spectrum S_trt = zero_spectrum();
|
||||
Spectrum S_trrt = zero_spectrum();
|
||||
for (size_t i = 0; i <= intervals; i++) {
|
||||
const float h = bsdf->extra->h.min + i * res;
|
||||
const float gamma_mi = h_to_gamma(h, b, wi);
|
||||
|
||||
const float3 wmi = sphg_dir(bsdf->tilt, gamma_mi, b);
|
||||
const float3 wmi_ = sphg_dir(0.0f, gamma_mi, b);
|
||||
|
||||
/* Sample `wh1`. */
|
||||
const float2 sample1 = make_float2(lcg_step_float(rng_quadrature),
|
||||
lcg_step_float(rng_quadrature));
|
||||
|
||||
const float3 wh1 = sample_wh(roughness, wi, wmi, sample1);
|
||||
const float cos_hi1 = dot(wi, wh1);
|
||||
if (!(cos_hi1 > 0.0f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float cos_mi1 = dot(wi, wmi);
|
||||
float cos_theta_t1;
|
||||
const float T1 = 1.0f - fresnel_dielectric(cos_hi1, eta, &cos_theta_t1);
|
||||
const float scale1 = bsdf_hair_huang_energy_scale(kg, cos_mi1, sqrt_roughness, eta);
|
||||
|
||||
/* Refraction at the first interface. */
|
||||
const float3 wt = refract_angle(wi, wh1, cos_theta_t1, inv_eta);
|
||||
const float phi_t = dir_phi(wt);
|
||||
const float gamma_mt = 2.0f * to_phi(phi_t, b) - gamma_mi;
|
||||
const float3 wmt = sphg_dir(-bsdf->tilt, gamma_mt, b);
|
||||
const float3 wmt_ = sphg_dir(0.0f, gamma_mt, b);
|
||||
|
||||
const float cos_mo1 = dot(-wt, wmi);
|
||||
const float cos_mi2 = dot(-wt, wmt);
|
||||
const float G1o = bsdf_Go(roughness2, cos_mi1, cos_mo1);
|
||||
if (!microfacet_visible(wi, -wt, wmi, wh1) || !microfacet_visible(wi, -wt, wmi_, wh1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float jacobian = d_gamma_d_h(sincos_phi_i, gamma_mi, b);
|
||||
const float weight = ((i == 0 || i == intervals) ? 0.5f : (i % 2 + 1)) * jacobian;
|
||||
|
||||
const Spectrum A_t = exp(mu_a / cos_theta(wt) *
|
||||
(is_circular(b) ?
|
||||
2.0f * cosf(gamma_mi - phi_t) :
|
||||
-len(to_point(gamma_mi, b) - to_point(gamma_mt + M_PI_F, b))));
|
||||
|
||||
const float scale2 = bsdf_hair_huang_energy_scale(kg, cos_mi2, sqrt_roughness, inv_eta);
|
||||
|
||||
/* TT */
|
||||
if (bsdf->extra->TT > 0.0f) {
|
||||
if (dot(wo, wt) >= inv_eta - 1e-5f) { /* Total internal reflection otherwise. */
|
||||
float3 wh2 = -wt + inv_eta * wo;
|
||||
const float rcp_norm_wh2 = 1.0f / len(wh2);
|
||||
wh2 *= rcp_norm_wh2;
|
||||
const float cos_mh2 = dot(wmt, wh2);
|
||||
if (cos_mh2 >= 0.0f) { /* Microfacet visibility from macronormal. */
|
||||
const float cos_hi2 = dot(-wt, wh2);
|
||||
const float cos_ho2 = dot(-wo, wh2);
|
||||
const float cos_mo2 = dot(-wo, wmt);
|
||||
|
||||
const float T2 = (1.0f - fresnel_dielectric_cos(cos_hi2, inv_eta)) * scale2;
|
||||
const float D2 = bsdf_D<MicrofacetType::GGX>(roughness2, cos_mh2);
|
||||
const float G2 = bsdf_G<MicrofacetType::GGX>(roughness2, cos_mi2, cos_mo2);
|
||||
|
||||
const Spectrum result = weight * T1 * scale1 * T2 * D2 * G1o * G2 * A_t / cos_mo1 *
|
||||
cos_mi1 * cos_hi2 * cos_ho2 * sqr(rcp_norm_wh2);
|
||||
|
||||
if (isfinite_safe(result)) {
|
||||
S_tt += bsdf->extra->TT * result * arc_length(bsdf->extra->e2, gamma_mt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TRT and beyond. */
|
||||
if (bsdf->extra->TRT > 0.0f) {
|
||||
/* Sample `wh2`. */
|
||||
const float2 sample2 = make_float2(lcg_step_float(rng_quadrature),
|
||||
lcg_step_float(rng_quadrature));
|
||||
const float3 wh2 = sample_wh(roughness, -wt, wmt, sample2);
|
||||
const float cos_hi2 = dot(-wt, wh2);
|
||||
if (!(cos_hi2 > 0.0f)) {
|
||||
continue;
|
||||
}
|
||||
const float R2 = fresnel_dielectric_cos(cos_hi2, inv_eta);
|
||||
|
||||
const float3 wtr = -reflect(wt, wh2);
|
||||
if (dot(-wtr, wo) < inv_eta - 1e-5f) {
|
||||
/* Total internal reflection. */
|
||||
S_trrt += weight * bsdf_hair_huang_eval_trrt(T1, R2, A_t);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!microfacet_visible(-wt, -wtr, wmt, wh2) || !microfacet_visible(-wt, -wtr, wmt_, wh2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float phi_tr = dir_phi(wtr);
|
||||
const float gamma_mtr = gamma_mi - 2.0f * (to_phi(phi_t, b) - to_phi(phi_tr, b)) + M_PI_F;
|
||||
const float3 wmtr = sphg_dir(-bsdf->tilt, gamma_mtr, b);
|
||||
const float3 wmtr_ = sphg_dir(0.0f, gamma_mtr, b);
|
||||
|
||||
float3 wh3 = wtr + inv_eta * wo;
|
||||
const float rcp_norm_wh3 = 1.0f / len(wh3);
|
||||
wh3 *= rcp_norm_wh3;
|
||||
const float cos_mh3 = dot(wmtr, wh3);
|
||||
if (cos_mh3 < 0.0f || !microfacet_visible(wtr, -wo, wmtr, wh3) ||
|
||||
!microfacet_visible(wtr, -wo, wmtr_, wh3))
|
||||
{
|
||||
S_trrt += weight * bsdf_hair_huang_eval_trrt(T1, R2, A_t);
|
||||
continue;
|
||||
}
|
||||
|
||||
const float cos_hi3 = dot(wh3, wtr);
|
||||
const float cos_ho3 = dot(wh3, -wo);
|
||||
const float cos_mi3 = dot(wmtr, wtr);
|
||||
|
||||
const float T3 = (1.0f - fresnel_dielectric_cos(cos_hi3, inv_eta)) *
|
||||
bsdf_hair_huang_energy_scale(kg, cos_mi3, sqrt_roughness, inv_eta);
|
||||
const float D3 = bsdf_D<MicrofacetType::GGX>(roughness2, cos_mh3);
|
||||
|
||||
const Spectrum A_tr = exp(mu_a / cos_theta(wtr) *
|
||||
-(is_circular(b) ?
|
||||
2.0f * fabsf(cosf(phi_tr - gamma_mt)) :
|
||||
len(to_point(gamma_mtr, b) - to_point(gamma_mt, b))));
|
||||
|
||||
const float cos_mo2 = dot(wmt, -wtr);
|
||||
const float G2o = bsdf_Go(roughness2, cos_mi2, cos_mo2);
|
||||
const float G3 = bsdf_G<MicrofacetType::GGX>(roughness2, cos_mi3, dot(wmtr, -wo));
|
||||
|
||||
const Spectrum result = weight * T1 * scale1 * R2 * scale2 * T3 * D3 * G1o * G2o * G3 * A_t *
|
||||
A_tr / (cos_mo1 * cos_mo2) * cos_mi1 * cos_mi2 * cos_hi3 * cos_ho3 *
|
||||
sqr(rcp_norm_wh3);
|
||||
|
||||
if (isfinite_safe(result)) {
|
||||
S_trt += bsdf->extra->TRT * result * arc_length(bsdf->extra->e2, gamma_mtr);
|
||||
}
|
||||
|
||||
S_trrt += weight * bsdf_hair_huang_eval_trrt(T1, R2, A_t);
|
||||
}
|
||||
}
|
||||
|
||||
/* TRRT+ terms, following the approach in [A practical and controllable hair and fur model for
|
||||
* production path tracing](https://doi.org/10.1145/2775280.2792559) by Chiang, Matt Jen-Yuan, et
|
||||
* al. */
|
||||
const float M = longitudinal_scattering(
|
||||
sin_theta(wi), cos_theta(wi), sin_theta(wo), cos_theta(wo), 4.0f * bsdf->roughness);
|
||||
const float N = M_1_2PI_F;
|
||||
|
||||
const float simpson_coeff = 2.0f / 3.0f * res;
|
||||
|
||||
return ((S_tt + S_trt) * sqr(inv_eta) + S_trrt * M * N * M_2_PI_F) * simpson_coeff;
|
||||
}
|
||||
|
||||
ccl_device int bsdf_hair_huang_sample(const KernelGlobals kg,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
ccl_private ShaderData *sd,
|
||||
const float3 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf,
|
||||
ccl_private float2 *sampled_roughness)
|
||||
{
|
||||
ccl_private HuangHairBSDF *bsdf = (ccl_private HuangHairBSDF *)sc;
|
||||
|
||||
const float roughness = bsdf->roughness;
|
||||
*sampled_roughness = make_float2(roughness, roughness);
|
||||
|
||||
kernel_assert(fabsf(bsdf->h) < bsdf->extra->radius);
|
||||
|
||||
/* Generate samples. */
|
||||
float sample_lobe = rand.x;
|
||||
const float sample_h = rand.y;
|
||||
const float2 sample_h1 = make_float2(rand.z, lcg_step_float(&sd->lcg_state));
|
||||
const float2 sample_h2 = make_float2(lcg_step_float(&sd->lcg_state),
|
||||
lcg_step_float(&sd->lcg_state));
|
||||
const float2 sample_h3 = make_float2(lcg_step_float(&sd->lcg_state),
|
||||
lcg_step_float(&sd->lcg_state));
|
||||
|
||||
/* Get `wi` in local coordinate. */
|
||||
const float3 wi = bsdf->extra->wi;
|
||||
|
||||
/* Get minor axis, assuming major axis is 1. */
|
||||
const float b = bsdf->aspect_ratio;
|
||||
|
||||
/* Sample `h` for farfield model, as the computed intersection might have numerical issues. */
|
||||
const float h_div_r = is_nearfield(bsdf) ? bsdf->h / bsdf->extra->radius :
|
||||
(sample_h * 2.0f - 1.0f);
|
||||
const float gamma_mi = h_to_gamma(h_div_r, b, wi);
|
||||
|
||||
/* Macronormal. */
|
||||
const float3 wmi_ = sphg_dir(0, gamma_mi, b);
|
||||
|
||||
/* Mesonormal. */
|
||||
float st;
|
||||
float ct;
|
||||
fast_sincosf(bsdf->tilt, &st, &ct);
|
||||
const float3 wmi = make_float3(wmi_.x * ct, st, wmi_.z * ct);
|
||||
const float cos_mi1 = dot(wmi, wi);
|
||||
|
||||
if (cos_mi1 < 0.0f || dot(wmi_, wi) < 0.0f) {
|
||||
/* Macro/mesonormal invisible. */
|
||||
*pdf = 0.0f;
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
/* Sample R lobe. */
|
||||
const float roughness2 = sqr(roughness);
|
||||
const float sqrt_roughness = sqrtf(roughness);
|
||||
const float3 wh1 = sample_wh(roughness, wi, wmi, sample_h1);
|
||||
const float3 wr = -reflect(wi, wh1);
|
||||
|
||||
/* Ensure that this is a valid sample. */
|
||||
if (!microfacet_visible(wi, wmi_, wh1)) {
|
||||
*pdf = 0.0f;
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
float cos_theta_t1;
|
||||
const float R1 = fresnel_dielectric(dot(wi, wh1), bsdf->eta, &cos_theta_t1);
|
||||
const float scale1 = bsdf_hair_huang_energy_scale(kg, cos_mi1, sqrt_roughness, bsdf->eta);
|
||||
const float R = bsdf->extra->R * R1 * scale1 * microfacet_visible(wr, wmi_, wh1) *
|
||||
bsdf_Go(roughness2, cos_mi1, dot(wmi, wr));
|
||||
|
||||
/* Sample TT lobe. */
|
||||
const float inv_eta = 1.0f / bsdf->eta;
|
||||
const float3 wt = refract_angle(wi, wh1, cos_theta_t1, inv_eta);
|
||||
const float phi_t = dir_phi(wt);
|
||||
|
||||
const float gamma_mt = 2.0f * to_phi(phi_t, b) - gamma_mi;
|
||||
const float3 wmt = sphg_dir(-bsdf->tilt, gamma_mt, b);
|
||||
const float3 wmt_ = sphg_dir(0.0f, gamma_mt, b);
|
||||
|
||||
const float3 wh2 = sample_wh(roughness, -wt, wmt, sample_h2);
|
||||
|
||||
const float3 wtr = -reflect(wt, wh2);
|
||||
|
||||
float3 wh3;
|
||||
float3 wtt;
|
||||
float3 wtrt;
|
||||
float3 wmtr;
|
||||
float3 wtrrt;
|
||||
Spectrum TT = zero_spectrum();
|
||||
Spectrum TRT = zero_spectrum();
|
||||
Spectrum TRRT = zero_spectrum();
|
||||
const float cos_mi2 = dot(-wt, wmt);
|
||||
|
||||
if (cos_mi2 > 0.0f && microfacet_visible(-wt, wmi_, wh1) && microfacet_visible(-wt, wmt_, wh2)) {
|
||||
const Spectrum mu_a = bsdf->sigma;
|
||||
const Spectrum A_t = exp(mu_a / cos_theta(wt) *
|
||||
(is_circular(b) ?
|
||||
2.0f * cosf(phi_t - gamma_mi) :
|
||||
-len(to_point(gamma_mi, b) - to_point(gamma_mt + M_PI_F, b))));
|
||||
|
||||
float cos_theta_t2;
|
||||
const float R2 = fresnel_dielectric(dot(-wt, wh2), inv_eta, &cos_theta_t2);
|
||||
const float T1 = (1.0f - R1) * scale1 * bsdf_Go(roughness2, cos_mi1, dot(wmi, -wt));
|
||||
const float T2 = 1.0f - R2;
|
||||
const float scale2 = bsdf_hair_huang_energy_scale(kg, cos_mi2, sqrt_roughness, inv_eta);
|
||||
|
||||
wtt = refract_angle(-wt, wh2, cos_theta_t2, bsdf->eta);
|
||||
|
||||
if (dot(wmt, -wtt) > 0.0f && T2 > 0.0f && microfacet_visible(-wtt, wmt_, wh2)) {
|
||||
TT = bsdf->extra->TT * T1 * A_t * T2 * scale2 * bsdf_Go(roughness2, cos_mi2, dot(wmt, -wtt));
|
||||
}
|
||||
|
||||
/* Sample TRT lobe. */
|
||||
const float phi_tr = dir_phi(wtr);
|
||||
const float gamma_mtr = gamma_mi - 2.0f * (to_phi(phi_t, b) - to_phi(phi_tr, b)) + M_PI_F;
|
||||
wmtr = sphg_dir(-bsdf->tilt, gamma_mtr, b);
|
||||
|
||||
wh3 = sample_wh(roughness, wtr, wmtr, sample_h3);
|
||||
|
||||
float cos_theta_t3;
|
||||
const float R3 = fresnel_dielectric(dot(wtr, wh3), inv_eta, &cos_theta_t3);
|
||||
|
||||
wtrt = refract_angle(wtr, wh3, cos_theta_t3, bsdf->eta);
|
||||
|
||||
const float cos_mi3 = dot(wmtr, wtr);
|
||||
if (cos_mi3 > 0.0f) {
|
||||
const Spectrum A_tr = exp(mu_a / cos_theta(wtr) *
|
||||
-(is_circular(b) ?
|
||||
2.0f * fabsf(cosf(phi_tr - gamma_mt)) :
|
||||
len(to_point(gamma_mt, b) - to_point(gamma_mtr, b))));
|
||||
|
||||
const Spectrum TR = T1 * R2 * scale2 * A_t * A_tr *
|
||||
bsdf_hair_huang_energy_scale(kg, cos_mi3, sqrt_roughness, inv_eta) *
|
||||
bsdf_Go(roughness2, cos_mi2, dot(wmt, -wtr));
|
||||
|
||||
const float T3 = 1.0f - R3;
|
||||
|
||||
if (T3 > 0.0f && microfacet_visible(wtr, -wtrt, make_float3(wmtr.x, 0.0f, wmtr.z), wh3)) {
|
||||
TRT = bsdf->extra->TRT * TR * make_spectrum(T3) *
|
||||
bsdf_Go(roughness2, cos_mi3, dot(wmtr, -wtrt));
|
||||
}
|
||||
|
||||
/* Sample TRRT+ terms, following the approach in [A practical and controllable hair and fur
|
||||
* model for production path tracing](https://doi.org/10.1145/2775280.2792559) by Chiang,
|
||||
* Matt Jen-Yuan, et al. */
|
||||
|
||||
/* Sample `theta_o`. */
|
||||
const float rand_theta = max(lcg_step_float(&sd->lcg_state), 1e-5f);
|
||||
const float fac = 1.0f +
|
||||
4.0f * bsdf->roughness *
|
||||
logf(rand_theta + (1.0f - rand_theta) * expf(-0.5f / bsdf->roughness));
|
||||
const float sin_theta_o = -fac * sin_theta(wi) +
|
||||
cos_from_sin(fac) *
|
||||
cosf(M_2PI_F * lcg_step_float(&sd->lcg_state)) * cos_theta(wi);
|
||||
const float cos_theta_o = cos_from_sin(sin_theta_o);
|
||||
|
||||
/* Sample `phi_o`. */
|
||||
const float phi_o = M_2PI_F * lcg_step_float(&sd->lcg_state);
|
||||
float sin_phi_o;
|
||||
float cos_phi_o;
|
||||
fast_sincosf(phi_o, &sin_phi_o, &cos_phi_o);
|
||||
|
||||
/* Compute outgoing direction. */
|
||||
wtrrt = make_float3(sin_phi_o * cos_theta_o, sin_theta_o, cos_phi_o * cos_theta_o);
|
||||
|
||||
/* Compute residual term by summing up the geometric series `A * T + A^2 * R * T + ...`.
|
||||
* Attenuations are approximated from previous interactions. */
|
||||
const Spectrum A_avg = sqrt(A_t * A_tr);
|
||||
/* `T` could be zero due to total internal reflection. Clamp to avoid numerical issues. */
|
||||
const float T_avg = max(0.5f * (T2 + T3), 1e-5f);
|
||||
const Spectrum A_res = A_avg * T_avg / (one_spectrum() - A_avg * (1.0f - T_avg));
|
||||
|
||||
TRRT = TR * R3 * A_res * bsdf_Go(roughness2, cos_mi3, dot(wmtr, -reflect(wtr, wh3)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Select lobe based on energy. */
|
||||
const float r = R;
|
||||
const float tt = average(TT);
|
||||
const float trt = average(TRT);
|
||||
const float trrt = average(TRRT);
|
||||
const float total_energy = r + tt + trt + trrt;
|
||||
|
||||
if (total_energy == 0.0f) {
|
||||
*pdf = 0.0f;
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
float3 local_O;
|
||||
|
||||
sample_lobe *= total_energy;
|
||||
if (sample_lobe < r) {
|
||||
local_O = wr;
|
||||
*eval = make_spectrum(total_energy);
|
||||
}
|
||||
else if (sample_lobe < (r + tt)) {
|
||||
local_O = wtt;
|
||||
*eval = TT / tt * total_energy;
|
||||
}
|
||||
else if (sample_lobe < (r + tt + trt)) {
|
||||
local_O = wtrt;
|
||||
*eval = TRT / trt * total_energy;
|
||||
}
|
||||
else {
|
||||
local_O = wtrrt;
|
||||
*eval = TRRT / trrt * make_spectrum(total_energy);
|
||||
}
|
||||
|
||||
/* Transform `wo` to global coordinate system. */
|
||||
*wo = to_global(local_O, bsdf->N, bsdf->extra->Y, bsdf->extra->Z);
|
||||
|
||||
/* Ensure the same pdf is returned for BSDF and emitter sampling. The importance sampling pdf is
|
||||
* already factored in the value so this value is only used for MIS. */
|
||||
*pdf = 1.0f;
|
||||
|
||||
return LABEL_GLOSSY | LABEL_REFLECT;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_hair_huang_eval(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderClosure *sc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
ccl_private HuangHairBSDF *bsdf = (ccl_private HuangHairBSDF *)sc;
|
||||
|
||||
kernel_assert(fabsf(bsdf->h) < bsdf->extra->radius);
|
||||
|
||||
/* Transform `wi`/`wo` from global coordinate system to local. */
|
||||
const float3 local_I = bsdf->extra->wi;
|
||||
const float3 local_O = to_local(wo, bsdf->N, bsdf->extra->Y, bsdf->extra->Z);
|
||||
|
||||
/* TODO: better estimation of the pdf */
|
||||
*pdf = 1.0f;
|
||||
|
||||
/* Early detection of `dot(wo, wmo) < 0`. */
|
||||
const float tan_tilt = tanf(bsdf->tilt);
|
||||
if (tan_tilt * tan_theta(local_O) < -1.0f) {
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
/* Compute visible azimuthal range from the incoming direction. */
|
||||
const float half_span = acosf(fmaxf(-tan_tilt * tan_theta(local_I), 0.0f));
|
||||
if (isnan_safe(half_span)) {
|
||||
/* Early detection of `dot(wi, wmi) < 0`. */
|
||||
return zero_spectrum();
|
||||
}
|
||||
const float r = bsdf->extra->radius;
|
||||
const float b = bsdf->aspect_ratio;
|
||||
const float phi_i = is_circular(b) ? 0.0f : dir_phi(local_I);
|
||||
|
||||
Interval<float> h = {phi_to_h(phi_i + half_span, b, local_I),
|
||||
phi_to_h(phi_i - half_span, b, local_I)};
|
||||
|
||||
/* Length of the integral interval. */
|
||||
float dh = 2.0f;
|
||||
|
||||
if (is_nearfield(bsdf)) {
|
||||
/* Reduce the integration interval to the subset that's visible to the current pixel.
|
||||
* Inspired by [An Efficient and Practical Near and Far Field Fur Reflectance Model]
|
||||
* (https://sites.cs.ucsb.edu/~lingqi/publications/paper_fur2.pdf) by Ling-Qi Yan, Henrik Wann
|
||||
* Jensen and Ravi Ramamoorthi. */
|
||||
const float half_pixel = bsdf->extra->pixel_coverage;
|
||||
const Interval<float> nearfield_h = intervals_intersection(
|
||||
Interval<float>{-r, r}, {bsdf->h - half_pixel, bsdf->h + half_pixel});
|
||||
|
||||
dh = nearfield_h.length() / r;
|
||||
h = intervals_intersection(h, nearfield_h);
|
||||
}
|
||||
|
||||
/* Pre-divide by radius for easier conversion to `gamma`. */
|
||||
h /= r;
|
||||
|
||||
/* Clamp for numerical stability at the boundaries. */
|
||||
h = intervals_intersection(h, {-0.999f, 0.999f});
|
||||
|
||||
if (h.is_empty()) {
|
||||
/* No overlap between the valid range and the visible range. Can happen at grazing `theta`
|
||||
* angles. */
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
bsdf->extra->h = h;
|
||||
|
||||
const float projected_area = cos_theta(local_I) * dh;
|
||||
|
||||
return (bsdf_hair_huang_eval_r(kg, sc, local_I, local_O) +
|
||||
bsdf_hair_huang_eval_residual(kg, sc, local_I, local_O, &sd->lcg_state)) /
|
||||
projected_area;
|
||||
}
|
||||
|
||||
/* Implements Filter Glossy by capping the effective roughness. */
|
||||
ccl_device void bsdf_hair_huang_blur(ccl_private ShaderClosure *sc, const float roughness)
|
||||
{
|
||||
ccl_private HuangHairBSDF *bsdf = (ccl_private HuangHairBSDF *)sc;
|
||||
|
||||
bsdf->roughness = fmaxf(roughness, bsdf->roughness);
|
||||
}
|
||||
|
||||
/* Hair Albedo. Computed by summing up geometric series, assuming circular cross-section and
|
||||
* specular reflection. */
|
||||
ccl_device Spectrum bsdf_hair_huang_albedo(const ccl_private ShaderData * /*sd*/,
|
||||
const ccl_private ShaderClosure *sc)
|
||||
{
|
||||
ccl_private HuangHairBSDF *bsdf = (ccl_private HuangHairBSDF *)sc;
|
||||
|
||||
const float3 wmi = make_float3(bsdf->h, 0.0f, cos_from_sin(bsdf->h));
|
||||
float cos_t;
|
||||
const float f = fresnel_dielectric(dot(wmi, bsdf->extra->wi), bsdf->eta, &cos_t);
|
||||
const float3 wt = refract_angle(bsdf->extra->wi, wmi, cos_t, 1.0f / bsdf->eta);
|
||||
const Spectrum A = exp(2.0f * bsdf->sigma * cos_t / (1.0f - sqr(wt.y)));
|
||||
|
||||
return safe_divide(A - 2.0f * f * A + f, one_spectrum() - f * A);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
59
blender-5.2.0/intern/cycles/kernel/closure/bsdf_ray_portal.h
Normal file
59
blender-5.2.0/intern/cycles/kernel/closure/bsdf_ray_portal.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/alloc.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct RayPortalClosure {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
float3 P;
|
||||
float3 D;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(RayPortalClosure), "RayPortalClosure is too large!");
|
||||
|
||||
ccl_device void bsdf_ray_portal_setup(ccl_private ShaderData *sd,
|
||||
const Spectrum weight,
|
||||
const float3 position,
|
||||
float3 direction)
|
||||
{
|
||||
/* Check cutoff weight. */
|
||||
const float sample_weight = fabsf(average(weight));
|
||||
if (!(sample_weight >= CLOSURE_WEIGHT_CUTOFF)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sd->closure_transparent_extinction += weight;
|
||||
|
||||
ccl_private RayPortalClosure *pc = (ccl_private RayPortalClosure *)closure_alloc(
|
||||
sd, sizeof(RayPortalClosure), CLOSURE_BSDF_RAY_PORTAL_ID, weight);
|
||||
|
||||
if (pc) {
|
||||
sd->flag |= SD_BSDF | SD_RAY_PORTAL;
|
||||
if (is_zero(direction)) {
|
||||
direction = -sd->wi;
|
||||
}
|
||||
pc->sample_weight = sample_weight;
|
||||
pc->N = sd->N;
|
||||
pc->P = position;
|
||||
pc->D = safe_normalize(direction);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_ray_portal_eval(const ccl_private ShaderClosure * /*sc*/,
|
||||
const float3 /*wi*/,
|
||||
const float3 /*wo*/,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
112
blender-5.2.0/intern/cycles/kernel/closure/bsdf_sheen.h
Normal file
112
blender-5.2.0/intern/cycles/kernel/closure/bsdf_sheen.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: Copyright 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Shading model by Tizian Zeltner, Brent Burley, Matt Jen-Yuan Chiang:
|
||||
* "Practical Multiple-Scattering Sheen Using Linearly Transformed Cosines" (2022)
|
||||
* https://tizianzeltner.com/projects/Zeltner2022Practical/
|
||||
*/
|
||||
|
||||
#include "kernel/sample/mapping.h"
|
||||
|
||||
#include "kernel/util/lookup_table.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct SheenBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
float roughness;
|
||||
float transformA, transformB;
|
||||
float3 T, B;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(SheenBsdf), "SheenBsdf is too large!");
|
||||
|
||||
ccl_device int bsdf_sheen_setup(KernelGlobals kg,
|
||||
const ccl_private ShaderData *sd,
|
||||
ccl_private SheenBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_SHEEN_ID;
|
||||
|
||||
bsdf->roughness = clamp(bsdf->roughness, 1e-3f, 1.0f);
|
||||
make_orthonormals_safe_tangent(bsdf->N, sd->wi, &bsdf->T, &bsdf->B);
|
||||
const float cosNI = dot(bsdf->N, sd->wi);
|
||||
|
||||
const int offset = kernel_data.tables.sheen_ltc;
|
||||
bsdf->transformA = lookup_table_read_2D(kg, cosNI, bsdf->roughness, offset, 32, 32);
|
||||
bsdf->transformB = lookup_table_read_2D(kg, cosNI, bsdf->roughness, offset + 32 * 32, 32, 32);
|
||||
const float albedo = lookup_table_read_2D(
|
||||
kg, cosNI, bsdf->roughness, offset + 2 * 32 * 32, 32, 32);
|
||||
|
||||
/* If the given roughness and angle result in an invalid LTC, skip the closure. */
|
||||
if (fabsf(bsdf->transformA) < 1e-5f || albedo < 1e-5f) {
|
||||
bsdf->type = CLOSURE_NONE_ID;
|
||||
bsdf->sample_weight = 0.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bsdf->weight *= albedo;
|
||||
bsdf->sample_weight *= albedo;
|
||||
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_sheen_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*wi*/,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private SheenBsdf *bsdf = (const ccl_private SheenBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
const float3 T = bsdf->T;
|
||||
const float3 B = bsdf->B;
|
||||
const float a = bsdf->transformA;
|
||||
const float b = bsdf->transformB;
|
||||
|
||||
const float3 localO = to_local(wo, T, B, N);
|
||||
|
||||
const float lenSqr = sqr(a * localO.x + b * localO.z) + sqr(a * localO.y) + sqr(localO.z);
|
||||
const float val = M_1_PI_F * fmaxf(localO.z, 0.0f) * sqr(a / lenSqr);
|
||||
|
||||
*pdf = val;
|
||||
return make_spectrum(val);
|
||||
}
|
||||
|
||||
ccl_device int bsdf_sheen_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 /*wi*/,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private SheenBsdf *bsdf = (const ccl_private SheenBsdf *)sc;
|
||||
const float3 N = bsdf->N;
|
||||
const float3 T = bsdf->T;
|
||||
const float3 B = bsdf->B;
|
||||
const float a = bsdf->transformA;
|
||||
const float b = bsdf->transformB;
|
||||
|
||||
const float2 disk = sample_uniform_disk(rand);
|
||||
const float diskZ = safe_sqrtf(1.0f - dot(disk, disk));
|
||||
const float3 localO = normalize(make_float3((disk.x - diskZ * b), disk.y, diskZ * a));
|
||||
|
||||
*wo = to_global(localO, T, B, N);
|
||||
|
||||
if (dot(Ng, *wo) <= 0) {
|
||||
*eval = zero_spectrum();
|
||||
*pdf = 0.0f;
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
const float lenSqr = sqr(a * localO.x + b * localO.z) + sqr(a * localO.y) + sqr(localO.z);
|
||||
const float val = M_1_PI_F * localO.z * sqr(a / lenSqr);
|
||||
*pdf = val;
|
||||
*eval = make_spectrum(val);
|
||||
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
189
blender-5.2.0/intern/cycles/kernel/closure/bsdf_toon.h
Normal file
189
blender-5.2.0/intern/cycles/kernel/closure/bsdf_toon.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/sample/mapping.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct ToonBsdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
float size;
|
||||
float smooth;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(ToonBsdf), "ToonBsdf is too large!");
|
||||
|
||||
ccl_device_inline int bsdf_toon_setup_common(ccl_private ToonBsdf *bsdf)
|
||||
{
|
||||
bsdf->size = clamp(bsdf->size, 1e-5f, 1.0f) * M_PI_2_F;
|
||||
bsdf->smooth = saturatef(bsdf->smooth) * M_PI_2_F;
|
||||
|
||||
return SD_BSDF | SD_BSDF_HAS_EVAL;
|
||||
}
|
||||
|
||||
/* DIFFUSE TOON */
|
||||
|
||||
ccl_device int bsdf_diffuse_toon_setup(ccl_private ToonBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_DIFFUSE_TOON_ID;
|
||||
return bsdf_toon_setup_common(bsdf);
|
||||
}
|
||||
|
||||
ccl_device float bsdf_toon_get_intensity(const float max_angle,
|
||||
const float smooth,
|
||||
const float angle)
|
||||
{
|
||||
float is;
|
||||
|
||||
if (angle < max_angle) {
|
||||
is = 1.0f;
|
||||
}
|
||||
else if (angle < (max_angle + smooth) && smooth != 0.0f) {
|
||||
is = (1.0f - (angle - max_angle) / smooth);
|
||||
}
|
||||
else {
|
||||
is = 0.0f;
|
||||
}
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
ccl_device float bsdf_toon_get_sample_angle(const float max_angle, const float smooth)
|
||||
{
|
||||
return fminf(max_angle + smooth, M_PI_2_F);
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_diffuse_toon_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 /*wi*/,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private ToonBsdf *bsdf = (const ccl_private ToonBsdf *)sc;
|
||||
const float max_angle = bsdf->size;
|
||||
const float smooth = bsdf->smooth;
|
||||
const float cosNO = dot(bsdf->N, wo);
|
||||
|
||||
if (cosNO >= 0.0f) {
|
||||
const float angle = safe_acosf(fmaxf(cosNO, 0.0f));
|
||||
const float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
|
||||
|
||||
if (angle < sample_angle) {
|
||||
const float eval = bsdf_toon_get_intensity(max_angle, smooth, angle);
|
||||
*pdf = M_1_2PI_F / one_minus_cos(sample_angle);
|
||||
return make_spectrum(*pdf * eval);
|
||||
}
|
||||
}
|
||||
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device int bsdf_diffuse_toon_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 /*wi*/,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private ToonBsdf *bsdf = (const ccl_private ToonBsdf *)sc;
|
||||
const float max_angle = bsdf->size;
|
||||
const float smooth = bsdf->smooth;
|
||||
const float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
|
||||
|
||||
float cosNO;
|
||||
*wo = sample_uniform_cone(bsdf->N, one_minus_cos(sample_angle), rand, &cosNO, pdf);
|
||||
|
||||
if (dot(Ng, *wo) > 0.0f) {
|
||||
const float angle = acosf(cosNO);
|
||||
*eval = make_spectrum(*pdf * bsdf_toon_get_intensity(max_angle, smooth, angle));
|
||||
return LABEL_REFLECT | LABEL_DIFFUSE;
|
||||
}
|
||||
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
/* GLOSSY TOON */
|
||||
|
||||
ccl_device int bsdf_glossy_toon_setup(ccl_private ToonBsdf *bsdf)
|
||||
{
|
||||
bsdf->type = CLOSURE_BSDF_GLOSSY_TOON_ID;
|
||||
return bsdf_toon_setup_common(bsdf);
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_glossy_toon_eval(const ccl_private ShaderClosure *sc,
|
||||
const float3 wi,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private ToonBsdf *bsdf = (const ccl_private ToonBsdf *)sc;
|
||||
const float max_angle = bsdf->size;
|
||||
const float smooth = bsdf->smooth;
|
||||
const float cosNI = dot(bsdf->N, wi);
|
||||
const float cosNO = dot(bsdf->N, wo);
|
||||
|
||||
if (cosNI > 0 && cosNO > 0) {
|
||||
/* reflect the view vector */
|
||||
const float3 R = (2 * cosNI) * bsdf->N - wi;
|
||||
const float cosRO = dot(R, wo);
|
||||
|
||||
const float angle = safe_acosf(fmaxf(cosRO, 0.0f));
|
||||
const float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
|
||||
|
||||
if (angle < sample_angle) {
|
||||
const float eval = bsdf_toon_get_intensity(max_angle, smooth, angle);
|
||||
*pdf = M_1_2PI_F / one_minus_cos(sample_angle);
|
||||
return make_spectrum(*pdf * eval);
|
||||
}
|
||||
}
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device int bsdf_glossy_toon_sample(const ccl_private ShaderClosure *sc,
|
||||
const float3 Ng,
|
||||
const float3 wi,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private ToonBsdf *bsdf = (const ccl_private ToonBsdf *)sc;
|
||||
const float max_angle = bsdf->size;
|
||||
const float smooth = bsdf->smooth;
|
||||
const float cosNI = dot(bsdf->N, wi);
|
||||
|
||||
if (cosNI > 0) {
|
||||
/* reflect the view vector */
|
||||
const float3 R = (2 * cosNI) * bsdf->N - wi;
|
||||
|
||||
const float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth);
|
||||
|
||||
float cosRO;
|
||||
*wo = sample_uniform_cone(R, one_minus_cos(sample_angle), rand, &cosRO, pdf);
|
||||
|
||||
/* make sure the direction we chose is still in the right hemisphere */
|
||||
if (dot(Ng, *wo) > 0.0f && dot(bsdf->N, *wo) > 0.0f) {
|
||||
const float angle = acosf(cosRO);
|
||||
*eval = make_spectrum(*pdf * bsdf_toon_get_intensity(max_angle, smooth, angle));
|
||||
return LABEL_GLOSSY | LABEL_REFLECT;
|
||||
}
|
||||
}
|
||||
|
||||
*pdf = 0.0f;
|
||||
*eval = zero_spectrum();
|
||||
return LABEL_NONE;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/alloc.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd,
|
||||
const Spectrum weight,
|
||||
const uint32_t path_flag)
|
||||
{
|
||||
/* Check cutoff weight. */
|
||||
const float sample_weight = fabsf(average(weight));
|
||||
if (!(sample_weight >= CLOSURE_WEIGHT_CUTOFF)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sd->closure_transparent_extinction += weight;
|
||||
|
||||
if (sd->flag & SD_TRANSPARENT) {
|
||||
/* Add weight to existing transparent BSDF. */
|
||||
for (int i = 0; i < sd->num_closure; i++) {
|
||||
ccl_private ShaderClosure *sc = &sd->closure[i];
|
||||
|
||||
if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) {
|
||||
sc->weight += weight;
|
||||
sc->sample_weight += sample_weight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
sd->flag |= SD_BSDF | SD_TRANSPARENT;
|
||||
|
||||
if (path_flag & PATH_RAY_TERMINATE) {
|
||||
/* In this case the number of closures is set to zero to disable
|
||||
* all others, but we still want to get transparency so increase
|
||||
* the number just for this. */
|
||||
sd->num_closure_left = 1;
|
||||
}
|
||||
|
||||
/* Create new transparent BSDF. */
|
||||
ccl_private ShaderClosure *bsdf = closure_alloc(
|
||||
sd, sizeof(ShaderClosure), CLOSURE_BSDF_TRANSPARENT_ID, weight);
|
||||
|
||||
if (bsdf) {
|
||||
bsdf->sample_weight = sample_weight;
|
||||
bsdf->N = sd->N;
|
||||
}
|
||||
else if (path_flag & PATH_RAY_TERMINATE) {
|
||||
sd->num_closure_left = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device Spectrum bsdf_transparent_eval(const ccl_private ShaderClosure * /*sc*/,
|
||||
const float3 /*wi*/,
|
||||
const float3 /*wo*/,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
|
||||
ccl_device int bsdf_transparent_sample(const ccl_private ShaderClosure * /*sc*/,
|
||||
const float3 /*Ng*/,
|
||||
const float3 wi,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
// only one direction is possible
|
||||
*wo = -wi;
|
||||
/* Some high number for MIS. */
|
||||
*pdf = 1e6f;
|
||||
*eval = one_spectrum() * 1e6f;
|
||||
return LABEL_TRANSMIT | LABEL_TRANSPARENT;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
574
blender-5.2.0/intern/cycles/kernel/closure/bsdf_util.h
Normal file
574
blender-5.2.0/intern/cycles/kernel/closure/bsdf_util.h
Normal file
@@ -0,0 +1,574 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/util/colorspace.h"
|
||||
#include "kernel/util/lookup_table.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/types_spectrum.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct FresnelThinFilm {
|
||||
float thickness;
|
||||
float ior;
|
||||
};
|
||||
|
||||
template<typename T> struct complex {
|
||||
T re;
|
||||
T im;
|
||||
|
||||
ccl_device_inline_method complex<T> operator*=(ccl_private const complex<T> &other)
|
||||
{
|
||||
const T im = this->re * other.im + this->im * other.re;
|
||||
this->re = this->re * other.re - this->im * other.im;
|
||||
this->im = im;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ccl_device_inline_method complex<T> operator*(ccl_private const float &other)
|
||||
{
|
||||
return complex<T>{this->re * other, this->im * other};
|
||||
}
|
||||
};
|
||||
|
||||
/* Compute fresnel reflectance for perpendicular (aka S-) and parallel (aka P-) polarized light.
|
||||
* If requested by the caller, r_cos_phi is set to the cosine of the phase shift on reflection.
|
||||
* Also returns the dot product of the refracted ray and the normal as `cos_theta_t`, as it is
|
||||
* used when computing the direction of the refracted ray. */
|
||||
ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i,
|
||||
const float eta,
|
||||
ccl_private float *r_cos_theta_t,
|
||||
ccl_private float2 *r_cos_phi)
|
||||
{
|
||||
kernel_assert(!isnan_safe(cos_theta_i));
|
||||
|
||||
/* Using Snell's law, calculate the squared cosine of the angle between the surface normal and
|
||||
* the transmitted ray. */
|
||||
const float eta_cos_theta_t_sq = sqr(eta) - (1.0f - sqr(cos_theta_i));
|
||||
if (eta_cos_theta_t_sq <= 0) {
|
||||
/* Total internal reflection. */
|
||||
if (r_cos_phi) {
|
||||
/* The following code would compute the proper phase shift on TIR.
|
||||
* However, for the current user of this computation (the iridescence code),
|
||||
* this doesn't actually affect the result, so don't bother with the computation for now.
|
||||
*
|
||||
* `const float fac = sqrtf(1.0f - sqr(cosThetaI) - sqr(eta));`
|
||||
* `r_phi->x = -2.0f * atanf(fac / cosThetaI);`
|
||||
* `r_phi->y = -2.0f * atanf(fac / (cosThetaI * sqr(eta)));`
|
||||
*/
|
||||
*r_cos_phi = one_float2();
|
||||
}
|
||||
return one_float2();
|
||||
}
|
||||
|
||||
cos_theta_i = fabsf(cos_theta_i);
|
||||
/* Relative to the surface normal. */
|
||||
const float cos_theta_t = -safe_sqrtf(eta_cos_theta_t_sq) / eta;
|
||||
|
||||
if (r_cos_theta_t) {
|
||||
*r_cos_theta_t = cos_theta_t;
|
||||
}
|
||||
|
||||
/* Amplitudes of reflected waves. */
|
||||
const float r_s = (cos_theta_i + eta * cos_theta_t) / (cos_theta_i - eta * cos_theta_t);
|
||||
const float r_p = (cos_theta_t + eta * cos_theta_i) / (eta * cos_theta_i - cos_theta_t);
|
||||
|
||||
if (r_cos_phi) {
|
||||
*r_cos_phi = make_float2(2 * (r_s >= 0.0f) - 1, 2 * (r_p >= 0.0f) - 1);
|
||||
}
|
||||
|
||||
/* Return squared amplitude to get the fraction of reflected energy. */
|
||||
return make_float2(sqr(r_s), sqr(r_p));
|
||||
}
|
||||
|
||||
/* Compute fresnel reflectance for unpolarized light. */
|
||||
ccl_device_forceinline float fresnel_dielectric(const float cos_theta_i,
|
||||
const float eta,
|
||||
ccl_private float *r_cos_theta_t)
|
||||
{
|
||||
return average(fresnel_dielectric_polarized(cos_theta_i, eta, r_cos_theta_t, nullptr));
|
||||
}
|
||||
|
||||
/* Refract the incident ray, given the cosine of the refraction angle and the relative refractive
|
||||
* index of the incoming medium w.r.t. the outgoing medium. */
|
||||
ccl_device_inline float3 refract_angle(const float3 incident,
|
||||
const float3 normal,
|
||||
const float cos_theta_t,
|
||||
const float inv_eta)
|
||||
{
|
||||
return (inv_eta * dot(normal, incident) + cos_theta_t) * normal - inv_eta * incident;
|
||||
}
|
||||
|
||||
ccl_device float fresnel_dielectric_cos(const float cosi, const float eta)
|
||||
{
|
||||
// compute fresnel reflectance without explicitly computing
|
||||
// the refracted direction
|
||||
const float c = fabsf(cosi);
|
||||
float g = eta * eta - 1 + c * c;
|
||||
if (g > 0) {
|
||||
g = sqrtf(g);
|
||||
const float A = (g - c) / (g + c);
|
||||
const float B = (c * (g + c) - 1) / (c * (g - c) + 1);
|
||||
return 0.5f * A * A * (1 + B * B);
|
||||
}
|
||||
return 1.0f; // TIR(no refracted component)
|
||||
}
|
||||
|
||||
/* Approximates the average single-scattering Fresnel for a given IOR.
|
||||
* This is defined as the integral over 0...1 of 2*cosI * F(cosI, eta) d_cosI, with F being
|
||||
* the real dielectric Fresnel.
|
||||
* The implementation here uses a numerical fit from "Revisiting Physically Based Shading
|
||||
* at Imageworks" by Christopher Kulla and Alejandro Conty. */
|
||||
ccl_device_inline float fresnel_dielectric_Fss(const float eta)
|
||||
{
|
||||
if (eta < 1.0f) {
|
||||
return 0.997118f + eta * (0.1014f - eta * (0.965241f + eta * 0.130607f));
|
||||
}
|
||||
return (eta - 1.0f) / (4.08567f + 1.00071f * eta);
|
||||
}
|
||||
|
||||
/* Computes the average single-scattering Fresnel for the F82 metallic model. */
|
||||
ccl_device_inline Spectrum fresnel_f82_Fss(const Spectrum F0, const Spectrum B)
|
||||
{
|
||||
return mix(F0, one_spectrum(), 1.0f / 21.0f) - B * (1.0f / 126.0f);
|
||||
}
|
||||
|
||||
/* Precompute the B term for the F82 metallic model, given a tint factor. */
|
||||
ccl_device_inline Spectrum fresnel_f82tint_B(const Spectrum F0, const Spectrum tint)
|
||||
{
|
||||
/* In the classic F82 model, the F82 input directly determines the value of the Fresnel
|
||||
* model at ~82°, similar to F0 and F90.
|
||||
* With F82-Tint, on the other hand, the value at 82° is the value of the classic Schlick
|
||||
* model multiplied by the tint input.
|
||||
* Therefore, the factor follows by setting F82Tint(cosI) = FSchlick(cosI) - b*cosI*(1-cosI)^6
|
||||
* and F82Tint(acos(1/7)) = FSchlick(acos(1/7)) * f82_tint and solving for b. */
|
||||
const float f = 6.0f / 7.0f;
|
||||
const float f5 = sqr(sqr(f)) * f;
|
||||
const Spectrum F_schlick = mix(F0, one_spectrum(), f5);
|
||||
return F_schlick * (7.0f / (f5 * f)) * (one_spectrum() - tint);
|
||||
}
|
||||
|
||||
/* Precompute the B term for the F82 metallic model, given the F82 value. */
|
||||
ccl_device_inline Spectrum fresnel_f82_B(const Spectrum F0, const Spectrum F82)
|
||||
{
|
||||
const float f = 6.0f / 7.0f;
|
||||
const float f5 = sqr(sqr(f)) * f;
|
||||
const Spectrum F_schlick = mix(F0, one_spectrum(), f5);
|
||||
return (7.0f / (f5 * f)) * (F_schlick - F82);
|
||||
}
|
||||
|
||||
ccl_device_inline float fresnel_f82_B(const float F0, const float F82)
|
||||
{
|
||||
const float f = 6.0f / 7.0f;
|
||||
const float f5 = sqr(sqr(f)) * f;
|
||||
const float F_schlick = mix(F0, 1.0f, f5);
|
||||
return (7.0f / (f5 * f)) * (F_schlick - F82);
|
||||
}
|
||||
|
||||
/* Evaluate the F82 metallic model for the given parameters. */
|
||||
ccl_device_inline Spectrum fresnel_f82(const float cosi, const Spectrum F0, const Spectrum B)
|
||||
{
|
||||
const float s = saturatef(1.0f - cosi);
|
||||
const float s5 = sqr(sqr(s)) * s;
|
||||
const Spectrum F_schlick = mix(F0, one_spectrum(), s5);
|
||||
return saturate(F_schlick - B * cosi * s5 * s);
|
||||
}
|
||||
|
||||
ccl_device_inline float fresnel_f82(const float cosi, const float F0, const float B)
|
||||
{
|
||||
const float s = saturatef(1.0f - cosi);
|
||||
const float s5 = sqr(sqr(s)) * s;
|
||||
const float F_schlick = mix(F0, 1.0f, s5);
|
||||
return clamp(F_schlick - B * cosi * s5 * s, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/* Evaluates the Fresnel equations at a dielectric-conductor interface, calculating reflectances
|
||||
* and phase shifts due to reflection if requested. The phase shifts phi_s and phi_p are returned
|
||||
* as phasor_s = exp(i * phi_s) and phasor_p = exp(i * phi_p).
|
||||
* This code is based on equations from section 14.4.1 of Principles of Optics 7th ed. by Born and
|
||||
* Wolf, but uses `n + ik` instead of `n(1 + ik)` for IOR. The phase shifts are calculated so that
|
||||
* phi_p = phi_s at 90 degree incidence to match fresnel_dielectric_polarized. */
|
||||
ccl_device_forceinline void fresnel_conductor_polarized(
|
||||
const float cosi,
|
||||
const float ambient_ior,
|
||||
const complex<float> conductor_ior,
|
||||
const float F82,
|
||||
ccl_private float &r_R_s,
|
||||
ccl_private float &r_R_p,
|
||||
ccl_private complex<float> *r_phasor_s = nullptr,
|
||||
ccl_private complex<float> *r_phasor_p = nullptr)
|
||||
{
|
||||
const float eta1 = ambient_ior;
|
||||
const float eta2 = conductor_ior.re;
|
||||
const float k2 = conductor_ior.im;
|
||||
|
||||
const float eta1_sq = sqr(eta1);
|
||||
const float eta2_sq = sqr(eta2);
|
||||
const float k2_sq = sqr(k2);
|
||||
const float two_eta2_k2 = 2.0f * eta2 * k2;
|
||||
|
||||
const float t1 = eta2_sq - k2_sq - eta1_sq * (1.0f - sqr(cosi));
|
||||
const float t2 = sqrt(sqr(t1) + sqr(two_eta2_k2));
|
||||
|
||||
const float u_sq = max(0.5f * (t2 + t1), 0.0f);
|
||||
const float v_sq = max(0.5f * (t2 - t1), 0.0f);
|
||||
const float u = sqrt(u_sq);
|
||||
const float v = sqrt(v_sq);
|
||||
|
||||
if (F82 >= 0.0f) {
|
||||
/* Calculate reflectance using the F82 model if the caller requested it. */
|
||||
/* Scale n and k by the film ior, and recompute F0. */
|
||||
const float n = eta2 / eta1;
|
||||
const float k_sq = sqr(k2 / eta1);
|
||||
const float F0 = (sqr(n - 1.0f) + k_sq) / (sqr(n + 1.0f) + k_sq);
|
||||
r_R_s = fresnel_f82(cosi, F0, fresnel_f82_B(F0, F82));
|
||||
r_R_p = r_R_s;
|
||||
}
|
||||
else {
|
||||
r_R_s = safe_divide(sqr(eta1 * cosi - u) + v_sq, sqr(eta1 * cosi + u) + v_sq);
|
||||
|
||||
const float t3 = (eta2_sq - k2_sq) * cosi;
|
||||
const float t4 = two_eta2_k2 * cosi;
|
||||
r_R_p = safe_divide(sqr(t3 - eta1 * u) + sqr(t4 - eta1 * v),
|
||||
sqr(t3 + eta1 * u) + sqr(t4 + eta1 * v));
|
||||
}
|
||||
|
||||
if (r_phasor_s && r_phasor_p) {
|
||||
const float re_s = -u_sq - v_sq + sqr(eta1 * cosi);
|
||||
const float im_s = -2.0f * eta1 * cosi * v;
|
||||
const float mag_s = sqrt(sqr(re_s) + sqr(im_s));
|
||||
r_phasor_s->re = (mag_s == 0.0f) ? 1.0f : re_s / mag_s;
|
||||
r_phasor_s->im = (mag_s == 0.0f) ? 0.0f : im_s / mag_s;
|
||||
|
||||
const float re_p = sqr((eta2_sq + k2_sq) * cosi) - eta1_sq * (u_sq + v_sq);
|
||||
const float im_p = 2.0f * eta1 * cosi * (two_eta2_k2 * u - (eta2_sq - k2_sq) * v);
|
||||
const float mag_p = sqrt(sqr(re_p) + sqr(im_p));
|
||||
r_phasor_p->re = mag_p == 0.0f ? 1.0f : re_p / mag_p;
|
||||
r_phasor_p->im = mag_p == 0.0f ? 0.0f : im_p / mag_p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates Fresnel reflectance at a dielectric-conductor interface given the relative IOR.
|
||||
*/
|
||||
ccl_device Spectrum fresnel_conductor(const float cosi, const complex<Spectrum> ior)
|
||||
{
|
||||
float R_s_x, R_s_y, R_s_z, R_p_x, R_p_y, R_p_z;
|
||||
fresnel_conductor_polarized(
|
||||
cosi, 1.0f, {ior.re.x, ior.im.x}, -1.0f, R_s_x, R_p_x, nullptr, nullptr);
|
||||
fresnel_conductor_polarized(
|
||||
cosi, 1.0f, {ior.re.y, ior.im.y}, -1.0f, R_s_y, R_p_y, nullptr, nullptr);
|
||||
fresnel_conductor_polarized(
|
||||
cosi, 1.0f, {ior.re.z, ior.im.z}, -1.0f, R_s_z, R_p_z, nullptr, nullptr);
|
||||
return (make_float3(R_s_x, R_s_y, R_s_z) + make_float3(R_p_x, R_p_y, R_p_z)) * 0.5f;
|
||||
}
|
||||
|
||||
/* Approximates the average single-scattering Fresnel for a physical conductor. */
|
||||
ccl_device_inline Spectrum fresnel_conductor_Fss(const complex<Spectrum> ior)
|
||||
{
|
||||
/* In order to estimate Fss of the conductor, we fit the F82 model to it based on the
|
||||
* value at 0° and ~82° and then use the analytic expression for its Fss. */
|
||||
const Spectrum F0 = fresnel_conductor(1.0f, ior);
|
||||
const Spectrum F82 = fresnel_conductor(1.0f / 7.0f, ior);
|
||||
return saturate(fresnel_f82_Fss(F0, fresnel_f82_B(F0, F82)));
|
||||
}
|
||||
|
||||
ccl_device float ior_from_F0(const float f0)
|
||||
{
|
||||
const float sqrt_f0 = sqrtf(clamp(f0, 0.0f, 0.99f));
|
||||
return (1.0f + sqrt_f0) / (1.0f - sqrt_f0);
|
||||
}
|
||||
|
||||
ccl_device float F0_from_ior(const float ior)
|
||||
{
|
||||
return sqr((ior - 1.0f) / (ior + 1.0f));
|
||||
}
|
||||
|
||||
ccl_device float schlick_fresnel(const float u)
|
||||
{
|
||||
const float m = clamp(1.0f - u, 0.0f, 1.0f);
|
||||
const float m2 = m * m;
|
||||
return m2 * m2 * m; // pow(m, 5)
|
||||
}
|
||||
|
||||
/* Calculate the fresnel color, which is a blend between white and the F0 color */
|
||||
ccl_device_forceinline Spectrum interpolate_fresnel_color(const float3 L,
|
||||
const float3 H,
|
||||
const float ior,
|
||||
Spectrum F0)
|
||||
{
|
||||
/* Compute the real Fresnel term and remap it from real_F0..1 to F0..1.
|
||||
* The reason why we use this remapping instead of directly doing the
|
||||
* Schlick approximation mix(F0, 1.0, (1.0-cosLH)^5) is that for cases
|
||||
* with similar IORs (e.g. ice in water), the relative IOR can be close
|
||||
* enough to 1.0 that the Schlick approximation becomes inaccurate. */
|
||||
const float real_F = fresnel_dielectric_cos(dot(L, H), ior);
|
||||
const float real_F0 = fresnel_dielectric_cos(1.0f, ior);
|
||||
|
||||
return mix(F0, one_spectrum(), inverse_lerp(real_F0, 1.0f, real_F));
|
||||
}
|
||||
|
||||
/* If the shading normal results in specular reflection in the lower hemisphere, raise the shading
|
||||
* normal towards the geometry normal so that the specular reflection is just above the surface.
|
||||
* Only used for glossy materials. */
|
||||
ccl_device float3 ensure_valid_specular_reflection(const float3 Ng, const float3 I, float3 N)
|
||||
{
|
||||
const float3 R = 2 * dot(N, I) * N - I;
|
||||
|
||||
const float Iz = dot(I, Ng);
|
||||
kernel_assert(Iz >= 0);
|
||||
|
||||
/* Reflection rays may always be at least as shallow as the incoming ray. */
|
||||
const float threshold = min(0.9f * Iz, 0.01f);
|
||||
if (dot(Ng, R) >= threshold) {
|
||||
return N;
|
||||
}
|
||||
|
||||
/* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane.
|
||||
* The X axis is found by normalizing the component of N that's orthogonal to Ng.
|
||||
* The Y axis isn't actually needed.
|
||||
*/
|
||||
const float3 X = safe_normalize_fallback(N - dot(N, Ng) * Ng, N);
|
||||
|
||||
/* Calculate N.z and N.x in the local coordinate system.
|
||||
*
|
||||
* The goal of this computation is to find a N' that is rotated towards Ng just enough
|
||||
* to lift R' above the threshold (here called t), therefore dot(R', Ng) = t.
|
||||
*
|
||||
* According to the standard reflection equation,
|
||||
* this means that we want dot(2*dot(N', I)*N' - I, Ng) = t.
|
||||
*
|
||||
* Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get
|
||||
* 2*dot(N', I)*N'.z - I.z = t.
|
||||
*
|
||||
* The rotation is simple to express in the coordinate system we formed -
|
||||
* since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane,
|
||||
* so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z .
|
||||
*
|
||||
* Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2).
|
||||
*
|
||||
* With these simplifications, we get the equation
|
||||
* 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t,
|
||||
* or
|
||||
* 2*sqrt(1 - N'.z^2)*I.x*N'.z = t + I.z * (1 - 2*N'.z^2),
|
||||
* after rearranging terms.
|
||||
* Raise both sides to the power of two and substitute terms with
|
||||
* a = I.x^2 + I.z^2,
|
||||
* b = 2*(a + Iz*t),
|
||||
* c = (Iz + t)^2,
|
||||
* we obtain
|
||||
* 4*a*N'.z^4 - 2*b*N'.z^2 + c = 0.
|
||||
*
|
||||
* The only unknown here is N'.z, so we can solve for that.
|
||||
*
|
||||
* The equation has four solutions in general, two can immediately be discarded because they're
|
||||
* negative so N' would lie in the lower hemisphere; one solves
|
||||
* 2*sqrt(1 - N'.z^2)*I.x*N'.z = -(t + I.z * (1 - 2*N'.z^2))
|
||||
* instead of the original equation (before squaring both sides).
|
||||
* Therefore only one root is valid.
|
||||
*/
|
||||
|
||||
const float Ix = dot(I, X);
|
||||
|
||||
const float a = sqr(Ix) + sqr(Iz);
|
||||
const float b = 2.0f * (a + Iz * threshold);
|
||||
const float c = sqr(threshold + Iz);
|
||||
|
||||
/* In order that the root formula solves 2*sqrt(1 - N'.z^2)*I.x*N'.z = t + I.z - 2*I.z*N'.z^2,
|
||||
* Ix and (t + I.z * (1 - 2*N'.z^2)) must have the same sign (the rest terms are non-negative by
|
||||
* definition). */
|
||||
const float Nz2 = (Ix < 0) ? 0.25f * (b + safe_sqrtf(sqr(b) - 4.0f * a * c)) / a :
|
||||
0.25f * (b - safe_sqrtf(sqr(b) - 4.0f * a * c)) / a;
|
||||
|
||||
const float Nx = safe_sqrtf(1.0f - Nz2);
|
||||
const float Nz = safe_sqrtf(Nz2);
|
||||
|
||||
return Nx * X + Nz * Ng;
|
||||
}
|
||||
|
||||
/* Do not call #ensure_valid_specular_reflection if the primitive type is curve or if the geometry
|
||||
* normal and the shading normal is the same. */
|
||||
ccl_device float3 maybe_ensure_valid_specular_reflection(ccl_private ShaderData *sd,
|
||||
const float3 N)
|
||||
{
|
||||
if ((sd->flag & SD_USE_BUMP_MAP_CORRECTION) == 0) {
|
||||
return N;
|
||||
}
|
||||
if ((sd->type & PRIMITIVE_CURVE) || isequal(sd->Ng, N)) {
|
||||
return N;
|
||||
}
|
||||
return ensure_valid_specular_reflection(sd->Ng, sd->wi, N);
|
||||
}
|
||||
|
||||
/* Principled Hair albedo and absorption coefficients. */
|
||||
ccl_device_inline float bsdf_principled_hair_albedo_roughness_scale(
|
||||
const float azimuthal_roughness)
|
||||
{
|
||||
const float x = azimuthal_roughness;
|
||||
return (((((0.245f * x) + 5.574f) * x - 10.73f) * x + 2.532f) * x - 0.215f) * x + 5.969f;
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum
|
||||
bsdf_principled_hair_sigma_from_reflectance(const Spectrum color, const float azimuthal_roughness)
|
||||
{
|
||||
const Spectrum sigma = log(max(color, zero_spectrum())) /
|
||||
bsdf_principled_hair_albedo_roughness_scale(azimuthal_roughness);
|
||||
return sigma * sigma;
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum bsdf_principled_hair_sigma_from_concentration(const float eumelanin,
|
||||
const float pheomelanin)
|
||||
{
|
||||
const float3 eumelanin_color = make_float3(0.506f, 0.841f, 1.653f);
|
||||
const float3 pheomelanin_color = make_float3(0.343f, 0.733f, 1.924f);
|
||||
|
||||
return eumelanin * rgb_to_spectrum(eumelanin_color) +
|
||||
pheomelanin * rgb_to_spectrum(pheomelanin_color);
|
||||
}
|
||||
|
||||
/* Computes the weight for base closure(s) which are layered under another closure.
|
||||
* layer_albedo is an estimate of the top layer's reflectivity, while weight is the closure weight
|
||||
* of the entire base+top combination. */
|
||||
ccl_device_inline Spectrum closure_layering_weight(const Spectrum layer_albedo,
|
||||
const Spectrum weight)
|
||||
{
|
||||
return weight * saturatef(1.0f - reduce_max(safe_divide_color(layer_albedo, weight)));
|
||||
}
|
||||
|
||||
/* ******** Thin-film iridescence implementation ********
|
||||
*
|
||||
* Based on "A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence"
|
||||
* by Laurent Belcour and Pascal Barla.
|
||||
* https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Evaluate the sensitivity functions for the Fourier-space spectral integration.
|
||||
*/
|
||||
ccl_device_inline complex<float> iridescence_lookup_sensitivity_channel(KernelGlobals kg,
|
||||
const int channel,
|
||||
const float OPD)
|
||||
{
|
||||
/* The LUT covers 0 to 60 um. */
|
||||
const float x = M_2PI_F * OPD / 60000.0f;
|
||||
const int size = THIN_FILM_TABLE_SIZE;
|
||||
const int base = kernel_data.tables.thin_film_table;
|
||||
return {lookup_table_read(kg, x, base + channel * size, size),
|
||||
lookup_table_read(kg, x, base + (channel + 3) * size, size)};
|
||||
}
|
||||
|
||||
ccl_device_inline float iridescence_airy_summation_channel(KernelGlobals kg,
|
||||
const int channel,
|
||||
const float R12,
|
||||
const float R23,
|
||||
const float OPD,
|
||||
const complex<float> phasor)
|
||||
{
|
||||
const float T121 = 1.0f - R12;
|
||||
const float R123 = R12 * R23;
|
||||
const float r123 = sqrtf(R123);
|
||||
const float Rs = sqr(T121) * R23 / (1.0f - R123);
|
||||
|
||||
/* Initialize complex number for exp(i * phi)^m, equivalent to {cos(m * phi), sin(m * phi)} as
|
||||
* used in equation 10. */
|
||||
complex<float> accumulator = phasor;
|
||||
|
||||
/* Perform summation over path order differences (equation 10). */
|
||||
float R = Rs + R12; /* C0 */
|
||||
float Cm = Rs - T121;
|
||||
|
||||
/* Truncate after m=3, higher differences have barely any impact. */
|
||||
for (int m = 1; m < 4; m++) {
|
||||
Cm *= r123;
|
||||
const complex<float> S = iridescence_lookup_sensitivity_channel(kg, channel, m * OPD);
|
||||
R += Cm * 2.0f * (accumulator.re * S.re + accumulator.im * S.im);
|
||||
accumulator *= phasor;
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
template<bool conductive>
|
||||
ccl_device float fresnel_iridescence_channel(KernelGlobals kg,
|
||||
const int channel,
|
||||
const float ambient_ior,
|
||||
const FresnelThinFilm thin_film,
|
||||
const float substrate_n,
|
||||
const float substrate_k,
|
||||
const float F82,
|
||||
const float cos_theta_1,
|
||||
ccl_private float *r_cos_theta_3)
|
||||
{
|
||||
/* For films below 1nm, the wave-optic-based Airy summation approach no longer applies,
|
||||
* so blend towards the case without coating. */
|
||||
float film_ior = thin_film.ior;
|
||||
if (thin_film.thickness < 1.0f) {
|
||||
film_ior = mix(ambient_ior, film_ior, smoothstep(0.0f, 1.0f, thin_film.thickness));
|
||||
}
|
||||
|
||||
float cos_theta_2;
|
||||
/* The real component of exp(i * phi12), equivalent to cos(phi12). */
|
||||
float2 phasor12_real;
|
||||
|
||||
/* Compute reflection at the top interface (ambient to film). */
|
||||
const float2 R12 = fresnel_dielectric_polarized(
|
||||
cos_theta_1, film_ior / ambient_ior, &cos_theta_2, &phasor12_real);
|
||||
if (isequal(R12, one_float2())) {
|
||||
/* TIR at the top interface. */
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* Compute reflection at the bottom interface (film to substrate). */
|
||||
float R23_s, R23_p;
|
||||
complex<float> phasor23_s, phasor23_p;
|
||||
if constexpr (conductive) {
|
||||
/* Material is a conductor. */
|
||||
fresnel_conductor_polarized(-cos_theta_2,
|
||||
film_ior,
|
||||
{substrate_n, substrate_k},
|
||||
F82,
|
||||
R23_s,
|
||||
R23_p,
|
||||
&phasor23_s,
|
||||
&phasor23_p);
|
||||
}
|
||||
else {
|
||||
/* Material is a dielectric. */
|
||||
float2 phasor23_real;
|
||||
const float2 R23 = fresnel_dielectric_polarized(
|
||||
-cos_theta_2, substrate_n / film_ior, r_cos_theta_3, &phasor23_real);
|
||||
if (isequal(R23, one_float2())) {
|
||||
/* TIR at the bottom interface.
|
||||
* All the Airy summation math still simplifies to 1.0 in this case. */
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
R23_s = R23.x;
|
||||
R23_p = R23.y;
|
||||
phasor23_s = {phasor23_real.x, 0.0f};
|
||||
phasor23_p = {phasor23_real.y, 0.0f};
|
||||
}
|
||||
|
||||
/* Compute optical path difference inside the thin film. */
|
||||
const float OPD = -2.0f * film_ior * thin_film.thickness * cos_theta_2;
|
||||
|
||||
/* Compute full phase shifts due to reflection, as a complex number exp(i * (phi23 + phi21)).
|
||||
* This complex form avoids the atan2 and cos calls needed to directly get the phase shift. */
|
||||
const complex<float> phasor_s = phasor23_s * -phasor12_real.x;
|
||||
const float R_s = iridescence_airy_summation_channel(kg, channel, R12.x, R23_s, OPD, phasor_s);
|
||||
|
||||
/* Perform Airy summation and average the polarizations. */
|
||||
const complex<float> phasor_p = phasor23_p * -phasor12_real.y;
|
||||
const float R_p = iridescence_airy_summation_channel(kg, channel, R12.y, R23_p, OPD, phasor_p);
|
||||
|
||||
return saturatef(0.5f * (R_s + R_p));
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
355
blender-5.2.0/intern/cycles/kernel/closure/bssrdf.h
Normal file
355
blender-5.2.0/intern/cycles/kernel/closure/bssrdf.h
Normal file
@@ -0,0 +1,355 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/alloc.h"
|
||||
#include "kernel/closure/bsdf_diffuse.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct Bssrdf {
|
||||
SHADER_CLOSURE_BASE;
|
||||
|
||||
Spectrum radius;
|
||||
Spectrum albedo;
|
||||
float anisotropy;
|
||||
|
||||
/* Parameters for refractive entry bounce. */
|
||||
float ior;
|
||||
float alpha;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderClosure) >= sizeof(Bssrdf), "Bssrdf is too large!");
|
||||
|
||||
/* Random Walk BSSRDF */
|
||||
|
||||
ccl_device float bssrdf_dipole_compute_Rd(const float alpha_prime, const float fourthirdA)
|
||||
{
|
||||
const float s = sqrtf(3.0f * (1.0f - alpha_prime));
|
||||
return 0.5f * alpha_prime * (1.0f + expf(-fourthirdA * s)) * expf(-s);
|
||||
}
|
||||
|
||||
ccl_device float bssrdf_dipole_compute_alpha_prime(const float rd, const float fourthirdA)
|
||||
{
|
||||
/* Little Newton solver. */
|
||||
if (rd < 1e-4f) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (rd >= 0.995f) {
|
||||
return 0.999999f;
|
||||
}
|
||||
|
||||
float x0 = 0.0f;
|
||||
float x1 = 1.0f;
|
||||
float xmid;
|
||||
float fmid;
|
||||
|
||||
constexpr const int max_num_iterations = 12;
|
||||
for (int i = 0; i < max_num_iterations; ++i) {
|
||||
xmid = 0.5f * (x0 + x1);
|
||||
fmid = bssrdf_dipole_compute_Rd(xmid, fourthirdA);
|
||||
if (fmid < rd) {
|
||||
x0 = xmid;
|
||||
}
|
||||
else {
|
||||
x1 = xmid;
|
||||
}
|
||||
}
|
||||
|
||||
return xmid;
|
||||
}
|
||||
|
||||
ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf, const ClosureType type)
|
||||
{
|
||||
|
||||
if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_RANDOM_WALK_LEGACY_ID) {
|
||||
/* Scale mean free path length so that Burley and Random Walk Legacy look similar to before. */
|
||||
bssrdf->radius *= 0.25f * M_1_PI_F;
|
||||
}
|
||||
else if (type == CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID) {
|
||||
/* Adjust radius based on IOR and albedo. */
|
||||
const float inv_eta = 1.0f / bssrdf->ior;
|
||||
const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * bssrdf->ior;
|
||||
const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) /
|
||||
(1.0f - F_dr); /* From Jensen's `Fdr` ratio formula. */
|
||||
|
||||
Spectrum alpha_prime;
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
GET_SPECTRUM_CHANNEL(alpha_prime, i) = bssrdf_dipole_compute_alpha_prime(
|
||||
GET_SPECTRUM_CHANNEL(bssrdf->albedo, i), fourthirdA);
|
||||
}
|
||||
|
||||
bssrdf->radius *= sqrt(3.0f * (one_spectrum() - alpha_prime));
|
||||
}
|
||||
}
|
||||
|
||||
/* Christensen-Burley BSSRDF.
|
||||
*
|
||||
* Approximate Reflectance Profiles from
|
||||
* http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
|
||||
*/
|
||||
|
||||
/* This is a bit arbitrary, just need big enough radius so it matches
|
||||
* the mean free length, but still not too big so sampling is still
|
||||
* effective. */
|
||||
#define BURLEY_TRUNCATE 16.0f
|
||||
#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
|
||||
|
||||
ccl_device_inline float bssrdf_burley_fitting(const float A)
|
||||
{
|
||||
/* Diffuse surface transmission, equation (6). */
|
||||
return 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
|
||||
}
|
||||
|
||||
/* Scale mean free path length so it gives similar looking result
|
||||
* to Cubic and Gaussian models. */
|
||||
ccl_device_inline Spectrum bssrdf_burley_compatible_mfp(Spectrum r)
|
||||
{
|
||||
return 0.25f * M_1_PI_F * r;
|
||||
}
|
||||
|
||||
ccl_device void bssrdf_burley_setup(ccl_private Bssrdf *bssrdf)
|
||||
{
|
||||
/* Mean free path length. */
|
||||
const Spectrum l = bssrdf_burley_compatible_mfp(bssrdf->radius);
|
||||
/* Surface albedo. */
|
||||
const Spectrum A = bssrdf->albedo;
|
||||
Spectrum s;
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
GET_SPECTRUM_CHANNEL(s, i) = bssrdf_burley_fitting(GET_SPECTRUM_CHANNEL(A, i));
|
||||
}
|
||||
|
||||
bssrdf->radius = l / s;
|
||||
}
|
||||
|
||||
ccl_device float bssrdf_burley_eval(const float d, const float r)
|
||||
{
|
||||
const float Rm = BURLEY_TRUNCATE * d;
|
||||
|
||||
if (r >= Rm) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Burley reflectance profile, equation (3).
|
||||
*
|
||||
* NOTES:
|
||||
* - Surface albedo is already included into `sc->weight`, no need to
|
||||
* multiply by this term here.
|
||||
* - This is normalized diffuse model, so the equation is multiplied
|
||||
* by `2*pi`, which also matches `cdf()`.
|
||||
*/
|
||||
const float exp_r_3_d = expf(-r / (3.0f * d));
|
||||
const float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
|
||||
return (exp_r_d + exp_r_3_d) / (4.0f * d);
|
||||
}
|
||||
|
||||
ccl_device float bssrdf_burley_pdf(const float d, const float r)
|
||||
{
|
||||
if (r == 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return bssrdf_burley_eval(d, r) * (1.0f / BURLEY_TRUNCATE_CDF);
|
||||
}
|
||||
|
||||
/* Find the radius for desired CDF value.
|
||||
* Returns scaled radius, meaning the result is to be scaled up by d.
|
||||
* Since there's no closed form solution we do Newton-Raphson method to find it.
|
||||
*/
|
||||
ccl_device_forceinline float bssrdf_burley_root_find(const float xi)
|
||||
{
|
||||
const float tolerance = 1e-6f;
|
||||
const int max_iteration_count = 10;
|
||||
/* Do initial guess based on manual curve fitting, this allows us to reduce
|
||||
* number of iterations to maximum 4 across the [0..1] range. We keep maximum
|
||||
* number of iteration higher just to be sure we didn't miss root in some
|
||||
* corner case.
|
||||
*/
|
||||
float r;
|
||||
if (xi <= 0.9f) {
|
||||
r = expf(xi * xi * 2.4f) - 1.0f;
|
||||
}
|
||||
else {
|
||||
/* TODO(sergey): Some nicer curve fit is possible here. */
|
||||
r = 15.0f;
|
||||
}
|
||||
/* Solve against scaled radius. */
|
||||
for (int i = 0; i < max_iteration_count; i++) {
|
||||
const float exp_r_3 = expf(-r / 3.0f);
|
||||
const float exp_r = exp_r_3 * exp_r_3 * exp_r_3;
|
||||
const float f = 1.0f - 0.25f * exp_r - 0.75f * exp_r_3 - xi;
|
||||
const float f_ = 0.25f * exp_r + 0.25f * exp_r_3;
|
||||
|
||||
if (fabsf(f) < tolerance || f_ == 0.0f) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = r - f / f_;
|
||||
r = fmaxf(r, 0.0f);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ccl_device void bssrdf_burley_sample(const float d,
|
||||
const float xi,
|
||||
ccl_private float *r,
|
||||
ccl_private float *h)
|
||||
{
|
||||
const float Rm = BURLEY_TRUNCATE * d;
|
||||
const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d;
|
||||
|
||||
*r = r_;
|
||||
|
||||
/* h^2 + r^2 = Rm^2 */
|
||||
*h = safe_sqrtf(Rm * Rm - r_ * r_);
|
||||
}
|
||||
|
||||
ccl_device float bssrdf_num_channels(const Spectrum radius)
|
||||
{
|
||||
float channels = 0;
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
if (GET_SPECTRUM_CHANNEL(radius, i) > 0.0f) {
|
||||
channels += 1.0f;
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
ccl_device void bssrdf_sample(const Spectrum radius,
|
||||
float xi,
|
||||
ccl_private float *r,
|
||||
ccl_private float *h)
|
||||
{
|
||||
const float num_channels = bssrdf_num_channels(radius);
|
||||
float sampled_radius;
|
||||
|
||||
/* Sample color channel and reuse random number. Only a subset of channels
|
||||
* may be used if their radius was too small to handle as BSSRDF. */
|
||||
xi *= num_channels;
|
||||
sampled_radius = 0.0f;
|
||||
|
||||
float sum = 0.0f;
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
const float channel_radius = GET_SPECTRUM_CHANNEL(radius, i);
|
||||
if (channel_radius > 0.0f) {
|
||||
const float next_sum = sum + 1.0f;
|
||||
if (xi < next_sum) {
|
||||
xi -= sum;
|
||||
sampled_radius = channel_radius;
|
||||
break;
|
||||
}
|
||||
sum = next_sum;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sample BSSRDF. */
|
||||
bssrdf_burley_sample(sampled_radius, xi, r, h);
|
||||
}
|
||||
|
||||
ccl_device_forceinline Spectrum bssrdf_eval(const Spectrum radius, const float r)
|
||||
{
|
||||
Spectrum result;
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
GET_SPECTRUM_CHANNEL(result, i) = bssrdf_burley_pdf(GET_SPECTRUM_CHANNEL(radius, i), r);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float bssrdf_pdf(const Spectrum radius, const float r)
|
||||
{
|
||||
const Spectrum pdf = bssrdf_eval(radius, r);
|
||||
return reduce_add(pdf) / bssrdf_num_channels(radius);
|
||||
}
|
||||
|
||||
/* Setup */
|
||||
|
||||
ccl_device_inline ccl_private Bssrdf *bssrdf_alloc(ccl_private ShaderData *sd, Spectrum weight)
|
||||
{
|
||||
const float sample_weight = fabsf(average(weight));
|
||||
if (sample_weight < CLOSURE_WEIGHT_CUTOFF) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ccl_private Bssrdf *bssrdf = (ccl_private Bssrdf *)closure_alloc(
|
||||
sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight);
|
||||
|
||||
if (bssrdf == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bssrdf->sample_weight = sample_weight;
|
||||
return bssrdf;
|
||||
}
|
||||
|
||||
ccl_device int bssrdf_setup(ccl_private ShaderData *sd,
|
||||
ccl_private Bssrdf *bssrdf,
|
||||
const uint32_t path_flag,
|
||||
ClosureType type)
|
||||
{
|
||||
/* Clamp anisotropy to avoid delta function. */
|
||||
if (type == CLOSURE_BSSRDF_RANDOM_WALK_ID) {
|
||||
bssrdf->anisotropy = clamp(bssrdf->anisotropy, -0.99f, 0.99f);
|
||||
}
|
||||
else {
|
||||
bssrdf->anisotropy = clamp(bssrdf->anisotropy, -0.99f, 0.9f);
|
||||
}
|
||||
|
||||
bssrdf->ior = clamp(bssrdf->ior, 1.01f, 3.8f);
|
||||
|
||||
int flag = 0;
|
||||
|
||||
if (type == CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID) {
|
||||
/* CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID uses a fixed roughness. */
|
||||
bssrdf->alpha = 1.0f;
|
||||
}
|
||||
|
||||
/* Verify if the radii are large enough to sample without precision issues. */
|
||||
int bssrdf_channels = SPECTRUM_CHANNELS;
|
||||
Spectrum diffuse_weight = zero_spectrum();
|
||||
|
||||
if (type == CLOSURE_BSSRDF_BURLEY_ID && (path_flag & PATH_RAY_DIFFUSE_ANCESTOR)) {
|
||||
/* Fall back to diffuse after a diffuse ancestor for Christensen-Burley. Can't see it that
|
||||
* well and it adds considerable noise due to probabilities of continuing the path getting
|
||||
* lower and lower. The disk sampling must probe in directions where most of the time
|
||||
* nothing will be hit. */
|
||||
bssrdf_channels = 0;
|
||||
diffuse_weight = bssrdf->weight;
|
||||
}
|
||||
else {
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
if (GET_SPECTRUM_CHANNEL(bssrdf->radius, i) < BSSRDF_MIN_RADIUS) {
|
||||
GET_SPECTRUM_CHANNEL(diffuse_weight, i) = GET_SPECTRUM_CHANNEL(bssrdf->weight, i);
|
||||
GET_SPECTRUM_CHANNEL(bssrdf->weight, i) = 0.0f;
|
||||
GET_SPECTRUM_CHANNEL(bssrdf->radius, i) = 0.0f;
|
||||
bssrdf_channels--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bssrdf_channels < SPECTRUM_CHANNELS) {
|
||||
/* Add diffuse BSDF if any radius too small. */
|
||||
bsdf_diffuse_setup(sd, bssrdf->N, diffuse_weight);
|
||||
}
|
||||
|
||||
/* Setup BSSRDF if radius is large enough. */
|
||||
if (bssrdf_channels > 0) {
|
||||
bssrdf->type = type;
|
||||
bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf_channels;
|
||||
|
||||
bssrdf_setup_radius(bssrdf, type);
|
||||
|
||||
flag |= SD_BSSRDF;
|
||||
}
|
||||
else {
|
||||
bssrdf->type = CLOSURE_NONE_ID;
|
||||
bssrdf->sample_weight = 0.0f;
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
65
blender-5.2.0/intern/cycles/kernel/closure/emissive.h
Normal file
65
blender-5.2.0/intern/cycles/kernel/closure/emissive.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* BACKGROUND CLOSURE */
|
||||
|
||||
ccl_device void background_setup(ccl_private ShaderData *sd, const Spectrum weight)
|
||||
{
|
||||
if (sd->flag & SD_EMISSION) {
|
||||
sd->closure_emission_background += weight;
|
||||
}
|
||||
else {
|
||||
sd->flag |= SD_EMISSION;
|
||||
sd->closure_emission_background = weight;
|
||||
}
|
||||
}
|
||||
|
||||
/* EMISSION CLOSURE */
|
||||
|
||||
ccl_device void emission_setup(ccl_private ShaderData *sd, const Spectrum weight)
|
||||
{
|
||||
if (sd->flag & SD_EMISSION) {
|
||||
sd->closure_emission_background += weight;
|
||||
}
|
||||
else {
|
||||
sd->flag |= SD_EMISSION;
|
||||
sd->closure_emission_background = weight;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the probability distribution function in the direction wi,
|
||||
* given the parameters and the light's surface normal. This MUST match
|
||||
* the PDF computed by sample(). */
|
||||
ccl_device float emissive_pdf(const float3 Ng, const float3 wi)
|
||||
{
|
||||
const float cosNI = fabsf(dot(Ng, wi));
|
||||
return (cosNI > 0.0f) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
ccl_device void emissive_sample(const float3 /*Ng*/,
|
||||
const float /*randu*/,
|
||||
const float /*randv*/,
|
||||
ccl_private float3 * /*wi*/,
|
||||
ccl_private float * /*pdf*/)
|
||||
{
|
||||
/* todo: not implemented and used yet */
|
||||
}
|
||||
|
||||
ccl_device Spectrum emissive_simple_eval(const float3 Ng, const float3 wi)
|
||||
{
|
||||
const float res = emissive_pdf(Ng, wi);
|
||||
|
||||
return make_spectrum(res);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
191
blender-5.2.0/intern/cycles/kernel/closure/volume.h
Normal file
191
blender-5.2.0/intern/cycles/kernel/closure/volume.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/volume_draine.h"
|
||||
#include "kernel/closure/volume_fournier_forand.h"
|
||||
#include "kernel/closure/volume_henyey_greenstein.h"
|
||||
#include "kernel/closure/volume_rayleigh.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* VOLUME EXTINCTION */
|
||||
|
||||
ccl_device void volume_extinction_setup(ccl_private ShaderData *sd, Spectrum weight)
|
||||
{
|
||||
if (sd->flag & SD_EXTINCTION) {
|
||||
sd->closure_transparent_extinction += weight;
|
||||
}
|
||||
else {
|
||||
sd->flag |= SD_EXTINCTION;
|
||||
sd->closure_transparent_extinction = weight;
|
||||
}
|
||||
}
|
||||
|
||||
/* VOLUME SCATTERING */
|
||||
|
||||
ccl_device Spectrum volume_phase_eval(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
switch (svc->type) {
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID:
|
||||
return volume_fournier_forand_eval(sd, svc, wo, pdf);
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID:
|
||||
return volume_rayleigh_eval(sd, wo, pdf);
|
||||
case CLOSURE_VOLUME_DRAINE_ID:
|
||||
return volume_draine_eval(sd, svc, wo, pdf);
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
|
||||
return volume_henyey_greenstein_eval(sd, svc, wo, pdf);
|
||||
default:
|
||||
kernel_assert(false);
|
||||
*pdf = 0.0f;
|
||||
return zero_spectrum();
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device int volume_phase_sample(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
switch (svc->type) {
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID:
|
||||
return volume_fournier_forand_sample(sd, svc, rand, eval, wo, pdf);
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID:
|
||||
return volume_rayleigh_sample(sd, rand, eval, wo, pdf);
|
||||
case CLOSURE_VOLUME_DRAINE_ID:
|
||||
return volume_draine_sample(sd, svc, rand, eval, wo, pdf);
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
|
||||
return volume_henyey_greenstein_sample(sd, svc, rand, eval, wo, pdf);
|
||||
default:
|
||||
kernel_assert(false);
|
||||
*pdf = 0.0f;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Widen the compact ray differential dD after a phase function scatter to
|
||||
* match the lobe's angular spread. See bsdf_widen_dD for details. */
|
||||
ccl_device_forceinline float volume_phase_widen_dD(const float prev_dD,
|
||||
const float sampled_roughness)
|
||||
{
|
||||
return max(prev_dD, sampled_roughness);
|
||||
}
|
||||
|
||||
ccl_device bool volume_phase_equal(const ccl_private ShaderClosure *c1,
|
||||
const ccl_private ShaderClosure *c2)
|
||||
{
|
||||
if (c1->type != c2->type) {
|
||||
return false;
|
||||
}
|
||||
switch (c1->type) {
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID: {
|
||||
ccl_private FournierForandVolume *v1 = (ccl_private FournierForandVolume *)c1;
|
||||
ccl_private FournierForandVolume *v2 = (ccl_private FournierForandVolume *)c2;
|
||||
return v1->c1 == v2->c1 && v1->c2 == v2->c2 && v1->c3 == v2->c3;
|
||||
}
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID:
|
||||
return true;
|
||||
case CLOSURE_VOLUME_DRAINE_ID: {
|
||||
ccl_private DraineVolume *v1 = (ccl_private DraineVolume *)c1;
|
||||
ccl_private DraineVolume *v2 = (ccl_private DraineVolume *)c2;
|
||||
return v1->g == v2->g && v1->alpha == v2->alpha;
|
||||
}
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: {
|
||||
ccl_private HenyeyGreensteinVolume *v1 = (ccl_private HenyeyGreensteinVolume *)c1;
|
||||
ccl_private HenyeyGreensteinVolume *v2 = (ccl_private HenyeyGreensteinVolume *)c2;
|
||||
return v1->g == v2->g;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Approximate phase functions as Henyey-Greenstein for volume guiding.
|
||||
* TODO: This is not ideal, we should use RIS guiding for non-HG phase functions. */
|
||||
ccl_device float volume_phase_get_g(const ccl_private ShaderVolumeClosure *svc)
|
||||
{
|
||||
switch (svc->type) {
|
||||
case CLOSURE_VOLUME_FOURNIER_FORAND_ID:
|
||||
/* TODO */
|
||||
return 1.0f;
|
||||
case CLOSURE_VOLUME_RAYLEIGH_ID:
|
||||
/* Approximate as isotropic */
|
||||
return 0.0f;
|
||||
case CLOSURE_VOLUME_DRAINE_ID:
|
||||
/* Approximate as HG, TODO */
|
||||
return ((ccl_private DraineVolume *)svc)->g;
|
||||
case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID:
|
||||
return ((ccl_private HenyeyGreensteinVolume *)svc)->g;
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Volume sampling utilities. */
|
||||
|
||||
/* Ignore paths that have volume throughput below this value, to avoid unnecessary work
|
||||
* and precision issues.
|
||||
* TODO: this value could be tweaked or turned into a probability to avoid unnecessary work in
|
||||
* volumes and subsurface scattering. */
|
||||
#define VOLUME_THROUGHPUT_EPSILON 1e-6f
|
||||
|
||||
ccl_device Spectrum volume_color_transmittance(Spectrum sigma, const float t)
|
||||
{
|
||||
return exp(-sigma * t);
|
||||
}
|
||||
|
||||
ccl_device float volume_channel_get(Spectrum value, const int channel)
|
||||
{
|
||||
return GET_SPECTRUM_CHANNEL(value, channel);
|
||||
}
|
||||
|
||||
/* Sample color channel proportional to throughput and single scattering albedo, to significantly
|
||||
* reduce noise with many bounce, following:
|
||||
*
|
||||
* "Practical and Controllable Subsurface Scattering for Production Path Tracing".
|
||||
* Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */
|
||||
ccl_device_inline Spectrum volume_sample_channel_pdf(Spectrum albedo, Spectrum throughput)
|
||||
{
|
||||
const Spectrum weights = fabs(throughput * albedo);
|
||||
const float sum_weights = reduce_add(weights);
|
||||
|
||||
if ((1.0f - sum_weights) < 1.0f) {
|
||||
/* The same as `sum_weights > 0.0f`, but avoids the case where `sum_weight` is denormal, which
|
||||
* could produce `nan` after division. */
|
||||
return weights / sum_weights;
|
||||
}
|
||||
|
||||
return make_spectrum(1.0f / SPECTRUM_CHANNELS);
|
||||
}
|
||||
|
||||
ccl_device int volume_sample_channel(Spectrum albedo,
|
||||
Spectrum throughput,
|
||||
ccl_private float *rand,
|
||||
ccl_private Spectrum *pdf)
|
||||
{
|
||||
*pdf = volume_sample_channel_pdf(albedo, throughput);
|
||||
|
||||
float pdf_sum = 0.0f;
|
||||
FOREACH_SPECTRUM_CHANNEL (i) {
|
||||
const float channel_pdf = GET_SPECTRUM_CHANNEL(*pdf, i);
|
||||
if (*rand < pdf_sum + channel_pdf) {
|
||||
/* Rescale to reuse. */
|
||||
*rand = (*rand - pdf_sum) / channel_pdf;
|
||||
return i;
|
||||
}
|
||||
pdf_sum += channel_pdf;
|
||||
}
|
||||
return SPECTRUM_CHANNELS - 1;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
62
blender-5.2.0/intern/cycles/kernel/closure/volume_draine.h
Normal file
62
blender-5.2.0/intern/cycles/kernel/closure/volume_draine.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/volume_util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* DRAINE CLOSURE */
|
||||
|
||||
struct DraineVolume {
|
||||
SHADER_CLOSURE_VOLUME_BASE;
|
||||
|
||||
float g;
|
||||
float alpha;
|
||||
};
|
||||
static_assert(sizeof(ShaderVolumeClosure) >= sizeof(DraineVolume), "DraineVolume is too large!");
|
||||
|
||||
ccl_device int volume_draine_setup(ccl_private DraineVolume *volume)
|
||||
{
|
||||
volume->type = CLOSURE_VOLUME_DRAINE_ID;
|
||||
/* clamp anisotropy */
|
||||
volume->g = signf(volume->g) * min(fabsf(volume->g), 1.0f - 1e-3f);
|
||||
|
||||
return SD_SCATTER;
|
||||
}
|
||||
|
||||
ccl_device Spectrum volume_draine_eval(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private DraineVolume *volume = (const ccl_private DraineVolume *)svc;
|
||||
|
||||
/* note that wi points towards the viewer */
|
||||
const float cos_theta = dot(-sd->wi, wo);
|
||||
*pdf = phase_draine(cos_theta, volume->g, volume->alpha);
|
||||
|
||||
return make_spectrum(*pdf);
|
||||
}
|
||||
|
||||
ccl_device int volume_draine_sample(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private DraineVolume *volume = (const ccl_private DraineVolume *)svc;
|
||||
|
||||
/* note that wi points towards the viewer and so is used negated */
|
||||
*wo = phase_draine_sample(-sd->wi, volume->g, volume->alpha, rand, pdf);
|
||||
*eval = make_spectrum(*pdf); /* perfect importance sampling */
|
||||
|
||||
return LABEL_VOLUME_SCATTER;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,73 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/volume_util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* FOURNIER-FORAND CLOSURE */
|
||||
|
||||
struct FournierForandVolume {
|
||||
SHADER_CLOSURE_VOLUME_BASE;
|
||||
|
||||
/* Precomputed coefficients, based on B and IOR */
|
||||
float c1, c2, c3;
|
||||
};
|
||||
static_assert(sizeof(ShaderVolumeClosure) >= sizeof(FournierForandVolume),
|
||||
"FournierForandVolume is too large!");
|
||||
|
||||
ccl_device int volume_fournier_forand_setup(ccl_private FournierForandVolume *volume,
|
||||
float B,
|
||||
float IOR)
|
||||
{
|
||||
volume->type = CLOSURE_VOLUME_FOURNIER_FORAND_ID;
|
||||
|
||||
/* clamp backscatter fraction to avoid delta function */
|
||||
B = min(fabsf(B), 0.5f - 1e-3f);
|
||||
IOR = max(IOR, 1.0f + 1e-3f);
|
||||
const float3 coeffs = phase_fournier_forand_coeffs(B, IOR);
|
||||
volume->c1 = coeffs.x;
|
||||
volume->c2 = coeffs.y;
|
||||
volume->c3 = coeffs.z;
|
||||
|
||||
return SD_SCATTER;
|
||||
}
|
||||
|
||||
ccl_device Spectrum volume_fournier_forand_eval(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private FournierForandVolume *volume = (const ccl_private FournierForandVolume *)svc;
|
||||
const float3 coeffs = make_float3(volume->c1, volume->c2, volume->c3);
|
||||
|
||||
/* note that wi points towards the viewer */
|
||||
const float cos_theta = dot(-sd->wi, wo);
|
||||
*pdf = phase_fournier_forand(cos_theta, coeffs);
|
||||
|
||||
return make_spectrum(*pdf);
|
||||
}
|
||||
|
||||
ccl_device int volume_fournier_forand_sample(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private FournierForandVolume *volume = (const ccl_private FournierForandVolume *)svc;
|
||||
const float3 coeffs = make_float3(volume->c1, volume->c2, volume->c3);
|
||||
|
||||
/* note that wi points towards the viewer and so is used negated */
|
||||
*wo = phase_fournier_forand_sample(-sd->wi, coeffs, rand, pdf);
|
||||
*eval = make_spectrum(*pdf); /* perfect importance sampling */
|
||||
|
||||
return LABEL_VOLUME_SCATTER;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/volume_util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* HENYEY-GREENSTEIN CLOSURE */
|
||||
|
||||
struct HenyeyGreensteinVolume {
|
||||
SHADER_CLOSURE_VOLUME_BASE;
|
||||
|
||||
float g;
|
||||
};
|
||||
static_assert(sizeof(ShaderVolumeClosure) >= sizeof(HenyeyGreensteinVolume),
|
||||
"HenyeyGreensteinVolume is too large!");
|
||||
|
||||
ccl_device int volume_henyey_greenstein_setup(ccl_private HenyeyGreensteinVolume *volume)
|
||||
{
|
||||
volume->type = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
|
||||
/* clamp anisotropy to avoid delta function */
|
||||
volume->g = signf(volume->g) * min(fabsf(volume->g), 1.0f - 1e-3f);
|
||||
|
||||
return SD_SCATTER;
|
||||
}
|
||||
|
||||
ccl_device Spectrum volume_henyey_greenstein_eval(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private HenyeyGreensteinVolume *volume = (const ccl_private HenyeyGreensteinVolume *)
|
||||
svc;
|
||||
|
||||
/* note that wi points towards the viewer */
|
||||
const float cos_theta = dot(-sd->wi, wo);
|
||||
*pdf = phase_henyey_greenstein(cos_theta, volume->g);
|
||||
|
||||
return make_spectrum(*pdf);
|
||||
}
|
||||
|
||||
ccl_device int volume_henyey_greenstein_sample(const ccl_private ShaderData *sd,
|
||||
const ccl_private ShaderVolumeClosure *svc,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const ccl_private HenyeyGreensteinVolume *volume = (const ccl_private HenyeyGreensteinVolume *)
|
||||
svc;
|
||||
|
||||
/* note that wi points towards the viewer and so is used negated */
|
||||
*wo = phase_henyey_greenstein_sample(-sd->wi, volume->g, rand, pdf);
|
||||
*eval = make_spectrum(*pdf); /* perfect importance sampling */
|
||||
|
||||
return LABEL_VOLUME_SCATTER;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
51
blender-5.2.0/intern/cycles/kernel/closure/volume_rayleigh.h
Normal file
51
blender-5.2.0/intern/cycles/kernel/closure/volume_rayleigh.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/closure/volume_util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* RAYLEIGH CLOSURE */
|
||||
|
||||
struct RayleighVolume {
|
||||
SHADER_CLOSURE_VOLUME_BASE;
|
||||
};
|
||||
static_assert(sizeof(ShaderVolumeClosure) >= sizeof(RayleighVolume),
|
||||
"RayleighVolume is too large!");
|
||||
|
||||
ccl_device int volume_rayleigh_setup(ccl_private RayleighVolume *volume)
|
||||
{
|
||||
volume->type = CLOSURE_VOLUME_RAYLEIGH_ID;
|
||||
return SD_SCATTER;
|
||||
}
|
||||
|
||||
ccl_device Spectrum volume_rayleigh_eval(const ccl_private ShaderData *sd,
|
||||
const float3 wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
/* note that wi points towards the viewer */
|
||||
const float cos_theta = dot(-sd->wi, wo);
|
||||
*pdf = phase_rayleigh(cos_theta);
|
||||
|
||||
return make_spectrum(*pdf);
|
||||
}
|
||||
|
||||
ccl_device int volume_rayleigh_sample(const ccl_private ShaderData *sd,
|
||||
const float2 rand,
|
||||
ccl_private Spectrum *eval,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
/* note that wi points towards the viewer and so is used negated */
|
||||
*wo = phase_rayleigh_sample(-sd->wi, rand, pdf);
|
||||
*eval = make_spectrum(*pdf); /* perfect importance sampling */
|
||||
|
||||
return LABEL_VOLUME_SCATTER;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
310
blender-5.2.0/intern/cycles/kernel/closure/volume_util.h
Normal file
310
blender-5.2.0/intern/cycles/kernel/closure/volume_util.h
Normal file
@@ -0,0 +1,310 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/math_fast.h"
|
||||
#include "util/projection.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Given a random number, sample a direction that makes an angle of theta with direction D. */
|
||||
ccl_device float3 phase_sample_direction(const float3 D, const float cos_theta, const float rand)
|
||||
{
|
||||
const float phi = M_2PI_F * rand;
|
||||
const float3 dir = spherical_cos_to_direction(cos_theta, phi);
|
||||
|
||||
float3 T;
|
||||
float3 B;
|
||||
make_orthonormals(D, &T, &B);
|
||||
return to_global(dir, T, B, D);
|
||||
}
|
||||
|
||||
/* Given cosine between rays, return probability density that a photon bounces
|
||||
* to that direction. The g parameter controls how different it is from the
|
||||
* uniform sphere. g=0 uniform diffuse-like, g=1 close to sharp single ray. */
|
||||
ccl_device float phase_henyey_greenstein(const float cos_theta, const float g)
|
||||
{
|
||||
if (fabsf(g) < 1e-3f) {
|
||||
return M_1_4PI_F;
|
||||
}
|
||||
const float fac = 1 + g * (g - 2 * cos_theta);
|
||||
return (1 - sqr(g)) / (M_4PI_F * fac * safe_sqrtf(fac));
|
||||
}
|
||||
|
||||
ccl_device float3 phase_henyey_greenstein_sample(const float3 D,
|
||||
const float g,
|
||||
const float2 rand,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
float cos_theta = 1 - 2 * rand.x;
|
||||
if (fabsf(g) >= 1e-3f) {
|
||||
const float k = (1 - sqr(g)) / (1 - g * cos_theta);
|
||||
cos_theta = (1 + sqr(g) - sqr(k)) / (2 * g);
|
||||
}
|
||||
|
||||
*pdf = phase_henyey_greenstein(cos_theta, g);
|
||||
|
||||
return phase_sample_direction(D, cos_theta, rand.y);
|
||||
}
|
||||
|
||||
/* Given cosine between rays, return probability density that a photon bounces to that direction
|
||||
* according to the constant Rayleigh phase function.
|
||||
* See https://doi.org/10.1364/JOSAA.28.002436 for details. */
|
||||
ccl_device float phase_rayleigh(const float cos_theta)
|
||||
{
|
||||
return (0.1875f * M_1_PI_F) * (1.0f + sqr(cos_theta));
|
||||
}
|
||||
|
||||
ccl_device float3 phase_rayleigh_sample(const float3 D, const float2 rand, ccl_private float *pdf)
|
||||
{
|
||||
const float a = 2 - 4 * rand.x;
|
||||
/* Metal doesn't have cbrtf, but since we compute u - 1/u anyways, we can just as well
|
||||
* use the inverse cube root for which there is a simple Quake-style fast implementation. */
|
||||
const float inv_u = -fast_inv_cbrtf(sqrtf(1 + sqr(a)) + a);
|
||||
const float cos_theta = 1 / inv_u - inv_u;
|
||||
*pdf = phase_rayleigh(cos_theta);
|
||||
|
||||
return phase_sample_direction(D, cos_theta, rand.y);
|
||||
}
|
||||
|
||||
/* Given cosine between rays, return probability density that a photon bounces to that direction
|
||||
* according to the Draine phase function. This is a generalization of the Henyey-Greenstein
|
||||
* function which bridges the cases of HG and Rayleigh scattering. The parameter g mainly controls
|
||||
* the first moment <cos theta>, and alpha the second moment <cos2 theta> of the exact phase
|
||||
* function. alpha=0 reduces to HG function, g=0, alpha=1 reduces to Rayleigh function, alpha=1
|
||||
* reduces to Cornette-Shanks function.
|
||||
* See https://doi.org/10.1086/379118 for details. */
|
||||
ccl_device float phase_draine(const float cos_theta, const float g, float alpha)
|
||||
{
|
||||
/* Check special cases. */
|
||||
if (fabsf(g) < 1e-3f && alpha > 0.999f) {
|
||||
return phase_rayleigh(cos_theta);
|
||||
}
|
||||
if (fabsf(alpha) < 1e-3f) {
|
||||
return phase_henyey_greenstein(cos_theta, g);
|
||||
}
|
||||
|
||||
const float g2 = sqr(g);
|
||||
const float fac = 1 + g2 - 2 * g * cos_theta;
|
||||
return ((1 - g2) * (1 + alpha * sqr(cos_theta))) /
|
||||
((1 + (alpha * (1 + 2 * g2)) * (1 / 3.0f)) * M_4PI_F * fac * sqrtf(fac));
|
||||
}
|
||||
|
||||
/* Adapted from the HLSL code provided in https://research.nvidia.com/labs/rtr/approximate-mie/ */
|
||||
ccl_device float phase_draine_sample_cos(const float g, const float alpha, const float rand)
|
||||
{
|
||||
if (fabsf(g) < 1e-2f) {
|
||||
/* Special case to prevent division by zero.
|
||||
* The sample technique is similar as in https://doi.org/10.1364/JOSAA.28.002436. */
|
||||
const float inv_alpha = 1.0f / alpha;
|
||||
const float b_2 = (3 + alpha) * inv_alpha * (0.5f - rand);
|
||||
const float inv_u = -fast_inv_cbrtf(b_2 + sqrtf(sqr(b_2) + sqr(inv_alpha) * inv_alpha));
|
||||
return 1 / inv_u - inv_u / alpha;
|
||||
}
|
||||
const float g2 = sqr(g);
|
||||
const float g3 = g * g2;
|
||||
const float g4 = sqr(g2);
|
||||
const float g6 = g2 * g4;
|
||||
const float pgp1_2 = sqr(1 + g2);
|
||||
const float T1a = alpha * (g4 - 1);
|
||||
const float T1a3 = sqr(T1a) * T1a;
|
||||
const float T2 = -1296 * (g2 - 1) * (alpha - alpha * g2) * T1a * (4 * g2 + alpha * pgp1_2);
|
||||
const float T9 = 2 + g2 + g3 * (1 + 2 * g2) * (2 * rand - 1);
|
||||
const float T3 = 3 * g2 * (1 + g * (2 * rand - 1)) + alpha * T9;
|
||||
const float T4a = 432 * T1a3 + T2 + 432 * (alpha * (1 - g2)) * sqr(T3);
|
||||
const float T10 = alpha * (2 * g4 - g2 - g6);
|
||||
const float T4b = 144 * T10;
|
||||
const float T4b3 = sqr(T4b) * T4b;
|
||||
const float T4 = T4a + sqrtf(-4 * T4b3 + sqr(T4a));
|
||||
const float inv_T4p3 = fast_inv_cbrtf(T4);
|
||||
const float T8 = 48 * M_CBRT2_F * T10;
|
||||
const float T6 = (2 * T1a + T8 * inv_T4p3 + 1 / (3 * M_CBRT2_F * inv_T4p3)) / (alpha * (1 - g2));
|
||||
const float T5 = 6 * (1 + g2) + T6;
|
||||
const float T7 = 6 * (1 + g2) - (8 * T3) / (alpha * (g2 - 1) * sqrtf(T5)) - T6;
|
||||
return (1 + g2 - 0.25f * sqr(sqrtf(T7) - sqrtf(T5))) / (2 * g);
|
||||
}
|
||||
|
||||
ccl_device float3 phase_draine_sample(
|
||||
const float3 D, const float g, float alpha, const float2 rand, ccl_private float *pdf)
|
||||
{
|
||||
/* Check special cases. */
|
||||
if (fabsf(g) < 1e-3f && alpha > 0.999f) {
|
||||
return phase_rayleigh_sample(D, rand, pdf);
|
||||
}
|
||||
if (fabsf(alpha) < 1e-3f) {
|
||||
return phase_henyey_greenstein_sample(D, g, rand, pdf);
|
||||
}
|
||||
|
||||
const float cos_theta = phase_draine_sample_cos(g, alpha, rand.x);
|
||||
*pdf = phase_draine(cos_theta, g, alpha);
|
||||
|
||||
return phase_sample_direction(D, cos_theta, rand.y);
|
||||
}
|
||||
|
||||
ccl_device float phase_fournier_forand_delta(const float n, const float sin_htheta_sqr)
|
||||
{
|
||||
const float u = 4 * sin_htheta_sqr;
|
||||
return u / (3 * sqr(n - 1));
|
||||
}
|
||||
|
||||
ccl_device_inline float3 phase_fournier_forand_coeffs(const float B, const float IOR)
|
||||
{
|
||||
const float d90 = phase_fournier_forand_delta(IOR, 0.5f);
|
||||
const float d180 = phase_fournier_forand_delta(IOR, 1.0f);
|
||||
const float v = -logf(2 * B * (d90 - 1) + 1) / logf(d90);
|
||||
return make_float3(IOR, v, (powf(d180, -v) - 1) / (d180 - 1));
|
||||
}
|
||||
|
||||
/* Given cosine between rays, return probability density that a photon bounces to that direction
|
||||
* according to the Fournier-Forand phase function. The n parameter is the particle index of
|
||||
* refraction and controls how much of the light is refracted. B is the particle backscatter
|
||||
* fraction, B = b_b / b.
|
||||
* See https://doi.org/10.1117/12.366488 for details. */
|
||||
ccl_device_inline float phase_fournier_forand_impl(float cos_theta,
|
||||
const float delta,
|
||||
const float pow_delta_v,
|
||||
const float v,
|
||||
float sin_htheta_sqr,
|
||||
const float pf_coeff)
|
||||
{
|
||||
const float m_delta = 1 - delta;
|
||||
const float m_pow_delta_v = 1 - pow_delta_v;
|
||||
|
||||
float pf;
|
||||
if (fabsf(m_delta) < 1e-3f) {
|
||||
/* Special case (first-order Taylor expansion) to avoid singularity at delta near 1.0 */
|
||||
pf = v * ((v - 1) - (v + 1) / sin_htheta_sqr) * (1 / (8 * M_PI_F));
|
||||
pf += v * (v + 1) * m_delta * (2 * (v - 1) - (2 * v + 1) / sin_htheta_sqr) *
|
||||
(1 / (24 * M_PI_F));
|
||||
}
|
||||
else {
|
||||
pf = (v * m_delta - m_pow_delta_v + (delta * m_pow_delta_v - v * m_delta) / sin_htheta_sqr) /
|
||||
(M_4PI_F * sqr(m_delta) * pow_delta_v);
|
||||
}
|
||||
pf += pf_coeff * (3 * sqr(cos_theta) - 1);
|
||||
return pf;
|
||||
}
|
||||
|
||||
ccl_device float phase_fournier_forand(const float cos_theta, const float3 coeffs)
|
||||
{
|
||||
if (fabsf(cos_theta) >= 1.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float n = coeffs.x;
|
||||
const float v = coeffs.y;
|
||||
const float pf_coeff = coeffs.z * (1.0f / (16.0f * M_PI_F));
|
||||
const float sin_htheta_sqr = 0.5f * (1 - cos_theta); /* `sin^2(theta / 2)`. */
|
||||
const float delta = phase_fournier_forand_delta(n, sin_htheta_sqr);
|
||||
|
||||
return phase_fournier_forand_impl(cos_theta, delta, powf(delta, v), v, sin_htheta_sqr, pf_coeff);
|
||||
}
|
||||
|
||||
ccl_device float phase_fournier_forand_newton(const float rand, const float3 coeffs)
|
||||
{
|
||||
const float n = coeffs.x;
|
||||
const float v = coeffs.y;
|
||||
const float cdf_coeff = coeffs.z * (1.0f / 8.0f);
|
||||
const float pf_coeff = coeffs.z * (1.0f / (16.0f * M_PI_F));
|
||||
|
||||
float cos_theta = 0.64278760968f; /* Initial guess: 50 degrees */
|
||||
for (int it = 0; it < 20; it++) {
|
||||
const float sin_htheta_sqr = 0.5f * (1 - cos_theta); /* `sin^2(theta / 2)`. */
|
||||
const float delta = phase_fournier_forand_delta(n, sin_htheta_sqr);
|
||||
const float pow_delta_v = powf(delta, v);
|
||||
const float m_delta = 1 - delta;
|
||||
const float m_pow_delta_v = 1 - pow_delta_v;
|
||||
|
||||
/* Evaluate CDF and phase functions */
|
||||
float cdf;
|
||||
if (fabsf(m_delta) < 1e-3f) {
|
||||
/* Special case (first-order Taylor expansion) to avoid singularity at delta near 1.0 */
|
||||
cdf = 1 + v * (1 - sin_htheta_sqr) * (1 - 0.5f * (v + 1) * m_delta);
|
||||
}
|
||||
else {
|
||||
cdf = (1 - pow_delta_v * delta - m_pow_delta_v * sin_htheta_sqr) / (m_delta * pow_delta_v);
|
||||
}
|
||||
cdf += cdf_coeff * cos_theta * (1 - sqr(cos_theta));
|
||||
const float pf = phase_fournier_forand_impl(
|
||||
cos_theta, delta, pow_delta_v, v, sin_htheta_sqr, pf_coeff);
|
||||
|
||||
/* Perform Newton iteration step */
|
||||
float new_cos_theta = cos_theta + M_1_2PI_F * (cdf - rand) / pf;
|
||||
|
||||
/* Don't step off past 1.0, approach the peak slowly */
|
||||
if (new_cos_theta >= 1.0f) {
|
||||
new_cos_theta = max(mix(cos_theta, 1.0f, 0.5f), 0.99f);
|
||||
}
|
||||
if (fabsf(cos_theta - new_cos_theta) < 1e-6f || new_cos_theta == 1.0f) {
|
||||
return new_cos_theta;
|
||||
}
|
||||
cos_theta = new_cos_theta;
|
||||
}
|
||||
/* Reached iteration limit, so give up and use what we have. */
|
||||
return cos_theta;
|
||||
}
|
||||
|
||||
ccl_device float3 phase_fournier_forand_sample(const float3 D,
|
||||
const float3 coeffs,
|
||||
const float2 rand,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const float cos_theta = phase_fournier_forand_newton(rand.x, coeffs);
|
||||
*pdf = phase_fournier_forand(cos_theta, coeffs);
|
||||
|
||||
return phase_sample_direction(D, cos_theta, rand.y);
|
||||
}
|
||||
|
||||
/* We approximate the Mie phase function for water droplets with diameters 0 < d < 50 um using a
|
||||
* mixture of Draine and Henyey-Greenstein, following
|
||||
* "An Approximate Mie Scattering Function for Fog and Cloud Rendering (Supplemental)"
|
||||
* https://research.nvidia.com/labs/rtr/approximate-mie/publications/approximate-mie-supplemental.pdf
|
||||
* For d > 1, the phase function is strong forward-scattering. For d very close to 0, the phase
|
||||
* function is a mixture of Henyey-Greenstein and Rayleigh.
|
||||
*/
|
||||
ccl_device void phase_mie_fitted_parameters(float d,
|
||||
ccl_private float *g_HG,
|
||||
ccl_private float *g_D,
|
||||
ccl_private float *alpha,
|
||||
ccl_private float *w)
|
||||
{
|
||||
d = fmaxf(d, 0.0f);
|
||||
if (d <= 0.1f) {
|
||||
/* Eq (11 - 14). */
|
||||
*g_HG = 13.8f * sqr(d);
|
||||
*g_D = 1.1456f * d * fast_sinf(9.29044f * d);
|
||||
*alpha = 250.0f;
|
||||
*w = 0.252977f - 312.983f * powf(d, 4.3f);
|
||||
}
|
||||
else if (d < 1.5f) {
|
||||
/* Eq (15 - 18). */
|
||||
const float log_d = fast_logf(d);
|
||||
*g_HG = 0.862f - 0.143f * sqr(log_d);
|
||||
const float a = (log_d - 0.238604f) * (log_d + 1.00667f);
|
||||
const float b = 0.507522f - 0.15677f * log_d;
|
||||
const float c = 1.19692f * fast_cosf(a / b) + 1.37932f * log_d + 0.0625835f;
|
||||
*g_D = 0.379685f * fast_cosf(c) + 0.344213f;
|
||||
*alpha = 250.0f;
|
||||
*w = 0.146209f * fast_cosf(3.38707f * log_d + 2.11193f) + 0.316072f + 0.0778917f * log_d;
|
||||
}
|
||||
else if (d < 5.0f) {
|
||||
/* Eq (19 - 22). */
|
||||
const float log_d = fast_logf(d);
|
||||
*g_HG = 0.0604931f * fast_logf(log_d) + 0.940256f;
|
||||
*g_D = 0.500411f - (0.081287f / (-2.0f * log_d + fast_tanf(log_d) + 1.27551f));
|
||||
*alpha = 7.30354f * log_d + 6.31675f;
|
||||
const float temp = fast_cosf(5.68947f * (fast_logf(log_d) - 0.0292149f));
|
||||
*w = 0.026914f * (log_d - temp) + 0.3764f;
|
||||
}
|
||||
else {
|
||||
/* Eq (7 - 10). */
|
||||
*g_HG = fast_expf(-0.0990567f / (d - 1.67154f));
|
||||
*g_D = fast_expf(-2.20679f / (d + 3.91029f) - 0.428934f);
|
||||
*alpha = fast_expf(3.62489f - 8.29288f / (d + 5.52825f));
|
||||
*w = fast_expf(-0.599085f / (d - 0.641583f) - 0.665888f);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
100
blender-5.2.0/intern/cycles/kernel/data_arrays.h
Normal file
100
blender-5.2.0/intern/cycles/kernel/data_arrays.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#ifndef KERNEL_DATA_ARRAY
|
||||
# define KERNEL_DATA_ARRAY(type, name)
|
||||
#endif
|
||||
#ifndef KERNEL_DATA_ARRAY_WRITABLE
|
||||
# define KERNEL_DATA_ARRAY_WRITABLE(type, name) KERNEL_DATA_ARRAY(type, name)
|
||||
#endif
|
||||
|
||||
/* BVH2, not used for OptiX or Embree. */
|
||||
KERNEL_DATA_ARRAY(float4, bvh_nodes)
|
||||
KERNEL_DATA_ARRAY(float4, bvh_leaf_nodes)
|
||||
KERNEL_DATA_ARRAY(uint, prim_type)
|
||||
KERNEL_DATA_ARRAY(uint, prim_visibility)
|
||||
KERNEL_DATA_ARRAY(uint, prim_index)
|
||||
KERNEL_DATA_ARRAY(uint, prim_object)
|
||||
KERNEL_DATA_ARRAY(uint, object_node)
|
||||
KERNEL_DATA_ARRAY(float2, prim_time)
|
||||
|
||||
/* objects */
|
||||
KERNEL_DATA_ARRAY(KernelObject, objects)
|
||||
KERNEL_DATA_ARRAY(Transform, object_motion_pass)
|
||||
KERNEL_DATA_ARRAY(DecomposedTransform, object_motion)
|
||||
KERNEL_DATA_ARRAY(uint, object_flag)
|
||||
KERNEL_DATA_ARRAY(uint, object_prim_offset)
|
||||
|
||||
/* cameras */
|
||||
KERNEL_DATA_ARRAY(DecomposedTransform, camera_motion)
|
||||
|
||||
/* triangles */
|
||||
KERNEL_DATA_ARRAY(uint, tri_shader)
|
||||
KERNEL_DATA_ARRAY(packed_uint3, tri_vindex)
|
||||
KERNEL_DATA_ARRAY(packed_float3, tri_verts)
|
||||
|
||||
/* curves */
|
||||
KERNEL_DATA_ARRAY(KernelCurve, curves)
|
||||
KERNEL_DATA_ARRAY(float4, curve_keys)
|
||||
KERNEL_DATA_ARRAY(KernelCurveSegment, curve_segments)
|
||||
|
||||
/* pointclouds */
|
||||
KERNEL_DATA_ARRAY(float4, points)
|
||||
KERNEL_DATA_ARRAY(uint, points_shader)
|
||||
|
||||
/* attributes */
|
||||
KERNEL_DATA_ARRAY(AttributeMap, attributes_map)
|
||||
KERNEL_DATA_ARRAY(float, attributes_float)
|
||||
KERNEL_DATA_ARRAY(float2, attributes_float2)
|
||||
KERNEL_DATA_ARRAY(packed_float3, attributes_float3)
|
||||
KERNEL_DATA_ARRAY(float4, attributes_float4)
|
||||
KERNEL_DATA_ARRAY(uchar4, attributes_uchar4)
|
||||
KERNEL_DATA_ARRAY(packed_normal, attributes_normal)
|
||||
|
||||
/* lights */
|
||||
KERNEL_DATA_ARRAY(KernelLightDistribution, light_distribution)
|
||||
KERNEL_DATA_ARRAY(KernelLight, lights)
|
||||
KERNEL_DATA_ARRAY(float2, light_background_marginal_cdf)
|
||||
KERNEL_DATA_ARRAY(float2, light_background_conditional_cdf)
|
||||
|
||||
/* light tree */
|
||||
KERNEL_DATA_ARRAY(KernelLightTreeNode, light_tree_nodes)
|
||||
KERNEL_DATA_ARRAY(KernelLightTreeEmitter, light_tree_emitters)
|
||||
KERNEL_DATA_ARRAY(uint, light_to_tree)
|
||||
KERNEL_DATA_ARRAY(uint, object_lookup_offset)
|
||||
KERNEL_DATA_ARRAY(uint, triangle_to_tree)
|
||||
|
||||
/* particles */
|
||||
KERNEL_DATA_ARRAY(KernelParticle, particles)
|
||||
|
||||
/* shaders */
|
||||
KERNEL_DATA_ARRAY(uint, svm_nodes)
|
||||
KERNEL_DATA_ARRAY(KernelShader, shaders)
|
||||
|
||||
/* lookup tables */
|
||||
KERNEL_DATA_ARRAY(float, lookup_table)
|
||||
|
||||
/* tabulated Sobol sample pattern */
|
||||
KERNEL_DATA_ARRAY(float, sample_pattern_lut)
|
||||
|
||||
/* ies lights */
|
||||
KERNEL_DATA_ARRAY(float, ies)
|
||||
|
||||
/* Volume. */
|
||||
KERNEL_DATA_ARRAY(KernelOctreeNode, volume_tree_nodes)
|
||||
KERNEL_DATA_ARRAY(KernelOctreeRoot, volume_tree_roots)
|
||||
KERNEL_DATA_ARRAY(int, volume_tree_root_ids)
|
||||
KERNEL_DATA_ARRAY(float, volume_step_size)
|
||||
|
||||
/* image textures */
|
||||
KERNEL_DATA_ARRAY(KernelImageTexture, image_textures)
|
||||
KERNEL_DATA_ARRAY_WRITABLE(KernelTileDescriptor, image_texture_tile_descriptors)
|
||||
KERNEL_DATA_ARRAY_WRITABLE(uint8_t, image_texture_tile_access_state)
|
||||
KERNEL_DATA_ARRAY(KernelImageUDIM, image_texture_udims)
|
||||
KERNEL_DATA_ARRAY(KernelImageInfo, image_info)
|
||||
|
||||
#undef KERNEL_DATA_ARRAY
|
||||
#undef KERNEL_DATA_ARRAY_WRITABLE
|
||||
265
blender-5.2.0/intern/cycles/kernel/data_template.h
Normal file
265
blender-5.2.0/intern/cycles/kernel/data_template.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifndef KERNEL_STRUCT_BEGIN
|
||||
# define KERNEL_STRUCT_BEGIN(name, parent)
|
||||
#endif
|
||||
#ifndef KERNEL_STRUCT_END
|
||||
# define KERNEL_STRUCT_END(name)
|
||||
#endif
|
||||
#ifndef KERNEL_STRUCT_MEMBER
|
||||
# define KERNEL_STRUCT_MEMBER(parent, type, name)
|
||||
#endif
|
||||
#ifndef KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
# define KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
#endif
|
||||
|
||||
/* Background. */
|
||||
|
||||
KERNEL_STRUCT_BEGIN(KernelBackground, background)
|
||||
/* xyz store direction, w the angle. float4 instead of float3 is used
|
||||
* to ensure consistent padding/alignment across devices. */
|
||||
KERNEL_STRUCT_MEMBER(background, float4, sun)
|
||||
KERNEL_STRUCT_MEMBER(background, int, use_sun_guiding)
|
||||
/* Only shader index. */
|
||||
KERNEL_STRUCT_MEMBER(background, int, surface_shader)
|
||||
KERNEL_STRUCT_MEMBER(background, int, volume_shader)
|
||||
KERNEL_STRUCT_MEMBER(background, int, transparent)
|
||||
KERNEL_STRUCT_MEMBER(background, float, transparent_roughness_squared_threshold)
|
||||
/* Sun sampling. */
|
||||
KERNEL_STRUCT_MEMBER(background, float, sun_weight)
|
||||
/* Importance map sampling. */
|
||||
KERNEL_STRUCT_MEMBER(background, float, map_weight)
|
||||
KERNEL_STRUCT_MEMBER(background, float, portal_weight)
|
||||
KERNEL_STRUCT_MEMBER(background, int, map_res_x)
|
||||
KERNEL_STRUCT_MEMBER(background, int, map_res_y)
|
||||
/* Ray differential used for generating the importance map. */
|
||||
KERNEL_STRUCT_MEMBER(background, float, map_dD)
|
||||
/* Multiple importance sampling. */
|
||||
KERNEL_STRUCT_MEMBER(background, int, use_mis)
|
||||
/* Light-group. */
|
||||
KERNEL_STRUCT_MEMBER(background, int, lightgroup)
|
||||
/* Object Index. */
|
||||
KERNEL_STRUCT_MEMBER(background, int, object_index)
|
||||
/* Padding. */
|
||||
KERNEL_STRUCT_MEMBER(background, int, pad1)
|
||||
KERNEL_STRUCT_END(KernelBackground)
|
||||
|
||||
/* BVH: own BVH2 if no native device acceleration struct used. */
|
||||
|
||||
KERNEL_STRUCT_BEGIN(KernelBVH, bvh)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, root)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, have_motion)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, have_curves)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, have_points)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, have_volumes)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, bvh_layout)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, use_bvh_steps)
|
||||
KERNEL_STRUCT_MEMBER(bvh, int, curve_subdivisions)
|
||||
KERNEL_STRUCT_END(KernelBVH)
|
||||
|
||||
/* Film. */
|
||||
|
||||
KERNEL_STRUCT_BEGIN(KernelFilm, film)
|
||||
/* XYZ to rendering color space transform. float4 instead of float3 to
|
||||
* ensure consistent padding/alignment across devices. */
|
||||
KERNEL_STRUCT_MEMBER(film, float4, xyz_to_r)
|
||||
KERNEL_STRUCT_MEMBER(film, float4, xyz_to_g)
|
||||
KERNEL_STRUCT_MEMBER(film, float4, xyz_to_b)
|
||||
KERNEL_STRUCT_MEMBER(film, float4, rgb_to_y)
|
||||
KERNEL_STRUCT_MEMBER(film, float4, white_xyz)
|
||||
/* Rec709 to rendering color space. */
|
||||
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_r)
|
||||
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_g)
|
||||
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_b)
|
||||
KERNEL_STRUCT_MEMBER(film, int, is_rec709)
|
||||
/* Exposure. */
|
||||
KERNEL_STRUCT_MEMBER(film, float, exposure)
|
||||
/* Passed used. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_flag)
|
||||
KERNEL_STRUCT_MEMBER(film, int, denoising_pass_flag)
|
||||
KERNEL_STRUCT_MEMBER(film, int, light_pass_flag)
|
||||
/* Pass offsets. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_stride)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_combined)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_depth)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_position)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_normal)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_roughness)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_motion)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_motion_weight)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_uv)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_object_id)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_material_id)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_color)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_glossy_color)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_transmission_color)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_indirect)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_glossy_indirect)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_transmission_indirect)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_indirect)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_direct)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_glossy_direct)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_transmission_direct)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_direct)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_scatter)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_scatter_denoised)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_transmit)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_transmit_denoised)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_majorant)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_volume_majorant_sample_count)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_emission)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_background)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_ao)
|
||||
KERNEL_STRUCT_MEMBER(film, float, pass_alpha_threshold)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher_sample_count)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher_matte)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_render_time)
|
||||
/* Cryptomatte. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, cryptomatte_passes)
|
||||
KERNEL_STRUCT_MEMBER(film, int, cryptomatte_depth)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_cryptomatte)
|
||||
/* Adaptive sampling. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_adaptive_aux_buffer)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_sample_count)
|
||||
/* Mist. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_mist)
|
||||
KERNEL_STRUCT_MEMBER(film, float, mist_start)
|
||||
KERNEL_STRUCT_MEMBER(film, float, mist_inv_depth)
|
||||
KERNEL_STRUCT_MEMBER(film, float, mist_falloff)
|
||||
/* Denoising. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_denoising_albedo)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_denoising_specular_albedo)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_denoising_normal)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_denoising_roughness)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_denoising_depth)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_denoising_backward_motion)
|
||||
KERNEL_STRUCT_MEMBER(film, int, denoising_pass_options_flag)
|
||||
/* AOVs. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_aov_color)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_aov_value)
|
||||
/* Light groups. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_lightgroup)
|
||||
/* Baking. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_bake_primitive)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_bake_seed)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_bake_differential)
|
||||
/* Shadow catcher. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, use_approximate_shadow_catcher)
|
||||
/* Path Guiding */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_guiding_color)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_guiding_probability)
|
||||
KERNEL_STRUCT_MEMBER(film, int, pass_guiding_avg_roughness)
|
||||
/* Padding. */
|
||||
KERNEL_STRUCT_MEMBER(film, int, pad1)
|
||||
KERNEL_STRUCT_END(KernelFilm)
|
||||
|
||||
/* Integrator. */
|
||||
|
||||
KERNEL_STRUCT_BEGIN(KernelIntegrator, integrator)
|
||||
/* Emission. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_direct_light)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_light_mis)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_light_tree)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, num_lights)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, num_distant_lights)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, num_background_lights)
|
||||
/* Portal sampling. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, num_portals)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, portal_offset)
|
||||
/* Flat light distribution. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, num_distribution)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_triangles)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_lights)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, light_inv_rr_threshold)
|
||||
/* Bounces. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, min_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, max_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, max_diffuse_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, max_glossy_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, max_transmission_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, max_volume_bounce)
|
||||
/* AO bounces. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, ao_bounces)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, ao_bounces_distance)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, ao_bounces_factor)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, ao_additive_factor)
|
||||
/* Transparency. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, transparent_min_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, transparent_max_bounce)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, transparent_shadows)
|
||||
/* Caustics. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, caustics_reflective)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, caustics_refractive)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, filter_glossy)
|
||||
/* Seed. */
|
||||
KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, seed)
|
||||
/* Clamp. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_direct)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_indirect)
|
||||
/* Caustics. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_caustics)
|
||||
/* Sampling pattern. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, sampling_pattern)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, scrambling_distance)
|
||||
/* Sobol pattern. */
|
||||
KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, tabulated_sobol_sequence_size)
|
||||
KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, sobol_index_mask)
|
||||
KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, blue_noise_sequence_length)
|
||||
/* Volume render. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_volumes)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, volume_ray_marching)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, volume_max_steps)
|
||||
/* Shadow catcher. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher)
|
||||
/* Closure filter. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, filter_closures)
|
||||
/* MIS debugging. */
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type)
|
||||
/* Path Guiding */
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, surface_guiding_probability)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, volume_guiding_probability)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, guiding_distribution_type)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, guiding_directional_sampling_type)
|
||||
KERNEL_STRUCT_MEMBER(integrator, float, guiding_roughness_threshold)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, train_guiding)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_surface_guiding)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_volume_guiding)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding_direct_light)
|
||||
KERNEL_STRUCT_MEMBER(integrator, int, use_guiding_mis_weights)
|
||||
|
||||
KERNEL_STRUCT_MEMBER(integrator, float2, pixel_jitter)
|
||||
KERNEL_STRUCT_END(KernelIntegrator)
|
||||
|
||||
/* Image. */
|
||||
|
||||
KERNEL_STRUCT_BEGIN(KernelImage, image)
|
||||
KERNEL_STRUCT_MEMBER(image, float, mip_bias)
|
||||
|
||||
/* Padding. */
|
||||
KERNEL_STRUCT_MEMBER(image, int, pad1)
|
||||
KERNEL_STRUCT_MEMBER(image, int, pad2)
|
||||
KERNEL_STRUCT_MEMBER(image, int, pad3)
|
||||
KERNEL_STRUCT_END(KernelImage)
|
||||
|
||||
/* SVM. For shader specialization. */
|
||||
|
||||
KERNEL_STRUCT_BEGIN(KernelSVMUsage, svm_usage)
|
||||
#define SHADER_NODE_TYPE(type) KERNEL_STRUCT_MEMBER(svm_usage, int, type)
|
||||
#define SHADER_NODE_TYPE_DERIVATIVE(type) \
|
||||
SHADER_NODE_TYPE(type) \
|
||||
SHADER_NODE_TYPE(type##_DERIVATIVE)
|
||||
#include "kernel/svm/node_types_template.h"
|
||||
KERNEL_STRUCT_END(KernelSVMUsage)
|
||||
|
||||
#undef KERNEL_STRUCT_BEGIN
|
||||
#undef KERNEL_STRUCT_MEMBER
|
||||
#undef KERNEL_STRUCT_MEMBER_DONT_SPECIALIZE
|
||||
#undef KERNEL_STRUCT_END
|
||||
70
blender-5.2.0/intern/cycles/kernel/device/cpu/CMakeLists.txt
Normal file
70
blender-5.2.0/intern/cycles/kernel/device/cpu/CMakeLists.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_CPU
|
||||
globals.cpp
|
||||
kernel.cpp
|
||||
kernel_avx2.cpp
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_CPU_HEADERS
|
||||
bvh.h
|
||||
compat.h
|
||||
image.h
|
||||
globals.h
|
||||
kernel.h
|
||||
kernel_arch.h
|
||||
kernel_arch_impl.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PUBLIC cycles_util
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# CPU module.
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
if(DEFINED CYCLES_KERNEL_FLAGS)
|
||||
set_source_files_properties(kernel.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}")
|
||||
endif()
|
||||
|
||||
if(CXX_HAS_AVX2 AND CXX_HAS_F16C)
|
||||
set_source_files_properties(kernel_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_F16C_FLAGS}")
|
||||
endif()
|
||||
|
||||
# Warnings to avoid using doubles in the kernel.
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
add_check_cxx_compiler_flags(
|
||||
CMAKE_CXX_FLAGS
|
||||
_has_cxxflag_float_conversion "-Werror=float-conversion"
|
||||
_has_cxxflag_double_promotion "-Werror=double-promotion"
|
||||
)
|
||||
unset(_has_cxxflag_float_conversion)
|
||||
unset(_has_cxxflag_double_promotion)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
list(APPEND LIB
|
||||
PUBLIC cycles_kernel_osl
|
||||
)
|
||||
endif()
|
||||
|
||||
cycles_add_library(cycles_kernel_cpu "${LIB}"
|
||||
${SRC_KERNEL_DEVICE_CPU}
|
||||
${SRC_KERNEL_DEVICE_CPU_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_cpu)
|
||||
|
||||
source_group("device\\cpu" FILES ${SRC_KERNEL_DEVICE_CPU} ${SRC_KERNEL_DEVICE_CPU_HEADERS})
|
||||
660
blender-5.2.0/intern/cycles/kernel/device/cpu/bvh.h
Normal file
660
blender-5.2.0/intern/cycles/kernel/device/cpu/bvh.h
Normal file
@@ -0,0 +1,660 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* CPU Embree implementation of ray-scene intersection. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <embree4/rtcore_geometry.h>
|
||||
#include <embree4/rtcore_ray.h>
|
||||
#include <embree4/rtcore_scene.h>
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
# include "kernel/device/oneapi/compat.h"
|
||||
# include "kernel/device/oneapi/globals.h"
|
||||
#else
|
||||
# include "kernel/device/cpu/compat.h"
|
||||
# include "kernel/device/cpu/globals.h"
|
||||
#endif
|
||||
|
||||
#include "kernel/bvh/intersect_filter.h"
|
||||
#include "kernel/bvh/types.h"
|
||||
#include "kernel/bvh/util.h"
|
||||
#include "kernel/geom/object.h"
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/integrator/state_util.h"
|
||||
#include "kernel/sample/lcg.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
using numhit_t = uint16_t;
|
||||
#else
|
||||
using numhit_t = uint32_t;
|
||||
#endif
|
||||
|
||||
/* Before Embree 4.4, the so-called Traversable functionality was exposed through Scene API.
|
||||
* So, in order to simplify code between different versions, we are defining the traversable class
|
||||
* and calls for older Embree versions as well. */
|
||||
#if RTC_VERSION < 40400
|
||||
# define RTCTraversable RTCScene
|
||||
# define rtcGetGeometryUserDataFromTraversable rtcGetGeometryUserDataFromScene
|
||||
# define rtcTraversableIntersect1 rtcIntersect1
|
||||
# define rtcTraversableOccluded1 rtcOccluded1
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
# define CYCLES_EMBREE_USED_FEATURES \
|
||||
(kernel_handler.get_specialization_constant<oneapi_embree_features>())
|
||||
#else
|
||||
# define CYCLES_EMBREE_USED_FEATURES \
|
||||
(RTCFeatureFlags)(RTC_FEATURE_FLAG_TRIANGLE | RTC_FEATURE_FLAG_INSTANCE | \
|
||||
RTC_FEATURE_FLAG_FILTER_FUNCTION_IN_ARGUMENTS | RTC_FEATURE_FLAG_POINT | \
|
||||
RTC_FEATURE_FLAG_MOTION_BLUR | RTC_FEATURE_FLAG_ROUND_CATMULL_ROM_CURVE | \
|
||||
RTC_FEATURE_FLAG_FLAT_CATMULL_ROM_CURVE | \
|
||||
RTC_FEATURE_FLAG_ROUND_LINEAR_CURVE)
|
||||
#endif
|
||||
|
||||
#define EMBREE_IS_HAIR(x) (x & 1)
|
||||
|
||||
/* Intersection context. */
|
||||
|
||||
struct CCLFirstHitContext : public RTCRayQueryContext {
|
||||
KernelGlobals kg;
|
||||
/* For avoiding self intersections */
|
||||
const Ray *ray;
|
||||
};
|
||||
|
||||
struct CCLShadowContext : public RTCRayQueryContext {
|
||||
#if defined(__KERNEL_ONEAPI__)
|
||||
ONEAPIKernelContext *oneapi_kernel_context;
|
||||
#else
|
||||
KernelGlobals kg;
|
||||
#endif
|
||||
|
||||
BVHShadowAllPayload *payload;
|
||||
};
|
||||
|
||||
struct CCLLocalContext : public RTCRayQueryContext {
|
||||
KernelGlobals kg;
|
||||
const Ray *ray;
|
||||
numhit_t max_hits;
|
||||
int local_object_id;
|
||||
LocalIntersection *local_isect;
|
||||
uint *lcg_state;
|
||||
bool is_sss;
|
||||
};
|
||||
|
||||
struct CCLVolumeContext : public RTCRayQueryContext {
|
||||
KernelGlobals kg;
|
||||
const Ray *ray;
|
||||
#ifdef __VOLUME_RECORD_ALL__
|
||||
numhit_t max_hits;
|
||||
#endif
|
||||
numhit_t num_hits;
|
||||
Intersection *vol_isect;
|
||||
};
|
||||
|
||||
/* Utilities. */
|
||||
|
||||
ccl_device_inline void kernel_embree_setup_ray(const Ray &ray,
|
||||
RTCRay &rtc_ray,
|
||||
const uint visibility)
|
||||
{
|
||||
rtc_ray.org_x = ray.P.x;
|
||||
rtc_ray.org_y = ray.P.y;
|
||||
rtc_ray.org_z = ray.P.z;
|
||||
rtc_ray.dir_x = ray.D.x;
|
||||
rtc_ray.dir_y = ray.D.y;
|
||||
rtc_ray.dir_z = ray.D.z;
|
||||
rtc_ray.tnear = ray.tmin;
|
||||
rtc_ray.tfar = ray.tmax;
|
||||
rtc_ray.time = ray.time;
|
||||
rtc_ray.mask = visibility;
|
||||
}
|
||||
|
||||
ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray,
|
||||
RTCRayHit &rayhit,
|
||||
const uint visibility)
|
||||
{
|
||||
kernel_embree_setup_ray(ray, rayhit.ray, visibility);
|
||||
rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
|
||||
rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
|
||||
}
|
||||
|
||||
ccl_device_inline int kernel_embree_get_hit_object(const RTCHit *hit)
|
||||
{
|
||||
return (hit->instID[0] != RTC_INVALID_GEOMETRY_ID ? hit->instID[0] : hit->geomID) / 2;
|
||||
}
|
||||
|
||||
ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg,
|
||||
const RTCHit *hit,
|
||||
const Ray *ray,
|
||||
const intptr_t prim_offset)
|
||||
{
|
||||
const int object = kernel_embree_get_hit_object(hit);
|
||||
|
||||
int prim;
|
||||
if ((ray->self.object == object) || (ray->self.light_object == object)) {
|
||||
prim = hit->primID + prim_offset;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool is_hair = hit->geomID & 1;
|
||||
if (is_hair) {
|
||||
prim = kernel_data_fetch(curve_segments, prim).prim;
|
||||
}
|
||||
|
||||
return intersection_skip_self_shadow(ray->self, object, prim);
|
||||
}
|
||||
|
||||
ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg,
|
||||
const RTCRay *ray,
|
||||
const RTCHit *hit,
|
||||
Intersection *isect,
|
||||
const intptr_t prim_offset)
|
||||
{
|
||||
isect->t = ray->tfar;
|
||||
isect->prim = hit->primID + prim_offset;
|
||||
isect->object = kernel_embree_get_hit_object(hit);
|
||||
|
||||
const bool is_hair = hit->geomID & 1;
|
||||
if (is_hair) {
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, isect->prim);
|
||||
isect->type = segment.type;
|
||||
isect->prim = segment.prim;
|
||||
isect->u = hit->u;
|
||||
isect->v = hit->v;
|
||||
}
|
||||
else {
|
||||
isect->type = kernel_data_fetch(objects, isect->object).primitive_type;
|
||||
isect->u = hit->u;
|
||||
isect->v = hit->v;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg,
|
||||
const RTCRay *ray,
|
||||
const RTCHit *hit,
|
||||
Intersection *isect)
|
||||
{
|
||||
intptr_t prim_offset;
|
||||
if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) {
|
||||
RTCTraversable inst_scene = (RTCTraversable)rtcGetGeometryUserDataFromTraversable(
|
||||
kernel_data.device_bvh, hit->instID[0]);
|
||||
prim_offset = intptr_t(rtcGetGeometryUserDataFromTraversable(inst_scene, hit->geomID));
|
||||
}
|
||||
else {
|
||||
prim_offset = intptr_t(
|
||||
rtcGetGeometryUserDataFromTraversable(kernel_data.device_bvh, hit->geomID));
|
||||
}
|
||||
kernel_embree_convert_hit(kg, ray, hit, isect, prim_offset);
|
||||
}
|
||||
|
||||
ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals kg,
|
||||
const RTCRay *ray,
|
||||
const RTCHit *hit,
|
||||
Intersection *isect,
|
||||
const int object,
|
||||
const intptr_t prim_offset)
|
||||
{
|
||||
isect->u = hit->u;
|
||||
isect->v = hit->v;
|
||||
isect->t = ray->tfar;
|
||||
isect->prim = hit->primID + prim_offset;
|
||||
isect->object = object;
|
||||
isect->type = kernel_data_fetch(objects, object).primitive_type;
|
||||
}
|
||||
|
||||
/* Ray filter functions. */
|
||||
|
||||
/* This gets called by Embree at every valid ray/object intersection.
|
||||
* Things like recording subsurface or shadow hits for later evaluation
|
||||
* as well as filtering for volume objects happen here.
|
||||
* Cycles' own BVH does that directly inside the traversal calls. */
|
||||
ccl_device_forceinline void kernel_embree_filter_intersection_func_impl(
|
||||
const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
/* Current implementation in Cycles assumes only single-ray intersection queries. */
|
||||
assert(args->N == 1);
|
||||
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLFirstHitContext *ctx = (CCLFirstHitContext *)(args->context);
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
KernelGlobalsGPU *kg = nullptr;
|
||||
#else
|
||||
const ThreadKernelGlobalsCPU *kg = ctx->kg;
|
||||
#endif
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
if (kernel_embree_is_self_intersection(
|
||||
kg, hit, cray, reinterpret_cast<intptr_t>(args->geometryUserPtr)))
|
||||
{
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, cray->self, kernel_embree_get_hit_object(hit))) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This gets called by Embree at every valid ray/object intersection.
|
||||
* Things like recording subsurface or shadow hits for later evaluation
|
||||
* as well as filtering for volume objects happen here.
|
||||
* Cycles' own BVH does that directly inside the traversal calls.
|
||||
*/
|
||||
ccl_device_forceinline void kernel_embree_filter_occluded_shadow_all_func_impl(
|
||||
const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
/* Current implementation in Cycles assumes only single-ray intersection queries. */
|
||||
assert(args->N == 1);
|
||||
|
||||
const RTCRay *ray = (RTCRay *)args->ray;
|
||||
const RTCHit *hit = (RTCHit *)args->hit;
|
||||
|
||||
CCLShadowContext *ctx = (CCLShadowContext *)(args->context);
|
||||
BVHShadowAllPayload &payload = *ctx->payload;
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
KernelGlobalsGPU *kg = nullptr;
|
||||
#else
|
||||
const ThreadKernelGlobalsCPU *kg = ctx->kg;
|
||||
#endif
|
||||
|
||||
Intersection isect;
|
||||
kernel_embree_convert_hit(
|
||||
kg, ray, hit, &isect, reinterpret_cast<intptr_t>(args->geometryUserPtr));
|
||||
|
||||
if (!bvh_shadow_all_anyhit_filter<ISECT_TEST_ALL & ~ISECT_TEST_VISIBILITY_FLAG>(
|
||||
kg, payload.state, payload, payload.base.ray_self, 0, isect))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
*args->valid = 0;
|
||||
}
|
||||
|
||||
ccl_device_forceinline void kernel_embree_filter_occluded_local_func_impl(
|
||||
const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
/* Current implementation in Cycles assumes only single-ray intersection queries. */
|
||||
assert(args->N == 1);
|
||||
|
||||
const RTCRay *ray = (RTCRay *)args->ray;
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLLocalContext *ctx = (CCLLocalContext *)(args->context);
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
KernelGlobalsGPU *kg = nullptr;
|
||||
#else
|
||||
const ThreadKernelGlobalsCPU *kg = ctx->kg;
|
||||
#endif
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
/* Check if it's hitting the correct object. */
|
||||
Intersection current_isect;
|
||||
if (ctx->is_sss) {
|
||||
kernel_embree_convert_sss_hit(kg,
|
||||
ray,
|
||||
hit,
|
||||
¤t_isect,
|
||||
ctx->local_object_id,
|
||||
reinterpret_cast<intptr_t>(args->geometryUserPtr));
|
||||
}
|
||||
else {
|
||||
kernel_embree_convert_hit(
|
||||
kg, ray, hit, ¤t_isect, reinterpret_cast<intptr_t>(args->geometryUserPtr));
|
||||
if (ctx->local_object_id != current_isect.object) {
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (intersection_skip_self_local(cray->self, current_isect.prim)) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* No intersection information requested, just return a hit. */
|
||||
if (ctx->max_hits == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore curves. */
|
||||
if (EMBREE_IS_HAIR(hit->geomID)) {
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
LocalIntersection *local_isect = ctx->local_isect;
|
||||
int hit_idx = 0;
|
||||
|
||||
if (ctx->lcg_state) {
|
||||
/* See triangle_intersect_subsurface() for the native equivalent. */
|
||||
for (int i = min((int)ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
|
||||
if (local_isect->hits[i].t == ray->tfar) {
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
local_isect->num_hits++;
|
||||
|
||||
if (local_isect->num_hits <= ctx->max_hits) {
|
||||
hit_idx = local_isect->num_hits - 1;
|
||||
}
|
||||
else {
|
||||
/* reservoir sampling: if we are at the maximum number of
|
||||
* hits, randomly replace element or skip it */
|
||||
hit_idx = lcg_step_uint(ctx->lcg_state) % local_isect->num_hits;
|
||||
|
||||
if (hit_idx >= ctx->max_hits) {
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Record closest intersection only. */
|
||||
if (local_isect->num_hits && current_isect.t > local_isect->hits[0].t) {
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
local_isect->num_hits = 1;
|
||||
}
|
||||
|
||||
/* record intersection */
|
||||
local_isect->hits[hit_idx] = current_isect;
|
||||
local_isect->Ng[hit_idx] = normalize(make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z));
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
}
|
||||
|
||||
ccl_device_forceinline void kernel_embree_filter_occluded_volume_all_func_impl(
|
||||
const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
/* Current implementation in Cycles assumes only single-ray intersection queries. */
|
||||
assert(args->N == 1);
|
||||
|
||||
const RTCRay *ray = (RTCRay *)args->ray;
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLVolumeContext *ctx = (CCLVolumeContext *)(args->context);
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
KernelGlobalsGPU *kg = nullptr;
|
||||
#else
|
||||
const ThreadKernelGlobalsCPU *kg = ctx->kg;
|
||||
#endif
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
#ifdef __VOLUME_RECORD_ALL__
|
||||
/* Append the intersection to the end of the array. */
|
||||
if (ctx->num_hits < ctx->max_hits) {
|
||||
#endif
|
||||
Intersection current_isect;
|
||||
kernel_embree_convert_hit(
|
||||
kg, ray, hit, ¤t_isect, reinterpret_cast<intptr_t>(args->geometryUserPtr));
|
||||
|
||||
if (bvh_volume_anyhit_triangle_filter<false>(
|
||||
kg, current_isect.object, current_isect.prim, cray->self, 0))
|
||||
{
|
||||
*args->valid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Intersection *isect = &ctx->vol_isect[ctx->num_hits];
|
||||
++ctx->num_hits;
|
||||
*isect = current_isect;
|
||||
#ifdef __VOLUME_RECORD_ALL__
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
/* Static wrappers so we can call the callbacks from out side the ONEAPIKernelContext class */
|
||||
RTC_SYCL_INDIRECTLY_CALLABLE static void ccl_always_inline
|
||||
kernel_embree_filter_intersection_func_static(const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLFirstHitContext *ctx = (CCLFirstHitContext *)(args->context);
|
||||
ONEAPIKernelContext *context = static_cast<ONEAPIKernelContext *>(ctx->kg);
|
||||
context->kernel_embree_filter_intersection_func_impl(args);
|
||||
}
|
||||
|
||||
RTC_SYCL_INDIRECTLY_CALLABLE static void ccl_always_inline
|
||||
kernel_embree_filter_occluded_shadow_all_func_static(const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLShadowContext *ctx = (CCLShadowContext *)(args->context);
|
||||
ONEAPIKernelContext *context = ctx->oneapi_kernel_context;
|
||||
context->kernel_embree_filter_occluded_shadow_all_func_impl(args);
|
||||
}
|
||||
|
||||
RTC_SYCL_INDIRECTLY_CALLABLE static void ccl_always_inline
|
||||
kernel_embree_filter_occluded_local_func_static(const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLLocalContext *ctx = (CCLLocalContext *)(args->context);
|
||||
ONEAPIKernelContext *context = static_cast<ONEAPIKernelContext *>(ctx->kg);
|
||||
context->kernel_embree_filter_occluded_local_func_impl(args);
|
||||
}
|
||||
|
||||
RTC_SYCL_INDIRECTLY_CALLABLE static void ccl_always_inline
|
||||
kernel_embree_filter_occluded_volume_all_func_static(const RTCFilterFunctionNArguments *args)
|
||||
{
|
||||
RTCHit *hit = (RTCHit *)args->hit;
|
||||
CCLVolumeContext *ctx = (CCLVolumeContext *)(args->context);
|
||||
ONEAPIKernelContext *context = static_cast<ONEAPIKernelContext *>(ctx->kg);
|
||||
context->kernel_embree_filter_occluded_volume_all_func_impl(args);
|
||||
}
|
||||
|
||||
# define kernel_embree_filter_intersection_func \
|
||||
ONEAPIKernelContext::kernel_embree_filter_intersection_func_static
|
||||
# define kernel_embree_filter_occluded_shadow_all_func \
|
||||
ONEAPIKernelContext::kernel_embree_filter_occluded_shadow_all_func_static
|
||||
# define kernel_embree_filter_occluded_local_func \
|
||||
ONEAPIKernelContext::kernel_embree_filter_occluded_local_func_static
|
||||
# define kernel_embree_filter_occluded_volume_all_func \
|
||||
ONEAPIKernelContext::kernel_embree_filter_occluded_volume_all_func_static
|
||||
#else
|
||||
# define kernel_embree_filter_intersection_func kernel_embree_filter_intersection_func_impl
|
||||
# define kernel_embree_filter_occluded_shadow_all_func \
|
||||
kernel_embree_filter_occluded_shadow_all_func_impl
|
||||
# define kernel_embree_filter_occluded_local_func kernel_embree_filter_occluded_local_func_impl
|
||||
# define kernel_embree_filter_occluded_volume_all_func \
|
||||
kernel_embree_filter_occluded_volume_all_func_impl
|
||||
#endif
|
||||
|
||||
/* Scene intersection. */
|
||||
|
||||
ccl_device_intersect bool kernel_embree_intersect(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility,
|
||||
ccl_private Intersection *isect)
|
||||
{
|
||||
isect->t = ray->tmax;
|
||||
CCLFirstHitContext ctx;
|
||||
rtcInitRayQueryContext(&ctx);
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(sirgienko): Cycles GPU back-ends passes nullptr to KernelGlobals and
|
||||
* uses global device allocation (CUDA, Optix, HIP) or passes all needed data
|
||||
* as a class context (Metal, oneAPI). So we need to pass this context here
|
||||
* in order to have an access to it later in Embree filter functions on GPU. */
|
||||
ctx.kg = (KernelGlobals)this;
|
||||
#else
|
||||
ctx.kg = kg;
|
||||
#endif
|
||||
|
||||
RTCRayHit ray_hit;
|
||||
ctx.ray = ray;
|
||||
kernel_embree_setup_rayhit(*ray, ray_hit, visibility);
|
||||
|
||||
RTCIntersectArguments args;
|
||||
rtcInitIntersectArguments(&args);
|
||||
args.filter = reinterpret_cast<RTCFilterFunctionN>(kernel_embree_filter_intersection_func);
|
||||
args.feature_mask = CYCLES_EMBREE_USED_FEATURES;
|
||||
args.context = &ctx;
|
||||
rtcTraversableIntersect1(kernel_data.device_bvh, &ray_hit, &args);
|
||||
if (ray_hit.hit.geomID == RTC_INVALID_GEOMETRY_ID ||
|
||||
ray_hit.hit.primID == RTC_INVALID_GEOMETRY_ID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
kernel_embree_convert_hit(kg, &ray_hit.ray, &ray_hit.hit, isect);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __BVH_LOCAL__
|
||||
ccl_device_intersect bool kernel_embree_intersect_local(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
const bool has_bvh = !(kernel_data_fetch(object_flag, local_object) &
|
||||
SD_OBJECT_TRANSFORM_APPLIED);
|
||||
CCLLocalContext ctx;
|
||||
rtcInitRayQueryContext(&ctx);
|
||||
# ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(sirgienko): Cycles GPU back-ends passes nullptr to KernelGlobals and
|
||||
* uses global device allocation (CUDA, Optix, HIP) or passes all needed data
|
||||
* as a class context (Metal, oneAPI). So we need to pass this context here
|
||||
* in order to have an access to it later in Embree filter functions on GPU. */
|
||||
ctx.kg = (KernelGlobals)this;
|
||||
# else
|
||||
ctx.kg = kg;
|
||||
# endif
|
||||
ctx.is_sss = has_bvh;
|
||||
ctx.lcg_state = lcg_state;
|
||||
ctx.max_hits = max_hits;
|
||||
ctx.ray = ray;
|
||||
ctx.local_isect = local_isect;
|
||||
if (local_isect) {
|
||||
local_isect->num_hits = 0;
|
||||
}
|
||||
ctx.local_object_id = local_object;
|
||||
RTCRay rtc_ray;
|
||||
kernel_embree_setup_ray(*ray, rtc_ray, PATH_RAY_VISIBILITY_ALL);
|
||||
|
||||
RTCOccludedArguments args;
|
||||
rtcInitOccludedArguments(&args);
|
||||
args.filter = reinterpret_cast<RTCFilterFunctionN>(kernel_embree_filter_occluded_local_func);
|
||||
args.feature_mask = CYCLES_EMBREE_USED_FEATURES;
|
||||
args.context = &ctx;
|
||||
|
||||
/* If this object has its own BVH, use it. */
|
||||
if (has_bvh) {
|
||||
float3 P = ray->P;
|
||||
float3 dir = ray->D;
|
||||
float3 idir = ray->D;
|
||||
# ifdef __OBJECT_MOTION__
|
||||
bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir);
|
||||
# else
|
||||
bvh_instance_push(kg, local_object, ray, &P, &dir, &idir);
|
||||
# endif
|
||||
|
||||
rtc_ray.org_x = P.x;
|
||||
rtc_ray.org_y = P.y;
|
||||
rtc_ray.org_z = P.z;
|
||||
rtc_ray.dir_x = dir.x;
|
||||
rtc_ray.dir_y = dir.y;
|
||||
rtc_ray.dir_z = dir.z;
|
||||
rtc_ray.tnear = ray->tmin;
|
||||
rtc_ray.tfar = ray->tmax;
|
||||
RTCTraversable scene = (RTCTraversable)rtcGetGeometryUserDataFromTraversable(
|
||||
kernel_data.device_bvh, local_object * 2);
|
||||
kernel_assert(scene);
|
||||
if (scene) {
|
||||
rtcTraversableOccluded1(scene, &rtc_ray, &args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
rtcTraversableOccluded1(kernel_data.device_bvh, &rtc_ray, &args);
|
||||
}
|
||||
|
||||
/* rtcOccluded1 sets tfar to -inf if a hit was found. */
|
||||
return (local_isect && local_isect->num_hits > 0) || (rtc_ray.tfar < 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
ccl_device_intersect void kernel_embree_intersect_shadow_all(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
BVHShadowAllPayload &payload)
|
||||
{
|
||||
CCLShadowContext ctx;
|
||||
rtcInitRayQueryContext(&ctx);
|
||||
# if defined(__KERNEL_ONEAPI__)
|
||||
ctx.oneapi_kernel_context = this;
|
||||
# else
|
||||
ctx.kg = kg;
|
||||
# endif
|
||||
ctx.payload = &payload;
|
||||
|
||||
RTCRay rtc_ray;
|
||||
kernel_embree_setup_ray(*ray, rtc_ray, payload.base.ray_visibility);
|
||||
|
||||
RTCOccludedArguments args;
|
||||
rtcInitOccludedArguments(&args);
|
||||
args.filter = reinterpret_cast<RTCFilterFunctionN>(
|
||||
kernel_embree_filter_occluded_shadow_all_func);
|
||||
args.feature_mask = CYCLES_EMBREE_USED_FEATURES;
|
||||
args.context = &ctx;
|
||||
|
||||
rtcTraversableOccluded1(kernel_data.device_bvh, &rtc_ray, &args);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
ccl_device_intersect uint kernel_embree_intersect_volume(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
# ifdef __VOLUME_RECORD_ALL__
|
||||
const uint max_hits,
|
||||
# endif
|
||||
const uint visibility)
|
||||
{
|
||||
CCLVolumeContext ctx;
|
||||
rtcInitRayQueryContext(&ctx);
|
||||
# ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(sirgienko) Cycles GPU back-ends passes nullptr to KernelGlobals and
|
||||
* uses global device allocation (CUDA, Optix, HIP) or passes all needed data
|
||||
* as a class context (Metal, oneAPI). So we need to pass this context here
|
||||
* in order to have an access to it later in Embree filter functions on GPU. */
|
||||
ctx.kg = (KernelGlobals)this;
|
||||
# else
|
||||
ctx.kg = kg;
|
||||
# endif
|
||||
ctx.vol_isect = isect;
|
||||
# ifdef __VOLUME_RECORD_ALL__
|
||||
ctx.max_hits = numhit_t(max_hits);
|
||||
# endif
|
||||
ctx.num_hits = numhit_t(0);
|
||||
ctx.ray = ray;
|
||||
RTCRay rtc_ray;
|
||||
kernel_embree_setup_ray(*ray, rtc_ray, visibility);
|
||||
RTCOccludedArguments args;
|
||||
rtcInitOccludedArguments(&args);
|
||||
args.filter = reinterpret_cast<RTCFilterFunctionN>(
|
||||
kernel_embree_filter_occluded_volume_all_func);
|
||||
args.feature_mask = CYCLES_EMBREE_USED_FEATURES;
|
||||
args.context = &ctx;
|
||||
rtcTraversableOccluded1(kernel_data.device_bvh, &rtc_ray, &args);
|
||||
return ctx.num_hits;
|
||||
}
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
22
blender-5.2.0/intern/cycles/kernel/device/cpu/compat.h
Normal file
22
blender-5.2.0/intern/cycles/kernel/device/cpu/compat.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Release kernel has too much false-positive maybe-uninitialized warnings,
|
||||
* which makes it possible to miss actual warnings.
|
||||
*/
|
||||
#if (defined(__GNUC__) && !defined(__clang__)) && defined(NDEBUG)
|
||||
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
# pragma GCC diagnostic ignored "-Wuninitialized"
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Assertions inside the kernel only work for the CPU device, so we wrap it in
|
||||
* a macro which is empty for other devices */
|
||||
|
||||
#define kernel_assert(cond) assert(cond)
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
43
blender-5.2.0/intern/cycles/kernel/device/cpu/globals.cpp
Normal file
43
blender-5.2.0/intern/cycles/kernel/device/cpu/globals.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "kernel/device/cpu/globals.h"
|
||||
#include "kernel/osl/globals.h"
|
||||
|
||||
#include "util/guiding.h" // IWYU pragma: keep
|
||||
#include "util/profiling.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ThreadKernelGlobalsCPU::ThreadKernelGlobalsCPU(const KernelGlobalsCPU &kernel_globals,
|
||||
OSLGlobals *osl_globals,
|
||||
Profiler &cpu_profiler,
|
||||
const int thread_index)
|
||||
: KernelGlobalsCPU(kernel_globals),
|
||||
#ifdef WITH_OSL
|
||||
osl(osl_globals, thread_index),
|
||||
#endif
|
||||
cpu_profiler_(cpu_profiler)
|
||||
{
|
||||
#ifndef WITH_OSL
|
||||
(void)thread_index;
|
||||
(void)osl_globals;
|
||||
#endif
|
||||
|
||||
#if defined(WITH_PATH_GUIDING)
|
||||
opgl_path_segment_storage = make_unique<openpgl::cpp::PathSegmentStorage>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadKernelGlobalsCPU::start_profiling()
|
||||
{
|
||||
cpu_profiler_.add_state(&profiler);
|
||||
}
|
||||
|
||||
void ThreadKernelGlobalsCPU::stop_profiling()
|
||||
{
|
||||
cpu_profiler_.remove_state(&profiler);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
113
blender-5.2.0/intern/cycles/kernel/device/cpu/globals.h
Normal file
113
blender-5.2.0/intern/cycles/kernel/device/cpu/globals.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/util/profiler.h"
|
||||
|
||||
#ifdef __OSL__
|
||||
# include "kernel/osl/globals.h"
|
||||
#endif
|
||||
|
||||
#include "util/guiding.h" // IWYU pragma: keep
|
||||
#include "util/types_image.h" // IWYU pragma: keep
|
||||
#include "util/unique_ptr.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct OSLGlobals;
|
||||
|
||||
/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in
|
||||
* the kernel, to access constant data. These are all stored as flat arrays.
|
||||
* these are really just standard arrays. We can't use actually globals because
|
||||
* multiple renders may be running inside the same process. */
|
||||
|
||||
/* Array for kernel data, with size to be able to assert on invalid data access. */
|
||||
template<typename T> struct kernel_array {
|
||||
const ccl_always_inline T &fetch(const int index) const
|
||||
{
|
||||
kernel_assert(index >= 0 && index < width);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
ccl_always_inline void write(const int index, const T &value) const
|
||||
{
|
||||
data[index] = value;
|
||||
}
|
||||
|
||||
T *data = nullptr;
|
||||
int width = 0;
|
||||
};
|
||||
|
||||
/* Constant globals shared between all threads. */
|
||||
struct KernelGlobalsCPU {
|
||||
#define KERNEL_DATA_ARRAY(type, name) kernel_array<const type> name;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) kernel_array<type> name;
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
KernelData data = {};
|
||||
|
||||
KernelImageLoadRequestedCPU image_load_requested_cpu;
|
||||
|
||||
ProfilingState profiler;
|
||||
};
|
||||
|
||||
/* Per-thread global state.
|
||||
*
|
||||
* To avoid pointer indirection, the constant globals are copied to each thread.
|
||||
*
|
||||
* This may not be ideal for cache pressure. Alternative would be to pass an
|
||||
* additional thread index to every function, and potentially to make the shared
|
||||
* part an actual global variable. That would match the GPU more closely, but
|
||||
* also require mutex locks for multiple Cycles instances. */
|
||||
struct ThreadKernelGlobalsCPU : public KernelGlobalsCPU {
|
||||
ThreadKernelGlobalsCPU(const KernelGlobalsCPU &kernel_globals,
|
||||
OSLGlobals *osl_globals_memory,
|
||||
Profiler &cpu_profiler,
|
||||
const int thread_index);
|
||||
|
||||
ThreadKernelGlobalsCPU(ThreadKernelGlobalsCPU &other) = delete;
|
||||
ThreadKernelGlobalsCPU(ThreadKernelGlobalsCPU &&other) noexcept = default;
|
||||
ThreadKernelGlobalsCPU &operator=(const ThreadKernelGlobalsCPU &other) = delete;
|
||||
ThreadKernelGlobalsCPU &operator=(ThreadKernelGlobalsCPU &&other) = delete;
|
||||
|
||||
void start_profiling();
|
||||
void stop_profiling();
|
||||
|
||||
#ifdef __OSL__
|
||||
OSLThreadData osl;
|
||||
#endif
|
||||
|
||||
#if defined(__PATH_GUIDING__)
|
||||
/* Pointers to shared global data structures. */
|
||||
openpgl::cpp::SampleStorage *opgl_sample_data_storage = nullptr;
|
||||
openpgl::cpp::Field *opgl_guiding_field = nullptr;
|
||||
|
||||
/* Local data structures owned by the thread. */
|
||||
unique_ptr<openpgl::cpp::PathSegmentStorage> opgl_path_segment_storage;
|
||||
unique_ptr<openpgl::cpp::SurfaceSamplingDistribution> opgl_surface_sampling_distribution;
|
||||
unique_ptr<openpgl::cpp::VolumeSamplingDistribution> opgl_volume_sampling_distribution;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Profiler &cpu_profiler_;
|
||||
};
|
||||
|
||||
using KernelGlobals = const ThreadKernelGlobalsCPU *;
|
||||
|
||||
/* Abstraction macros */
|
||||
#define kernel_data_fetch(name, index) (kg->name.fetch(index))
|
||||
#define kernel_data_write(name, index, value) (kg->name.write(index, value))
|
||||
#define kernel_data_array(name) (kg->name.data)
|
||||
#define kernel_data (kg->data)
|
||||
#if defined(WITH_PATH_GUIDING)
|
||||
# define guiding_guiding_field kg->opgl_guiding_field
|
||||
# define guiding_ssd kg->opgl_surface_sampling_distribution
|
||||
# define guiding_vsd kg->opgl_volume_sampling_distribution
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
418
blender-5.2.0/intern/cycles/kernel/device/cpu/image.h
Normal file
418
blender-5.2.0/intern/cycles/kernel/device/cpu/image.h
Normal file
@@ -0,0 +1,418 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/device/cpu/compat.h"
|
||||
#include "kernel/device/cpu/globals.h"
|
||||
#include "kernel/util/image_2d.h"
|
||||
|
||||
#include "util/defines.h"
|
||||
#include "util/half.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Make template functions private so symbols don't conflict between kernels with different
|
||||
* instruction sets. */
|
||||
namespace {
|
||||
|
||||
#define SET_CUBIC_SPLINE_WEIGHTS(u, t) \
|
||||
{ \
|
||||
u[0] = (((-1.0f / 6.0f) * t + 0.5f) * t - 0.5f) * t + (1.0f / 6.0f); \
|
||||
u[1] = ((0.5f * t - 1.0f) * t) * t + (2.0f / 3.0f); \
|
||||
u[2] = ((-0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f / 6.0f); \
|
||||
u[3] = (1.0f / 6.0f) * t * t * t; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
ccl_device_inline float frac(const float x, int *ix)
|
||||
{
|
||||
int i = float_to_int(x) - ((x < 0.0f) ? 1 : 0);
|
||||
*ix = i;
|
||||
return x - (float)i;
|
||||
}
|
||||
|
||||
template<typename TexT, typename OutT = float4> struct ImageInterpolator {
|
||||
|
||||
static ccl_always_inline OutT zero()
|
||||
{
|
||||
if constexpr (std::is_same_v<OutT, float4>) {
|
||||
return zero_float4();
|
||||
}
|
||||
else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static ccl_always_inline float4 read(const float4 r)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
static ccl_always_inline float4 read(const uchar4 r)
|
||||
{
|
||||
const 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(const uchar r)
|
||||
{
|
||||
return r * (1.0f / 255.0f);
|
||||
}
|
||||
|
||||
static ccl_always_inline float read(const float r)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
static ccl_always_inline float4 read(half4 r)
|
||||
{
|
||||
return half4_to_float4_image(r);
|
||||
}
|
||||
|
||||
static ccl_always_inline float read(half r)
|
||||
{
|
||||
return half_to_float_image(r);
|
||||
}
|
||||
|
||||
static ccl_always_inline float read(const uint16_t r)
|
||||
{
|
||||
return r * (1.0f / 65535.0f);
|
||||
}
|
||||
|
||||
static ccl_always_inline float4 read(ushort4 r)
|
||||
{
|
||||
const 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, const int x, int y, const int width, const 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, const int x, int y, const int width, const int height)
|
||||
{
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) {
|
||||
return zero();
|
||||
}
|
||||
return read(data[y * width + x]);
|
||||
}
|
||||
|
||||
static ccl_always_inline int wrap_periodic(int x, const int width)
|
||||
{
|
||||
x %= width;
|
||||
if (x < 0) {
|
||||
x += width;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static ccl_always_inline int wrap_clamp(const int x, const int width)
|
||||
{
|
||||
return clamp(x, 0, width - 1);
|
||||
}
|
||||
|
||||
static ccl_always_inline int wrap_mirror(const int x, const int width)
|
||||
{
|
||||
const int m = abs(x + (x < 0)) % (2 * width);
|
||||
if (m >= width) {
|
||||
return 2 * width - m - 1;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ******** 2D interpolation ******** */
|
||||
|
||||
static ccl_always_inline OutT interp_closest(const KernelImageInfo &info, const float x, float y)
|
||||
{
|
||||
const int width = info.width;
|
||||
const int height = info.height;
|
||||
int ix, iy;
|
||||
frac(x, &ix);
|
||||
frac(y, &iy);
|
||||
switch (info.extension) {
|
||||
case EXTENSION_REPEAT:
|
||||
ix = wrap_periodic(ix, width);
|
||||
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();
|
||||
}
|
||||
break;
|
||||
case EXTENSION_EXTEND:
|
||||
ix = wrap_clamp(ix, width);
|
||||
iy = wrap_clamp(iy, height);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
ix = wrap_mirror(ix, width);
|
||||
iy = wrap_mirror(iy, height);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
}
|
||||
|
||||
const TexT *data = (const TexT *)info.data;
|
||||
return read(data, ix, iy, width, height);
|
||||
}
|
||||
|
||||
static ccl_always_inline OutT interp_linear(const KernelImageInfo &info, const float x, float y)
|
||||
{
|
||||
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;
|
||||
const float tx = frac(x - 0.5f, &ix);
|
||||
const float ty = frac(y - 0.5f, &iy);
|
||||
const TexT *data = (const TexT *)info.data;
|
||||
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
nix = wrap_mirror(ix + 1, width);
|
||||
ix = wrap_mirror(ix, width);
|
||||
niy = wrap_mirror(iy + 1, height);
|
||||
iy = wrap_mirror(iy, height);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
}
|
||||
|
||||
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 KernelImageInfo &info, const float x, float y)
|
||||
{
|
||||
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;
|
||||
const float tx = frac(x - 0.5f, &ix);
|
||||
const float ty = frac(y - 0.5f, &iy);
|
||||
|
||||
int pix, piy;
|
||||
int nix, niy;
|
||||
int 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);
|
||||
piy = wrap_periodic(iy - 1, height);
|
||||
niy = wrap_periodic(iy + 1, height);
|
||||
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;
|
||||
niy = iy + 1;
|
||||
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);
|
||||
niy = wrap_clamp(iy + 1, height);
|
||||
nniy = wrap_clamp(iy + 2, height);
|
||||
iy = wrap_clamp(iy, height);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
pix = wrap_mirror(ix - 1, width);
|
||||
nix = wrap_mirror(ix + 1, width);
|
||||
nnix = wrap_mirror(ix + 2, width);
|
||||
ix = wrap_mirror(ix, width);
|
||||
|
||||
piy = wrap_mirror(iy - 1, height);
|
||||
niy = wrap_mirror(iy + 1, height);
|
||||
nniy = wrap_mirror(iy + 2, height);
|
||||
iy = wrap_mirror(iy, height);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
#define DATA(x, y) (read_clip(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)))
|
||||
|
||||
SET_CUBIC_SPLINE_WEIGHTS(u, tx);
|
||||
SET_CUBIC_SPLINE_WEIGHTS(v, ty);
|
||||
|
||||
/* Actual interpolation. */
|
||||
return TERM(0) + TERM(1) + TERM(2) + TERM(3);
|
||||
#undef TERM
|
||||
#undef DATA
|
||||
}
|
||||
|
||||
static ccl_always_inline OutT interp(const KernelImageInfo &info, const float x, float y)
|
||||
{
|
||||
switch (info.interpolation) {
|
||||
case INTERPOLATION_CLOSEST:
|
||||
return interp_closest(info, x, y);
|
||||
case INTERPOLATION_LINEAR:
|
||||
return interp_linear(info, x, y);
|
||||
default:
|
||||
return interp_cubic(info, x, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#undef SET_CUBIC_SPLINE_WEIGHTS
|
||||
|
||||
ccl_device float4 kernel_image_interp(KernelGlobals kg,
|
||||
ShaderData *sd,
|
||||
const int image_texture_id,
|
||||
dual2 uv)
|
||||
{
|
||||
if (image_texture_id == KERNEL_IMAGE_NONE) {
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
const ccl_global KernelImageTexture &tex = kernel_data_fetch(image_textures, image_texture_id);
|
||||
const ccl_global KernelImageInfo *info;
|
||||
|
||||
float2 xy = zero_float2();
|
||||
|
||||
if (tex.tile_descriptor_offset != UINT_MAX) {
|
||||
/* Wrapping. */
|
||||
if (!kernel_image_tile_wrap(ExtensionType(tex.extension), uv.val)) {
|
||||
return zero_float4();
|
||||
}
|
||||
|
||||
/* Tile mapping */
|
||||
const KernelTileDescriptor tile_descriptor = kernel_image_tile_map(
|
||||
kg, sd, tex, image_texture_id, uv, xy);
|
||||
|
||||
if (!kernel_tile_descriptor_loaded(tile_descriptor)) {
|
||||
return (tile_descriptor == KERNEL_TILE_LOAD_FAILED) ? IMAGE_MISSING_RGBA : tex.average_color;
|
||||
}
|
||||
|
||||
info = &kernel_data_fetch(image_info, kernel_tile_descriptor_image_info_id(tile_descriptor));
|
||||
}
|
||||
else {
|
||||
/* Full image sampling. */
|
||||
if (tex.image_info_id == KERNEL_IMAGE_NONE) {
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
|
||||
/* Convert to pixel space. */
|
||||
info = &kernel_data_fetch(image_info, tex.image_info_id);
|
||||
xy = make_float2(uv.val.x * info->width, uv.val.y * info->height);
|
||||
}
|
||||
|
||||
if (UNLIKELY(!info->data)) {
|
||||
return zero_float4();
|
||||
}
|
||||
|
||||
switch (info->data_type) {
|
||||
case IMAGE_DATA_TYPE_HALF: {
|
||||
const float f = ImageInterpolator<half, float>::interp(*info, xy.x, xy.y);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
case IMAGE_DATA_TYPE_BYTE: {
|
||||
const float f = ImageInterpolator<uchar, float>::interp(*info, xy.x, xy.y);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
case IMAGE_DATA_TYPE_USHORT: {
|
||||
const float f = ImageInterpolator<uint16_t, float>::interp(*info, xy.x, xy.y);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
case IMAGE_DATA_TYPE_FLOAT: {
|
||||
const float f = ImageInterpolator<float, float>::interp(*info, xy.x, xy.y);
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
case IMAGE_DATA_TYPE_HALF4:
|
||||
return ImageInterpolator<half4>::interp(*info, xy.x, xy.y);
|
||||
case IMAGE_DATA_TYPE_BYTE4:
|
||||
return ImageInterpolator<uchar4>::interp(*info, xy.x, xy.y);
|
||||
case IMAGE_DATA_TYPE_USHORT4:
|
||||
return ImageInterpolator<ushort4>::interp(*info, xy.x, xy.y);
|
||||
case IMAGE_DATA_TYPE_FLOAT4:
|
||||
return ImageInterpolator<float4>::interp(*info, xy.x, xy.y);
|
||||
default:
|
||||
assert(0);
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_forceinline float4 kernel_image_interp_with_udim(KernelGlobals kg,
|
||||
ShaderData *sd,
|
||||
const int udim_id,
|
||||
dual2 uv)
|
||||
{
|
||||
const int image_texture_id = kernel_image_udim_map(kg, udim_id, uv.val);
|
||||
if (image_texture_id == KERNEL_IMAGE_NONE) {
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
|
||||
return kernel_image_interp(kg, sd, image_texture_id, uv);
|
||||
}
|
||||
|
||||
} /* Namespace. */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
86
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel.cpp
Normal file
86
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* CPU kernel entry points */
|
||||
|
||||
/* On x86-64, our minimum is SSE4.2, so avoid the extra kernel and compile this
|
||||
* one with SSE4.2 intrinsics.
|
||||
*/
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
# define __KERNEL_SSE__
|
||||
# define __KERNEL_SSE2__
|
||||
# define __KERNEL_SSE3__
|
||||
# define __KERNEL_SSSE3__
|
||||
# define __KERNEL_SSE42__
|
||||
#endif
|
||||
|
||||
/* When building kernel for native machine detect kernel features from the flags
|
||||
* set by compiler.
|
||||
*/
|
||||
#ifdef WITH_KERNEL_NATIVE
|
||||
# ifdef __SSE4_2__
|
||||
# ifndef __KERNEL_SSE42__
|
||||
# define __KERNEL_SSE42__
|
||||
# endif
|
||||
# endif
|
||||
# ifdef __AVX__
|
||||
# ifndef __KERNEL_SSE__
|
||||
# define __KERNEL_SSE__
|
||||
# endif
|
||||
# define __KERNEL_AVX__
|
||||
# endif
|
||||
# ifdef __AVX2__
|
||||
# ifndef __KERNEL_SSE__
|
||||
# define __KERNEL_SSE__
|
||||
# endif
|
||||
# define __KERNEL_AVX2__
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* quiet unused define warnings */
|
||||
#if defined(__KERNEL_SSE2__)
|
||||
/* do nothing */
|
||||
#endif
|
||||
|
||||
#include "kernel/device/cpu/globals.h"
|
||||
|
||||
#include "kernel/device/cpu/kernel.h"
|
||||
#define KERNEL_ARCH cpu
|
||||
#include "kernel/device/cpu/kernel_arch_impl.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Memory Copy */
|
||||
|
||||
void kernel_const_copy(KernelGlobalsCPU *kg, const char *name, void *host, size_t /*unused*/)
|
||||
{
|
||||
if (strcmp(name, "data") == 0) {
|
||||
kg->data = *(KernelData *)host;
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_global_memory_copy(KernelGlobalsCPU *kg,
|
||||
const char *name,
|
||||
void *mem,
|
||||
const size_t size)
|
||||
{
|
||||
if (false) {
|
||||
}
|
||||
|
||||
#define KERNEL_DATA_ARRAY(type, tname) \
|
||||
else if (strcmp(name, #tname) == 0) { \
|
||||
kg->tname.data = (type *)mem; \
|
||||
kg->tname.width = size; \
|
||||
}
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
41
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel.h
Normal file
41
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* CPU Kernel Interface */
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "util/half.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#define KERNEL_NAME_JOIN(x, y, z) x##_##y##_##z
|
||||
#define KERNEL_NAME_EVAL(arch, name) KERNEL_NAME_JOIN(kernel, arch, name)
|
||||
#define KERNEL_FUNCTION_FULL_NAME(name) KERNEL_NAME_EVAL(KERNEL_ARCH, name)
|
||||
|
||||
struct IntegratorStateCPU;
|
||||
struct KernelGlobalsCPU;
|
||||
struct KernelData;
|
||||
|
||||
KernelGlobalsCPU *kernel_globals_create();
|
||||
void kernel_globals_free(KernelGlobalsCPU *kg);
|
||||
|
||||
void *kernel_osl_memory(const KernelGlobalsCPU *kg);
|
||||
bool kernel_osl_use(const KernelGlobalsCPU *kg);
|
||||
|
||||
void kernel_const_copy(KernelGlobalsCPU *kg, const char *name, void *host, const size_t size);
|
||||
void kernel_global_memory_copy(KernelGlobalsCPU *kg,
|
||||
const char *name,
|
||||
void *mem,
|
||||
const size_t size);
|
||||
|
||||
#define KERNEL_ARCH cpu
|
||||
#include "kernel/device/cpu/kernel_arch.h"
|
||||
|
||||
#define KERNEL_ARCH cpu_avx2
|
||||
#include "kernel/device/cpu/kernel_arch.h"
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
148
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel_arch.h
Normal file
148
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel_arch.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Templated common declaration part of all CPU kernels. */
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Integrator.
|
||||
*/
|
||||
|
||||
#define KERNEL_INTEGRATOR_FUNCTION(name) \
|
||||
void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \
|
||||
const ThreadKernelGlobalsCPU *ccl_restrict kg, IntegratorStateCPU *state)
|
||||
|
||||
#define KERNEL_INTEGRATOR_SHADE_FUNCTION(name) \
|
||||
void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \
|
||||
const ThreadKernelGlobalsCPU *ccl_restrict kg, \
|
||||
IntegratorStateCPU *state, \
|
||||
ccl_global float *render_buffer)
|
||||
|
||||
#define KERNEL_INTEGRATOR_INIT_FUNCTION(name) \
|
||||
bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \
|
||||
const ThreadKernelGlobalsCPU *ccl_restrict kg, \
|
||||
IntegratorStateCPU *state, \
|
||||
KernelWorkTile *tile, \
|
||||
ccl_global float *render_buffer)
|
||||
|
||||
KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_camera);
|
||||
KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_bake);
|
||||
KERNEL_INTEGRATOR_SHADE_FUNCTION(megakernel);
|
||||
|
||||
#undef KERNEL_INTEGRATOR_FUNCTION
|
||||
#undef KERNEL_INTEGRATOR_INIT_FUNCTION
|
||||
#undef KERNEL_INTEGRATOR_SHADE_FUNCTION
|
||||
|
||||
#define KERNEL_FILM_CONVERT_FUNCTION(name) \
|
||||
void KERNEL_FUNCTION_FULL_NAME(film_convert_##name)(const KernelFilmConvert *kfilm_convert, \
|
||||
const float *buffer, \
|
||||
float *pixel, \
|
||||
const int width, \
|
||||
const int buffer_stride, \
|
||||
const int pixel_stride); \
|
||||
void KERNEL_FUNCTION_FULL_NAME(film_convert_half_rgba_##name)( \
|
||||
const KernelFilmConvert *kfilm_convert, \
|
||||
const float *buffer, \
|
||||
half4 *pixel, \
|
||||
const int width, \
|
||||
const int buffer_stride);
|
||||
|
||||
KERNEL_FILM_CONVERT_FUNCTION(depth)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(mist)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(sample_count)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(volume_majorant)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(float)
|
||||
|
||||
KERNEL_FILM_CONVERT_FUNCTION(light_path)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(rgbe)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(float3)
|
||||
|
||||
KERNEL_FILM_CONVERT_FUNCTION(motion)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(cryptomatte)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(shadow_catcher)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(shadow_catcher_matte_with_shadow)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(combined)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(float4)
|
||||
|
||||
#undef KERNEL_FILM_CONVERT_FUNCTION
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Shader evaluation.
|
||||
*/
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset);
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset);
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)(
|
||||
const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset);
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_volume_density)(const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset);
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Adaptive sampling.
|
||||
*/
|
||||
|
||||
bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)(
|
||||
const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int y,
|
||||
const float threshold,
|
||||
const int reset,
|
||||
const int offset,
|
||||
int stride);
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int y,
|
||||
const int start_x,
|
||||
const int width,
|
||||
const int offset,
|
||||
int stride);
|
||||
void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int start_y,
|
||||
const int height,
|
||||
const int offset,
|
||||
int stride);
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Cryptomatte.
|
||||
*/
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
int pixel_index);
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Volume Scattering Probability Guiding.
|
||||
*/
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(volume_guiding_filter_x)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int y,
|
||||
const int center_x,
|
||||
const int min_x,
|
||||
const int max_x,
|
||||
const int offset,
|
||||
int stride);
|
||||
void KERNEL_FUNCTION_FULL_NAME(volume_guiding_filter_y)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int center_y,
|
||||
const int height,
|
||||
const int offset,
|
||||
int stride);
|
||||
|
||||
#undef KERNEL_ARCH
|
||||
401
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel_arch_impl.h
Normal file
401
blender-5.2.0/intern/cycles/kernel/device/cpu/kernel_arch_impl.h
Normal file
@@ -0,0 +1,401 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Templated common implementation part of all CPU kernels.
|
||||
*
|
||||
* The idea is that particular `.cpp` files sets needed optimization flags and
|
||||
* simply includes this file without worry of copying actual implementation over.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// clang-format off
|
||||
#include "kernel/device/cpu/compat.h"
|
||||
|
||||
#ifndef KERNEL_STUB
|
||||
# include "kernel/globals.h"
|
||||
|
||||
# include "kernel/device/cpu/image.h"
|
||||
|
||||
# include "kernel/integrator/state.h"
|
||||
# include "kernel/integrator/state_flow.h"
|
||||
# include "kernel/integrator/state_util.h"
|
||||
|
||||
# include "kernel/integrator/init_from_camera.h"
|
||||
# include "kernel/integrator/init_from_bake.h"
|
||||
# include "kernel/integrator/megakernel.h"
|
||||
|
||||
# include "kernel/film/adaptive_sampling.h"
|
||||
# include "kernel/film/cryptomatte_passes.h"
|
||||
# include "kernel/film/read.h"
|
||||
# include "kernel/film/volume_guiding_denoise.h"
|
||||
|
||||
# include "kernel/bake/bake.h"
|
||||
|
||||
#else
|
||||
# define STUB_ASSERT(arch, name) \
|
||||
assert(!(#name " kernel stub for architecture " #arch " was called!"))
|
||||
#endif /* KERNEL_STUB */
|
||||
// clang-format on
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Integrator.
|
||||
*/
|
||||
|
||||
#ifdef KERNEL_STUB
|
||||
# define KERNEL_INVOKE(name, ...) (STUB_ASSERT(KERNEL_ARCH, name), 0)
|
||||
#else
|
||||
# define KERNEL_INVOKE(name, ...) integrator_##name(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* TODO: Either use something like get_work_pixel(), or simplify tile which is passed here, so
|
||||
* that it does not contain unused fields. */
|
||||
#define DEFINE_INTEGRATOR_INIT_KERNEL(name) \
|
||||
bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const ThreadKernelGlobalsCPU *kg, \
|
||||
IntegratorStateCPU *state, \
|
||||
KernelWorkTile *tile, \
|
||||
ccl_global float *render_buffer) \
|
||||
{ \
|
||||
(void)kg; \
|
||||
(void)state; \
|
||||
(void)tile; \
|
||||
(void)render_buffer; \
|
||||
return KERNEL_INVOKE( \
|
||||
name, kg, state, tile, render_buffer, tile->x, tile->y, tile->start_sample); \
|
||||
}
|
||||
|
||||
#define DEFINE_INTEGRATOR_SHADE_KERNEL(name) \
|
||||
void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const ThreadKernelGlobalsCPU *kg, \
|
||||
IntegratorStateCPU *state, \
|
||||
ccl_global float *render_buffer) \
|
||||
{ \
|
||||
(void)kg; \
|
||||
(void)state; \
|
||||
(void)render_buffer; \
|
||||
KERNEL_INVOKE(name, kg, state, render_buffer); \
|
||||
}
|
||||
|
||||
DEFINE_INTEGRATOR_INIT_KERNEL(init_from_camera)
|
||||
DEFINE_INTEGRATOR_INIT_KERNEL(init_from_bake)
|
||||
DEFINE_INTEGRATOR_SHADE_KERNEL(megakernel)
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Shader evaluation.
|
||||
*/
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, shader_eval_displace);
|
||||
(void)kg;
|
||||
(void)input;
|
||||
(void)output;
|
||||
(void)offset;
|
||||
#else
|
||||
uint cache_miss_unused = false;
|
||||
kernel_displace_evaluate(kg, input, output, &cache_miss_unused, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, shader_eval_background);
|
||||
(void)kg;
|
||||
(void)input;
|
||||
(void)output;
|
||||
(void)offset;
|
||||
#else
|
||||
uint cache_miss_unused = false;
|
||||
kernel_background_evaluate(kg, input, output, &cache_miss_unused, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)(
|
||||
const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, shader_eval_curve_shadow_transparency);
|
||||
(void)kg;
|
||||
(void)input;
|
||||
(void)output;
|
||||
(void)offset;
|
||||
#else
|
||||
uint cache_miss_unused = false;
|
||||
kernel_curve_shadow_transparency_evaluate(kg, input, output, &cache_miss_unused, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(shader_eval_volume_density)(const ThreadKernelGlobalsCPU *kg,
|
||||
const KernelShaderEvalInput *input,
|
||||
float *output,
|
||||
const int offset)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, shader_eval_volume_density);
|
||||
(void)kg;
|
||||
(void)input;
|
||||
(void)output;
|
||||
(void)offset;
|
||||
#else
|
||||
uint cache_miss_unused = false;
|
||||
kernel_volume_density_evaluate(kg, input, output, &cache_miss_unused, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Adaptive sampling.
|
||||
*/
|
||||
|
||||
bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)(
|
||||
const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int y,
|
||||
const float threshold,
|
||||
const int reset,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_convergence_check);
|
||||
(void)kg;
|
||||
(void)render_buffer;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)threshold;
|
||||
(void)reset;
|
||||
(void)offset;
|
||||
(void)stride;
|
||||
return false;
|
||||
#else
|
||||
return film_adaptive_sampling_convergence_check(
|
||||
kg, render_buffer, x, y, threshold, reset, offset, stride);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int y,
|
||||
const int start_x,
|
||||
const int width,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_x);
|
||||
(void)kg;
|
||||
(void)render_buffer;
|
||||
(void)y;
|
||||
(void)start_x;
|
||||
(void)width;
|
||||
(void)offset;
|
||||
(void)stride;
|
||||
#else
|
||||
film_adaptive_sampling_filter_x(kg, render_buffer, y, start_x, width, offset, stride);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int start_y,
|
||||
const int height,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_y);
|
||||
(void)kg;
|
||||
(void)render_buffer;
|
||||
(void)x;
|
||||
(void)start_y;
|
||||
(void)height;
|
||||
(void)offset;
|
||||
(void)stride;
|
||||
#else
|
||||
film_adaptive_sampling_filter_y(kg, render_buffer, x, start_y, height, offset, stride);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Cryptomatte.
|
||||
*/
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int pixel_index)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, cryptomatte_postprocess);
|
||||
(void)kg;
|
||||
(void)render_buffer;
|
||||
(void)pixel_index;
|
||||
#else
|
||||
film_cryptomatte_post(kg, render_buffer, pixel_index);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Volume Scattering Probability Guiding.
|
||||
*/
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(volume_guiding_filter_x)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int y,
|
||||
const int center_x,
|
||||
const int min_x,
|
||||
const int max_x,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, volume_guiding_filter_x);
|
||||
(void)kg;
|
||||
(void)render_buffer;
|
||||
(void)y;
|
||||
(void)center_x;
|
||||
(void)min_x;
|
||||
(void)max_x;
|
||||
(void)offset;
|
||||
(void)stride;
|
||||
#else
|
||||
volume_guiding_filter_x(kg, render_buffer, y, center_x, min_x, max_x, offset, stride);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KERNEL_FUNCTION_FULL_NAME(volume_guiding_filter_y)(const ThreadKernelGlobalsCPU *kg,
|
||||
ccl_global float *render_buffer,
|
||||
const int x,
|
||||
const int min_y,
|
||||
const int max_y,
|
||||
const int offset,
|
||||
const int stride)
|
||||
{
|
||||
#ifdef KERNEL_STUB
|
||||
STUB_ASSERT(KERNEL_ARCH, volume_guiding_filter_y);
|
||||
(void)kg;
|
||||
(void)render_buffer;
|
||||
(void)x;
|
||||
(void)min_y;
|
||||
(void)max_y;
|
||||
(void)offset;
|
||||
(void)stride;
|
||||
#else
|
||||
volume_guiding_filter_y(kg, render_buffer, x, min_y, max_y, offset, stride);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Film Convert.
|
||||
*/
|
||||
|
||||
#ifdef KERNEL_STUB
|
||||
|
||||
# define KERNEL_FILM_CONVERT_FUNCTION(name, is_float) \
|
||||
void KERNEL_FUNCTION_FULL_NAME(film_convert_##name)(const KernelFilmConvert *kfilm_convert, \
|
||||
const float *buffer, \
|
||||
float *pixel, \
|
||||
const int width, \
|
||||
const int buffer_stride, \
|
||||
const int pixel_stride) \
|
||||
{ \
|
||||
STUB_ASSERT(KERNEL_ARCH, film_convert_##name); \
|
||||
(void)kfilm_convert; \
|
||||
(void)buffer; \
|
||||
(void)pixel; \
|
||||
(void)width; \
|
||||
(void)buffer_stride; \
|
||||
(void)pixel_stride; \
|
||||
} \
|
||||
void KERNEL_FUNCTION_FULL_NAME(film_convert_half_rgba_##name)( \
|
||||
const KernelFilmConvert *kfilm_convert, \
|
||||
const float *buffer, \
|
||||
half4 *pixel, \
|
||||
const int width, \
|
||||
const int buffer_stride) \
|
||||
{ \
|
||||
STUB_ASSERT(KERNEL_ARCH, film_convert_##name); \
|
||||
(void)kfilm_convert; \
|
||||
(void)buffer; \
|
||||
(void)pixel; \
|
||||
(void)width; \
|
||||
(void)buffer_stride; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# define KERNEL_FILM_CONVERT_FUNCTION(name, is_float) \
|
||||
void KERNEL_FUNCTION_FULL_NAME(film_convert_##name)(const KernelFilmConvert *kfilm_convert, \
|
||||
const float *buffer, \
|
||||
float *pixel, \
|
||||
const int width, \
|
||||
const int buffer_stride, \
|
||||
const int pixel_stride) \
|
||||
{ \
|
||||
for (int i = 0; i < width; i++, buffer += buffer_stride, pixel += pixel_stride) { \
|
||||
film_get_pass_pixel_##name(kfilm_convert, buffer, pixel); \
|
||||
} \
|
||||
} \
|
||||
void KERNEL_FUNCTION_FULL_NAME(film_convert_half_rgba_##name)( \
|
||||
const KernelFilmConvert *kfilm_convert, \
|
||||
const float *buffer, \
|
||||
half4 *pixel, \
|
||||
const int width, \
|
||||
const int buffer_stride) \
|
||||
{ \
|
||||
for (int i = 0; i < width; i++, buffer += buffer_stride, pixel++) { \
|
||||
float pixel_rgba[4] = {0.0f, 0.0f, 0.0f, 1.0f}; \
|
||||
film_get_pass_pixel_##name(kfilm_convert, buffer, pixel_rgba); \
|
||||
if (is_float) { \
|
||||
pixel_rgba[1] = pixel_rgba[0]; \
|
||||
pixel_rgba[2] = pixel_rgba[0]; \
|
||||
} \
|
||||
film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel_rgba); \
|
||||
*pixel = float4_to_half4_display( \
|
||||
make_float4(pixel_rgba[0], pixel_rgba[1], pixel_rgba[2], pixel_rgba[3])); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
KERNEL_FILM_CONVERT_FUNCTION(depth, true)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(mist, true)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(sample_count, true)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(volume_majorant, true)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(float, true)
|
||||
|
||||
KERNEL_FILM_CONVERT_FUNCTION(light_path, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(rgbe, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(float3, false)
|
||||
|
||||
KERNEL_FILM_CONVERT_FUNCTION(motion, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(cryptomatte, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(shadow_catcher, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(shadow_catcher_matte_with_shadow, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(combined, false)
|
||||
KERNEL_FILM_CONVERT_FUNCTION(float4, false)
|
||||
|
||||
#undef KERNEL_FILM_CONVERT_FUNCTION
|
||||
|
||||
#undef KERNEL_INVOKE
|
||||
#undef DEFINE_INTEGRATOR_SHADE_KERNEL
|
||||
#undef DEFINE_INTEGRATOR_INIT_KERNEL
|
||||
|
||||
#undef KERNEL_STUB
|
||||
#undef STUB_ASSERT
|
||||
#undef KERNEL_ARCH
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Optimized CPU kernel entry points. This file is compiled with AVX2
|
||||
* optimization flags and nearly all functions inlined, while kernel.cpp
|
||||
* is compiled without for other CPU's. */
|
||||
|
||||
#include "util/optimization.h"
|
||||
|
||||
#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2
|
||||
# define KERNEL_STUB
|
||||
#else
|
||||
/* SSE optimization disabled for now on 32 bit, see bug #36316. */
|
||||
# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86)))
|
||||
# define __KERNEL_SSE__
|
||||
# define __KERNEL_SSE2__
|
||||
# define __KERNEL_SSE3__
|
||||
# define __KERNEL_SSSE3__
|
||||
# define __KERNEL_SSE42__
|
||||
# define __KERNEL_AVX__
|
||||
# define __KERNEL_AVX2__
|
||||
# endif
|
||||
#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */
|
||||
|
||||
#include "kernel/device/cpu/globals.h"
|
||||
#include "kernel/device/cpu/kernel.h"
|
||||
#define KERNEL_ARCH cpu_avx2
|
||||
#include "kernel/device/cpu/kernel_arch_impl.h"
|
||||
235
blender-5.2.0/intern/cycles/kernel/device/cuda/CMakeLists.txt
Normal file
235
blender-5.2.0/intern/cycles/kernel/device/cuda/CMakeLists.txt
Normal file
@@ -0,0 +1,235 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_CUDA
|
||||
kernel.cu
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_CUDA_HEADERS
|
||||
compat.h
|
||||
config.h
|
||||
globals.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
function(cuda_get_version out_version)
|
||||
execute_process(COMMAND ${CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE NVCC_OUT)
|
||||
string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\1" CUDA_VERSION_MAJOR "${NVCC_OUT}")
|
||||
string(REGEX REPLACE ".*release ([0-9]+)\\.([0-9]+).*" "\\2" CUDA_VERSION_MINOR "${NVCC_OUT}")
|
||||
set(${out_version} "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cuda_add_common_flags cuda_version arch in_flags out_flags)
|
||||
set(flags ${in_flags})
|
||||
|
||||
if(CUDA_HOST_COMPILER)
|
||||
set(flags ${flags} -ccbin="${CUDA_HOST_COMPILER}")
|
||||
endif()
|
||||
|
||||
set(flags ${flags}
|
||||
# Helps with compatibility when using recent clang host compiler.
|
||||
"-std=c++17"
|
||||
--use_fast_math
|
||||
-Wno-deprecated-gpu-targets)
|
||||
|
||||
if(WITH_CYCLES_DEBUG)
|
||||
set(flags ${flags}
|
||||
-D WITH_CYCLES_DEBUG
|
||||
--ptxas-options="-v")
|
||||
endif()
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
set(flags ${flags} -D WITH_NANOVDB)
|
||||
endif()
|
||||
|
||||
if("${cuda_version}" GREATER_EQUAL 123 AND "${arch}" STREQUAL "sm_120")
|
||||
# Enable jump table generation for the SVM switch statement.
|
||||
set(flags ${flags} --jump-table-density 80)
|
||||
endif()
|
||||
|
||||
if(NOT WITH_CYCLES_CUDA_BUILD_SERIAL AND "${cuda_version}" GREATER_EQUAL 129)
|
||||
# Only use split compile with few binaries, to avoid excessive memory usage.
|
||||
# This is mainly helpful for quick local builds for one architecture.
|
||||
list(LENGTH CYCLES_CUDA_BINARIES_ARCH _num_binaries)
|
||||
if(_num_binaries LESS_EQUAL 2)
|
||||
set(flags ${flags} --split-compile=0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${out_flags} ${flags} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
if(WITH_CYCLES_CUDA_BINARIES)
|
||||
# 64 bit only
|
||||
set(CUDA_BITS 64)
|
||||
|
||||
# CUDA version
|
||||
cuda_get_version(CUDA_VERSION)
|
||||
|
||||
# warn for other versions
|
||||
if((CUDA_VERSION STREQUAL "101") OR
|
||||
(CUDA_VERSION STREQUAL "102") OR
|
||||
(CUDA_VERSION_MAJOR STREQUAL "11") OR
|
||||
(CUDA_VERSION_MAJOR STREQUAL "12"))
|
||||
else()
|
||||
message(WARNING
|
||||
"CUDA version ${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR} detected, "
|
||||
"build may succeed but only CUDA 12, 11, 10.2 and 10.1 have been tested")
|
||||
endif()
|
||||
|
||||
# build for each arch
|
||||
set(cuda_sources kernel.cu
|
||||
${SRC_KERNEL_DEVICE_CUDA_HEADERS}
|
||||
$<TARGET_PROPERTY:cycles_kernel,INTERFACE_SOURCES>
|
||||
)
|
||||
set(cuda_cubins "")
|
||||
|
||||
# Modifies in parent scope:
|
||||
# - `cuda_cubins`: appended with compressed `cubin/ptx` output.
|
||||
function(CYCLES_CUDA_KERNEL_ADD arch prev_arch name flags sources experimental)
|
||||
if(${arch} MATCHES "compute_.*")
|
||||
set(format "ptx")
|
||||
else()
|
||||
set(format "cubin")
|
||||
endif()
|
||||
set(cuda_file ${name}_${arch}.${format})
|
||||
set(cuda_file_compressed ${cuda_file}.zst)
|
||||
|
||||
set(kernel_sources ${sources})
|
||||
if(NOT ${prev_arch} STREQUAL "none")
|
||||
if(${prev_arch} MATCHES "compute_.*")
|
||||
set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.ptx)
|
||||
else()
|
||||
set(kernel_sources ${kernel_sources} ${name}_${prev_arch}.cubin)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(cuda_kernel_src "/${name}.cu")
|
||||
|
||||
set(cuda_flags ${flags}
|
||||
-D CCL_NAMESPACE_BEGIN=
|
||||
-D CCL_NAMESPACE_END=
|
||||
-D NVCC
|
||||
-D _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH
|
||||
-m ${CUDA_BITS}
|
||||
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../..
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/${cuda_file})
|
||||
|
||||
cuda_add_common_flags(${cuda_version} ${arch} "${cuda_flags}" cuda_flags)
|
||||
|
||||
set(_cuda_nvcc_args
|
||||
-arch=${arch}
|
||||
${CUDA_NVCC_FLAGS}
|
||||
--${format}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}${cuda_kernel_src}
|
||||
${cuda_flags}
|
||||
)
|
||||
|
||||
if(WITH_COMPILER_CCACHE AND CCACHE_PROGRAM)
|
||||
add_custom_command(
|
||||
OUTPUT ${cuda_file}
|
||||
COMMAND ${CCACHE_PROGRAM} ${cuda_nvcc_executable} ${_cuda_nvcc_args}
|
||||
DEPENDS ${kernel_sources})
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT ${cuda_file}
|
||||
COMMAND ${cuda_nvcc_executable} ${_cuda_nvcc_args}
|
||||
DEPENDS ${kernel_sources})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${cuda_file_compressed}
|
||||
COMMAND "$<TARGET_FILE:zstd_compress>" ${cuda_file} ${cuda_file_compressed}
|
||||
DEPENDS ${cuda_file})
|
||||
|
||||
unset(_cuda_nvcc_args)
|
||||
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${cuda_file_compressed}" ${CYCLES_INSTALL_PATH}/lib)
|
||||
list(APPEND cuda_cubins ${cuda_file_compressed})
|
||||
set(cuda_cubins "${cuda_cubins}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(prev_arch "none")
|
||||
foreach(arch ${CYCLES_CUDA_BINARIES_ARCH})
|
||||
if(${arch} MATCHES ".*_3.")
|
||||
message(STATUS "CUDA binaries for ${arch} are no longer supported, skipped.")
|
||||
elseif(${arch} MATCHES "compute_7." AND DEFINED CUDA11_NVCC_EXECUTABLE)
|
||||
# Use CUDA 11 if available for the default PTX kernel. This allows us to
|
||||
# keep the driver requirements for user machines low.
|
||||
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA11_TOOLKIT_ROOT_DIR})
|
||||
set(cuda_version 110)
|
||||
elseif((${arch} MATCHES ".*_5." OR ${arch} MATCHES ".*_6." OR ${arch} MATCHES ".*_70") AND "${CUDA_VERSION}" GREATER_EQUAL 130)
|
||||
# Support for Maxwell, Pascal and Volta was dropped in CUDA 13
|
||||
if(DEFINED CUDA11_NVCC_EXECUTABLE)
|
||||
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA11_TOOLKIT_ROOT_DIR})
|
||||
set(cuda_version 110)
|
||||
else()
|
||||
message(STATUS "CUDA binaries for ${arch} are no longer supported with CUDA 13.0+, skipped.")
|
||||
endif()
|
||||
elseif(${arch} MATCHES ".*_7." AND "${CUDA_VERSION}" LESS 100)
|
||||
message(STATUS "CUDA binaries for ${arch} require CUDA 10.0+, skipped.")
|
||||
elseif(${arch} MATCHES ".*_8.")
|
||||
if("${CUDA_VERSION}" GREATER_EQUAL 111) # Support for sm_86 was introduced in CUDA 11
|
||||
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(cuda_version ${CUDA_VERSION})
|
||||
elseif(DEFINED CUDA11_NVCC_EXECUTABLE)
|
||||
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA11_TOOLKIT_ROOT_DIR})
|
||||
set(cuda_version 110)
|
||||
else()
|
||||
message(STATUS "CUDA binaries for ${arch} require CUDA 11.1+, skipped.")
|
||||
endif()
|
||||
elseif(${arch} MATCHES ".*_10." OR ${arch} MATCHES ".*_120")
|
||||
if("${CUDA_VERSION}" GREATER_EQUAL 128) # Support for sm_100, sm_101, sm_120 was introduced in CUDA 12.8
|
||||
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(cuda_version ${CUDA_VERSION})
|
||||
else()
|
||||
message(STATUS "CUDA binaries for ${arch} require CUDA 12.8+, skipped.")
|
||||
endif()
|
||||
else()
|
||||
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(cuda_version ${CUDA_VERSION})
|
||||
endif()
|
||||
if(DEFINED cuda_nvcc_executable AND DEFINED cuda_toolkit_root_dir)
|
||||
# Compile regular kernel
|
||||
cycles_cuda_kernel_add(${arch} ${prev_arch} kernel "" "${cuda_sources}" FALSE)
|
||||
|
||||
if(WITH_CYCLES_CUDA_BUILD_SERIAL)
|
||||
set(prev_arch ${arch})
|
||||
endif()
|
||||
|
||||
unset(cuda_nvcc_executable)
|
||||
unset(cuda_toolkit_root_dir)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_custom_target(cycles_kernel_cuda
|
||||
ALL
|
||||
DEPENDS ${cuda_cubins}
|
||||
SOURCES ${SRC_KERNEL_DEVICE_CUDA} ${SRC_KERNEL_DEVICE_CUDA_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_cuda)
|
||||
|
||||
source_group("device\\cuda" FILES ${SRC_KERNEL_DEVICE_CUDA} ${SRC_KERNEL_DEVICE_CUDA_HEADERS})
|
||||
|
||||
add_dependencies(cycles_kernel cycles_kernel_cuda)
|
||||
endif()
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda)
|
||||
119
blender-5.2.0/intern/cycles/kernel/device/cuda/compat.h
Normal file
119
blender-5.2.0/intern/cycles/kernel/device/cuda/compat.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define __KERNEL_GPU__
|
||||
#define __KERNEL_CUDA__
|
||||
#define CCL_NAMESPACE_BEGIN
|
||||
#define CCL_NAMESPACE_END
|
||||
|
||||
#ifndef ATTR_FALLTHROUGH
|
||||
# define ATTR_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/* Manual definitions so we can compile without CUDA toolkit. */
|
||||
|
||||
#ifdef __CUDACC_RTC__
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef CYCLES_CUBIN_CC
|
||||
# define FLT_MIN 1.175494350822287507969e-38f
|
||||
# define FLT_MAX 340282346638528859811704183484516925440.0f
|
||||
# define FLT_EPSILON 1.192092896e-07F
|
||||
#endif
|
||||
|
||||
/* Qualifiers */
|
||||
|
||||
#define ccl_device __device__ __inline__
|
||||
#define ccl_device_extern extern "C" __device__
|
||||
#define ccl_device_inline __device__ __inline__
|
||||
#define ccl_device_forceinline __device__ __forceinline__
|
||||
#define ccl_device_noinline __device__ __noinline__
|
||||
#define ccl_device_noinline_cpu ccl_device
|
||||
#define ccl_device_inline_method ccl_device
|
||||
#define ccl_device_template_spec template<> ccl_device_inline
|
||||
#define ccl_global
|
||||
#define ccl_inline_constant __constant__
|
||||
#define ccl_device_constant __constant__ __device__
|
||||
#define ccl_static_constexpr static constexpr
|
||||
#define ccl_constant const
|
||||
#define ccl_gpu_shared __shared__
|
||||
#define ccl_private
|
||||
#define ccl_ray_data ccl_private
|
||||
#define ccl_may_alias
|
||||
#define ccl_restrict __restrict__
|
||||
#define ccl_align(n) __align__(n)
|
||||
#define ccl_optional_struct_init
|
||||
#define ccl_attr_maybe_unused [[maybe_unused]]
|
||||
|
||||
/* No assert supported for CUDA */
|
||||
|
||||
#define kernel_assert(cond)
|
||||
|
||||
/* GPU thread, block, grid size and index */
|
||||
|
||||
#define ccl_gpu_thread_idx_x (threadIdx.x)
|
||||
#define ccl_gpu_block_dim_x (blockDim.x)
|
||||
#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) 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)
|
||||
|
||||
/* GPU warp synchronization. */
|
||||
|
||||
#define ccl_gpu_syncthreads() __syncthreads()
|
||||
#define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate)
|
||||
|
||||
/* GPU texture objects */
|
||||
|
||||
typedef unsigned long long CUtexObject;
|
||||
typedef CUtexObject ccl_gpu_image_object_2D;
|
||||
|
||||
template<typename T>
|
||||
ccl_device_forceinline T ccl_gpu_image_object_read_2D(const ccl_gpu_image_object_2D texobj,
|
||||
const float x,
|
||||
const float y)
|
||||
{
|
||||
return tex2D<T>(texobj, x, y);
|
||||
}
|
||||
|
||||
/* Use fast math functions */
|
||||
|
||||
#define cosf(x) __cosf(((float)(x)))
|
||||
#define sinf(x) __sinf(((float)(x)))
|
||||
#define powf(x, y) __powf(((float)(x)), ((float)(y)))
|
||||
#define tanf(x) __tanf(((float)(x)))
|
||||
#define logf(x) __logf(((float)(x)))
|
||||
#define expf(x) __expf(((float)(x)))
|
||||
|
||||
/* Half */
|
||||
|
||||
typedef unsigned short half;
|
||||
|
||||
ccl_device_forceinline half __float2half(const float f)
|
||||
{
|
||||
half val;
|
||||
asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f));
|
||||
return val;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float __half2float(const half h)
|
||||
{
|
||||
float val;
|
||||
asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h));
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Types */
|
||||
|
||||
#include "util/half.h"
|
||||
#include "util/types.h"
|
||||
88
blender-5.2.0/intern/cycles/kernel/device/cuda/config.h
Normal file
88
blender-5.2.0/intern/cycles/kernel/device/cuda/config.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Device data taken from CUDA occupancy calculator.
|
||||
*
|
||||
* Terminology
|
||||
* - CUDA GPUs have multiple streaming multiprocessors
|
||||
* - Each multiprocessor executes multiple thread blocks
|
||||
* - Each thread block contains a number of threads, also known as the block size
|
||||
* - Multiprocessors have a fixed number of registers, and the amount of registers
|
||||
* used by each threads limits the number of threads per block.
|
||||
*/
|
||||
|
||||
/* 5.x, 6.x */
|
||||
#if __CUDA_ARCH__ <= 699
|
||||
# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536
|
||||
# define GPU_MULTIPROCESSOR_MAX_BLOCKS 32
|
||||
# define GPU_BLOCK_MAX_THREADS 1024
|
||||
# define GPU_THREAD_MAX_REGISTERS 255
|
||||
|
||||
/* tunable parameters */
|
||||
# define GPU_KERNEL_BLOCK_NUM_THREADS 256
|
||||
/* CUDA 9.0 seems to cause slowdowns on high-end Pascal cards unless we increase the number of
|
||||
* registers */
|
||||
# if __CUDACC_VER_MAJOR__ >= 9 && __CUDA_ARCH__ >= 600
|
||||
# define GPU_KERNEL_MAX_REGISTERS 64
|
||||
# else
|
||||
# define GPU_KERNEL_MAX_REGISTERS 48
|
||||
# endif
|
||||
|
||||
/* 7.x, 8.x, 12.x */
|
||||
#elif __CUDA_ARCH__ <= 1299
|
||||
# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536
|
||||
# define GPU_MULTIPROCESSOR_MAX_BLOCKS 32
|
||||
# define GPU_BLOCK_MAX_THREADS 1024
|
||||
# define GPU_THREAD_MAX_REGISTERS 255
|
||||
|
||||
/* tunable parameters */
|
||||
# define GPU_KERNEL_BLOCK_NUM_THREADS 384
|
||||
# define GPU_KERNEL_MAX_REGISTERS 168
|
||||
|
||||
/* unknown architecture */
|
||||
#else
|
||||
# error "Unknown or unsupported CUDA architecture, can't determine launch bounds"
|
||||
#endif
|
||||
|
||||
/* Compute number of threads per block and minimum blocks per multiprocessor
|
||||
* given the maximum number of registers per thread. */
|
||||
#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \
|
||||
extern "C" __global__ void __launch_bounds__(block_num_threads, \
|
||||
GPU_MULTIPRESSOR_MAX_REGISTERS / \
|
||||
(block_num_threads * thread_num_registers))
|
||||
|
||||
#define ccl_gpu_kernel_threads(block_num_threads) \
|
||||
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
|
||||
#define ccl_gpu_kernel_within_bounds(i, n) ((i) < (n))
|
||||
|
||||
/* Define a function object where "func" is the lambda body, and additional parameters are used to
|
||||
* specify captured state */
|
||||
#define ccl_gpu_kernel_lambda(func, ...) \
|
||||
struct KernelLambda { \
|
||||
__VA_ARGS__; \
|
||||
__device__ int operator()(const int state) \
|
||||
{ \
|
||||
return (func); \
|
||||
} \
|
||||
} ccl_gpu_kernel_lambda_pass
|
||||
|
||||
/* sanity checks */
|
||||
|
||||
#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS
|
||||
# error "Maximum number of threads per block exceeded"
|
||||
#endif
|
||||
|
||||
#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \
|
||||
GPU_MULTIPROCESSOR_MAX_BLOCKS
|
||||
# error "Maximum number of blocks per multiprocessor exceeded"
|
||||
#endif
|
||||
|
||||
#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS
|
||||
# error "Maximum number of registers per thread exceeded"
|
||||
#endif
|
||||
48
blender-5.2.0/intern/cycles/kernel/device/cuda/globals.h
Normal file
48
blender-5.2.0/intern/cycles/kernel/device/cuda/globals.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/util/profiler.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Not actually used, just a nullptr pointer that gets passed everywhere, which we
|
||||
* hope gets optimized out by the compiler. */
|
||||
struct KernelGlobalsGPU {
|
||||
int unused[1];
|
||||
};
|
||||
using KernelGlobals = const ccl_global KernelGlobalsGPU *ccl_restrict;
|
||||
|
||||
struct KernelParamsCUDA {
|
||||
/* Global scene data and textures */
|
||||
KernelData data;
|
||||
#define KERNEL_DATA_ARRAY(type, name) const type *name;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) type *name;
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
/* Integrator state */
|
||||
IntegratorStateGPU integrator_state;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL_GPU__
|
||||
__constant__ KernelParamsCUDA kernel_params;
|
||||
#endif
|
||||
|
||||
/* Abstraction macros */
|
||||
#define kernel_data kernel_params.data
|
||||
#define kernel_data_fetch(name, index) kernel_params.name[(index)]
|
||||
#define kernel_data_write(name, index, value) kernel_params.name[(index)] = (value)
|
||||
#define kernel_data_array(name) (kernel_params.name)
|
||||
#define kernel_integrator_state kernel_params.integrator_state
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
16
blender-5.2.0/intern/cycles/kernel/device/cuda/kernel.cu
Normal file
16
blender-5.2.0/intern/cycles/kernel/device/cuda/kernel.cu
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* CUDA kernel entry points */
|
||||
|
||||
#ifdef __CUDA_ARCH__
|
||||
|
||||
# include "kernel/device/cuda/compat.h"
|
||||
# include "kernel/device/cuda/config.h"
|
||||
# include "kernel/device/cuda/globals.h"
|
||||
|
||||
# include "kernel/device/gpu/image.h"
|
||||
# include "kernel/device/gpu/kernel.h"
|
||||
|
||||
#endif
|
||||
18
blender-5.2.0/intern/cycles/kernel/device/gpu/block_sizes.h
Normal file
18
blender-5.2.0/intern/cycles/kernel/device/gpu/block_sizes.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2017-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __HIP__
|
||||
# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 1024
|
||||
# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 1024
|
||||
# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 1024
|
||||
#else
|
||||
# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512
|
||||
# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512
|
||||
# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512
|
||||
#endif
|
||||
|
||||
#define GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY (~0)
|
||||
#define GPU_PARALLEL_SORT_BLOCK_SIZE 1024
|
||||
173
blender-5.2.0/intern/cycles/kernel/device/gpu/image.h
Normal file
173
blender-5.2.0/intern/cycles/kernel/device/gpu/image.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/* SPDX-FileCopyrightText: 2017-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
#include "kernel/util/image_2d.h"
|
||||
#include "util/defines.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device_inline float frac(const float x, ccl_private int *ix)
|
||||
{
|
||||
int i = float_to_int(x) - ((x < 0.0f) ? 1 : 0);
|
||||
*ix = i;
|
||||
return x - (float)i;
|
||||
}
|
||||
|
||||
/* w0, w1, w2, and w3 are the four cubic B-spline basis functions. */
|
||||
ccl_device float cubic_w0(const float a)
|
||||
{
|
||||
return (1.0f / 6.0f) * (a * (a * (-a + 3.0f) - 3.0f) + 1.0f);
|
||||
}
|
||||
ccl_device float cubic_w1(const float a)
|
||||
{
|
||||
return (1.0f / 6.0f) * (a * a * (3.0f * a - 6.0f) + 4.0f);
|
||||
}
|
||||
ccl_device float cubic_w2(const float a)
|
||||
{
|
||||
return (1.0f / 6.0f) * (a * (a * (-3.0f * a + 3.0f) + 3.0f) + 1.0f);
|
||||
}
|
||||
ccl_device float cubic_w3(const float a)
|
||||
{
|
||||
return (1.0f / 6.0f) * (a * a * a);
|
||||
}
|
||||
|
||||
/* g0 and g1 are the two amplitude functions. */
|
||||
ccl_device float cubic_g0(const float a)
|
||||
{
|
||||
return cubic_w0(a) + cubic_w1(a);
|
||||
}
|
||||
ccl_device float cubic_g1(const float a)
|
||||
{
|
||||
return cubic_w2(a) + cubic_w3(a);
|
||||
}
|
||||
|
||||
/* h0 and h1 are the two offset functions */
|
||||
ccl_device float cubic_h0(const float a)
|
||||
{
|
||||
return (cubic_w1(a) / cubic_g0(a)) - 1.0f;
|
||||
}
|
||||
ccl_device float cubic_h1(const float a)
|
||||
{
|
||||
return (cubic_w3(a) / cubic_g1(a)) + 1.0f;
|
||||
}
|
||||
|
||||
/* Fast bicubic texture lookup using 4 bilinear lookups, adapted from CUDA samples. */
|
||||
template<typename T>
|
||||
ccl_device_noinline T kernel_image_interp_bicubic(const ccl_global KernelImageInfo &info,
|
||||
const float2 uv)
|
||||
{
|
||||
ccl_gpu_image_object_2D tex = (ccl_gpu_image_object_2D)info.data;
|
||||
|
||||
const float x = (uv.x * (float)info.width) - 0.5f;
|
||||
const float y = (uv.y * (float)info.height) - 0.5f;
|
||||
|
||||
const float px = floorf(x);
|
||||
const float py = floorf(y);
|
||||
const float fx = x - px;
|
||||
const float fy = y - py;
|
||||
|
||||
const float g0x = cubic_g0(fx);
|
||||
const float g1x = cubic_g1(fx);
|
||||
/* Note +0.5 offset to compensate for CUDA linear filtering convention. */
|
||||
const float x0 = (px + cubic_h0(fx) + 0.5f) * info.inv_width;
|
||||
const float x1 = (px + cubic_h1(fx) + 0.5f) * info.inv_width;
|
||||
const float y0 = (py + cubic_h0(fy) + 0.5f) * info.inv_height;
|
||||
const float y1 = (py + cubic_h1(fy) + 0.5f) * info.inv_height;
|
||||
|
||||
return cubic_g0(fy) * (g0x * ccl_gpu_image_object_read_2D<T>(tex, x0, y0) +
|
||||
g1x * ccl_gpu_image_object_read_2D<T>(tex, x1, y0)) +
|
||||
cubic_g1(fy) * (g0x * ccl_gpu_image_object_read_2D<T>(tex, x0, y1) +
|
||||
g1x * ccl_gpu_image_object_read_2D<T>(tex, x1, y1));
|
||||
}
|
||||
|
||||
ccl_device float4 kernel_image_interp(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const int image_texture_id,
|
||||
dual2 uv)
|
||||
{
|
||||
if (image_texture_id == KERNEL_IMAGE_NONE) {
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
const ccl_global KernelImageTexture &tex = kernel_data_fetch(image_textures, image_texture_id);
|
||||
const ccl_global KernelImageInfo *info;
|
||||
|
||||
float2 sample_uv;
|
||||
|
||||
if (tex.tile_descriptor_offset != KERNEL_TILE_LOAD_NONE) {
|
||||
/* Wrapping. */
|
||||
if (!kernel_image_tile_wrap(ExtensionType(tex.extension), uv.val)) {
|
||||
return zero_float4();
|
||||
}
|
||||
|
||||
/* Tile mapping */
|
||||
float2 xy = zero_float2();
|
||||
const KernelTileDescriptor tile_descriptor = kernel_image_tile_map(
|
||||
kg, sd, tex, image_texture_id, uv, xy);
|
||||
|
||||
if (!kernel_tile_descriptor_loaded(tile_descriptor)) {
|
||||
return (tile_descriptor == KERNEL_TILE_LOAD_FAILED) ? IMAGE_MISSING_RGBA : tex.average_color;
|
||||
}
|
||||
|
||||
info = &kernel_data_fetch(image_info, kernel_tile_descriptor_image_info_id(tile_descriptor));
|
||||
|
||||
/* Convert to normalized space again. */
|
||||
sample_uv = make_float2(xy.x * info->inv_width, xy.y * info->inv_height);
|
||||
}
|
||||
else {
|
||||
/* Full image sampling. */
|
||||
if (tex.image_info_id == KERNEL_IMAGE_NONE) {
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
|
||||
info = &kernel_data_fetch(image_info, tex.image_info_id);
|
||||
sample_uv = uv.val;
|
||||
}
|
||||
|
||||
/* float4, byte4, ushort4 and half4 */
|
||||
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) {
|
||||
return kernel_image_interp_bicubic<float4>(*info, sample_uv);
|
||||
}
|
||||
else {
|
||||
ccl_gpu_image_object_2D tex = (ccl_gpu_image_object_2D)info->data;
|
||||
return ccl_gpu_image_object_read_2D<float4>(tex, sample_uv.x, sample_uv.y);
|
||||
}
|
||||
}
|
||||
/* float, byte and half */
|
||||
else {
|
||||
float f;
|
||||
|
||||
if (info->interpolation == INTERPOLATION_CUBIC || info->interpolation == INTERPOLATION_SMART) {
|
||||
f = kernel_image_interp_bicubic<float>(*info, sample_uv);
|
||||
}
|
||||
else {
|
||||
ccl_gpu_image_object_2D tex = (ccl_gpu_image_object_2D)info->data;
|
||||
f = ccl_gpu_image_object_read_2D<float>(tex, sample_uv.x, sample_uv.y);
|
||||
}
|
||||
|
||||
return make_float4(f, f, f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_forceinline float4 kernel_image_interp_with_udim(KernelGlobals kg,
|
||||
ccl_private ShaderData *sd,
|
||||
const int udim_id,
|
||||
dual2 uv)
|
||||
{
|
||||
const int image_texture_id = kernel_image_udim_map(kg, udim_id, uv.val);
|
||||
if (image_texture_id == KERNEL_IMAGE_NONE) {
|
||||
return IMAGE_MISSING_RGBA;
|
||||
}
|
||||
|
||||
return kernel_image_interp(kg, sd, image_texture_id, uv);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
1366
blender-5.2.0/intern/cycles/kernel/device/gpu/kernel.h
Normal file
1366
blender-5.2.0/intern/cycles/kernel/device/gpu/kernel.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Given an array of states, build an array of indices for which the states
|
||||
* are active.
|
||||
*
|
||||
* Shared memory requirement is `sizeof(int) * (number_of_warps + 1)`. */
|
||||
|
||||
#include "kernel/device/gpu/block_sizes.h"
|
||||
#include "util/atomic.h"
|
||||
|
||||
/* TODO: abstract more device differences, define `ccl_gpu_local_syncthreads`,
|
||||
* `ccl_gpu_thread_warp`, `ccl_gpu_warp_index`, `ccl_gpu_num_warps` for all devices
|
||||
* and keep device specific code in `compat.h`. */
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
|
||||
template<typename IsActiveOp>
|
||||
void gpu_parallel_active_index_array_impl(const uint num_states,
|
||||
ccl_global int *ccl_restrict indices,
|
||||
ccl_global int *ccl_restrict num_indices,
|
||||
IsActiveOp is_active_op)
|
||||
{
|
||||
const sycl::nd_item<1> &item_id = sycl::ext::oneapi::this_work_item::get_nd_item<1>();
|
||||
const uint blocksize = item_id.get_local_range(0);
|
||||
|
||||
sycl::multi_ptr<int[GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE + 1],
|
||||
sycl::access::address_space::local_space>
|
||||
ptr = sycl::ext::oneapi::group_local_memory<
|
||||
int[GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE + 1]>(item_id.get_group());
|
||||
int *warp_offset = *ptr;
|
||||
|
||||
/* NOTE(@nsirgien): Here we calculate the same value as below but
|
||||
* faster for DPC++ : seems CUDA converting "%", "/", "*" based calculations below into
|
||||
* something faster already but DPC++ doesn't, so it's better to use
|
||||
* direct request of needed parameters - switching from this computation to computation below
|
||||
* will cause 2.5x performance slowdown. */
|
||||
const uint thread_index = item_id.get_local_id(0);
|
||||
const uint thread_warp = item_id.get_sub_group().get_local_id();
|
||||
|
||||
const uint warp_index = item_id.get_sub_group().get_group_id();
|
||||
const uint num_warps = item_id.get_sub_group().get_group_range()[0];
|
||||
|
||||
const uint state_index = item_id.get_global_id(0);
|
||||
|
||||
/* Test if state corresponding to this thread is active. */
|
||||
const uint is_active = (state_index < num_states) ? is_active_op(state_index) : 0;
|
||||
#else /* !__KERNEL__ONEAPI__ */
|
||||
# ifndef __KERNEL_METAL__
|
||||
template<typename IsActiveOp>
|
||||
__device__
|
||||
# endif
|
||||
void
|
||||
gpu_parallel_active_index_array_impl(const uint num_states,
|
||||
ccl_global int *indices,
|
||||
ccl_global int *num_indices,
|
||||
# ifdef __KERNEL_METAL__
|
||||
const uint is_active,
|
||||
const uint blocksize,
|
||||
const int thread_index,
|
||||
const uint state_index,
|
||||
const int ccl_gpu_warp_size,
|
||||
const int thread_warp,
|
||||
const int warp_index,
|
||||
const int num_warps,
|
||||
threadgroup int *warp_offset)
|
||||
{
|
||||
# else
|
||||
IsActiveOp is_active_op)
|
||||
{
|
||||
extern ccl_gpu_shared int warp_offset[];
|
||||
|
||||
# ifndef __KERNEL_METAL__
|
||||
const uint blocksize = ccl_gpu_block_dim_x;
|
||||
# endif
|
||||
|
||||
const uint thread_index = ccl_gpu_thread_idx_x;
|
||||
const uint thread_warp = thread_index % ccl_gpu_warp_size;
|
||||
|
||||
const uint warp_index = thread_index / ccl_gpu_warp_size;
|
||||
const uint num_warps = blocksize / ccl_gpu_warp_size;
|
||||
|
||||
const uint state_index = ccl_gpu_block_idx_x * blocksize + thread_index;
|
||||
|
||||
/* Test if state corresponding to this thread is active. */
|
||||
const uint is_active = (state_index < num_states) ? is_active_op(state_index) : 0;
|
||||
# endif
|
||||
#endif /* !__KERNEL_ONEAPI__ */
|
||||
/* For each thread within a warp compute how many other active states precede it. */
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
const uint thread_offset = sycl::exclusive_scan_over_group(
|
||||
item_id.get_sub_group(), is_active, std::plus<>());
|
||||
#else
|
||||
const uint thread_offset = popcount(ccl_gpu_ballot(is_active) &
|
||||
ccl_gpu_thread_mask(thread_warp));
|
||||
#endif
|
||||
|
||||
/* Last thread in warp stores number of active states for each warp. */
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
if (thread_warp == item_id.get_sub_group().get_local_range()[0] - 1) {
|
||||
#else
|
||||
if (thread_warp == ccl_gpu_warp_size - 1) {
|
||||
#endif
|
||||
warp_offset[warp_index] = thread_offset + is_active;
|
||||
}
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(@nsirgien): For us here only local memory writing (warp_offset) is important,
|
||||
* so faster local barriers can be used. */
|
||||
ccl_gpu_local_syncthreads();
|
||||
#else
|
||||
ccl_gpu_syncthreads();
|
||||
#endif
|
||||
|
||||
/* Last thread in block converts per-warp sizes to offsets, increments global size of
|
||||
* index array and gets offset to write to. */
|
||||
if (thread_index == blocksize - 1) {
|
||||
/* TODO: parallelize this. */
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_warps; i++) {
|
||||
int num_active = warp_offset[i];
|
||||
warp_offset[i] = offset;
|
||||
offset += num_active;
|
||||
}
|
||||
|
||||
const uint block_num_active = warp_offset[warp_index] + thread_offset + is_active;
|
||||
warp_offset[num_warps] = atomic_fetch_and_add_uint32(num_indices, block_num_active);
|
||||
}
|
||||
|
||||
#ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(@nsirgien): For us here only important local memory writing (warp_offset),
|
||||
* so faster local barriers can be used. */
|
||||
ccl_gpu_local_syncthreads();
|
||||
#else
|
||||
ccl_gpu_syncthreads();
|
||||
#endif
|
||||
|
||||
/* Write to index array. */
|
||||
if (is_active) {
|
||||
const uint block_offset = warp_offset[num_warps];
|
||||
indices[block_offset + warp_offset[warp_index] + thread_offset] = state_index;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __KERNEL_METAL__
|
||||
|
||||
# define gpu_parallel_active_index_array(num_states, indices, num_indices, is_active_op) \
|
||||
const uint is_active = (ccl_gpu_global_id_x() < num_states) ? \
|
||||
is_active_op(ccl_gpu_global_id_x()) : \
|
||||
0; \
|
||||
gpu_parallel_active_index_array_impl(num_states, \
|
||||
indices, \
|
||||
num_indices, \
|
||||
is_active, \
|
||||
metal_local_size, \
|
||||
metal_local_id, \
|
||||
metal_global_id, \
|
||||
simdgroup_size, \
|
||||
simd_lane_index, \
|
||||
simd_group_index, \
|
||||
num_simd_groups, \
|
||||
(threadgroup int *)threadgroup_array)
|
||||
#elif defined(__KERNEL_ONEAPI__)
|
||||
|
||||
# define gpu_parallel_active_index_array(num_states, indices, num_indices, is_active_op) \
|
||||
gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op)
|
||||
|
||||
#else
|
||||
|
||||
# define gpu_parallel_active_index_array(num_states, indices, num_indices, is_active_op) \
|
||||
gpu_parallel_active_index_array_impl(num_states, indices, num_indices, is_active_op)
|
||||
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Parallel prefix sum.
|
||||
*
|
||||
* TODO: actually make this work in parallel.
|
||||
*
|
||||
* This is used for an array the size of the number of shaders in the scene
|
||||
* which is not usually huge, so might not be a significant bottleneck. */
|
||||
|
||||
#include "util/atomic.h"
|
||||
|
||||
__device__ void gpu_parallel_prefix_sum(const int global_id,
|
||||
ccl_global int *counter,
|
||||
ccl_global int *prefix_sum,
|
||||
const int num_values)
|
||||
{
|
||||
if (global_id != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
const int new_offset = offset + counter[i];
|
||||
prefix_sum[i] = offset;
|
||||
counter[i] = 0;
|
||||
offset = new_offset;
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,165 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Given an array of states, build an array of indices for which the states
|
||||
* are active and sorted by a given key. The prefix sum of the number of active
|
||||
* states per key must have already been computed.
|
||||
*
|
||||
* TODO: there may be ways to optimize this to avoid this many atomic ops? */
|
||||
|
||||
#include "kernel/device/gpu/block_sizes.h"
|
||||
#include "util/atomic.h"
|
||||
|
||||
#if defined(__KERNEL_LOCAL_ATOMIC_SORT__)
|
||||
|
||||
ccl_device_inline void gpu_parallel_sort_bucket_pass(const uint num_states,
|
||||
const uint partition_size,
|
||||
const uint max_shaders,
|
||||
const uint queued_kernel,
|
||||
ccl_global ushort *d_queued_kernel,
|
||||
ccl_global uint *d_shader_sort_key,
|
||||
ccl_global int *partition_key_offsets,
|
||||
ccl_gpu_shared int *buckets,
|
||||
const ushort local_id,
|
||||
const ushort local_size,
|
||||
const uint grid_id)
|
||||
{
|
||||
/* Zero the bucket sizes. */
|
||||
for (uint i = local_id; i < max_shaders; i += local_size) {
|
||||
atomic_store_local(&buckets[i], 0);
|
||||
}
|
||||
|
||||
# ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(@nsirgien): For us here only local memory writing (buckets) is important,
|
||||
* so faster local barriers can be used. */
|
||||
ccl_gpu_local_syncthreads();
|
||||
# else
|
||||
ccl_gpu_syncthreads();
|
||||
# endif
|
||||
|
||||
/* Determine bucket sizes within the partitions. */
|
||||
|
||||
const uint partition_start = partition_size * uint(grid_id);
|
||||
const uint partition_end = min(num_states, partition_start + partition_size);
|
||||
|
||||
for (int state_index = partition_start + uint(local_id); state_index < partition_end;
|
||||
state_index += uint(local_size))
|
||||
{
|
||||
ushort kernel_index = d_queued_kernel[state_index];
|
||||
if (kernel_index == queued_kernel) {
|
||||
uint key = d_shader_sort_key[state_index] % max_shaders;
|
||||
atomic_fetch_and_add_uint32_shared(&buckets[key], 1);
|
||||
}
|
||||
}
|
||||
|
||||
# ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(@nsirgien): For us here only local memory writing (buckets) is important,
|
||||
* so faster local barriers can be used. */
|
||||
ccl_gpu_local_syncthreads();
|
||||
# else
|
||||
ccl_gpu_syncthreads();
|
||||
# endif
|
||||
|
||||
/* Calculate the partition's local offsets from the prefix sum of bucket sizes. */
|
||||
|
||||
if (local_id == 0) {
|
||||
int offset = 0;
|
||||
for (int i = 0; i < max_shaders; i++) {
|
||||
partition_key_offsets[i + uint(grid_id) * (max_shaders + 1)] = offset;
|
||||
offset = offset + atomic_load_local(&buckets[i]);
|
||||
}
|
||||
|
||||
/* Store the number of active states in this partition. */
|
||||
partition_key_offsets[max_shaders + uint(grid_id) * (max_shaders + 1)] = offset;
|
||||
}
|
||||
}
|
||||
|
||||
ccl_device_inline void gpu_parallel_sort_write_pass(const uint num_states,
|
||||
const uint partition_size,
|
||||
const uint max_shaders,
|
||||
const uint queued_kernel,
|
||||
const int num_states_limit,
|
||||
ccl_global int *indices,
|
||||
ccl_global ushort *d_queued_kernel,
|
||||
ccl_global uint *d_shader_sort_key,
|
||||
ccl_global int *partition_key_offsets,
|
||||
ccl_gpu_shared int *local_offset,
|
||||
const ushort local_id,
|
||||
const ushort local_size,
|
||||
const uint grid_id)
|
||||
{
|
||||
/* Calculate each partition's global offset from the prefix sum of the active state counts per
|
||||
* partition. */
|
||||
|
||||
int partition_offset = 0;
|
||||
for (uint i = 0; i < grid_id; i++) {
|
||||
partition_offset += partition_key_offsets[max_shaders + i * (max_shaders + 1)];
|
||||
}
|
||||
|
||||
ccl_global int *key_offsets = partition_key_offsets + grid_id * (max_shaders + 1);
|
||||
for (uint i = local_id; i < max_shaders; i += local_size) {
|
||||
atomic_store_local(&local_offset[i], key_offsets[i] + partition_offset);
|
||||
}
|
||||
|
||||
# ifdef __KERNEL_ONEAPI__
|
||||
/* NOTE(@nsirgien): For us here only local memory writing (local_offset) is important,
|
||||
* so faster local barriers can be used. */
|
||||
ccl_gpu_local_syncthreads();
|
||||
# else
|
||||
ccl_gpu_syncthreads();
|
||||
# endif
|
||||
|
||||
/* Write the sorted active indices. */
|
||||
|
||||
const uint partition_start = partition_size * uint(grid_id);
|
||||
const uint partition_end = min(num_states, partition_start + partition_size);
|
||||
|
||||
for (int state_index = partition_start + uint(local_id); state_index < partition_end;
|
||||
state_index += uint(local_size))
|
||||
{
|
||||
ushort kernel_index = d_queued_kernel[state_index];
|
||||
if (kernel_index == queued_kernel) {
|
||||
uint key = d_shader_sort_key[state_index] % max_shaders;
|
||||
int index = atomic_fetch_and_add_uint32_shared(&local_offset[key], 1);
|
||||
if (index < num_states_limit) {
|
||||
indices[index] = state_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __KERNEL_LOCAL_ATOMIC_SORT__ */
|
||||
|
||||
template<typename GetKeyOp>
|
||||
__device__ void gpu_parallel_sorted_index_array(const uint state_index,
|
||||
const uint num_states,
|
||||
const int num_states_limit,
|
||||
ccl_global int *indices,
|
||||
ccl_global int *num_indices,
|
||||
ccl_global int *key_counter,
|
||||
ccl_global int *key_prefix_sum,
|
||||
GetKeyOp get_key_op)
|
||||
{
|
||||
const int key = (state_index < num_states) ? get_key_op(state_index) :
|
||||
GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY;
|
||||
|
||||
if (key != GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY) {
|
||||
const uint index = atomic_fetch_and_add_uint32(&key_prefix_sum[key], 1);
|
||||
if (index < num_states_limit) {
|
||||
/* Assign state index. */
|
||||
indices[index] = state_index;
|
||||
}
|
||||
else {
|
||||
/* Can't process this state now, increase the counter again so that
|
||||
* it will be handled in another iteration. */
|
||||
atomic_fetch_and_add_uint32(&key_counter[key], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
* Utility functions for work stealing
|
||||
*/
|
||||
|
||||
/* Map global work index to tile, pixel X/Y and sample. */
|
||||
ccl_device_inline void get_work_pixel(const ccl_global KernelWorkTile *tile,
|
||||
const uint global_work_index,
|
||||
ccl_private uint *x,
|
||||
ccl_private uint *y,
|
||||
ccl_private uint *sample)
|
||||
{
|
||||
uint sample_offset, pixel_offset;
|
||||
|
||||
if (kernel_data.integrator.scrambling_distance < 0.9f) {
|
||||
/* Keep threads for the same sample together. */
|
||||
const uint tile_pixels = tile->w * tile->h;
|
||||
sample_offset = global_work_index / tile_pixels;
|
||||
pixel_offset = global_work_index - sample_offset * tile_pixels;
|
||||
}
|
||||
else {
|
||||
/* Keeping threads for the same pixel together.
|
||||
* Appears to improve performance by a few % on CUDA and OptiX. */
|
||||
sample_offset = global_work_index % tile->num_samples;
|
||||
pixel_offset = global_work_index / tile->num_samples;
|
||||
}
|
||||
|
||||
const uint y_offset = pixel_offset / tile->w;
|
||||
const uint x_offset = pixel_offset - y_offset * tile->w;
|
||||
|
||||
*x = tile->x + x_offset;
|
||||
*y = tile->y + y_offset;
|
||||
*sample = tile->start_sample + sample_offset;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
126
blender-5.2.0/intern/cycles/kernel/device/hip/CMakeLists.txt
Normal file
126
blender-5.2.0/intern/cycles/kernel/device/hip/CMakeLists.txt
Normal file
@@ -0,0 +1,126 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_HIP
|
||||
kernel.cpp
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_HIP_HEADERS
|
||||
compat.h
|
||||
config.h
|
||||
globals.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP)
|
||||
# build for each arch
|
||||
set(hip_sources
|
||||
kernel.cpp
|
||||
${SRC_KERNEL_DEVICE_HIP_HEADERS}
|
||||
$<TARGET_PROPERTY:cycles_kernel,INTERFACE_SOURCES>
|
||||
)
|
||||
set(hip_fatbins "")
|
||||
|
||||
# Modifies in parent scope:
|
||||
# - `hip_fatbins`: appended with compressed `fatbin` output.
|
||||
function(CYCLES_HIP_KERNEL_ADD arch name flags sources experimental)
|
||||
set(format "fatbin")
|
||||
set(hip_file ${name}_${arch}.${format})
|
||||
set(hip_file_compressed ${hip_file}.zst)
|
||||
set(kernel_sources ${sources})
|
||||
|
||||
set(hip_kernel_src "/${name}.cpp")
|
||||
|
||||
if(WIN32)
|
||||
set(hip_command ${CMAKE_COMMAND})
|
||||
set(hip_flags
|
||||
-E env "HIP_PATH=${HIP_ROOT_DIR}"
|
||||
${HIP_HIPCC_EXECUTABLE})
|
||||
else()
|
||||
set(hip_command ${HIP_HIPCC_EXECUTABLE})
|
||||
set(hip_flags "")
|
||||
endif()
|
||||
|
||||
# There's a few bugs in the HIP compiler causing some scenes to fail to render,
|
||||
# or render improperly with specific combinations of device and/or compiler.
|
||||
# - Vega (gfx90x) fails to render some scenes
|
||||
# - Other GPUs (E.g. RDNA3) render volumes improperly with HIP 6.1.40252
|
||||
# A workaround is to set -O1 opt level during kernel compilation for these
|
||||
# configurations.
|
||||
if(WIN32 AND ((${arch} MATCHES "gfx90[a-z0-9]+") OR (${HIP_VERSION} STREQUAL "6.1.40252")))
|
||||
set(hip_opt_flags "-O1")
|
||||
else()
|
||||
set(hip_opt_flags "")
|
||||
endif()
|
||||
|
||||
set(hip_flags
|
||||
${hip_flags}
|
||||
--offload-arch=${arch}
|
||||
${HIP_HIPCC_FLAGS}
|
||||
--genco
|
||||
${CMAKE_CURRENT_SOURCE_DIR}${hip_kernel_src}
|
||||
${flags}
|
||||
-D CCL_NAMESPACE_BEGIN=
|
||||
-D CCL_NAMESPACE_END=
|
||||
-D HIPCC
|
||||
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../..
|
||||
-Wno-parentheses-equality
|
||||
-Wno-unused-value
|
||||
-ffast-math
|
||||
-std=c++17
|
||||
${hip_opt_flags}
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/${hip_file})
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
set(hip_flags ${hip_flags}
|
||||
-D WITH_NANOVDB)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEBUG)
|
||||
set(hip_flags ${hip_flags} -D WITH_CYCLES_DEBUG)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${hip_file}
|
||||
COMMAND ${hip_command} ${hip_flags}
|
||||
DEPENDS ${kernel_sources})
|
||||
add_custom_command(
|
||||
OUTPUT ${hip_file_compressed}
|
||||
COMMAND "$<TARGET_FILE:zstd_compress>" ${hip_file} ${hip_file_compressed}
|
||||
DEPENDS ${hip_file})
|
||||
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hip_file_compressed}" ${CYCLES_INSTALL_PATH}/lib)
|
||||
list(APPEND hip_fatbins ${hip_file_compressed})
|
||||
set(hip_fatbins "${hip_fatbins}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
foreach(arch ${CYCLES_HIP_BINARIES_ARCH})
|
||||
# Compile regular kernel
|
||||
cycles_hip_kernel_add(${arch} kernel "" "${hip_sources}" FALSE)
|
||||
endforeach()
|
||||
|
||||
add_custom_target(cycles_kernel_hip
|
||||
ALL
|
||||
DEPENDS ${hip_fatbins}
|
||||
SOURCES ${SRC_KERNEL_DEVICE_HIP} ${SRC_KERNEL_DEVICE_HIP_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_hip)
|
||||
|
||||
source_group("device\\hip" FILES ${SRC_KERNEL_DEVICE_HIP} ${SRC_KERNEL_DEVICE_HIP_HEADERS})
|
||||
|
||||
add_dependencies(cycles_kernel cycles_kernel_hip)
|
||||
endif()
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIP}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIP_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip)
|
||||
103
blender-5.2.0/intern/cycles/kernel/device/hip/compat.h
Normal file
103
blender-5.2.0/intern/cycles/kernel/device/hip/compat.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define __KERNEL_GPU__
|
||||
#define __KERNEL_HIP__
|
||||
#define CCL_NAMESPACE_BEGIN
|
||||
#define CCL_NAMESPACE_END
|
||||
|
||||
#ifndef ATTR_FALLTHROUGH
|
||||
# define ATTR_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#ifdef __HIPCC_RTC__
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef CYCLES_HIPBIN_CC
|
||||
# define FLT_MIN 1.175494350822287507969e-38f
|
||||
# define FLT_MAX 340282346638528859811704183484516925440.0f
|
||||
# define FLT_EPSILON 1.192092896e-07F
|
||||
#endif
|
||||
|
||||
/* Qualifiers */
|
||||
|
||||
#define ccl_device __device__ __inline__
|
||||
#define ccl_device_extern extern "C" __device__
|
||||
#define ccl_device_inline __device__ __inline__
|
||||
#define ccl_device_forceinline __device__ __forceinline__
|
||||
#define ccl_device_noinline __device__ __noinline__
|
||||
#define ccl_device_noinline_cpu ccl_device
|
||||
#define ccl_device_inline_method ccl_device
|
||||
#define ccl_device_template_spec template<> ccl_device_inline
|
||||
#define ccl_global
|
||||
#define ccl_inline_constant __constant__
|
||||
#define ccl_device_constant __constant__ __device__
|
||||
#define ccl_static_constexpr static constexpr
|
||||
#define ccl_constant const
|
||||
#define ccl_gpu_shared __shared__
|
||||
#define ccl_private
|
||||
#define ccl_ray_data ccl_private
|
||||
#define ccl_may_alias
|
||||
#define ccl_restrict __restrict__
|
||||
#define ccl_align(n) __align__(n)
|
||||
#define ccl_optional_struct_init
|
||||
#define ccl_attr_maybe_unused [[maybe_unused]]
|
||||
|
||||
#define kernel_assert(cond)
|
||||
|
||||
/* Types */
|
||||
#ifdef __HIP__
|
||||
# include "hip/hip_fp16.h"
|
||||
# include "hip/hip_runtime.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <immintrin.h>
|
||||
#endif
|
||||
|
||||
#define ccl_gpu_thread_idx_x (threadIdx.x)
|
||||
#define ccl_gpu_block_dim_x (blockDim.x)
|
||||
#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((1ull << thread_warp) - 1)
|
||||
|
||||
#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)
|
||||
|
||||
/* GPU warp synchronization */
|
||||
|
||||
#define ccl_gpu_syncthreads() __syncthreads()
|
||||
#define ccl_gpu_ballot(predicate) __ballot(predicate)
|
||||
|
||||
/* GPU texture objects */
|
||||
typedef hipTextureObject_t ccl_gpu_image_object_2D;
|
||||
|
||||
template<typename T>
|
||||
ccl_device_forceinline T ccl_gpu_image_object_read_2D(const ccl_gpu_image_object_2D texobj,
|
||||
const float x,
|
||||
const float y)
|
||||
{
|
||||
return tex2D<T>(texobj, x, y);
|
||||
}
|
||||
|
||||
/* Use fast math functions */
|
||||
|
||||
#define cosf(x) __cosf(((float)(x)))
|
||||
#define sinf(x) __sinf(((float)(x)))
|
||||
#define powf(x, y) __powf(((float)(x)), ((float)(y)))
|
||||
#define tanf(x) __tanf(((float)(x)))
|
||||
#define logf(x) __logf(((float)(x)))
|
||||
#define expf(x) __expf(((float)(x)))
|
||||
|
||||
/* Types */
|
||||
|
||||
#include "util/half.h"
|
||||
#include "util/types.h"
|
||||
68
blender-5.2.0/intern/cycles/kernel/device/hip/config.h
Normal file
68
blender-5.2.0/intern/cycles/kernel/device/hip/config.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Device data taken from HIP occupancy calculator.
|
||||
*
|
||||
* Terminology
|
||||
* - HIP GPUs have multiple streaming multiprocessors
|
||||
* - Each multiprocessor executes multiple thread blocks
|
||||
* - Each thread block contains a number of threads, also known as the block size
|
||||
* - Multiprocessors have a fixed number of registers, and the amount of registers
|
||||
* used by each threads limits the number of threads per block.
|
||||
*/
|
||||
|
||||
/* Launch Bound Definitions */
|
||||
#define GPU_MULTIPRESSOR_MAX_REGISTERS 65536
|
||||
#define GPU_MULTIPROCESSOR_MAX_BLOCKS 64
|
||||
#define GPU_BLOCK_MAX_THREADS 1024
|
||||
#define GPU_THREAD_MAX_REGISTERS 255
|
||||
|
||||
#define GPU_KERNEL_BLOCK_NUM_THREADS 1024
|
||||
#define GPU_KERNEL_MAX_REGISTERS 64
|
||||
|
||||
/* For performance tuning of HIPRT kernels we might have to change the number
|
||||
* that's why we don't use GPU_KERNEL_BLOCK_NUM_THREADS. */
|
||||
#define GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS 1024
|
||||
|
||||
/* Compute number of threads per block and minimum blocks per multiprocessor
|
||||
* given the maximum number of registers per thread. */
|
||||
#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \
|
||||
extern "C" __global__ void __launch_bounds__(block_num_threads, \
|
||||
GPU_MULTIPRESSOR_MAX_REGISTERS / \
|
||||
(block_num_threads * thread_num_registers))
|
||||
|
||||
#define ccl_gpu_kernel_threads(block_num_threads) \
|
||||
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
|
||||
#define ccl_gpu_kernel_within_bounds(i, n) ((i) < (n))
|
||||
|
||||
/* Define a function object where "func" is the lambda body, and additional parameters are used to
|
||||
* specify captured state */
|
||||
#define ccl_gpu_kernel_lambda(func, ...) \
|
||||
struct KernelLambda { \
|
||||
__VA_ARGS__; \
|
||||
__device__ int operator()(const int state) \
|
||||
{ \
|
||||
return (func); \
|
||||
} \
|
||||
} ccl_gpu_kernel_lambda_pass
|
||||
|
||||
/* sanity checks */
|
||||
|
||||
#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS
|
||||
# error "Maximum number of threads per block exceeded"
|
||||
#endif
|
||||
|
||||
#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \
|
||||
GPU_MULTIPROCESSOR_MAX_BLOCKS
|
||||
# error "Maximum number of blocks per multiprocessor exceeded"
|
||||
#endif
|
||||
|
||||
#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS
|
||||
# error "Maximum number of registers per thread exceeded"
|
||||
#endif
|
||||
48
blender-5.2.0/intern/cycles/kernel/device/hip/globals.h
Normal file
48
blender-5.2.0/intern/cycles/kernel/device/hip/globals.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/util/profiler.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Not actually used, just a nullptr pointer that gets passed everywhere, which we
|
||||
* hope gets optimized out by the compiler. */
|
||||
struct KernelGlobalsGPU {
|
||||
int unused[1];
|
||||
};
|
||||
using KernelGlobals = const ccl_global KernelGlobalsGPU *ccl_restrict;
|
||||
|
||||
struct KernelParamsHIP {
|
||||
/* Global scene data and textures */
|
||||
KernelData data;
|
||||
#define KERNEL_DATA_ARRAY(type, name) const type *name;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) type *name;
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
/* Integrator state */
|
||||
IntegratorStateGPU integrator_state;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL_GPU__
|
||||
__constant__ KernelParamsHIP kernel_params;
|
||||
#endif
|
||||
|
||||
/* Abstraction macros */
|
||||
#define kernel_data kernel_params.data
|
||||
#define kernel_data_fetch(name, index) kernel_params.name[(index)]
|
||||
#define kernel_data_write(name, index, value) kernel_params.name[(index)] = (value)
|
||||
#define kernel_data_array(name) (kernel_params.name)
|
||||
#define kernel_integrator_state kernel_params.integrator_state
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
16
blender-5.2.0/intern/cycles/kernel/device/hip/kernel.cpp
Normal file
16
blender-5.2.0/intern/cycles/kernel/device/hip/kernel.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* HIP kernel entry points */
|
||||
|
||||
#ifdef __HIP_DEVICE_COMPILE__
|
||||
|
||||
# include "kernel/device/hip/compat.h"
|
||||
# include "kernel/device/hip/config.h"
|
||||
# include "kernel/device/hip/globals.h"
|
||||
|
||||
# include "kernel/device/gpu/image.h"
|
||||
# include "kernel/device/gpu/kernel.h"
|
||||
|
||||
#endif
|
||||
144
blender-5.2.0/intern/cycles/kernel/device/hiprt/CMakeLists.txt
Normal file
144
blender-5.2.0/intern/cycles/kernel/device/hiprt/CMakeLists.txt
Normal file
@@ -0,0 +1,144 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_HIPRT
|
||||
kernel.cpp
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_HIPRT_HEADERS
|
||||
bvh.h
|
||||
globals.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_HIPRT_SDK_HEADERS
|
||||
hiprt/hiprt_common.h
|
||||
hiprt/hiprt_device.h
|
||||
hiprt/hiprt_types.h
|
||||
hiprt/hiprt_vec.h
|
||||
hiprt/hiprt_math.h
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_HIPRT_SDK
|
||||
hiprt/impl/Aabb.h
|
||||
hiprt/impl/BvhNode.h
|
||||
hiprt/impl/Geometry.h
|
||||
hiprt/impl/hiprt_device_impl.h
|
||||
hiprt/impl/hiprt_kernels_bitcode.h
|
||||
hiprt/impl/Instance.h
|
||||
hiprt/impl/QrDecomposition.h
|
||||
hiprt/impl/Quaternion.h
|
||||
hiprt/impl/Scene.h
|
||||
hiprt/impl/Transform.h
|
||||
hiprt/impl/Triangle.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIPRT)
|
||||
set(HIPRT_COMPILER_PARALLEL_JOBS 1 CACHE STRING "Number of parallel compiler instances to use for HIP-RT kernels")
|
||||
mark_as_advanced(HIPRT_COMPILER_PARALLEL_JOBS)
|
||||
|
||||
if(WIN32)
|
||||
set(hiprt_compile_command ${CMAKE_COMMAND})
|
||||
set(hiprt_compile_flags
|
||||
-E env "HIP_PATH=${HIP_ROOT_DIR}"
|
||||
${HIP_HIPCC_EXECUTABLE})
|
||||
else()
|
||||
set(hiprt_compile_command ${HIP_HIPCC_EXECUTABLE})
|
||||
set(hiprt_compile_flags "")
|
||||
endif()
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
set(hiprt_compile_flags ${hiprt_compile_flags} -D WITH_NANOVDB)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEBUG)
|
||||
set(hiprt_compile_flags ${hiprt_compile_flags} -D WITH_CYCLES_DEBUG)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND (${HIP_VERSION} STREQUAL "6.1.40252"))
|
||||
message(WARNING "HIP SDK ${HIP_VERSION} has known rendering artifacts with HIPRT. 5.7 is recommended instead")
|
||||
endif()
|
||||
|
||||
set(hiprt_sources
|
||||
kernel.cpp
|
||||
${SRC_KERNEL_DEVICE_HIPRT_HEADERS}
|
||||
$<TARGET_PROPERTY:cycles_kernel,INTERFACE_SOURCES>
|
||||
)
|
||||
|
||||
set(kernel_sources ${hiprt_sources})
|
||||
set(hiprt_kernel_src "/kernel.cpp")
|
||||
set(hiprt_compile_flags
|
||||
${hiprt_compile_flags}
|
||||
${HIP_HIPCC_FLAGS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}${hiprt_kernel_src}
|
||||
-D CCL_NAMESPACE_BEGIN=
|
||||
-D CCL_NAMESPACE_END=
|
||||
-D HIPCC
|
||||
-D __KERNEL_HIPRT__
|
||||
-std=c++17
|
||||
-mllvm
|
||||
-amdgpu-early-inline-all=false
|
||||
-mllvm
|
||||
-amdgpu-function-calls=true
|
||||
-parallel-jobs=${HIPRT_COMPILER_PARALLEL_JOBS}
|
||||
--genco
|
||||
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../..
|
||||
-I ${HIPRT_INCLUDE_DIR}
|
||||
-Wno-parentheses-equality
|
||||
-Wno-unused-value
|
||||
-ffast-math
|
||||
)
|
||||
|
||||
set(hiprt_hipfb "")
|
||||
foreach(arch ${CYCLES_HIP_BINARIES_ARCH})
|
||||
set(hiprt_file ${CMAKE_CURRENT_BINARY_DIR}/kernel_rt_${arch}.hipfb)
|
||||
set(hiprt_file_compressed ${hiprt_file}.zst)
|
||||
set(hiprt_flags
|
||||
${hiprt_compile_flags}
|
||||
--offload-arch=${arch}
|
||||
-o ${hiprt_file})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${hiprt_file}
|
||||
COMMAND ${hiprt_compile_command} ${hiprt_flags}
|
||||
DEPENDS ${kernel_sources}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${hiprt_file_compressed}
|
||||
COMMAND "$<TARGET_FILE:zstd_compress>" ${hiprt_file} ${hiprt_file_compressed}
|
||||
DEPENDS ${hiprt_file}
|
||||
)
|
||||
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hiprt_file_compressed}" ${CYCLES_INSTALL_PATH}/lib)
|
||||
list(APPEND hiprt_hipfb ${hiprt_file_compressed})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(cycles_kernel_hiprt
|
||||
ALL
|
||||
DEPENDS ${hiprt_hipfb}
|
||||
SOURCES ${SRC_KERNEL_DEVICE_HIPRT} ${SRC_KERNEL_DEVICE_HIPRT_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_hiprt)
|
||||
|
||||
source_group("device\\hiprt" FILES ${SRC_KERNEL_DEVICE_HIPRT} ${SRC_KERNEL_DEVICE_HIPRT_HEADERS})
|
||||
|
||||
add_dependencies(cycles_kernel cycles_kernel_hip)
|
||||
endif()
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIPRT}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hiprt)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIPRT_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hiprt)
|
||||
if(WITH_CYCLES_DEVICE_HIPRT)
|
||||
delayed_install(${HIPRT_INCLUDE_DIR} "${SRC_KERNEL_DEVICE_HIPRT_SDK_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hiprt/hiprt)
|
||||
delayed_install(${HIPRT_INCLUDE_DIR} "${SRC_KERNEL_DEVICE_HIPRT_SDK}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hiprt/hiprt/impl)
|
||||
endif()
|
||||
740
blender-5.2.0/intern/cycles/kernel/device/hiprt/bvh.h
Normal file
740
blender-5.2.0/intern/cycles/kernel/device/hiprt/bvh.h
Normal file
@@ -0,0 +1,740 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/bvh/intersect_filter.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct LocalPayload {
|
||||
RaySelfPrimitives self;
|
||||
float ray_time;
|
||||
int local_object;
|
||||
uint max_hits;
|
||||
uint *lcg_state;
|
||||
LocalIntersection *local_isect;
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Utilities.
|
||||
*/
|
||||
|
||||
ccl_device_forceinline void set_hiprt_ray(const ccl_private Ray &ray,
|
||||
ccl_private hiprtRay &ray_hip)
|
||||
{
|
||||
ray_hip.direction = ray.D;
|
||||
ray_hip.origin = ray.P;
|
||||
ray_hip.maxT = ray.tmax;
|
||||
ray_hip.minT = ray.tmin;
|
||||
}
|
||||
|
||||
ccl_device_inline void set_intersect_point(const hiprtHit &hit, ccl_private Intersection *isect)
|
||||
{
|
||||
const int object = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
|
||||
isect->t = hit.t;
|
||||
isect->u = hit.uv.x;
|
||||
isect->v = hit.uv.y;
|
||||
|
||||
isect->object = object;
|
||||
isect->type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
if (isect->type & PRIMITIVE_CURVE) {
|
||||
/* For curves the isect->type is a packed segment information, which is different from the
|
||||
* primitive type associated with the object. */
|
||||
|
||||
/* TODO(sergey): Try to solve this with less fetches.
|
||||
*
|
||||
* Ideally avoid having HIP-RT specific custom_prim_info tables, allowing them to be removed
|
||||
* in order to minimize the memory usage. */
|
||||
|
||||
const int2 data_offset = kernel_data_fetch(custom_prim_info_offset, object);
|
||||
const int2 prim_info = kernel_data_fetch(custom_prim_info, hit.primID + data_offset.x);
|
||||
isect->prim = prim_info.x + data_offset.y;
|
||||
isect->type = prim_info.y;
|
||||
}
|
||||
else {
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object);
|
||||
isect->prim = hit.primID + prim_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Custom intersection functions.
|
||||
*/
|
||||
|
||||
ccl_device_inline bool curve_custom_intersect(const hiprtRay &ray,
|
||||
BVHPayload *payload,
|
||||
hiprtHit &hit)
|
||||
|
||||
{
|
||||
/* Could also cast shadow payload to get the elements needed to do the intersection no need to
|
||||
* write a separate function for shadow intersection. */
|
||||
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object_id = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
|
||||
/* `data_offset.x`: where the data (prim id, type )for the geometry of the current object begins
|
||||
* the prim_id that is in hiprtHit hit is local to the particular geometry so we add the above
|
||||
* `ofstream` to map prim id in hiprtHit to the one compatible to what next stage expects
|
||||
* `data_offset.y`: the offset that has to be added to a local primitive to get the global
|
||||
* `primitive id = kernel_data_fetch(object_prim_offset, object_id);` */
|
||||
const int2 data_offset = kernel_data_fetch(custom_prim_info_offset, object_id);
|
||||
|
||||
const int prim_offset = data_offset.y;
|
||||
|
||||
const int2 prim_info = kernel_data_fetch(custom_prim_info, hit.primID + data_offset.x);
|
||||
const int curve_index = prim_info.x;
|
||||
const int key_value = prim_info.y;
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, payload->ray_self, object_id)) {
|
||||
return false; /* Ignore hit - continue traversal. */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (intersection_skip_self_shadow(payload->ray_self, object_id, curve_index + prim_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float ray_time = payload->ray_time;
|
||||
|
||||
if ((key_value & PRIMITIVE_MOTION) && kernel_data.bvh.use_bvh_steps) {
|
||||
const int time_offset = kernel_data_fetch(prim_time_offset, object_id);
|
||||
const float2 prims_time = kernel_data_fetch(prims_time, hit.primID + time_offset);
|
||||
if (ray_time < prims_time.x || ray_time > prims_time.y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Intersection isect;
|
||||
const bool b_hit = curve_intersect(kg,
|
||||
&isect,
|
||||
ray.origin,
|
||||
ray.direction,
|
||||
ray.minT,
|
||||
ray.maxT,
|
||||
object_id,
|
||||
curve_index + prim_offset,
|
||||
ray_time,
|
||||
key_value);
|
||||
if (b_hit) {
|
||||
hit.uv.x = isect.u;
|
||||
hit.uv.y = isect.v;
|
||||
hit.t = isect.t;
|
||||
}
|
||||
|
||||
return b_hit;
|
||||
}
|
||||
|
||||
ccl_device_inline bool motion_triangle_custom_intersect(const hiprtRay &ray,
|
||||
BVHPayload *payload,
|
||||
hiprtHit &hit)
|
||||
{
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object_id = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
const int2 data_offset = kernel_data_fetch(custom_prim_info_offset, object_id);
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object_id);
|
||||
|
||||
const int prim_id_local = kernel_data_fetch(custom_prim_info, hit.primID + data_offset.x).x;
|
||||
const int prim_id_global = prim_id_local + prim_offset;
|
||||
|
||||
if (intersection_skip_self_shadow(payload->ray_self, object_id, prim_id_global)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Intersection isect;
|
||||
const bool b_hit = motion_triangle_intersect(kg,
|
||||
&isect,
|
||||
ray.origin,
|
||||
ray.direction,
|
||||
ray.minT,
|
||||
ray.maxT,
|
||||
payload->ray_time,
|
||||
payload->ray_visibility,
|
||||
object_id,
|
||||
prim_id_global,
|
||||
hit.instanceID);
|
||||
|
||||
if (b_hit) {
|
||||
hit.uv.x = isect.u;
|
||||
hit.uv.y = isect.v;
|
||||
hit.t = isect.t;
|
||||
}
|
||||
|
||||
return b_hit;
|
||||
}
|
||||
|
||||
ccl_device_inline bool motion_triangle_custom_local_intersect(const hiprtRay &ray,
|
||||
LocalPayload *payload,
|
||||
hiprtHit &hit)
|
||||
{
|
||||
#ifdef __OBJECT_MOTION__
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object_id = payload->local_object;
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object_id);
|
||||
const int2 data_offset = kernel_data_fetch(custom_prim_info_offset, object_id);
|
||||
|
||||
const int prim_id_local = kernel_data_fetch(custom_prim_info, hit.primID + data_offset.x).x;
|
||||
const int prim_id_global = prim_id_local + prim_offset;
|
||||
|
||||
if (intersection_skip_self_local(payload->self, prim_id_global)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return motion_triangle_intersect_local(kg,
|
||||
payload->local_isect,
|
||||
ray.origin,
|
||||
ray.direction,
|
||||
payload->ray_time,
|
||||
object_id,
|
||||
prim_id_global,
|
||||
ray.minT,
|
||||
ray.maxT,
|
||||
payload->lcg_state,
|
||||
payload->max_hits);
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline bool motion_triangle_custom_volume_intersect(const hiprtRay &ray,
|
||||
BVHPayload *payload,
|
||||
hiprtHit &hit)
|
||||
{
|
||||
#ifdef __OBJECT_MOTION__
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
|
||||
const int2 data_offset = kernel_data_fetch(custom_prim_info_offset, object);
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object);
|
||||
|
||||
const int prim_id_local = kernel_data_fetch(custom_prim_info, hit.primID + data_offset.x).x;
|
||||
const int prim = prim_id_local + prim_offset;
|
||||
|
||||
if (bvh_volume_anyhit_triangle_filter(
|
||||
kg, object, prim, payload->ray_self, payload->ray_visibility))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Intersection isect;
|
||||
const bool b_hit = motion_triangle_intersect(kg,
|
||||
&isect,
|
||||
ray.origin,
|
||||
ray.direction,
|
||||
ray.minT,
|
||||
ray.maxT,
|
||||
payload->ray_time,
|
||||
payload->ray_visibility,
|
||||
object,
|
||||
prim,
|
||||
prim_id_local);
|
||||
|
||||
if (b_hit) {
|
||||
hit.uv.x = isect.u;
|
||||
hit.uv.y = isect.v;
|
||||
hit.t = isect.t;
|
||||
}
|
||||
|
||||
return b_hit;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline bool point_custom_intersect(const hiprtRay &ray,
|
||||
BVHPayload *payload,
|
||||
hiprtHit &hit)
|
||||
{
|
||||
#if defined(__POINTCLOUD__)
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object_id = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
const int2 data_offset = kernel_data_fetch(custom_prim_info_offset, object_id);
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object_id);
|
||||
|
||||
const int2 prim_info = kernel_data_fetch(custom_prim_info, hit.primID + data_offset.x);
|
||||
const int prim_id_local = prim_info.x;
|
||||
const int prim_id_global = prim_id_local + prim_offset;
|
||||
|
||||
const int primitive_type = prim_info.y;
|
||||
|
||||
# ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, payload->ray_self, object_id)) {
|
||||
return false; /* Ignore hit - continue traversal */
|
||||
}
|
||||
# endif
|
||||
|
||||
if (intersection_skip_self_shadow(payload->ray_self, object_id, prim_id_global)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float ray_time = payload->ray_time;
|
||||
|
||||
if ((primitive_type & PRIMITIVE_MOTION_POINT) && kernel_data.bvh.use_bvh_steps) {
|
||||
const int time_offset = kernel_data_fetch(prim_time_offset, object_id);
|
||||
const float2 prims_time = kernel_data_fetch(prims_time, hit.primID + time_offset);
|
||||
if (ray_time < prims_time.x || ray_time > prims_time.y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Intersection isect;
|
||||
const bool b_hit = point_intersect(kg,
|
||||
&isect,
|
||||
ray.origin,
|
||||
ray.direction,
|
||||
ray.minT,
|
||||
ray.maxT,
|
||||
object_id,
|
||||
prim_id_global,
|
||||
ray_time,
|
||||
primitive_type);
|
||||
|
||||
if (b_hit) {
|
||||
hit.uv.x = isect.u;
|
||||
hit.uv.y = isect.v;
|
||||
hit.t = isect.t;
|
||||
}
|
||||
|
||||
return b_hit;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Intersection filters.
|
||||
*/
|
||||
|
||||
ccl_device_inline bool closest_intersection_filter(const hiprtRay &ray,
|
||||
BVHPayload *payload,
|
||||
const hiprtHit &hit)
|
||||
{
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object_id = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object_id);
|
||||
const int prim = hit.primID + prim_offset;
|
||||
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(kg, payload->ray_self, object_id)) {
|
||||
return true; /* Ignore hit - continue traversal. */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (intersection_skip_self_shadow(payload->ray_self, object_id, prim)) {
|
||||
return true; /* Ignore hit - continue traversal. */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ccl_device_inline bool shadow_intersection_filter(const hiprtRay &ray,
|
||||
BVHShadowAllPayload *payload,
|
||||
const hiprtHit &hit)
|
||||
|
||||
{
|
||||
KernelGlobals kg = nullptr;
|
||||
Intersection isect;
|
||||
set_intersect_point(hit, &isect);
|
||||
return bvh_shadow_all_anyhit_filter<ISECT_TEST_ALL, PRIMITIVE_ALL & ~PRIMITIVE_CURVE>(
|
||||
kg, payload->state, *payload, payload->ray_self, payload->ray_visibility, isect);
|
||||
}
|
||||
|
||||
ccl_device_inline bool shadow_intersection_filter_curve(const hiprtRay &ray,
|
||||
BVHShadowAllPayload *payload,
|
||||
const hiprtHit &hit)
|
||||
|
||||
{
|
||||
KernelGlobals kg = nullptr;
|
||||
Intersection isect;
|
||||
set_intersect_point(hit, &isect);
|
||||
return bvh_shadow_all_anyhit_filter<ISECT_TEST_ALL, PRIMITIVE_CURVE>(
|
||||
kg, payload->state, *payload, payload->ray_self, payload->ray_visibility, isect);
|
||||
}
|
||||
|
||||
ccl_device_inline bool local_intersection_filter(const hiprtRay &ray,
|
||||
LocalPayload *payload,
|
||||
const hiprtHit &hit)
|
||||
{
|
||||
#ifdef __BVH_LOCAL__
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object_id = payload->local_object;
|
||||
const uint max_hits = payload->max_hits;
|
||||
|
||||
/* Triangle primitive uses hardware intersection, other primitives do custom intersection
|
||||
* which does reservoir sampling for intersections. For the custom primitives only check
|
||||
* whether we can stop traversal early on. The rest of the checks here only do for the
|
||||
* regular triangles. */
|
||||
const int primitive_type = kernel_data_fetch(objects, object_id).primitive_type;
|
||||
if (primitive_type != PRIMITIVE_TRIANGLE) {
|
||||
if (max_hits == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object_id);
|
||||
const int prim = hit.primID + prim_offset;
|
||||
# ifndef __RAY_OFFSET__
|
||||
if (intersection_skip_self_local(payload->self, prim)) {
|
||||
return true; /* Continue search. */
|
||||
}
|
||||
# endif
|
||||
|
||||
if (max_hits == 0) {
|
||||
return false; /* Stop search. */
|
||||
}
|
||||
|
||||
const int hit_index = local_intersect_get_record_index(
|
||||
payload->local_isect, hit.t, payload->lcg_state, max_hits);
|
||||
if (hit_index == -1) {
|
||||
return true; /* Continue search. */
|
||||
}
|
||||
|
||||
Intersection *isect = &payload->local_isect->hits[hit_index];
|
||||
isect->t = hit.t;
|
||||
isect->u = hit.uv.x;
|
||||
isect->v = hit.uv.y;
|
||||
isect->prim = prim;
|
||||
isect->object = object_id;
|
||||
isect->type = primitive_type;
|
||||
|
||||
payload->local_isect->Ng[hit_index] = hit.normal;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_inline bool volume_triangle_intersection_filter(const hiprtRay &ray,
|
||||
BVHPayload *payload,
|
||||
const hiprtHit &hit)
|
||||
{
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object = kernel_data_fetch(user_instance_id, hit.instanceID);
|
||||
const int prim_offset = kernel_data_fetch(object_prim_offset, object);
|
||||
const int prim = hit.primID + prim_offset;
|
||||
|
||||
if (bvh_volume_anyhit_triangle_filter(
|
||||
kg, object, prim, payload->ray_self, payload->ray_visibility))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HIPRT_DEVICE bool intersectFunc(const uint geom_type,
|
||||
const uint ray_type,
|
||||
const hiprtFuncTableHeader &tableHeader,
|
||||
const hiprtRay &ray,
|
||||
void *payload,
|
||||
hiprtHit &hit)
|
||||
{
|
||||
const uint index = tableHeader.numGeomTypes * ray_type + geom_type;
|
||||
switch (index) {
|
||||
case Curve_Intersect_Function:
|
||||
case Curve_Intersect_Shadow:
|
||||
return curve_custom_intersect(ray, (BVHPayload *)payload, hit);
|
||||
case Motion_Triangle_Intersect_Function:
|
||||
case Motion_Triangle_Intersect_Shadow:
|
||||
return motion_triangle_custom_intersect(ray, (BVHPayload *)payload, hit);
|
||||
case Motion_Triangle_Intersect_Local:
|
||||
return motion_triangle_custom_local_intersect(ray, (LocalPayload *)payload, hit);
|
||||
case Motion_Triangle_Intersect_Volume:
|
||||
return motion_triangle_custom_volume_intersect(ray, (BVHPayload *)payload, hit);
|
||||
case Point_Intersect_Function:
|
||||
case Point_Intersect_Shadow:
|
||||
return point_custom_intersect(ray, (BVHPayload *)payload, hit);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HIPRT_DEVICE bool filterFunc(const uint geom_type,
|
||||
const uint ray_type,
|
||||
const hiprtFuncTableHeader &tableHeader,
|
||||
const hiprtRay &ray,
|
||||
void *payload,
|
||||
const hiprtHit &hit)
|
||||
{
|
||||
const uint index = tableHeader.numGeomTypes * ray_type + geom_type;
|
||||
switch (index) {
|
||||
case Triangle_Filter_Closest:
|
||||
return closest_intersection_filter(ray, (BVHPayload *)payload, hit);
|
||||
case Curve_Filter_Shadow:
|
||||
return shadow_intersection_filter_curve(ray, (BVHShadowAllPayload *)payload, hit);
|
||||
case Triangle_Filter_Shadow:
|
||||
case Motion_Triangle_Filter_Shadow:
|
||||
case Point_Filter_Shadow:
|
||||
return shadow_intersection_filter(ray, (BVHShadowAllPayload *)payload, hit);
|
||||
case Triangle_Filter_Local:
|
||||
case Motion_Triangle_Filter_Local:
|
||||
return local_intersection_filter(ray, (LocalPayload *)payload, hit);
|
||||
case Triangle_Filter_Volume:
|
||||
return volume_triangle_intersection_filter(ray, (BVHPayload *)payload, hit);
|
||||
case Motion_Triangle_Filter_Volume:
|
||||
/* Motion triangle volume filtering is done in the custom intersection function. */
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/* --------------------------------------------------------------------
|
||||
* BVH functions.
|
||||
*/
|
||||
|
||||
ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility,
|
||||
ccl_private Intersection *isect)
|
||||
{
|
||||
isect->t = ray->tmax;
|
||||
isect->u = 0.0f;
|
||||
isect->v = 0.0f;
|
||||
isect->prim = PRIM_NONE;
|
||||
isect->object = OBJECT_NONE;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
isect->t = ray->tmax;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kernel_data.device_bvh == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hiprtRay ray_hip;
|
||||
set_hiprt_ray(*ray, ray_hip);
|
||||
|
||||
BVHPayload payload;
|
||||
payload.ray_self = ray->self;
|
||||
payload.ray_visibility = visibility;
|
||||
payload.ray_time = ray->time;
|
||||
|
||||
Stack stack(kg->global_stack_buffer, kg->shared_stack);
|
||||
Instance_Stack instance_stack;
|
||||
|
||||
hiprtHit hit;
|
||||
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
|
||||
hiprtSceneTraversalAnyHitCustomStack traversal((hiprtScene)kernel_data.device_bvh,
|
||||
ray_hip,
|
||||
stack,
|
||||
instance_stack,
|
||||
visibility,
|
||||
hiprtTraversalHintDefault,
|
||||
&payload,
|
||||
kernel_params.table_closest_intersect,
|
||||
0 /* RAY_TYPE */,
|
||||
ray->time);
|
||||
hit = traversal.getNextHit();
|
||||
}
|
||||
else {
|
||||
hiprtSceneTraversalClosestCustomStack traversal((hiprtScene)kernel_data.device_bvh,
|
||||
ray_hip,
|
||||
stack,
|
||||
instance_stack,
|
||||
visibility,
|
||||
hiprtTraversalHintDefault,
|
||||
&payload,
|
||||
kernel_params.table_closest_intersect,
|
||||
0 /* RAY_TYPE */,
|
||||
ray->time);
|
||||
|
||||
hit = traversal.getNextHit();
|
||||
}
|
||||
|
||||
if (hit.hasHit()) {
|
||||
set_intersect_point(hit, isect);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ccl_device_intersect bool scene_intersect_shadow(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility)
|
||||
{
|
||||
Intersection isect;
|
||||
return scene_intersect(kg, ray, visibility, &isect);
|
||||
}
|
||||
|
||||
#ifdef __BVH_LOCAL__
|
||||
template<bool single_hit = false>
|
||||
ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
if (local_isect != nullptr) {
|
||||
local_isect->num_hits = 0;
|
||||
}
|
||||
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int primitive_type = kernel_data_fetch(objects, local_object).primitive_type;
|
||||
if (!(primitive_type & PRIMITIVE_TRIANGLE)) {
|
||||
/* Local intersection functions are only considering triangle and motion triangle primitives.
|
||||
* If the local intersection is requested from other primitives (curve or point cloud) perform
|
||||
* an early return to avoid tree traversal with no primitive intersection. */
|
||||
return false;
|
||||
}
|
||||
|
||||
float3 P = ray->P;
|
||||
float3 dir = bvh_clamp_direction(ray->D);
|
||||
float3 idir = bvh_inverse_direction(dir);
|
||||
|
||||
const uint object_flag = kernel_data_fetch(object_flag, local_object);
|
||||
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
# ifdef __OBJECT_MOTION__
|
||||
bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir);
|
||||
# else
|
||||
bvh_instance_push(kg, local_object, ray, &P, &dir, &idir);
|
||||
# endif
|
||||
}
|
||||
|
||||
hiprtRay ray_hip;
|
||||
ray_hip.origin = P;
|
||||
ray_hip.direction = dir;
|
||||
ray_hip.maxT = ray->tmax;
|
||||
ray_hip.minT = ray->tmin;
|
||||
|
||||
LocalPayload payload = {0};
|
||||
payload.self = ray->self;
|
||||
payload.ray_time = ray->time;
|
||||
payload.local_object = local_object;
|
||||
payload.max_hits = max_hits;
|
||||
payload.lcg_state = lcg_state;
|
||||
payload.local_isect = local_isect;
|
||||
|
||||
Stack stack(kg->global_stack_buffer, kg->shared_stack);
|
||||
Instance_Stack instance_stack;
|
||||
|
||||
hiprtGeometry local_geom = (hiprtGeometry)(kernel_data_fetch(blas_ptr, local_object));
|
||||
|
||||
hiprtHit hit;
|
||||
if (primitive_type == PRIMITIVE_MOTION_TRIANGLE) {
|
||||
/* Motion triangle BVH uses custom primitives which requires custom traversal. */
|
||||
hiprtGeomCustomTraversalAnyHitCustomStack traversal(local_geom,
|
||||
ray_hip,
|
||||
stack,
|
||||
hiprtTraversalHintDefault,
|
||||
&payload,
|
||||
kernel_params.table_local_intersect,
|
||||
2);
|
||||
hit = traversal.getNextHit();
|
||||
}
|
||||
else {
|
||||
hiprtGeomTraversalAnyHitCustomStack traversal(local_geom,
|
||||
ray_hip,
|
||||
stack,
|
||||
hiprtTraversalHintDefault,
|
||||
&payload,
|
||||
kernel_params.table_local_intersect,
|
||||
2);
|
||||
hit = traversal.getNextHit();
|
||||
}
|
||||
|
||||
return hit.hasHit();
|
||||
}
|
||||
#endif /*__BVH_LOCAL__ */
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
ccl_device_inline void scene_intersect_shadow_all_hiprt(
|
||||
KernelGlobals kg,
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
ccl_private BVHShadowAllPayload &ccl_restrict payload)
|
||||
{
|
||||
hiprtRay ray_hip;
|
||||
set_hiprt_ray(*ray, ray_hip);
|
||||
|
||||
Stack stack(kg->global_stack_buffer, kg->shared_stack);
|
||||
Instance_Stack instance_stack;
|
||||
|
||||
hiprtSceneTraversalAnyHitCustomStack traversal((hiprtScene)kernel_data.device_bvh,
|
||||
ray_hip,
|
||||
stack,
|
||||
instance_stack,
|
||||
payload.ray_visibility,
|
||||
hiprtTraversalHintDefault,
|
||||
&payload,
|
||||
kernel_params.table_shadow_intersect,
|
||||
1 /* RAY_TYPE */,
|
||||
ray->time);
|
||||
|
||||
const hiprtHit hit = traversal.getNextHit();
|
||||
(void)hit;
|
||||
}
|
||||
#endif /* __TRANSPARENT_SHADOWS__ */
|
||||
|
||||
#ifdef __VOLUME__
|
||||
ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
isect->t = ray->tmax;
|
||||
isect->u = 0.0f;
|
||||
isect->v = 0.0f;
|
||||
isect->prim = PRIM_NONE;
|
||||
isect->object = OBJECT_NONE;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
|
||||
if (!intersection_ray_valid(ray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hiprtRay ray_hip;
|
||||
set_hiprt_ray(*ray, ray_hip);
|
||||
|
||||
BVHPayload payload;
|
||||
payload.ray_self = ray->self;
|
||||
payload.ray_visibility = visibility;
|
||||
payload.ray_time = ray->time;
|
||||
|
||||
Stack stack(kg->global_stack_buffer, kg->shared_stack);
|
||||
Instance_Stack instance_stack;
|
||||
|
||||
hiprtSceneTraversalClosestCustomStack traversal((hiprtScene)kernel_data.device_bvh,
|
||||
ray_hip,
|
||||
stack,
|
||||
instance_stack,
|
||||
visibility,
|
||||
hiprtTraversalHintDefault,
|
||||
&payload,
|
||||
kernel_params.table_volume_intersect,
|
||||
3 /* RAY_TYPE */,
|
||||
ray->time);
|
||||
|
||||
const hiprtHit hit = traversal.getNextHit();
|
||||
if (hit.hasHit()) {
|
||||
set_intersect_point(hit, isect);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* __VOLUME__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
153
blender-5.2.0/intern/cycles/kernel/device/hiprt/globals.h
Normal file
153
blender-5.2.0/intern/cycles/kernel/device/hiprt/globals.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/util/profiler.h" // IWYU pragma: export
|
||||
|
||||
#include "util/color.h" // IWYU pragma: export
|
||||
#include "util/types_image.h" // IWYU pragma: export
|
||||
|
||||
/* The size of global stack available to each thread (memory reserved for each thread in
|
||||
* global_stack_buffer). */
|
||||
#define HIPRT_THREAD_STACK_SIZE 64
|
||||
|
||||
/* LDS (Local Data Storage) allocation for each thread, the number is obtained empirically. */
|
||||
#define HIPRT_SHARED_STACK_SIZE 24
|
||||
|
||||
/* HIPRT_THREAD_GROUP_SIZE is the number of threads per work group for intersection kernels
|
||||
* The default number of threads per work-group is 1024, however, since HIP RT intersection kernels
|
||||
* use local memory, and the local memory size in those kernels scales up with the number of
|
||||
* threads, the number of threads to is scaled down to 256 to avoid going over maximum local memory
|
||||
* and to strike a balance between memory access and the number of waves.
|
||||
*
|
||||
* Total local stack size would be number of threads * HIPRT_SHARED_STACK_SIZE. */
|
||||
#define HIPRT_THREAD_GROUP_SIZE 256
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct KernelGlobalsGPU {
|
||||
hiprtGlobalStackBuffer global_stack_buffer;
|
||||
hiprtSharedStackBuffer shared_stack;
|
||||
};
|
||||
|
||||
using KernelGlobals = ccl_global KernelGlobalsGPU *ccl_restrict;
|
||||
|
||||
/* This macro allocates shared memory and to pass the shared memory down to intersection functions
|
||||
* KernelGlobals is used. */
|
||||
#define HIPRT_INIT_KERNEL_GLOBAL() \
|
||||
ccl_gpu_shared int shared_stack[HIPRT_SHARED_STACK_SIZE * HIPRT_THREAD_GROUP_SIZE]; \
|
||||
ccl_global KernelGlobalsGPU kg_gpu; \
|
||||
KernelGlobals kg = &kg_gpu; \
|
||||
kg->shared_stack.stackData = &shared_stack[0]; \
|
||||
kg->shared_stack.stackSize = HIPRT_SHARED_STACK_SIZE; \
|
||||
kg->global_stack_buffer = stack_buffer;
|
||||
|
||||
struct KernelParamsHIPRT {
|
||||
KernelData data;
|
||||
#define KERNEL_DATA_ARRAY(type, name) const type *name;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) type *name;
|
||||
KERNEL_DATA_ARRAY(int, user_instance_id)
|
||||
KERNEL_DATA_ARRAY(uint64_t, blas_ptr)
|
||||
KERNEL_DATA_ARRAY(int2, custom_prim_info)
|
||||
KERNEL_DATA_ARRAY(int2, custom_prim_info_offset)
|
||||
KERNEL_DATA_ARRAY(float2, prims_time)
|
||||
KERNEL_DATA_ARRAY(int, prim_time_offset)
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
/* Integrator state */
|
||||
IntegratorStateGPU integrator_state;
|
||||
|
||||
hiprtFuncTable table_closest_intersect;
|
||||
hiprtFuncTable table_shadow_intersect;
|
||||
hiprtFuncTable table_local_intersect;
|
||||
hiprtFuncTable table_volume_intersect;
|
||||
};
|
||||
|
||||
/* Intersection_Function_Table_Index defines index values to retrieve custom intersection
|
||||
* functions from function table. */
|
||||
|
||||
enum Intersection_Function_Table_Index {
|
||||
// Triangles use the intersection function provided by HIP RT and don't need custom intersection
|
||||
// functions
|
||||
// Custom intersection functions for closest intersect.
|
||||
Curve_Intersect_Function = 1, // Custom intersection for curves
|
||||
Motion_Triangle_Intersect_Function, // Custom intersection for triangles with vertex motion blur
|
||||
// attributes.
|
||||
Point_Intersect_Function, // Custom intersection for point cloud.
|
||||
// Custom intersection functions for shadow rendering are the same as the function for closest
|
||||
// intersect.
|
||||
// However, the table indices are different
|
||||
Triangle_Intersect_Shadow_None,
|
||||
Curve_Intersect_Shadow,
|
||||
Motion_Triangle_Intersect_Shadow,
|
||||
Point_Intersect_Shadow,
|
||||
// Custom intersection functions for subsurface scattering.
|
||||
// Only motion triangles have valid custom intersection function
|
||||
Triangle_Intersect_Local_None,
|
||||
Curve_Intersect_Local_None,
|
||||
Motion_Triangle_Intersect_Local,
|
||||
Point_Intersect_Local_None,
|
||||
// Custom intersection functions for volume rendering.
|
||||
// Only motion triangles have valid custom intersection function
|
||||
Triangle_Intersect_Volume_None,
|
||||
Curve_Intersect_Volume_None,
|
||||
Motion_Triangle_Intersect_Volume,
|
||||
Point_Intersect_Volume_None,
|
||||
};
|
||||
|
||||
// Filter functions, filter hits, i.e. test whether a hit should be accepted or not, and whether
|
||||
// traversal should stop or continue.
|
||||
enum Filter_Function_Table_Index {
|
||||
Triangle_Filter_Closest = 0, // Filter function for triangles for closest intersect, no custom
|
||||
// intersection function is needed.
|
||||
Curve_Filter_Opaque_None, // No filter function is needed and everything is handled in the
|
||||
// intersection function.
|
||||
Motion_Triangle_Filter_Opaque_None, // No filter function is needed and everything is handled in
|
||||
// intersection function.
|
||||
Point_Filter_Opaque_Non, // No filter function is needed.
|
||||
// Filter function for all primitives for shadow intersection.
|
||||
// All primitives use the same function but each has a different index in the table.
|
||||
Triangle_Filter_Shadow,
|
||||
Curve_Filter_Shadow,
|
||||
Motion_Triangle_Filter_Shadow,
|
||||
Point_Filter_Shadow,
|
||||
// Filter functions for subsurface scattering. Triangles and motion triangles need function
|
||||
// assignment. They indices for triangles and motion triangles point to the same function. Points
|
||||
// and curves dont need any function since subsurface scattering is not applied on either.
|
||||
Triangle_Filter_Local, // Filter functions for triangles
|
||||
Curve_Filter_Local_None, // Subsurface scattering is not applied on curves, no filter function
|
||||
// is
|
||||
// needed.
|
||||
Motion_Triangle_Filter_Local,
|
||||
Point_Filter_Local_None,
|
||||
// Filter functions for volume rendering.
|
||||
// Volume rendering only applies to triangles and motion triangles.
|
||||
// Triangles and motion triangles use the same filter functions for volume rendering
|
||||
Triangle_Filter_Volume,
|
||||
Curve_Filter_Volume_None,
|
||||
Motion_Triangle_Filter_Volume,
|
||||
Point_Filter_Volume_None,
|
||||
};
|
||||
|
||||
#ifdef __KERNEL_GPU__
|
||||
__constant__ KernelParamsHIPRT kernel_params;
|
||||
|
||||
typedef hiprtGlobalStack Stack;
|
||||
typedef hiprtEmptyInstanceStack Instance_Stack;
|
||||
#endif
|
||||
|
||||
/* Abstraction macros */
|
||||
#define kernel_data kernel_params.data
|
||||
#define kernel_data_fetch(name, index) kernel_params.name[(index)]
|
||||
#define kernel_data_write(name, index, value) kernel_params.name[(index)] = (value)
|
||||
#define kernel_data_array(name) (kernel_params.name)
|
||||
#define kernel_integrator_state kernel_params.integrator_state
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
140
blender-5.2.0/intern/cycles/kernel/device/hiprt/kernel.cpp
Normal file
140
blender-5.2.0/intern/cycles/kernel/device/hiprt/kernel.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef __HIP_DEVICE_COMPILE__
|
||||
|
||||
# include "kernel/device/hip/compat.h"
|
||||
# include "kernel/device/hip/config.h"
|
||||
|
||||
# include <hiprt/impl/hiprt_device_impl.h>
|
||||
|
||||
# include "kernel/device/hiprt/globals.h"
|
||||
|
||||
# include "kernel/device/gpu/image.h"
|
||||
|
||||
# include "kernel/tables.h"
|
||||
|
||||
# include "kernel/integrator/state.h"
|
||||
# include "kernel/integrator/state_flow.h"
|
||||
# include "kernel/integrator/state_util.h"
|
||||
|
||||
# include "kernel/integrator/intersect_closest.h"
|
||||
# include "kernel/integrator/intersect_dedicated_light.h"
|
||||
# include "kernel/integrator/intersect_mnee.h"
|
||||
# include "kernel/integrator/intersect_shadow.h"
|
||||
# include "kernel/integrator/intersect_subsurface.h"
|
||||
# include "kernel/integrator/intersect_volume_stack.h"
|
||||
# include "kernel/integrator/shade_surface.h"
|
||||
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_closest,
|
||||
const ccl_global int *path_index_array,
|
||||
ccl_global float *render_buffer,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_closest(kg, state, render_buffer));
|
||||
}
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_shadow,
|
||||
const ccl_global int *path_index_array,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_shadow(kg, state));
|
||||
}
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_subsurface,
|
||||
const ccl_global int *path_index_array,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_subsurface(kg, state));
|
||||
}
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_volume_stack,
|
||||
const ccl_global int *path_index_array,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_volume_stack(kg, state));
|
||||
}
|
||||
}
|
||||
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_dedicated_light,
|
||||
const ccl_global int *path_index_array,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_dedicated_light(kg, state));
|
||||
}
|
||||
}
|
||||
|
||||
ccl_gpu_kernel_postfix
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_shade_surface_raytrace,
|
||||
const ccl_global int *path_index_array,
|
||||
ccl_global float *render_buffer,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_shade_surface_raytrace(kg, state, render_buffer));
|
||||
}
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
ccl_gpu_kernel_threads(GPU_HIPRT_KERNEL_BLOCK_NUM_THREADS)
|
||||
ccl_gpu_kernel_signature(integrator_intersect_mnee,
|
||||
const ccl_global int *path_index_array,
|
||||
const int work_size,
|
||||
ccl_global hiprtGlobalStackBuffer stack_buffer)
|
||||
{
|
||||
const int global_index = ccl_gpu_global_id_x();
|
||||
if (global_index < work_size) {
|
||||
HIPRT_INIT_KERNEL_GLOBAL()
|
||||
const int state = (path_index_array) ? path_index_array[global_index] : global_index;
|
||||
ccl_gpu_kernel_call(integrator_intersect_mnee(kg, state));
|
||||
}
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
#endif /* __HIP_DEVICE_COMPILE__ */
|
||||
@@ -0,0 +1,45 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_METAL
|
||||
kernel.metal
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_METAL_HEADERS
|
||||
bvh.h
|
||||
compat.h
|
||||
context_begin.h
|
||||
context_end.h
|
||||
function_constants.h
|
||||
globals.h
|
||||
)
|
||||
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_DEVICE_METAL)
|
||||
add_library(cycles_kernel_metal INTERFACE)
|
||||
target_sources(cycles_kernel_metal INTERFACE
|
||||
${SRC_KERNEL_DEVICE_METAL}
|
||||
${SRC_KERNEL_DEVICE_METAL_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_metal)
|
||||
|
||||
source_group("device\\metal" FILES ${SRC_KERNEL_DEVICE_METAL} ${SRC_KERNEL_DEVICE_METAL_HEADERS})
|
||||
|
||||
add_dependencies(cycles_kernel cycles_kernel_metal)
|
||||
endif()
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_METAL}" ${CYCLES_INSTALL_PATH}/source/kernel/device/metal)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_METAL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/metal)
|
||||
573
blender-5.2.0/intern/cycles/kernel/device/metal/bvh.h
Normal file
573
blender-5.2.0/intern/cycles/kernel/device/metal/bvh.h
Normal file
@@ -0,0 +1,573 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* MetalRT implementation of ray-scene intersection. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/bvh/types.h"
|
||||
#include "kernel/bvh/util.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Payload types.
|
||||
*
|
||||
* Best practice is to minimize the size of MetalRT payloads to avoid heavy spilling during
|
||||
* intersection tests.
|
||||
*/
|
||||
|
||||
struct MetalRTIntersectionPayload {
|
||||
int self_prim;
|
||||
int self_object;
|
||||
uint visibility;
|
||||
};
|
||||
|
||||
struct MetalRTIntersectionLocalPayload_single_hit {
|
||||
int self_prim;
|
||||
#if defined(__METALRT_MOTION__)
|
||||
int self_object;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct MetalRTLocalHit {
|
||||
uint prim;
|
||||
float t, u, v;
|
||||
};
|
||||
|
||||
/* Payload for the local intersection queries.
|
||||
* It embeds a subset of storage that is typically found in the LocalIntersection. This is because
|
||||
* it is not possible to store a pointer to the actual LocalIntersection in the payload. So some
|
||||
* data is duplicated into the payload and then copied back to the LocalIntersection. */
|
||||
struct MetalRTIntersectionLocalPayload {
|
||||
int self_prim;
|
||||
#if defined(__METALRT_MOTION__)
|
||||
int self_object;
|
||||
#endif
|
||||
|
||||
uint lcg_state;
|
||||
MetalRTLocalHit hits[LOCAL_MAX_HITS];
|
||||
int max_hits;
|
||||
int num_hits;
|
||||
bool has_lcg_state;
|
||||
};
|
||||
|
||||
struct MetalRTIntersectionShadowPayload {
|
||||
RaySelfPrimitives self;
|
||||
uint visibility;
|
||||
};
|
||||
|
||||
#ifdef __HAIR__
|
||||
ccl_device_forceinline bool curve_ribbon_accept(KernelGlobals kg,
|
||||
const float u,
|
||||
float t,
|
||||
const ccl_private Ray *ray,
|
||||
const int object,
|
||||
const int prim,
|
||||
const int type)
|
||||
{
|
||||
KernelCurve kcurve = kernel_data_fetch(curves, prim);
|
||||
|
||||
int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
|
||||
int k1 = k0 + 1;
|
||||
int ka = max(k0 - 1, kcurve.first_key);
|
||||
int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
|
||||
|
||||
/* We can ignore motion blur here because we don't need the positions, and it doesn't affect the
|
||||
* radius. */
|
||||
const int position_offset = kernel_data_fetch(objects, object).position_offset;
|
||||
float radius[4];
|
||||
radius[0] = kernel_data_fetch(curve_keys, position_offset + ka).w;
|
||||
radius[1] = kernel_data_fetch(curve_keys, position_offset + k0).w;
|
||||
radius[2] = kernel_data_fetch(curve_keys, position_offset + k1).w;
|
||||
radius[3] = kernel_data_fetch(curve_keys, position_offset + kb).w;
|
||||
const float r = metal::catmull_rom(u, radius[0], radius[1], radius[2], radius[3]);
|
||||
|
||||
/* MPJ TODO: Can we ignore motion and/or object transforms here? Depends on scaling? */
|
||||
float3 ray_P = ray->P;
|
||||
float3 ray_D = ray->D;
|
||||
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
float3 idir;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
bvh_instance_motion_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
|
||||
# else
|
||||
bvh_instance_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
|
||||
# endif
|
||||
}
|
||||
|
||||
/* ignore self intersections */
|
||||
const float avoidance_factor = 2.0f;
|
||||
return t * len(ray_D) > avoidance_factor * r;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float curve_ribbon_v(KernelGlobals kg,
|
||||
const float u,
|
||||
float t,
|
||||
const ccl_private Ray *ray,
|
||||
const int object,
|
||||
const int prim,
|
||||
const int type)
|
||||
{
|
||||
# if defined(__METALRT_MOTION__)
|
||||
float time = ray->time;
|
||||
# else
|
||||
float time = 0.0f;
|
||||
# endif
|
||||
|
||||
const bool is_motion = (type & PRIMITIVE_MOTION);
|
||||
|
||||
KernelCurve kcurve = kernel_data_fetch(curves, prim);
|
||||
|
||||
int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
|
||||
int k1 = k0 + 1;
|
||||
int ka = max(k0 - 1, kcurve.first_key);
|
||||
int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
|
||||
|
||||
float4 curve[4];
|
||||
if (!is_motion) {
|
||||
const int position_offset = kernel_data_fetch(objects, object).position_offset;
|
||||
curve[0] = kernel_data_fetch(curve_keys, position_offset + ka);
|
||||
curve[1] = kernel_data_fetch(curve_keys, position_offset + k0);
|
||||
curve[2] = kernel_data_fetch(curve_keys, position_offset + k1);
|
||||
curve[3] = kernel_data_fetch(curve_keys, position_offset + kb);
|
||||
}
|
||||
else {
|
||||
motion_curve_keys(kg, object, time, ka, k0, k1, kb, curve);
|
||||
}
|
||||
|
||||
float3 ray_P = ray->P;
|
||||
float3 ray_D = ray->D;
|
||||
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
float3 idir;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
bvh_instance_motion_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
|
||||
# else
|
||||
bvh_instance_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
|
||||
# endif
|
||||
}
|
||||
|
||||
const float4 P_curve4 = metal::catmull_rom(u, curve[0], curve[1], curve[2], curve[3]);
|
||||
const float r_curve = P_curve4.w;
|
||||
|
||||
float3 P = ray_P + ray_D * t;
|
||||
const float3 P_curve = make_float3(P_curve4);
|
||||
|
||||
const float4 dPdu4 = metal::catmull_rom_derivative(u, curve[0], curve[1], curve[2], curve[3]);
|
||||
const float3 dPdu = make_float3(dPdu4);
|
||||
|
||||
const float3 tangent = normalize(dPdu);
|
||||
const float3 bitangent = normalize(cross(tangent, -ray_D));
|
||||
|
||||
float v = dot(P - P_curve, bitangent) / r_curve;
|
||||
return clamp(v, -1.0, 1.0f);
|
||||
}
|
||||
#endif /* __HAIR__ */
|
||||
|
||||
/* Scene intersection. */
|
||||
|
||||
ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility,
|
||||
ccl_private Intersection *isect)
|
||||
{
|
||||
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
|
||||
metalrt_intersector_type metalrt_intersect;
|
||||
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
|
||||
metalrt_intersect.assume_geometry_type(
|
||||
metal::raytracing::geometry_type::triangle |
|
||||
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
|
||||
metal::raytracing::geometry_type::none) |
|
||||
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
|
||||
metal::raytracing::geometry_type::none));
|
||||
|
||||
typename metalrt_intersector_type::result_type intersection;
|
||||
|
||||
MetalRTIntersectionPayload payload;
|
||||
payload.self_prim = ray->self.prim;
|
||||
payload.self_object = ray->self.object;
|
||||
payload.visibility = visibility;
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
|
||||
#if defined(__METALRT_MOTION__)
|
||||
intersection = metalrt_intersect.intersect(r,
|
||||
metal_ancillaries->accel_struct,
|
||||
ray_mask,
|
||||
ray->time,
|
||||
metal_ancillaries->ift_default,
|
||||
payload);
|
||||
#else
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_default, payload);
|
||||
#endif
|
||||
|
||||
if (intersection.type == intersection_type::none) {
|
||||
isect->t = ray->tmax;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isect->object = intersection.instance_id;
|
||||
isect->t = intersection.distance;
|
||||
if (intersection.type == intersection_type::triangle) {
|
||||
isect->prim = intersection.primitive_id + intersection.user_instance_id;
|
||||
isect->type = kernel_data_fetch(objects, intersection.instance_id).primitive_type;
|
||||
isect->u = intersection.triangle_barycentric_coord.x;
|
||||
isect->v = intersection.triangle_barycentric_coord.y;
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
else if (kernel_data.bvh.have_curves && intersection.type == intersection_type::curve) {
|
||||
int prim = intersection.primitive_id + intersection.user_instance_id;
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
|
||||
isect->prim = segment.prim;
|
||||
isect->type = segment.type;
|
||||
isect->u = intersection.curve_parameter;
|
||||
|
||||
if ((segment.type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
|
||||
isect->v = curve_ribbon_v(kg,
|
||||
intersection.curve_parameter,
|
||||
intersection.distance,
|
||||
ray,
|
||||
intersection.instance_id,
|
||||
segment.prim,
|
||||
segment.type);
|
||||
}
|
||||
else {
|
||||
isect->v = 0.0f;
|
||||
}
|
||||
}
|
||||
#endif /* __HAIR__ */
|
||||
#ifdef __POINTCLOUD__
|
||||
else if (kernel_data.bvh.have_points && intersection.type == intersection_type::bounding_box) {
|
||||
const int object = intersection.instance_id;
|
||||
const uint prim = intersection.primitive_id + intersection.user_instance_id;
|
||||
const int prim_type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
float3 idir;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
bvh_instance_motion_push(nullptr, object, ray, &r.origin, &r.direction, &idir);
|
||||
# else
|
||||
bvh_instance_push(nullptr, object, ray, &r.origin, &r.direction, &idir);
|
||||
# endif
|
||||
}
|
||||
|
||||
if (prim_type & PRIMITIVE_POINT) {
|
||||
if (!point_intersect(nullptr,
|
||||
isect,
|
||||
r.origin,
|
||||
r.direction,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
object,
|
||||
prim,
|
||||
ray->time,
|
||||
prim_type))
|
||||
{
|
||||
/* Shouldn't get here */
|
||||
kernel_assert(!"Intersection mismatch");
|
||||
isect->t = ray->tmax;
|
||||
isect->type = PRIMITIVE_NONE;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif /* __POINTCLOUD__ */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ccl_device_intersect bool scene_intersect_shadow(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility)
|
||||
{
|
||||
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
|
||||
metalrt_intersector_type metalrt_intersect;
|
||||
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
|
||||
metalrt_intersect.assume_geometry_type(
|
||||
metal::raytracing::geometry_type::triangle |
|
||||
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
|
||||
metal::raytracing::geometry_type::none) |
|
||||
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
|
||||
metal::raytracing::geometry_type::none));
|
||||
|
||||
typename metalrt_intersector_type::result_type intersection;
|
||||
|
||||
metalrt_intersect.accept_any_intersection(true);
|
||||
|
||||
MetalRTIntersectionShadowPayload payload;
|
||||
payload.self = ray->self;
|
||||
payload.visibility = visibility;
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
|
||||
#if defined(__METALRT_MOTION__)
|
||||
intersection = metalrt_intersect.intersect(r,
|
||||
metal_ancillaries->accel_struct,
|
||||
ray_mask,
|
||||
ray->time,
|
||||
metal_ancillaries->ift_shadow,
|
||||
payload);
|
||||
#else
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_shadow, payload);
|
||||
#endif
|
||||
return (intersection.type != intersection_type::none);
|
||||
}
|
||||
|
||||
#ifdef __BVH_LOCAL__
|
||||
template<bool single_hit = false>
|
||||
ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
uint primitive_id_offset = kernel_data_fetch(object_prim_offset, local_object);
|
||||
|
||||
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
|
||||
|
||||
# if defined(__METALRT_MOTION__)
|
||||
metalrt_intersector_type metalrt_intersect;
|
||||
typename metalrt_intersector_type::result_type intersection;
|
||||
# else
|
||||
metalrt_blas_intersector_type metalrt_intersect;
|
||||
typename metalrt_blas_intersector_type::result_type intersection;
|
||||
|
||||
if (!(kernel_data_fetch(object_flag, local_object) & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
/* Transform the ray into object's local space. */
|
||||
Transform itfm = kernel_data_fetch(objects, local_object).itfm;
|
||||
r.origin = transform_point(&itfm, r.origin);
|
||||
r.direction = transform_direction(&itfm, r.direction);
|
||||
}
|
||||
# endif
|
||||
|
||||
metalrt_intersect.assume_geometry_type(metal::raytracing::geometry_type::triangle);
|
||||
|
||||
if (single_hit) {
|
||||
MetalRTIntersectionLocalPayload_single_hit payload;
|
||||
payload.self_prim = ray->self.prim - primitive_id_offset;
|
||||
|
||||
# if defined(__METALRT_MOTION__)
|
||||
/* We can't skip over the top-level BVH in the motion blur case, so still need to do
|
||||
* the self-object check. */
|
||||
payload.self_object = local_object;
|
||||
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
|
||||
intersection = metalrt_intersect.intersect(r,
|
||||
metal_ancillaries->accel_struct,
|
||||
~0,
|
||||
ray->time,
|
||||
metal_ancillaries->ift_local_single_hit_mblur,
|
||||
payload);
|
||||
# else
|
||||
/* We only need custom intersection filtering (i.e. non_opaque) if we are performing a
|
||||
* self-primitive intersection check. */
|
||||
metalrt_intersect.force_opacity((ray->self.prim == PRIM_NONE) ?
|
||||
metal::raytracing::forced_opacity::opaque :
|
||||
metal::raytracing::forced_opacity::non_opaque);
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r,
|
||||
metal_ancillaries->blas_accel_structs[local_object].blas,
|
||||
metal_ancillaries->ift_local_single_hit,
|
||||
payload);
|
||||
# endif
|
||||
|
||||
if (intersection.type == intersection_type::none) {
|
||||
local_isect->num_hits = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint prim = intersection.primitive_id + primitive_id_offset;
|
||||
int prim_type = kernel_data_fetch(objects, local_object).primitive_type;
|
||||
|
||||
local_isect->num_hits = 1;
|
||||
local_isect->hits[0].prim = prim;
|
||||
local_isect->hits[0].type = prim_type;
|
||||
local_isect->hits[0].object = local_object;
|
||||
local_isect->hits[0].u = intersection.triangle_barycentric_coord.x;
|
||||
local_isect->hits[0].v = intersection.triangle_barycentric_coord.y;
|
||||
local_isect->hits[0].t = intersection.distance;
|
||||
|
||||
const int position_offset = kernel_data_fetch(objects, local_object).position_offset;
|
||||
const packed_uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
const float3 tri_a = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.x));
|
||||
const float3 tri_b = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.y));
|
||||
const float3 tri_c = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.z));
|
||||
local_isect->Ng[0] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
MetalRTIntersectionLocalPayload payload;
|
||||
payload.self_prim = ray->self.prim - primitive_id_offset;
|
||||
payload.max_hits = max_hits;
|
||||
payload.num_hits = 0;
|
||||
if (lcg_state) {
|
||||
payload.has_lcg_state = true;
|
||||
payload.lcg_state = *lcg_state;
|
||||
}
|
||||
else {
|
||||
payload.has_lcg_state = false;
|
||||
}
|
||||
|
||||
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
|
||||
|
||||
# if defined(__METALRT_MOTION__)
|
||||
/* We can't skip over the top-level BVH in the motion blur case, so still need to do
|
||||
* the self-object check. */
|
||||
payload.self_object = local_object;
|
||||
intersection = metalrt_intersect.intersect(r,
|
||||
metal_ancillaries->accel_struct,
|
||||
~0,
|
||||
ray->time,
|
||||
metal_ancillaries->ift_local_mblur,
|
||||
payload);
|
||||
# else
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r,
|
||||
metal_ancillaries->blas_accel_structs[local_object].blas,
|
||||
metal_ancillaries->ift_local,
|
||||
payload);
|
||||
# endif
|
||||
|
||||
if (max_hits == 0) {
|
||||
/* Special case for when no hit information is requested, just report that something was hit
|
||||
*/
|
||||
return (intersection.type != intersection_type::none);
|
||||
}
|
||||
|
||||
if (lcg_state) {
|
||||
*lcg_state = payload.lcg_state;
|
||||
}
|
||||
|
||||
const int num_hits = payload.num_hits;
|
||||
if (local_isect) {
|
||||
|
||||
/* Record geometric normal */
|
||||
int prim_type = kernel_data_fetch(objects, local_object).primitive_type;
|
||||
|
||||
/* Number of hits counted can be higher than recorded due to reservoir sampling. */
|
||||
local_isect->num_hits = num_hits;
|
||||
const int num_recorded_hits = min(payload.num_hits, max_hits);
|
||||
for (int hit = 0; hit < num_recorded_hits; hit++) {
|
||||
const uint prim = payload.hits[hit].prim + primitive_id_offset;
|
||||
local_isect->hits[hit].prim = prim;
|
||||
local_isect->hits[hit].t = payload.hits[hit].t;
|
||||
local_isect->hits[hit].u = payload.hits[hit].u;
|
||||
local_isect->hits[hit].v = payload.hits[hit].v;
|
||||
local_isect->hits[hit].object = local_object;
|
||||
local_isect->hits[hit].type = prim_type;
|
||||
|
||||
const int position_offset = kernel_data_fetch(objects, local_object).position_offset;
|
||||
const packed_uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
const float3 tri_a = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.x));
|
||||
const float3 tri_b = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.y));
|
||||
const float3 tri_c = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.z));
|
||||
local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
|
||||
}
|
||||
}
|
||||
return num_hits > 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
ccl_device_intersect void scene_intersect_shadow_all_metalrt(
|
||||
const ccl_private Ray *ray, ccl_private BVHShadowAllPayload &ccl_restrict payload)
|
||||
{
|
||||
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
|
||||
metalrt_intersector_type metalrt_intersect;
|
||||
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
|
||||
metalrt_intersect.assume_geometry_type(
|
||||
metal::raytracing::geometry_type::triangle |
|
||||
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
|
||||
metal::raytracing::geometry_type::none) |
|
||||
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
|
||||
metal::raytracing::geometry_type::none));
|
||||
|
||||
uint ray_mask = payload.base.ray_visibility & 0xFF;
|
||||
if (0 == ray_mask && (payload.base.ray_visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
|
||||
typename metalrt_intersector_type::result_type intersection;
|
||||
|
||||
# if defined(__METALRT_MOTION__)
|
||||
intersection = metalrt_intersect.intersect(r,
|
||||
metal_ancillaries->accel_struct,
|
||||
ray_mask,
|
||||
ray->time,
|
||||
metal_ancillaries->ift_shadow_all,
|
||||
payload);
|
||||
# else
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_shadow_all, payload);
|
||||
# endif
|
||||
|
||||
(void)intersection;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
|
||||
metalrt_intersector_type metalrt_intersect;
|
||||
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
|
||||
metalrt_intersect.set_geometry_cull_mode(metal::raytracing::geometry_cull_mode::bounding_box |
|
||||
metal::raytracing::geometry_cull_mode::curve);
|
||||
metalrt_intersect.assume_geometry_type(
|
||||
metal::raytracing::geometry_type::triangle |
|
||||
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
|
||||
metal::raytracing::geometry_type::none) |
|
||||
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
|
||||
metal::raytracing::geometry_type::none));
|
||||
|
||||
MetalRTIntersectionShadowPayload payload;
|
||||
payload.self = ray->self;
|
||||
payload.visibility = visibility;
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
|
||||
typename metalrt_intersector_type::result_type intersection;
|
||||
|
||||
# if defined(__METALRT_MOTION__)
|
||||
intersection = metalrt_intersect.intersect(r,
|
||||
metal_ancillaries->accel_struct,
|
||||
ray_mask,
|
||||
ray->time,
|
||||
metal_ancillaries->ift_volume,
|
||||
payload);
|
||||
# else
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_volume, payload);
|
||||
# endif
|
||||
|
||||
if (intersection.type == intersection_type::triangle) {
|
||||
isect->prim = intersection.primitive_id + intersection.user_instance_id;
|
||||
isect->type = kernel_data_fetch(objects, intersection.instance_id).primitive_type;
|
||||
isect->u = intersection.triangle_barycentric_coord.x;
|
||||
isect->v = intersection.triangle_barycentric_coord.y;
|
||||
isect->object = intersection.instance_id;
|
||||
isect->t = intersection.distance;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
407
blender-5.2.0/intern/cycles/kernel/device/metal/compat.h
Normal file
407
blender-5.2.0/intern/cycles/kernel/device/metal/compat.h
Normal file
@@ -0,0 +1,407 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define __KERNEL_GPU__
|
||||
#define __KERNEL_METAL__
|
||||
#define CCL_NAMESPACE_BEGIN
|
||||
#define CCL_NAMESPACE_END
|
||||
|
||||
#ifndef ATTR_FALLTHROUGH
|
||||
# define ATTR_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#include <metal_atomic>
|
||||
#include <metal_pack>
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
#ifdef __KERNEL_METALRT__
|
||||
using namespace metal::raytracing;
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
#pragma clang diagnostic ignored "-Wuninitialized"
|
||||
#pragma clang diagnostic ignored "-Wc++17-extensions"
|
||||
#pragma clang diagnostic ignored "-Wmacro-redefined"
|
||||
|
||||
/* Qualifiers */
|
||||
|
||||
#define ccl_device
|
||||
#define ccl_device_inline ccl_device __attribute__((always_inline))
|
||||
#define ccl_device_forceinline ccl_device __attribute__((always_inline))
|
||||
#if defined(__KERNEL_METAL_APPLE__)
|
||||
# define ccl_device_noinline ccl_device
|
||||
#else
|
||||
# define ccl_device_noinline ccl_device __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#define ccl_device_extern extern "C"
|
||||
#define ccl_device_noinline_cpu ccl_device
|
||||
#define ccl_device_inline_method ccl_device
|
||||
#define ccl_device_template_spec template<> ccl_device_inline
|
||||
#define ccl_global device
|
||||
#define ccl_inline_constant static constant constexpr
|
||||
#define ccl_device_constant constant
|
||||
#define ccl_static_constexpr static constant constexpr
|
||||
#define ccl_constant constant
|
||||
#define ccl_gpu_shared threadgroup
|
||||
#define ccl_private thread
|
||||
#ifdef __KERNEL_METALRT__
|
||||
# define ccl_ray_data ray_data
|
||||
#else
|
||||
# define ccl_ray_data ccl_private
|
||||
#endif
|
||||
#define ccl_may_alias
|
||||
#define ccl_restrict __restrict
|
||||
#define ccl_align(n) alignas(n)
|
||||
#define ccl_optional_struct_init
|
||||
#define ccl_attr_maybe_unused
|
||||
// Not supported by older MacOS versions (e.g., 13.0)
|
||||
// #define ccl_attr_maybe_unused [[maybe_unused]]
|
||||
|
||||
/* No assert supported for Metal */
|
||||
|
||||
#define kernel_assert(cond)
|
||||
|
||||
#define offsetof(t, d) __builtin_offsetof(t, d)
|
||||
|
||||
#define ccl_gpu_global_id_x() metal_global_id
|
||||
#define ccl_gpu_warp_size simdgroup_size
|
||||
#define ccl_gpu_thread_idx_x simd_group_index
|
||||
#define ccl_gpu_thread_mask(thread_warp) uint64_t((1ull << thread_warp) - 1)
|
||||
|
||||
#define ccl_gpu_ballot(predicate) ((uint64_t)((simd_vote::vote_t)simd_ballot(predicate)))
|
||||
#define ccl_gpu_syncthreads() threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||
|
||||
// clang-format off
|
||||
|
||||
/* kernel.h adapters */
|
||||
|
||||
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
|
||||
#define ccl_gpu_kernel_threads(block_num_threads)
|
||||
|
||||
/* Convert a comma-separated list into a semicolon-separated list
|
||||
* (so that we can generate a struct based on kernel entry-point parameters). */
|
||||
#define FN0()
|
||||
#define FN1(p1) p1;
|
||||
#define FN2(p1, p2) p1; p2;
|
||||
#define FN3(p1, p2, p3) p1; p2; p3;
|
||||
#define FN4(p1, p2, p3, p4) p1; p2; p3; p4;
|
||||
#define FN5(p1, p2, p3, p4, p5) p1; p2; p3; p4; p5;
|
||||
#define FN6(p1, p2, p3, p4, p5, p6) p1; p2; p3; p4; p5; p6;
|
||||
#define FN7(p1, p2, p3, p4, p5, p6, p7) p1; p2; p3; p4; p5; p6; p7;
|
||||
#define FN8(p1, p2, p3, p4, p5, p6, p7, p8) p1; p2; p3; p4; p5; p6; p7; p8;
|
||||
#define FN9(p1, p2, p3, p4, p5, p6, p7, p8, p9) p1; p2; p3; p4; p5; p6; p7; p8; p9;
|
||||
#define FN10(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10;
|
||||
#define FN11(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11;
|
||||
#define FN12(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12;
|
||||
#define FN13(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13;
|
||||
#define FN14(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14;
|
||||
#define FN15(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15;
|
||||
#define FN16(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16;
|
||||
#define FN17(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17;
|
||||
#define FN18(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18;
|
||||
#define FN19(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19;
|
||||
#define FN20(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20;
|
||||
#define GET_LAST_ARG(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, ...) p20
|
||||
#define PARAMS_MAKER(...) GET_LAST_ARG(__VA_ARGS__, FN20, FN19, FN18, FN17, FN16, FN15, FN14, FN13, FN12, FN11, FN10, FN9, FN8, FN7, FN6, FN5, FN4, FN3, FN2, FN1, FN0)
|
||||
|
||||
/* Generate a struct containing the entry-point parameters and a "run"
|
||||
* method which can access them implicitly via this-> */
|
||||
|
||||
#ifdef __METAL_GLOBAL_BUILTINS__
|
||||
|
||||
#define ccl_gpu_kernel_signature(name, ...) \
|
||||
struct kernel_gpu_##name \
|
||||
{ \
|
||||
PARAMS_MAKER(__VA_ARGS__)(__VA_ARGS__) \
|
||||
void run(thread MetalKernelContext& context, \
|
||||
threadgroup atomic_int *threadgroup_array) ccl_global const; \
|
||||
}; \
|
||||
kernel void cycles_metal_##name(device const kernel_gpu_##name *params_struct, \
|
||||
constant KernelParamsMetal &ccl_restrict _launch_params_metal, \
|
||||
constant MetalAncillaries *_metal_ancillaries, \
|
||||
threadgroup atomic_int *threadgroup_array[[ threadgroup(0) ]]) { \
|
||||
MetalKernelContext context(_launch_params_metal, _metal_ancillaries); \
|
||||
params_struct->run(context, threadgroup_array); \
|
||||
} \
|
||||
void kernel_gpu_##name::run(thread MetalKernelContext& context, \
|
||||
threadgroup atomic_int *threadgroup_array) ccl_global const
|
||||
|
||||
#else
|
||||
|
||||
/* On macOS versions before 14.x, builtin constants (e.g. metal_global_id) must
|
||||
* be accessed through attributed entry-point parameters. */
|
||||
|
||||
#define ccl_gpu_kernel_signature(name, ...) \
|
||||
struct kernel_gpu_##name \
|
||||
{ \
|
||||
PARAMS_MAKER(__VA_ARGS__)(__VA_ARGS__) \
|
||||
void run(thread MetalKernelContext& context, \
|
||||
threadgroup atomic_int *threadgroup_array, \
|
||||
const uint metal_global_id, \
|
||||
const ushort metal_local_id, \
|
||||
const ushort metal_local_size, \
|
||||
const uint metal_grid_id, \
|
||||
uint simdgroup_size, \
|
||||
uint simd_lane_index, \
|
||||
uint simd_group_index, \
|
||||
uint num_simd_groups) ccl_global const; \
|
||||
}; \
|
||||
kernel void cycles_metal_##name(device const kernel_gpu_##name *params_struct, \
|
||||
constant KernelParamsMetal &ccl_restrict _launch_params_metal, \
|
||||
constant MetalAncillaries *_metal_ancillaries, \
|
||||
threadgroup atomic_int *threadgroup_array[[ threadgroup(0) ]], \
|
||||
const uint metal_global_id [[thread_position_in_grid]], \
|
||||
const ushort metal_local_id [[thread_position_in_threadgroup]], \
|
||||
const ushort metal_local_size [[threads_per_threadgroup]], \
|
||||
const uint metal_grid_id [[threadgroup_position_in_grid]], \
|
||||
uint simdgroup_size [[threads_per_simdgroup]], \
|
||||
uint simd_lane_index [[thread_index_in_simdgroup]], \
|
||||
uint simd_group_index [[simdgroup_index_in_threadgroup]], \
|
||||
uint num_simd_groups [[simdgroups_per_threadgroup]]) { \
|
||||
MetalKernelContext context(_launch_params_metal, _metal_ancillaries); \
|
||||
params_struct->run(context, threadgroup_array, metal_global_id, metal_local_id, metal_local_size, metal_grid_id, simdgroup_size, simd_lane_index, simd_group_index, num_simd_groups); \
|
||||
} \
|
||||
void kernel_gpu_##name::run(thread MetalKernelContext& context, \
|
||||
threadgroup atomic_int *threadgroup_array, \
|
||||
const uint metal_global_id, \
|
||||
const ushort metal_local_id, \
|
||||
const ushort metal_local_size, \
|
||||
const uint metal_grid_id, \
|
||||
uint simdgroup_size, \
|
||||
uint simd_lane_index, \
|
||||
uint simd_group_index, \
|
||||
uint num_simd_groups) ccl_global const
|
||||
|
||||
#endif /* __METAL_GLOBAL_BUILTINS__ */
|
||||
|
||||
#define ccl_gpu_kernel_postfix
|
||||
#define ccl_gpu_kernel_call(x) context.x
|
||||
#define ccl_gpu_kernel_within_bounds(i,n) true
|
||||
|
||||
/* define a function object where "func" is the lambda body, and additional parameters are used to specify captured state. */
|
||||
#define ccl_gpu_kernel_lambda(func, ...) \
|
||||
struct KernelLambda \
|
||||
{ \
|
||||
KernelLambda(ccl_private MetalKernelContext &_context) : context(_context) {} \
|
||||
ccl_private MetalKernelContext &context; \
|
||||
__VA_ARGS__; \
|
||||
int operator()(const int state) const { return (func); } \
|
||||
} ccl_gpu_kernel_lambda_pass(context)
|
||||
|
||||
// clang-format on
|
||||
|
||||
/* make_type definitions with Metal style element initializers */
|
||||
ccl_device_forceinline float2 make_float2(const float x, const float y)
|
||||
{
|
||||
return float2(x, y);
|
||||
}
|
||||
|
||||
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
|
||||
{
|
||||
return float3(x, y, z);
|
||||
}
|
||||
|
||||
ccl_device_forceinline float4 make_float4(const float x,
|
||||
const float y,
|
||||
const float z,
|
||||
const float w)
|
||||
{
|
||||
return float4(x, y, z, w);
|
||||
}
|
||||
|
||||
ccl_device_forceinline int2 make_int2(const int x, const int y)
|
||||
{
|
||||
return int2(x, y);
|
||||
}
|
||||
|
||||
ccl_device_forceinline int3 make_int3(const int x, const int y, const int z)
|
||||
{
|
||||
return int3(x, y, z);
|
||||
}
|
||||
|
||||
ccl_device_forceinline int4 make_int4(const int x, const int y, const int z, const int w)
|
||||
{
|
||||
return int4(x, y, z, w);
|
||||
}
|
||||
|
||||
ccl_device_forceinline uint2 make_uint2(const uint x, const uint y)
|
||||
{
|
||||
return uint2(x, y);
|
||||
}
|
||||
|
||||
ccl_device_forceinline uint3 make_uint3(const uint x, const uint y, const uint z)
|
||||
{
|
||||
return uint3(x, y, z);
|
||||
}
|
||||
|
||||
ccl_device_forceinline uint4 make_uint4(const uint x, const uint y, const uint z, const uint w)
|
||||
{
|
||||
return uint4(x, y, z, w);
|
||||
}
|
||||
|
||||
ccl_device_forceinline uchar4 make_uchar4(const uchar x,
|
||||
const uchar y,
|
||||
const uchar z,
|
||||
const uchar w)
|
||||
{
|
||||
return uchar4(x, y, z, w);
|
||||
}
|
||||
|
||||
/* Math functions */
|
||||
|
||||
#define __uint_as_float(x) as_type<float>(x)
|
||||
#define __float_as_uint(x) as_type<uint>(x)
|
||||
#define __int_as_float(x) as_type<float>(x)
|
||||
#define __float_as_int(x) as_type<int>(x)
|
||||
#define __float2half(x) half(x)
|
||||
#define powf(x, y) pow(float(x), float(y))
|
||||
#define fabsf(x) fabs(float(x))
|
||||
#define copysignf(x, y) copysign(float(x), float(y))
|
||||
#define asinf(x) asin(float(x))
|
||||
#define acosf(x) acos(float(x))
|
||||
#define atanf(x) atan(float(x))
|
||||
#define floorf(x) floor(float(x))
|
||||
#define ceilf(x) ceil(float(x))
|
||||
#define roundf(x) round(float(x))
|
||||
#define hypotf(x, y) hypot(float(x), float(y))
|
||||
#define atan2f(x, y) atan2(float(x), float(y))
|
||||
#define fmaxf(x, y) fmax(float(x), float(y))
|
||||
#define fminf(x, y) fmin(float(x), float(y))
|
||||
#define fmodf(x, y) fmod(float(x), float(y))
|
||||
#define sinhf(x) sinh(float(x))
|
||||
#define coshf(x) cosh(float(x))
|
||||
#define tanhf(x) tanh(float(x))
|
||||
#define saturatef(x) saturate(float(x))
|
||||
#define ldexpf(x, y) ldexp(float(x), int(y))
|
||||
|
||||
/* Use native functions with possibly lower precision for performance,
|
||||
* no issues found so far. */
|
||||
#define trigmode fast
|
||||
#define sinf(x) trigmode::sin(float(x))
|
||||
#define cosf(x) trigmode::cos(float(x))
|
||||
#define tanf(x) trigmode::tan(float(x))
|
||||
#define expf(x) trigmode::exp(float(x))
|
||||
#define sqrtf(x) trigmode::sqrt(float(x))
|
||||
#define logf(x) trigmode::log(float(x))
|
||||
|
||||
#define __device__
|
||||
|
||||
#ifdef __KERNEL_METALRT__
|
||||
|
||||
# if defined(__METALRT_MOTION__)
|
||||
# define METALRT_TAGS instancing, instance_motion, primitive_motion
|
||||
# define METALRT_BLAS_TAGS , primitive_motion
|
||||
# else
|
||||
# define METALRT_TAGS instancing
|
||||
# define METALRT_BLAS_TAGS
|
||||
# endif /* __METALRT_MOTION__ */
|
||||
|
||||
# if defined(__METALRT_EXTENDED_LIMITS__)
|
||||
# define METALRT_LIMITS , extended_limits
|
||||
# else
|
||||
# define METALRT_LIMITS
|
||||
# endif /* __METALRT_MOTION__ */
|
||||
|
||||
typedef acceleration_structure<METALRT_TAGS> metalrt_as_type;
|
||||
typedef intersection_function_table<triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS>
|
||||
metalrt_ift_type;
|
||||
typedef metal::raytracing::intersector<triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS>
|
||||
metalrt_intersector_type;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
typedef acceleration_structure<primitive_motion> metalrt_blas_as_type;
|
||||
typedef intersection_function_table<triangle_data, curve_data, primitive_motion METALRT_LIMITS>
|
||||
metalrt_blas_ift_type;
|
||||
typedef metal::raytracing::intersector<triangle_data, curve_data, primitive_motion METALRT_LIMITS>
|
||||
metalrt_blas_intersector_type;
|
||||
# else
|
||||
typedef acceleration_structure<> metalrt_blas_as_type;
|
||||
typedef intersection_function_table<triangle_data, curve_data METALRT_LIMITS>
|
||||
metalrt_blas_ift_type;
|
||||
typedef metal::raytracing::intersector<triangle_data, curve_data METALRT_LIMITS>
|
||||
metalrt_blas_intersector_type;
|
||||
# endif
|
||||
|
||||
#endif /* __KERNEL_METALRT__ */
|
||||
|
||||
/* texture bindings and sampler setup */
|
||||
|
||||
/* TextureParamsMetal is reinterpreted as Texture2DParamsMetal. */
|
||||
struct TextureParamsMetal {
|
||||
uint64_t tex;
|
||||
};
|
||||
struct Texture2DParamsMetal {
|
||||
texture2d<float, access::sample> tex;
|
||||
};
|
||||
|
||||
#ifdef __KERNEL_METALRT__
|
||||
struct MetalRTBlasWrapper {
|
||||
metalrt_blas_as_type blas;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Additional Metal-specific resources which aren't encoded in KernelData.
|
||||
* IMPORTANT: If this layout changes, ANCILLARY_SLOT_COUNT and the host-side encoding must change
|
||||
* to match. */
|
||||
struct MetalAncillaries {
|
||||
device TextureParamsMetal *textures;
|
||||
|
||||
#ifdef __KERNEL_METALRT__
|
||||
metalrt_as_type accel_struct;
|
||||
constant MetalRTBlasWrapper *blas_accel_structs;
|
||||
metalrt_ift_type ift_default;
|
||||
metalrt_ift_type ift_shadow;
|
||||
metalrt_ift_type ift_shadow_all;
|
||||
metalrt_ift_type ift_volume;
|
||||
metalrt_blas_ift_type ift_local;
|
||||
metalrt_ift_type ift_local_mblur;
|
||||
metalrt_blas_ift_type ift_local_single_hit;
|
||||
metalrt_ift_type ift_local_single_hit_mblur;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "util/half.h"
|
||||
#include "util/types.h"
|
||||
|
||||
enum SamplerType {
|
||||
SamplerFilterNearest_AddressRepeat,
|
||||
SamplerFilterNearest_AddressClampEdge,
|
||||
SamplerFilterNearest_AddressClampZero,
|
||||
SamplerFilterNearest_AddressMirroredRepeat,
|
||||
|
||||
SamplerFilterLinear_AddressRepeat,
|
||||
SamplerFilterLinear_AddressClampEdge,
|
||||
SamplerFilterLinear_AddressClampZero,
|
||||
SamplerFilterLinear_AddressMirroredRepeat,
|
||||
|
||||
SamplerCount
|
||||
};
|
||||
|
||||
constexpr constant array<sampler, SamplerCount> metal_samplers = {
|
||||
sampler(address::repeat, filter::nearest),
|
||||
sampler(address::clamp_to_edge, filter::nearest),
|
||||
sampler(address::clamp_to_zero, filter::nearest),
|
||||
sampler(address::mirrored_repeat, filter::nearest),
|
||||
sampler(address::repeat, filter::linear),
|
||||
sampler(address::clamp_to_edge, filter::linear),
|
||||
sampler(address::clamp_to_zero, filter::linear),
|
||||
sampler(address::mirrored_repeat, filter::linear),
|
||||
};
|
||||
|
||||
#ifdef __METAL_GLOBAL_BUILTINS__
|
||||
const uint metal_global_id [[thread_position_in_grid]];
|
||||
const ushort metal_local_id [[thread_position_in_threadgroup]];
|
||||
const ushort metal_local_size [[threads_per_threadgroup]];
|
||||
const uint metal_grid_id [[threadgroup_position_in_grid]];
|
||||
const uint simdgroup_size [[threads_per_simdgroup]];
|
||||
const uint simd_lane_index [[thread_index_in_simdgroup]];
|
||||
const uint simd_group_index [[simdgroup_index_in_threadgroup]];
|
||||
const uint num_simd_groups [[simdgroups_per_threadgroup]];
|
||||
#endif /* __METAL_GLOBAL_BUILTINS__ */
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifdef WITH_NANOVDB
|
||||
# include "kernel/util/nanovdb.h"
|
||||
#endif
|
||||
|
||||
/* Open the Metal kernel context class
|
||||
* Necessary to access resource bindings */
|
||||
class MetalKernelContext {
|
||||
public:
|
||||
constant KernelParamsMetal &launch_params_metal;
|
||||
constant MetalAncillaries *metal_ancillaries;
|
||||
|
||||
MetalKernelContext(constant KernelParamsMetal &_launch_params_metal, constant MetalAncillaries * _metal_ancillaries)
|
||||
: launch_params_metal(_launch_params_metal), metal_ancillaries(_metal_ancillaries)
|
||||
{}
|
||||
|
||||
MetalKernelContext(constant KernelParamsMetal &_launch_params_metal)
|
||||
: launch_params_metal(_launch_params_metal)
|
||||
{}
|
||||
|
||||
/* texture fetch adapter functions */
|
||||
using ccl_gpu_image_object_2D = uint64_t;
|
||||
|
||||
template<typename T>
|
||||
inline __attribute__((__always_inline__))
|
||||
T ccl_gpu_image_object_read_2D(ccl_gpu_image_object_2D tex, const float x, float y) const {
|
||||
kernel_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// texture2d
|
||||
template<>
|
||||
inline __attribute__((__always_inline__))
|
||||
float4 ccl_gpu_image_object_read_2D(ccl_gpu_image_object_2D tex, const float x, float y) const {
|
||||
const uint tid(tex);
|
||||
const uint sid(tex >> 32);
|
||||
return ((ccl_global Texture2DParamsMetal*)metal_ancillaries->textures)[tid].tex.sample(metal_samplers[sid], float2(x, y));
|
||||
}
|
||||
template<>
|
||||
inline __attribute__((__always_inline__))
|
||||
float ccl_gpu_image_object_read_2D(ccl_gpu_image_object_2D tex, const float x, float y) const {
|
||||
const uint tid(tex);
|
||||
const uint sid(tex >> 32);
|
||||
return ((ccl_global Texture2DParamsMetal*)metal_ancillaries->textures)[tid].tex.sample(metal_samplers[sid], float2(x, y)).x;
|
||||
}
|
||||
|
||||
# include "kernel/device/gpu/image.h"
|
||||
|
||||
// clang-format on
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
}
|
||||
; /* end of MetalKernelContext class definition */
|
||||
|
||||
/* Silently redirect into the MetalKernelContext instance */
|
||||
/* NOTE: These macros will need maintaining as entry-points change. */
|
||||
|
||||
#undef kernel_integrator_state
|
||||
#define kernel_integrator_state context.launch_params_metal.integrator_state
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
enum {
|
||||
Kernel_DummyConstant,
|
||||
#define KERNEL_STRUCT_MEMBER(parent, type, name) KernelData_##parent##_##name,
|
||||
#include "kernel/data_template.h"
|
||||
|
||||
KernelData_kernel_features
|
||||
};
|
||||
|
||||
#ifdef __KERNEL_METAL__
|
||||
# define KERNEL_STRUCT_MEMBER(parent, type, name) \
|
||||
constant type kernel_data_##parent##_##name \
|
||||
[[function_constant(KernelData_##parent##_##name)]];
|
||||
# include "kernel/data_template.h"
|
||||
|
||||
constant int kernel_data_kernel_features [[function_constant(KernelData_kernel_features)]];
|
||||
#endif
|
||||
40
blender-5.2.0/intern/cycles/kernel/device/metal/globals.h
Normal file
40
blender-5.2.0/intern/cycles/kernel/device/metal/globals.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/util/profiler.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
struct KernelParamsMetal {
|
||||
|
||||
#define KERNEL_DATA_ARRAY(type, name) const ccl_global type *name;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) ccl_global type *name;
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
const IntegratorStateGPU integrator_state;
|
||||
const KernelData data;
|
||||
};
|
||||
|
||||
struct KernelGlobalsGPU {
|
||||
int unused[1];
|
||||
};
|
||||
|
||||
using KernelGlobals = const ccl_global KernelGlobalsGPU *ccl_restrict;
|
||||
|
||||
/* Abstraction macros */
|
||||
#define kernel_data launch_params_metal.data
|
||||
#define kernel_data_fetch(name, index) launch_params_metal.name[index]
|
||||
#define kernel_data_write(name, index, value) launch_params_metal.name[index] = (value)
|
||||
#define kernel_data_array(name) launch_params_metal.name
|
||||
#define kernel_integrator_state launch_params_metal.integrator_state
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
746
blender-5.2.0/intern/cycles/kernel/device/metal/kernel.metal
Normal file
746
blender-5.2.0/intern/cycles/kernel/device/metal/kernel.metal
Normal file
@@ -0,0 +1,746 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Metal kernel entry points. */
|
||||
|
||||
/* NOTE: Must come prior to other includes. */
|
||||
#include "kernel/device/metal/compat.h"
|
||||
#include "kernel/device/metal/globals.h"
|
||||
|
||||
/* NOTE: Must come prior to the kernel.h. */
|
||||
#include "kernel/device/metal/function_constants.h"
|
||||
|
||||
/* NOTE: Must come prior to the rest of the includes. */
|
||||
#include "kernel/device/gpu/kernel.h"
|
||||
|
||||
/* The rest of the includes. */
|
||||
#include "kernel/bvh/intersect_filter.h"
|
||||
#include "kernel/geom/geom_intersect.h"
|
||||
|
||||
/* MetalRT intersection handlers. */
|
||||
|
||||
#ifdef __KERNEL_METALRT__
|
||||
|
||||
/* Intersection return types. */
|
||||
|
||||
/* For a bounding box intersection function. */
|
||||
struct BoundingBoxIntersectionResult {
|
||||
bool accept [[accept_intersection]];
|
||||
bool continue_search [[continue_search]];
|
||||
float distance [[distance]];
|
||||
};
|
||||
|
||||
/* For a primitive intersection function. */
|
||||
struct PrimitiveIntersectionResult {
|
||||
bool accept [[accept_intersection]];
|
||||
bool continue_search [[continue_search]];
|
||||
};
|
||||
|
||||
enum { METALRT_HIT_TRIANGLE, METALRT_HIT_CURVE, METALRT_HIT_BOUNDING_BOX };
|
||||
|
||||
/* Hit functions. */
|
||||
|
||||
[[intersection(triangle, triangle_data, curve_data)]] PrimitiveIntersectionResult
|
||||
__intersection__local_tri_single_hit(
|
||||
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload_single_hit &payload [[payload]],
|
||||
uint primitive_id [[primitive_id]])
|
||||
{
|
||||
PrimitiveIntersectionResult result;
|
||||
result.continue_search = true;
|
||||
result.accept = (payload.self_prim != primitive_id);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__local_tri_single_hit_mblur(
|
||||
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload_single_hit &payload [[payload]],
|
||||
# if defined(__METALRT_MOTION__)
|
||||
uint object [[instance_id]],
|
||||
# endif
|
||||
uint primitive_id [[primitive_id]])
|
||||
{
|
||||
PrimitiveIntersectionResult result;
|
||||
result.continue_search = true;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
result.accept = (payload.self_prim != primitive_id) && (payload.self_object == object);
|
||||
# else
|
||||
result.accept = (payload.self_prim != primitive_id);
|
||||
# endif
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TReturn, uint intersection_type>
|
||||
TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
|
||||
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload,
|
||||
const uint prim,
|
||||
const float2 barycentrics,
|
||||
const float ray_tmax)
|
||||
{
|
||||
TReturn result;
|
||||
|
||||
# ifdef __BVH_LOCAL__
|
||||
if (payload.self_prim == prim) {
|
||||
/* Only intersect with matching object and skip self-intersection. */
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
const int max_hits = payload.max_hits;
|
||||
if (max_hits == 0) {
|
||||
/* Special case for when no hit information is requested, just report that something was hit.
|
||||
*/
|
||||
result.accept = true;
|
||||
result.continue_search = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Make a copy of the lcg_state in the private address space, allowing to use utility function
|
||||
* to find the hit index to write the intersection to. This function is used from both HW-RT
|
||||
* code-path and non-HW-RT, making it hard to deal with the address spaces in the function
|
||||
* signature. Hopefully, compiler is smart enough to eliminate this temporary copy. */
|
||||
uint lcg_state = payload.lcg_state;
|
||||
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
const int hit_index = context.local_intersect_get_record_index(
|
||||
&payload, ray_tmax, payload.has_lcg_state ? &lcg_state : nullptr, max_hits);
|
||||
|
||||
payload.lcg_state = lcg_state;
|
||||
|
||||
if (hit_index == -1) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
payload.hits[hit_index].prim = prim;
|
||||
payload.hits[hit_index].t = ray_tmax;
|
||||
payload.hits[hit_index].u = barycentrics.x;
|
||||
payload.hits[hit_index].v = barycentrics.y;
|
||||
|
||||
/* Continue tracing (without this the trace call would return after the first hit). */
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
# endif
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(triangle, triangle_data, curve_data)]] PrimitiveIntersectionResult
|
||||
__intersection__local_tri(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload
|
||||
[[payload]],
|
||||
uint primitive_id [[primitive_id]],
|
||||
float2 barycentrics [[barycentric_coord]],
|
||||
float ray_tmax [[distance]])
|
||||
{
|
||||
/* instance_id, aka the user_id has been removed. If we take this function we optimized the
|
||||
* SSS for starting traversal from a primitive acceleration structure instead of the root of the
|
||||
* global AS. this means we will always be intersecting the correct object no need for the
|
||||
* user-id to check */
|
||||
return metalrt_local_hit<PrimitiveIntersectionResult, METALRT_HIT_TRIANGLE>(
|
||||
launch_params_metal, payload, primitive_id, barycentrics, ray_tmax);
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__local_tri_mblur(
|
||||
constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload [[payload]],
|
||||
uint primitive_id [[primitive_id]],
|
||||
# if defined(__METALRT_MOTION__)
|
||||
uint object [[instance_id]],
|
||||
# endif
|
||||
float2 barycentrics [[barycentric_coord]],
|
||||
float ray_tmax [[distance]])
|
||||
{
|
||||
# if defined(__METALRT_MOTION__)
|
||||
if (payload.self_object != object) {
|
||||
PrimitiveIntersectionResult result;
|
||||
result.continue_search = true;
|
||||
result.accept = false;
|
||||
return result;
|
||||
}
|
||||
# endif
|
||||
|
||||
return metalrt_local_hit<PrimitiveIntersectionResult, METALRT_HIT_TRIANGLE>(
|
||||
launch_params_metal, payload, primitive_id, barycentrics, ray_tmax);
|
||||
}
|
||||
|
||||
inline bool metalrt_curve_skip_end_cap(const int type, const float u)
|
||||
{
|
||||
return ((u == 0.0f || u == 1.0f) && (type & PRIMITIVE_CURVE) != PRIMITIVE_CURVE_THICK_LINEAR);
|
||||
}
|
||||
|
||||
inline Intersection get_intersection(constant KernelParamsMetal &launch_params_metal,
|
||||
const float t,
|
||||
const float2 uv,
|
||||
uint object,
|
||||
uint prim)
|
||||
{
|
||||
Intersection isect;
|
||||
isect.t = t;
|
||||
isect.u = uv.x;
|
||||
isect.v = uv.y;
|
||||
isect.prim = prim;
|
||||
isect.object = object;
|
||||
isect.type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
# ifdef __HAIR__
|
||||
if (isect.type & PRIMITIVE_CURVE) {
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
|
||||
isect.type = segment.type;
|
||||
isect.prim = segment.prim;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (isect.type & PRIMITIVE_POINT) {
|
||||
isect.u = 0.0f;
|
||||
isect.v = 0.0f;
|
||||
}
|
||||
|
||||
return isect;
|
||||
}
|
||||
|
||||
template<uint intersection_type>
|
||||
bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
|
||||
ray_data MetalKernelContext::BVHShadowAllPayload &payload,
|
||||
uint object,
|
||||
uint prim,
|
||||
const float2 uv,
|
||||
const float t,
|
||||
const ccl_private Ray *ray = nullptr)
|
||||
{
|
||||
# if defined(__TRANSPARENT_SHADOWS__)
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const Intersection isect = get_intersection(launch_params_metal, t, uv, object, prim);
|
||||
|
||||
# ifdef __HAIR__
|
||||
if constexpr (intersection_type == METALRT_HIT_CURVE) {
|
||||
/* Filter out curve end-caps. */
|
||||
if (metalrt_curve_skip_end_cap(isect.type, isect.u)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((isect.type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
|
||||
if (!context.curve_ribbon_accept(
|
||||
nullptr, isect.u, isect.t, ray, object, isect.prim, isect.type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif /* __HAIR__ */
|
||||
|
||||
constexpr uint enabled_primitive_types = (intersection_type == METALRT_HIT_CURVE) ?
|
||||
PRIMITIVE_CURVE :
|
||||
(PRIMITIVE_ALL & ~PRIMITIVE_CURVE);
|
||||
return context
|
||||
.bvh_shadow_all_anyhit_filter<MetalKernelContext::ISECT_TEST_ALL, enabled_primitive_types>(
|
||||
kg, payload.state, payload, payload.base.ray_self, payload.base.ray_visibility, isect);
|
||||
|
||||
# else /* __TRANSPARENT_SHADOWS__ */
|
||||
payload.throughput = 0.0f;
|
||||
return false;
|
||||
# endif /* __TRANSPARENT_SHADOWS__ */
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__tri_shadow_all(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::BVHShadowAllPayload &payload
|
||||
[[payload]],
|
||||
const unsigned int object [[instance_id]],
|
||||
const unsigned int primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const float2 uv [[barycentric_coord]],
|
||||
const float t [[distance]])
|
||||
{
|
||||
uint prim = primitive_id + primitive_id_offset;
|
||||
|
||||
PrimitiveIntersectionResult result;
|
||||
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_TRIANGLE>(
|
||||
launch_params_metal, payload, object, prim, uv, t);
|
||||
result.accept = !result.continue_search;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__volume_tri(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
|
||||
[[payload]],
|
||||
const unsigned int object [[instance_id]],
|
||||
const unsigned int primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]])
|
||||
{
|
||||
PrimitiveIntersectionResult result;
|
||||
result.continue_search = true;
|
||||
|
||||
KernelGlobals kg = nullptr;
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
|
||||
uint prim = primitive_id + primitive_id_offset;
|
||||
|
||||
if (context.bvh_volume_anyhit_triangle_filter(
|
||||
kg, object, prim, payload.self, payload.visibility))
|
||||
{
|
||||
result.accept = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.accept = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TReturnType, uint intersection_type>
|
||||
inline TReturnType metalrt_visibility_test(
|
||||
constant KernelParamsMetal &launch_params_metal,
|
||||
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload,
|
||||
const uint object,
|
||||
uint prim,
|
||||
const float u,
|
||||
const float t = 0.0f,
|
||||
const ccl_private Ray *ray = nullptr)
|
||||
{
|
||||
TReturnType result;
|
||||
|
||||
if ((kernel_data_fetch(objects, object).visibility & payload.visibility) == 0) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
# ifdef __HAIR__
|
||||
if constexpr (intersection_type == METALRT_HIT_CURVE) {
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
|
||||
int type = segment.type;
|
||||
prim = segment.prim;
|
||||
|
||||
/* Filter out curve end-caps. */
|
||||
if (metalrt_curve_skip_end_cap(type, u)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
if (!context.curve_ribbon_accept(nullptr, u, t, ray, object, prim, type)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
if (payload.self_object == object && payload.self_prim == prim) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
result.accept = true;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename TReturnType, uint intersection_type>
|
||||
inline TReturnType metalrt_visibility_test_shadow(
|
||||
constant KernelParamsMetal &launch_params_metal,
|
||||
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload,
|
||||
const uint object,
|
||||
uint prim,
|
||||
const float u,
|
||||
const float t = 0.0f,
|
||||
const ccl_private Ray *ray = nullptr)
|
||||
{
|
||||
TReturnType result;
|
||||
|
||||
if ((kernel_data_fetch(objects, object).visibility & payload.visibility) == 0) {
|
||||
result.accept = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
# ifdef __HAIR__
|
||||
if constexpr (intersection_type == METALRT_HIT_CURVE) {
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
|
||||
int type = segment.type;
|
||||
prim = segment.prim;
|
||||
|
||||
/* Filter out curve end-caps. */
|
||||
if (metalrt_curve_skip_end_cap(type, u)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
if (!context.curve_ribbon_accept(nullptr, u, t, ray, object, prim, type)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
|
||||
/* Shadow ray early termination. */
|
||||
# ifdef __SHADOW_LINKING__
|
||||
if (context.intersection_skip_shadow_link(nullptr, payload.self, object)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (context.intersection_skip_self_shadow(payload.self, object, prim)) {
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
result.accept = true;
|
||||
result.continue_search = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.accept = true;
|
||||
result.continue_search = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__tri(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
|
||||
const unsigned int object [[instance_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const unsigned int primitive_id [[primitive_id]])
|
||||
{
|
||||
PrimitiveIntersectionResult result;
|
||||
result.continue_search = true;
|
||||
|
||||
if ((kernel_data_fetch(objects, object).visibility & payload.visibility) == 0) {
|
||||
result.accept = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.accept = (payload.self_object != object ||
|
||||
payload.self_prim != (primitive_id + primitive_id_offset));
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__tri_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
|
||||
[[payload]],
|
||||
const unsigned int object [[instance_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const unsigned int primitive_id [[primitive_id]])
|
||||
{
|
||||
uint prim = primitive_id + primitive_id_offset;
|
||||
PrimitiveIntersectionResult result =
|
||||
metalrt_visibility_test_shadow<PrimitiveIntersectionResult, METALRT_HIT_TRIANGLE>(
|
||||
launch_params_metal, payload, object, prim, 0.0f);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Primitive intersection functions. */
|
||||
|
||||
[[intersection(
|
||||
curve, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__curve(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
|
||||
const uint object [[instance_id]],
|
||||
const uint primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
float distance [[distance]],
|
||||
const float3 ray_P [[origin]],
|
||||
const float3 ray_D [[direction]],
|
||||
float u [[curve_parameter]],
|
||||
const float ray_tmin [[min_distance]],
|
||||
const float ray_tmax [[max_distance]]
|
||||
# if defined(__METALRT_MOTION__)
|
||||
,
|
||||
const float time [[time]]
|
||||
# endif
|
||||
)
|
||||
{
|
||||
uint prim = primitive_id + primitive_id_offset;
|
||||
|
||||
Ray ray;
|
||||
ray.P = ray_P;
|
||||
ray.D = ray_D;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
ray.time = time;
|
||||
# endif
|
||||
|
||||
PrimitiveIntersectionResult result =
|
||||
metalrt_visibility_test<PrimitiveIntersectionResult, METALRT_HIT_CURVE>(
|
||||
launch_params_metal, payload, object, prim, u, distance, &ray);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
curve, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__curve_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
|
||||
[[payload]],
|
||||
const uint object [[instance_id]],
|
||||
const uint primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
float distance [[distance]],
|
||||
const float3 ray_P [[origin]],
|
||||
const float3 ray_D [[direction]],
|
||||
float u [[curve_parameter]],
|
||||
const float ray_tmin [[min_distance]],
|
||||
const float ray_tmax [[max_distance]]
|
||||
# if defined(__METALRT_MOTION__)
|
||||
,
|
||||
const float time [[time]]
|
||||
# endif
|
||||
)
|
||||
{
|
||||
uint prim = primitive_id + primitive_id_offset;
|
||||
|
||||
Ray ray;
|
||||
ray.P = ray_P;
|
||||
ray.D = ray_D;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
ray.time = time;
|
||||
# endif
|
||||
|
||||
PrimitiveIntersectionResult result =
|
||||
metalrt_visibility_test_shadow<PrimitiveIntersectionResult, METALRT_HIT_CURVE>(
|
||||
launch_params_metal, payload, object, prim, u, distance, &ray);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(
|
||||
curve, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
|
||||
__intersection__curve_shadow_all(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::BVHShadowAllPayload &payload
|
||||
[[payload]],
|
||||
const uint object [[instance_id]],
|
||||
const uint primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const float3 ray_P [[origin]],
|
||||
const float3 ray_D [[direction]],
|
||||
# if defined(__METALRT_MOTION__)
|
||||
const float time [[time]],
|
||||
# endif
|
||||
float u [[curve_parameter]],
|
||||
float t [[distance]])
|
||||
{
|
||||
uint prim = primitive_id + primitive_id_offset;
|
||||
|
||||
PrimitiveIntersectionResult result;
|
||||
|
||||
Ray ray;
|
||||
ray.P = ray_P;
|
||||
ray.D = ray_D;
|
||||
# if defined(__METALRT_MOTION__)
|
||||
/* TODO(sergey): The time is not really needed.
|
||||
* Only ray direction and origin are needed in curve_ribbon_accept(), so there might be a room
|
||||
* for cleanup here. */
|
||||
ray.time = time;
|
||||
# endif
|
||||
|
||||
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_CURVE>(
|
||||
launch_params_metal, payload, object, prim, float2(u, 0), t, &ray);
|
||||
result.accept = !result.continue_search;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
# ifdef __POINTCLOUD__
|
||||
ccl_device_inline void metalrt_intersection_point_shadow_all(
|
||||
constant KernelParamsMetal &launch_params_metal,
|
||||
ray_data MetalKernelContext::BVHShadowAllPayload &payload,
|
||||
const uint object,
|
||||
const uint prim,
|
||||
const uint type,
|
||||
const float3 ray_P,
|
||||
const float3 ray_D,
|
||||
float time,
|
||||
const float ray_tmin,
|
||||
const float ray_tmax,
|
||||
thread BoundingBoxIntersectionResult &result)
|
||||
{
|
||||
Intersection isect;
|
||||
isect.t = ray_tmax;
|
||||
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
if (context.point_intersect(
|
||||
nullptr, &isect, ray_P, ray_D, ray_tmin, isect.t, object, prim, time, type))
|
||||
{
|
||||
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_BOUNDING_BOX>(
|
||||
launch_params_metal, payload, object, prim, float2(isect.u, isect.v), isect.t);
|
||||
result.accept = !result.continue_search;
|
||||
|
||||
if (result.accept) {
|
||||
result.distance = isect.t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[intersection(bounding_box,
|
||||
triangle_data,
|
||||
curve_data,
|
||||
METALRT_TAGS METALRT_LIMITS)]] BoundingBoxIntersectionResult
|
||||
__intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
|
||||
const uint object [[instance_id]],
|
||||
const uint primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const float3 ray_origin [[origin]],
|
||||
const float3 ray_direction [[direction]],
|
||||
# if defined(__METALRT_MOTION__)
|
||||
const float time [[time]],
|
||||
# endif
|
||||
const float ray_tmin [[min_distance]],
|
||||
const float ray_tmax [[max_distance]])
|
||||
{
|
||||
const uint prim = primitive_id + primitive_id_offset;
|
||||
const int type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
BoundingBoxIntersectionResult result;
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
result.distance = ray_tmax;
|
||||
|
||||
Intersection isect;
|
||||
isect.t = ray_tmax;
|
||||
|
||||
# ifndef __METALRT_MOTION__
|
||||
const float time = 0.0f;
|
||||
# endif
|
||||
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
if (context.point_intersect(
|
||||
nullptr, &isect, ray_origin, ray_direction, ray_tmin, isect.t, object, prim, time, type))
|
||||
{
|
||||
result = metalrt_visibility_test<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>(
|
||||
launch_params_metal, payload, object, prim, isect.u);
|
||||
if (result.accept) {
|
||||
result.distance = isect.t;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
# endif /* __POINTCLOUD__ */
|
||||
|
||||
[[intersection(bounding_box,
|
||||
triangle_data,
|
||||
curve_data,
|
||||
METALRT_TAGS METALRT_LIMITS)]] BoundingBoxIntersectionResult
|
||||
__intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
|
||||
[[payload]],
|
||||
const uint object [[instance_id]],
|
||||
const uint primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const float3 ray_origin [[origin]],
|
||||
const float3 ray_direction [[direction]],
|
||||
# if defined(__METALRT_MOTION__)
|
||||
const float time [[time]],
|
||||
# endif
|
||||
const float ray_tmin [[min_distance]],
|
||||
const float ray_tmax [[max_distance]])
|
||||
{
|
||||
const uint prim = primitive_id + primitive_id_offset;
|
||||
const int type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
BoundingBoxIntersectionResult result;
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
result.distance = ray_tmax;
|
||||
|
||||
# ifdef __POINTCLOUD__
|
||||
|
||||
Intersection isect;
|
||||
isect.t = ray_tmax;
|
||||
|
||||
# ifndef __METALRT_MOTION__
|
||||
const float time = 0.0f;
|
||||
# endif
|
||||
|
||||
MetalKernelContext context(launch_params_metal);
|
||||
if (context.point_intersect(
|
||||
nullptr, &isect, ray_origin, ray_direction, ray_tmin, isect.t, object, prim, time, type))
|
||||
{
|
||||
result =
|
||||
metalrt_visibility_test_shadow<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>(
|
||||
launch_params_metal, payload, object, prim, isect.u);
|
||||
if (result.accept) {
|
||||
result.distance = isect.t;
|
||||
}
|
||||
}
|
||||
|
||||
# endif /* __POINTCLOUD__ */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[intersection(bounding_box,
|
||||
triangle_data,
|
||||
curve_data,
|
||||
METALRT_TAGS METALRT_LIMITS)]] BoundingBoxIntersectionResult
|
||||
__intersection__point_shadow_all(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
|
||||
ray_data MetalKernelContext::BVHShadowAllPayload &payload
|
||||
[[payload]],
|
||||
const uint object [[instance_id]],
|
||||
const uint primitive_id [[primitive_id]],
|
||||
const uint primitive_id_offset [[user_instance_id]],
|
||||
const float3 ray_origin [[origin]],
|
||||
const float3 ray_direction [[direction]],
|
||||
# if defined(__METALRT_MOTION__)
|
||||
const float time [[time]],
|
||||
# endif
|
||||
const float ray_tmin [[min_distance]],
|
||||
const float ray_tmax [[max_distance]])
|
||||
{
|
||||
const uint prim = primitive_id + primitive_id_offset;
|
||||
const int type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
BoundingBoxIntersectionResult result;
|
||||
result.accept = false;
|
||||
result.continue_search = true;
|
||||
result.distance = ray_tmax;
|
||||
|
||||
# ifdef __POINTCLOUD__
|
||||
|
||||
metalrt_intersection_point_shadow_all(launch_params_metal,
|
||||
payload,
|
||||
object,
|
||||
prim,
|
||||
type,
|
||||
ray_origin,
|
||||
ray_direction,
|
||||
# if defined(__METALRT_MOTION__)
|
||||
time,
|
||||
# else
|
||||
0.0f,
|
||||
# endif
|
||||
ray_tmin,
|
||||
ray_tmax,
|
||||
result);
|
||||
|
||||
# endif /* __POINTCLOUD__ */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* __KERNEL_METALRT__ */
|
||||
296
blender-5.2.0/intern/cycles/kernel/device/oneapi/CMakeLists.txt
Normal file
296
blender-5.2.0/intern/cycles/kernel/device/oneapi/CMakeLists.txt
Normal file
@@ -0,0 +1,296 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_ONEAPI
|
||||
kernel.cpp
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_ONEAPI_HEADERS
|
||||
compat.h
|
||||
context_begin.h
|
||||
context_end.h
|
||||
context_intersect_begin.h
|
||||
context_intersect_end.h
|
||||
globals.h
|
||||
kernel.h
|
||||
kernel_templates.h
|
||||
../cpu/bvh.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_DEVICE_ONEAPI)
|
||||
if(WITH_CYCLES_ONEAPI_BINARIES)
|
||||
set(cycles_kernel_oneapi_lib_suffix "_aot")
|
||||
else()
|
||||
set(cycles_kernel_oneapi_lib_suffix "_jit")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/cycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.dll)
|
||||
set(cycles_kernel_oneapi_linker_lib ${CMAKE_CURRENT_BINARY_DIR}/cycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.lib)
|
||||
else()
|
||||
set(cycles_kernel_oneapi_lib ${CMAKE_CURRENT_BINARY_DIR}/libcycles_kernel_oneapi${cycles_kernel_oneapi_lib_suffix}.so)
|
||||
endif()
|
||||
|
||||
set(cycles_oneapi_kernel_sources
|
||||
${SRC_KERNEL_DEVICE_ONEAPI}
|
||||
${SRC_KERNEL_DEVICE_ONEAPI_HEADERS}
|
||||
$<TARGET_PROPERTY:cycles_kernel,INTERFACE_SOURCES>
|
||||
)
|
||||
|
||||
set(SYCL_OFFLINE_COMPILER_PARALLEL_JOBS 1 CACHE STRING "Number of parallel compiler instances to use for device binaries compilation (expect ~8GB peak memory usage per instance).")
|
||||
mark_as_advanced(SYCL_OFFLINE_COMPILER_PARALLEL_JOBS)
|
||||
|
||||
if(WITH_CYCLES_ONEAPI_BINARIES)
|
||||
message(STATUS "${SYCL_OFFLINE_COMPILER_PARALLEL_JOBS} instance(s) of oneAPI offline compiler will be used.")
|
||||
endif()
|
||||
set(sycl_compiler_flags
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${SRC_KERNEL_DEVICE_ONEAPI}
|
||||
-fsycl
|
||||
-fsycl-unnamed-lambda
|
||||
-fdelayed-template-parsing
|
||||
-fsycl-device-code-split=per_kernel
|
||||
-fsycl-max-parallel-link-jobs=${SYCL_OFFLINE_COMPILER_PARALLEL_JOBS}
|
||||
--offload-compress
|
||||
--offload-compression-level=19
|
||||
-shared
|
||||
-DWITH_ONEAPI
|
||||
-O2
|
||||
-ffast-math
|
||||
-D__KERNEL_LOCAL_ATOMIC_SORT__
|
||||
-o"${cycles_kernel_oneapi_lib}"
|
||||
-I"${CMAKE_CURRENT_SOURCE_DIR}/../../.."
|
||||
)
|
||||
# SYCL_CPP_FLAGS is a variable that the user can set to pass extra compiler options.
|
||||
if(DEFINED SYCL_CPP_FLAGS)
|
||||
list(APPEND sycl_compiler_flags ${SYCL_CPP_FLAGS})
|
||||
endif()
|
||||
|
||||
# Set defaults for spir64 and spir64_gen options
|
||||
if(NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64)
|
||||
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64 "-options '-cl-fast-relaxed-math -ze-intel-enable-auto-large-GRF-mode -ze-opt-regular-grf-kernel integrator_intersect -ze-opt-large-grf-kernel shade_surface -ze-opt-no-local-to-generic'")
|
||||
endif()
|
||||
if(NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen)
|
||||
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "${CYCLES_ONEAPI_SYCL_OPTIONS_spir64}" CACHE STRING "Extra build options for spir64_gen target")
|
||||
mark_as_advanced(CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen)
|
||||
endif()
|
||||
# Enable `zebin`, a graphics binary format with improved compatibility.
|
||||
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "--format zebin ")
|
||||
|
||||
# Host execution won't use GPU binaries, no need to compile them.
|
||||
if(WITH_CYCLES_ONEAPI_BINARIES)
|
||||
# Add the list of Intel devices to build binaries for.
|
||||
foreach(device ${CYCLES_ONEAPI_INTEL_BINARIES_ARCH})
|
||||
# Run `ocloc` ids to test if the device is supported.
|
||||
execute_process(
|
||||
COMMAND ${OCLOC_ENV_COMMAND} ${OCLOC_BINARY_FULL_FILEPATH} ids ${device}
|
||||
RESULT_VARIABLE oclocids_ret
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT oclocids_ret EQUAL 0)
|
||||
list(REMOVE_ITEM CYCLES_ONEAPI_INTEL_BINARIES_ARCH ${device})
|
||||
message(STATUS
|
||||
"Cycles oneAPI: "
|
||||
"binaries for ${device} not supported by Intel Graphics Compiler/ocloc, skipped."
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
list(JOIN CYCLES_ONEAPI_INTEL_BINARIES_ARCH "," gen_devices_string)
|
||||
if("${gen_devices_string}" STREQUAL "")
|
||||
# Don't compile spir64_gen if no device is targeted
|
||||
message(STATUS "Cycles oneAPI: skipping spir64_gen compilation as no devices are targeted.")
|
||||
list(REMOVE_ITEM CYCLES_ONEAPI_SYCL_TARGETS spir64_gen)
|
||||
else()
|
||||
string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "-device ${gen_devices_string} ")
|
||||
endif()
|
||||
else()
|
||||
list(REMOVE_ITEM CYCLES_ONEAPI_SYCL_TARGETS spir64_gen)
|
||||
endif()
|
||||
|
||||
# Iterate over all targets and their options.
|
||||
list(JOIN CYCLES_ONEAPI_SYCL_TARGETS "," targets_string)
|
||||
list(APPEND sycl_compiler_flags -fsycl-targets=${targets_string})
|
||||
foreach(target ${CYCLES_ONEAPI_SYCL_TARGETS})
|
||||
if(DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_${target})
|
||||
list(APPEND sycl_compiler_flags
|
||||
"-Xsycl-target-backend=${target} \"${CYCLES_ONEAPI_SYCL_OPTIONS_${target}}\""
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(WITH_NANOVDB)
|
||||
list(APPEND sycl_compiler_flags
|
||||
-DWITH_NANOVDB)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_EMBREE AND EMBREE_SYCL_SUPPORT)
|
||||
list(APPEND sycl_compiler_flags
|
||||
-DWITH_EMBREE
|
||||
-DWITH_EMBREE_GPU
|
||||
-DEMBREE_MAJOR_VERSION=${EMBREE_MAJOR_VERSION}
|
||||
-I"${EMBREE_INCLUDE_DIRS}")
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND sycl_compiler_flags
|
||||
-ladvapi32.lib
|
||||
)
|
||||
endif()
|
||||
|
||||
set(next_library_mode "")
|
||||
foreach(library ${EMBREE_LIBRARIES})
|
||||
string(TOLOWER "${library}" library_lower)
|
||||
if(("${library_lower}" STREQUAL "optimized") OR
|
||||
("${library_lower}" STREQUAL "debug"))
|
||||
set(next_library_mode "${library_lower}")
|
||||
else()
|
||||
if(next_library_mode STREQUAL "")
|
||||
list(APPEND EMBREE_TBB_LIBRARIES_optimized ${library})
|
||||
list(APPEND EMBREE_TBB_LIBRARIES_debug ${library})
|
||||
else()
|
||||
list(APPEND EMBREE_TBB_LIBRARIES_${next_library_mode} ${library})
|
||||
endif()
|
||||
set(next_library_mode "")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(library ${TBB_LIBRARIES})
|
||||
string(TOLOWER "${library}" library_lower)
|
||||
if(("${library_lower}" STREQUAL "optimized") OR
|
||||
("${library_lower}" STREQUAL "debug"))
|
||||
set(next_library_mode "${library_lower}")
|
||||
else()
|
||||
if(next_library_mode STREQUAL "")
|
||||
list(APPEND EMBREE_TBB_LIBRARIES_optimized ${library})
|
||||
list(APPEND EMBREE_TBB_LIBRARIES_debug ${library})
|
||||
else()
|
||||
list(APPEND EMBREE_TBB_LIBRARIES_${next_library_mode} ${library})
|
||||
endif()
|
||||
set(next_library_mode "")
|
||||
endif()
|
||||
endforeach()
|
||||
list(APPEND sycl_compiler_flags
|
||||
"$<$<CONFIG:Release>:${EMBREE_TBB_LIBRARIES_optimized}>"
|
||||
"$<$<CONFIG:RelWithDebInfo>:${EMBREE_TBB_LIBRARIES_optimized}>"
|
||||
"$<$<CONFIG:MinSizeRel>:${EMBREE_TBB_LIBRARIES_optimized}>"
|
||||
"$<$<CONFIG:Debug>:${EMBREE_TBB_LIBRARIES_debug}>"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_DEBUG)
|
||||
list(APPEND sycl_compiler_flags -DWITH_CYCLES_DEBUG)
|
||||
endif()
|
||||
|
||||
get_filename_component(sycl_compiler_root ${SYCL_COMPILER} DIRECTORY)
|
||||
|
||||
if(WIN32) # Add Windows specific compiler flags.
|
||||
list(APPEND sycl_compiler_flags
|
||||
-fms-extensions
|
||||
-fms-compatibility
|
||||
-D_WINDLL
|
||||
-D_MBCS
|
||||
-DWIN32
|
||||
-D_WINDOWS
|
||||
-D_CRT_NONSTDC_NO_DEPRECATE
|
||||
-D_CRT_SECURE_NO_DEPRECATE
|
||||
-DONEAPI_EXPORT
|
||||
)
|
||||
else() # Add Linux specific compiler flags.
|
||||
list(APPEND sycl_compiler_flags -fPIC)
|
||||
list(APPEND sycl_compiler_flags -fvisibility=hidden)
|
||||
|
||||
# Add $ORIGIN to `cycles_kernel_oneapi.so` RPATH so `libsycl.so` and
|
||||
# `libpi_level_zero.so` can be placed next to it and get found.
|
||||
list(APPEND sycl_compiler_flags -Wl,-rpath,'$$ORIGIN')
|
||||
endif()
|
||||
|
||||
# Create CONFIG specific compiler flags.
|
||||
set(sycl_compiler_flags_Release ${sycl_compiler_flags})
|
||||
set(sycl_compiler_flags_Debug ${sycl_compiler_flags})
|
||||
set(sycl_compiler_flags_RelWithDebInfo ${sycl_compiler_flags})
|
||||
|
||||
list(APPEND sycl_compiler_flags_Release
|
||||
-DNDEBUG
|
||||
)
|
||||
list(APPEND sycl_compiler_flags_RelWithDebInfo
|
||||
-DNDEBUG
|
||||
-g
|
||||
)
|
||||
list(APPEND sycl_compiler_flags_Debug
|
||||
-g
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND sycl_compiler_flags_Debug
|
||||
-D_DEBUG
|
||||
-nostdlib
|
||||
-Xclang --dependent-lib=msvcrtd
|
||||
)
|
||||
|
||||
list(APPEND sycl_compiler_flags
|
||||
-L"${sycl_compiler_root}/../lib" # To find sycl.lib
|
||||
-L"${sycl_compiler_root}/../compiler/lib/intel64_win" # To find libircmt.lib (when using `icpx`)
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${cycles_kernel_oneapi_lib} ${cycles_kernel_oneapi_linker_lib}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
"PATH=${OCLOC_INSTALL_DIR}\;${sycl_compiler_root}"
|
||||
${SYCL_COMPILER}
|
||||
"$<$<CONFIG:Release>:${sycl_compiler_flags_Release}>"
|
||||
"$<$<CONFIG:RelWithDebInfo>:${sycl_compiler_flags_RelWithDebInfo}>"
|
||||
"$<$<CONFIG:Debug>:${sycl_compiler_flags_Debug}>"
|
||||
"$<$<CONFIG:MinSizeRel>:${sycl_compiler_flags_Release}>"
|
||||
COMMAND_EXPAND_LISTS
|
||||
DEPENDS ${cycles_oneapi_kernel_sources} ${SYCL_COMPILER})
|
||||
else()
|
||||
# The following join/replace operations are to prevent cmake from
|
||||
# escaping space chars with backslashes in add_custom_command.
|
||||
list(JOIN sycl_compiler_flags_Release " " sycl_compiler_flags_Release_str)
|
||||
string(REPLACE " " ";" sycl_compiler_flags_Release_str ${sycl_compiler_flags_Release_str})
|
||||
list(JOIN sycl_compiler_flags_RelWithDebInfo " " sycl_compiler_flags_RelWithDebInfo_str)
|
||||
string(REPLACE " " ";" sycl_compiler_flags_RelWithDebInfo_str ${sycl_compiler_flags_RelWithDebInfo_str})
|
||||
list(JOIN sycl_compiler_flags_Debug " " sycl_compiler_flags_Debug_str)
|
||||
string(REPLACE " " ";" sycl_compiler_flags_Debug_str ${sycl_compiler_flags_Debug_str})
|
||||
add_custom_command(
|
||||
OUTPUT ${cycles_kernel_oneapi_lib}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env
|
||||
"LD_LIBRARY_PATH=${sycl_compiler_root}/../lib:${OCLOC_LD_LIBRARY_PATH}"
|
||||
# `$ENV{PATH}` is for compiler to find `ld`.
|
||||
"PATH=${OCLOC_INSTALL_DIR}/bin:${sycl_compiler_root}:$ENV{PATH}"
|
||||
${SYCL_COMPILER}
|
||||
"$<$<CONFIG:Release>:${sycl_compiler_flags_Release_str}>"
|
||||
"$<$<CONFIG:RelWithDebInfo>:${sycl_compiler_flags_RelWithDebInfo_str}>"
|
||||
"$<$<CONFIG:Debug>:${sycl_compiler_flags_Debug_str}>"
|
||||
"$<$<CONFIG:MinSizeRel>:${sycl_compiler_flags_Release_str}>"
|
||||
COMMAND_EXPAND_LISTS
|
||||
DEPENDS ${cycles_oneapi_kernel_sources} ${SYCL_COMPILER})
|
||||
endif()
|
||||
|
||||
# install dynamic libraries required at runtime
|
||||
delayed_install("" "${cycles_kernel_oneapi_lib}" ${cycles_kernel_runtime_lib_target_path})
|
||||
|
||||
add_custom_target(cycles_kernel_oneapi
|
||||
ALL
|
||||
DEPENDS ${cycles_kernel_oneapi_lib}
|
||||
SOURCES ${SRC_KERNEL_DEVICE_ONEAPI} ${SRC_KERNEL_DEVICE_ONEAPI_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_oneapi)
|
||||
|
||||
source_group("device\\oneapi" FILES ${SRC_KERNEL_DEVICE_ONEAPI} ${SRC_KERNEL_DEVICE_ONEAPI_HEADERS})
|
||||
|
||||
add_dependencies(cycles_kernel cycles_kernel_oneapi)
|
||||
endif()
|
||||
270
blender-5.2.0/intern/cycles/kernel/device/oneapi/compat.h
Normal file
270
blender-5.2.0/intern/cycles/kernel/device/oneapi/compat.h
Normal file
@@ -0,0 +1,270 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define __KERNEL_GPU__
|
||||
#define __KERNEL_ONEAPI__
|
||||
#define __KERNEL_64_BIT__
|
||||
|
||||
#ifdef WITH_EMBREE_GPU
|
||||
# define __KERNEL_GPU_RAYTRACING__
|
||||
#endif
|
||||
|
||||
#define CCL_NAMESPACE_BEGIN
|
||||
#define CCL_NAMESPACE_END
|
||||
|
||||
#include <cstdint>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef __NODES_MAX_GROUP__
|
||||
# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX
|
||||
#endif
|
||||
#ifndef __NODES_FEATURES__
|
||||
# define __NODES_FEATURES__ NODE_FEATURE_ALL
|
||||
#endif
|
||||
|
||||
/* This one does not have an abstraction.
|
||||
* It's used by other devices directly.
|
||||
*/
|
||||
|
||||
#define __device__
|
||||
|
||||
/* Qualifier wrappers for different names on different devices */
|
||||
|
||||
#define ccl_device inline
|
||||
#define ccl_device_extern extern "C"
|
||||
#define ccl_global
|
||||
#define ccl_always_inline __attribute__((always_inline))
|
||||
#define ccl_device_inline __attribute__((always_inline))
|
||||
#define ccl_noinline __attribute__((noinline))
|
||||
#define ccl_inline_constant const constexpr
|
||||
#define ccl_device_constant static constexpr
|
||||
#define ccl_static_constexpr static constexpr
|
||||
#define ccl_device_forceinline __attribute__((always_inline))
|
||||
#define ccl_device_noinline __attribute__((noinline))
|
||||
#define ccl_device_noinline_cpu ccl_device
|
||||
#define ccl_device_inline_method ccl_device
|
||||
#define ccl_device_template_spec template<> ccl_device_inline
|
||||
#define ccl_restrict __restrict__
|
||||
#define ccl_optional_struct_init
|
||||
#define ccl_private
|
||||
#define ccl_ray_data ccl_private
|
||||
#define ccl_gpu_shared
|
||||
#define ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
#define ccl_constant const
|
||||
#define ccl_try_align(...) __attribute__((aligned(__VA_ARGS__)))
|
||||
#define ccl_align(n) __attribute__((aligned(n)))
|
||||
#define kernel_assert(cond)
|
||||
#define ccl_may_alias
|
||||
#define ccl_attr_maybe_unused [[maybe_unused]]
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
/* kernel.h adapters */
|
||||
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
|
||||
#define ccl_gpu_kernel_threads(block_num_threads)
|
||||
|
||||
# define __ccl_gpu_kernel_signature(name, ...) \
|
||||
void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
||||
size_t kernel_global_size, \
|
||||
size_t kernel_local_size, \
|
||||
sycl::handler &cgh, \
|
||||
__VA_ARGS__) { \
|
||||
(void)(kg); \
|
||||
cgh.parallel_for( \
|
||||
sycl::nd_range<1>(kernel_global_size, kernel_local_size), \
|
||||
[=](sycl::nd_item<1> item) {
|
||||
|
||||
# define ccl_gpu_kernel_signature __ccl_gpu_kernel_signature
|
||||
|
||||
# define ccl_gpu_kernel_postfix \
|
||||
}); \
|
||||
}
|
||||
|
||||
#define ccl_gpu_kernel_call(x) ((ONEAPIKernelContext*)kg)->x
|
||||
#define ccl_gpu_kernel_within_bounds(i, n) ((i) < (n))
|
||||
|
||||
#define ccl_gpu_kernel_lambda(func, ...) \
|
||||
struct KernelLambda \
|
||||
{ \
|
||||
KernelLambda(const ONEAPIKernelContext *_kg) : kg(_kg) {} \
|
||||
ccl_private const ONEAPIKernelContext *kg; \
|
||||
__VA_ARGS__; \
|
||||
int operator()(const int state) const { return (func); } \
|
||||
} ccl_gpu_kernel_lambda_pass((ONEAPIKernelContext *)kg)
|
||||
|
||||
/* GPU thread, block, grid size and index */
|
||||
|
||||
# define ccl_gpu_thread_idx_x (sycl::ext::oneapi::this_work_item::get_nd_item<1>().get_local_id(0))
|
||||
# define ccl_gpu_block_dim_x (sycl::ext::oneapi::this_work_item::get_nd_item<1>().get_local_range(0))
|
||||
# define ccl_gpu_block_idx_x (sycl::ext::oneapi::this_work_item::get_nd_item<1>().get_group(0))
|
||||
# define ccl_gpu_grid_dim_x (sycl::ext::oneapi::this_work_item::get_nd_item<1>().get_group_range(0))
|
||||
# define ccl_gpu_warp_size (sycl::ext::oneapi::this_work_item::get_sub_group().get_local_range()[0])
|
||||
# define ccl_gpu_thread_mask(thread_warp) uint(0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp))
|
||||
|
||||
# define ccl_gpu_global_id_x() (sycl::ext::oneapi::this_work_item::get_nd_item<1>().get_global_id(0))
|
||||
# define ccl_gpu_global_size_x() (sycl::ext::oneapi::this_work_item::get_nd_item<1>().get_global_range(0))
|
||||
|
||||
/* GPU warp synchronization */
|
||||
# define ccl_gpu_syncthreads() sycl::ext::oneapi::this_work_item::get_nd_item<1>().barrier()
|
||||
# define ccl_gpu_local_syncthreads() sycl::ext::oneapi::this_work_item::get_nd_item<1>().barrier(sycl::access::fence_space::local_space)
|
||||
|
||||
/* A ballot in SYCL is only available as an Intel extension and its DPC++ v6.3 implementation
|
||||
* does not support devices with sub-group sizes above 64. Summing values (of any type) within
|
||||
* sub-groups can be achieved with the SYCL core feature inclusive_scan_over_group, which has
|
||||
* better support on non-Intel devices. */
|
||||
# define ccl_gpu_ballot(predicate) 0; static_assert(false, "Use sycl::inclusive_scan_over_group on oneAPI device instead of ccl_gpu_ballot")
|
||||
|
||||
/* Debug defines */
|
||||
#if defined(__SYCL_DEVICE_ONLY__)
|
||||
# define CCL_ONEAPI_CONSTANT __attribute__((opencl_constant))
|
||||
#else
|
||||
# define CCL_ONEAPI_CONSTANT
|
||||
#endif
|
||||
|
||||
#define sycl_printf(format, ...) { \
|
||||
static const CCL_ONEAPI_CONSTANT char fmt[] = format; \
|
||||
sycl::ext::oneapi::experimental::printf(fmt, __VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
#define sycl_printf_(format) { \
|
||||
static const CCL_ONEAPI_CONSTANT char fmt[] = format; \
|
||||
sycl::ext::oneapi::experimental::printf(fmt); \
|
||||
}
|
||||
|
||||
/* GPU texture objects */
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
/* Types */
|
||||
|
||||
/* It's not possible to use sycl types like sycl::float3, sycl::int3, etc
|
||||
* because these types have different interfaces from blender version. */
|
||||
|
||||
using uchar = unsigned char;
|
||||
using sycl::half;
|
||||
|
||||
/* math functions */
|
||||
ccl_device_forceinline float __uint_as_float(unsigned int x)
|
||||
{
|
||||
return sycl::bit_cast<float>(x);
|
||||
}
|
||||
ccl_device_forceinline unsigned int __float_as_uint(const float x)
|
||||
{
|
||||
return sycl::bit_cast<unsigned int>(x);
|
||||
}
|
||||
ccl_device_forceinline float __int_as_float(const int x)
|
||||
{
|
||||
return sycl::bit_cast<float>(x);
|
||||
}
|
||||
ccl_device_forceinline int __float_as_int(const float x)
|
||||
{
|
||||
return sycl::bit_cast<int>(x);
|
||||
}
|
||||
|
||||
#define fabsf(x) sycl::fabs((x))
|
||||
#define copysignf(x, y) sycl::copysign((x), (y))
|
||||
#define asinf(x) sycl::asin((x))
|
||||
#define acosf(x) sycl::acos((x))
|
||||
#define atanf(x) sycl::atan((x))
|
||||
#define floorf(x) sycl::floor((x))
|
||||
#define ceilf(x) sycl::ceil((x))
|
||||
#define roundf(x) sycl::round((x))
|
||||
#define sinhf(x) sycl::sinh((x))
|
||||
#define coshf(x) sycl::cosh((x))
|
||||
#define tanhf(x) sycl::tanh((x))
|
||||
#define hypotf(x, y) sycl::hypot((x), (y))
|
||||
#define atan2f(x, y) sycl::atan2((x), (y))
|
||||
#define fmaxf(x, y) sycl::fmax((x), (y))
|
||||
#define fminf(x, y) sycl::fmin((x), (y))
|
||||
#define fmodf(x, y) sycl::fmod((x), (y))
|
||||
#define lgammaf(x) sycl::lgamma((x))
|
||||
#define ldexpf(x, y) sycl::ldexp((x), (y))
|
||||
|
||||
#define cosf(x) sycl::native::cos(((float)(x)))
|
||||
#define sinf(x) sycl::native::sin(((float)(x)))
|
||||
#define powf(x, y) sycl::native::powr(((float)(x)), ((float)(y)))
|
||||
#define tanf(x) sycl::native::tan(((float)(x)))
|
||||
#define logf(x) sycl::native::log(((float)(x)))
|
||||
#define expf(x) sycl::native::exp(((float)(x)))
|
||||
#define sqrtf(x) sycl::native::sqrt(((float)(x)))
|
||||
|
||||
#define __forceinline __attribute__((always_inline))
|
||||
|
||||
/* Types */
|
||||
#include "util/half.h"
|
||||
#include "util/types.h"
|
||||
|
||||
static_assert(
|
||||
sizeof(sycl::ext::oneapi::experimental::sampled_image_handle::raw_image_handle_type) ==
|
||||
sizeof(uint64_t));
|
||||
typedef uint64_t ccl_gpu_image_object_2D;
|
||||
typedef uint64_t ccl_gpu_image_object_3D;
|
||||
|
||||
template<typename T>
|
||||
ccl_device_forceinline T ccl_gpu_image_object_read_2D(const ccl_gpu_image_object_2D texobj,
|
||||
const float x,
|
||||
const float y)
|
||||
{
|
||||
/* Generic implementation not possible due to limitation with SYCL bindless sampled images
|
||||
* not being able to read in a format, which is different from the supported data type of
|
||||
* the texture.
|
||||
* But looks it looks like this is not a problem at the moment. */
|
||||
static_assert(false);
|
||||
return T();
|
||||
}
|
||||
|
||||
template<>
|
||||
ccl_device_forceinline float ccl_gpu_image_object_read_2D<float>(
|
||||
const ccl_gpu_image_object_2D texobj, const float x, const float y)
|
||||
{
|
||||
sycl::ext::oneapi::experimental::sampled_image_handle image(
|
||||
(sycl::ext::oneapi::experimental::sampled_image_handle::raw_image_handle_type)texobj);
|
||||
return sycl::ext::oneapi::experimental::sample_image<float>(image, sycl::float2{x, y});
|
||||
}
|
||||
|
||||
template<>
|
||||
ccl_device_forceinline float4 ccl_gpu_image_object_read_2D<float4>(
|
||||
const ccl_gpu_image_object_2D texobj, const float x, const float y)
|
||||
{
|
||||
sycl::ext::oneapi::experimental::sampled_image_handle image(
|
||||
(sycl::ext::oneapi::experimental::sampled_image_handle::raw_image_handle_type)texobj);
|
||||
return sycl::ext::oneapi::experimental::sample_image<float4, sycl::vec<float, 4>>(
|
||||
image, sycl::float2{x, y});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ccl_device_forceinline T ccl_gpu_image_object_read_3D(const ccl_gpu_image_object_3D texobj,
|
||||
const float x,
|
||||
const float y,
|
||||
const float z)
|
||||
{
|
||||
/* A generic implementation is not possible due to limitations with SYCL bindless sampled images
|
||||
* not being able to read in a format that is different from the supported data type of
|
||||
* the texture.
|
||||
* However, it looks like this is not a problem at the moment, but I am leaving a static
|
||||
* assert in order to easily detect if it becomes a problem in the future. */
|
||||
static_assert(false);
|
||||
return T();
|
||||
}
|
||||
|
||||
template<>
|
||||
ccl_device_forceinline float ccl_gpu_image_object_read_3D<float>(
|
||||
const ccl_gpu_image_object_3D texobj, const float x, const float y, const float z)
|
||||
{
|
||||
sycl::ext::oneapi::experimental::sampled_image_handle image(
|
||||
(sycl::ext::oneapi::experimental::sampled_image_handle::raw_image_handle_type)texobj);
|
||||
return sycl::ext::oneapi::experimental::sample_image<float>(image, sycl::float3{x, y, z});
|
||||
}
|
||||
|
||||
template<>
|
||||
ccl_device_forceinline float4 ccl_gpu_image_object_read_3D<float4>(
|
||||
const ccl_gpu_image_object_3D texobj, const float x, const float y, const float z)
|
||||
{
|
||||
sycl::ext::oneapi::experimental::sampled_image_handle image(
|
||||
(sycl::ext::oneapi::experimental::sampled_image_handle::raw_image_handle_type)texobj);
|
||||
return sycl::ext::oneapi::experimental::sample_image<float4, sycl::vec<float, 4>>(
|
||||
image, sycl::float3{x, y, z});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "kernel/util/nanovdb.h"
|
||||
|
||||
/* clang-format off */
|
||||
struct ONEAPIKernelContext : public KernelGlobalsGPU {
|
||||
public:
|
||||
# include "kernel/device/gpu/image.h"
|
||||
/* clang-format on */
|
||||
@@ -0,0 +1,8 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
}
|
||||
; /* end of ONEAPIKernelContext class definition */
|
||||
|
||||
#undef kernel_integrator_state
|
||||
#define kernel_integrator_state (*(kg->integrator_state))
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#if defined(WITH_EMBREE_GPU)
|
||||
# undef ccl_gpu_kernel_signature
|
||||
# define ccl_gpu_kernel_signature(name, ...) \
|
||||
void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \
|
||||
size_t kernel_global_size, \
|
||||
size_t kernel_local_size, \
|
||||
sycl::handler &cgh, \
|
||||
__VA_ARGS__) \
|
||||
{ \
|
||||
(void)(kg); \
|
||||
cgh.parallel_for( \
|
||||
sycl::nd_range<1>(kernel_global_size, kernel_local_size), \
|
||||
[=](sycl::nd_item<1> item, sycl::kernel_handler oneapi_kernel_handler) { \
|
||||
((ONEAPIKernelContext*)kg)->kernel_handler = oneapi_kernel_handler;
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#if defined(WITH_EMBREE_GPU)
|
||||
# undef ccl_gpu_kernel_signature
|
||||
# define ccl_gpu_kernel_signature __ccl_gpu_kernel_signature
|
||||
#endif
|
||||
47
blender-5.2.0/intern/cycles/kernel/device/oneapi/globals.h
Normal file
47
blender-5.2.0/intern/cycles/kernel/device/oneapi/globals.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/util/profiler.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* NOTE(@nsirgien): With SYCL we can't declare __constant__ global variable, which will be
|
||||
* accessible from device code, like it has been done for Cycles CUDA backend. So, the backend will
|
||||
* allocate this "constant" memory regions and store pointers to them in oneAPI context class */
|
||||
|
||||
struct IntegratorStateGPU;
|
||||
struct IntegratorQueueCounter;
|
||||
|
||||
struct KernelGlobalsGPU {
|
||||
|
||||
#define KERNEL_DATA_ARRAY(type, name) const type *__##name = nullptr;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) type *__##name = nullptr;
|
||||
#include "kernel/data_arrays.h"
|
||||
IntegratorStateGPU *integrator_state;
|
||||
const KernelData *__data;
|
||||
sycl::kernel_handler kernel_handler;
|
||||
};
|
||||
|
||||
using KernelGlobals = ccl_global KernelGlobalsGPU *ccl_restrict;
|
||||
|
||||
#define kernel_data (*(__data))
|
||||
#define kernel_integrator_state (*(integrator_state))
|
||||
|
||||
/* data lookup defines */
|
||||
|
||||
#define kernel_data_fetch(name, index) __##name[(index)]
|
||||
#define kernel_data_write(name, index, value) __##name[(index)] = (value)
|
||||
#define kernel_data_array(name) __##name
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
758
blender-5.2.0/intern/cycles/kernel/device/oneapi/kernel.cpp
Normal file
758
blender-5.2.0/intern/cycles/kernel/device/oneapi/kernel.cpp
Normal file
@@ -0,0 +1,758 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_ONEAPI
|
||||
|
||||
# include "kernel.h"
|
||||
# include <iostream>
|
||||
# include <map>
|
||||
# include <set>
|
||||
|
||||
/* <algorithm> is needed until included upstream in sycl/detail/property_list_base.hpp */
|
||||
# include <algorithm>
|
||||
# include <sycl/sycl.hpp>
|
||||
|
||||
# include "kernel/device/oneapi/compat.h"
|
||||
# include "kernel/device/oneapi/globals.h"
|
||||
# include "kernel/device/oneapi/kernel_templates.h"
|
||||
|
||||
# include "kernel/device/gpu/kernel.h"
|
||||
|
||||
# include "device/kernel.cpp"
|
||||
|
||||
static OneAPIErrorCallback s_error_cb = nullptr;
|
||||
static void *s_error_user_ptr = nullptr;
|
||||
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
static RTCFeatureFlags oneapi_embree_features_from_kernel_features(const uint kernel_features)
|
||||
{
|
||||
unsigned int feature_flags = RTC_FEATURE_FLAG_TRIANGLE | RTC_FEATURE_FLAG_INSTANCE |
|
||||
RTC_FEATURE_FLAG_FILTER_FUNCTION_IN_ARGUMENTS;
|
||||
|
||||
if (kernel_features & KERNEL_FEATURE_HAIR_THICK) {
|
||||
feature_flags |= RTC_FEATURE_FLAG_ROUND_CATMULL_ROM_CURVE |
|
||||
RTC_FEATURE_FLAG_ROUND_LINEAR_CURVE;
|
||||
}
|
||||
if (kernel_features & KERNEL_FEATURE_HAIR) {
|
||||
feature_flags |= RTC_FEATURE_FLAG_FLAT_CATMULL_ROM_CURVE;
|
||||
}
|
||||
if (kernel_features & KERNEL_FEATURE_POINTCLOUD) {
|
||||
feature_flags |= RTC_FEATURE_FLAG_POINT;
|
||||
}
|
||||
if (kernel_features & KERNEL_FEATURE_OBJECT_MOTION) {
|
||||
feature_flags |= RTC_FEATURE_FLAG_MOTION_BLUR;
|
||||
}
|
||||
|
||||
return (RTCFeatureFlags)feature_flags;
|
||||
}
|
||||
# endif
|
||||
|
||||
void oneapi_set_error_cb(OneAPIErrorCallback cb, void *user_ptr)
|
||||
{
|
||||
s_error_cb = cb;
|
||||
s_error_user_ptr = user_ptr;
|
||||
}
|
||||
|
||||
size_t oneapi_suggested_gpu_kernel_size(const DeviceKernel kernel)
|
||||
{
|
||||
/* This defines are available only to the device code, so making this function
|
||||
* seems to be the most reasonable way to provide access to them for the host code. */
|
||||
switch (kernel) {
|
||||
case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY:
|
||||
return GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE;
|
||||
|
||||
case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY:
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES:
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES:
|
||||
return GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE;
|
||||
|
||||
case DEVICE_KERNEL_INTEGRATOR_SORT_BUCKET_PASS:
|
||||
case DEVICE_KERNEL_INTEGRATOR_SORT_WRITE_PASS:
|
||||
return GPU_PARALLEL_SORT_BLOCK_SIZE;
|
||||
|
||||
case DEVICE_KERNEL_PREFIX_SUM:
|
||||
return GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE;
|
||||
|
||||
default:
|
||||
return (size_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE(@nsirgien): Execution of this simple kernel will check basic functionality like
|
||||
* memory allocations, memory transfers and execution of kernel with USM memory. */
|
||||
bool oneapi_run_test_kernel(SyclQueue *queue_)
|
||||
{
|
||||
assert(queue_);
|
||||
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
|
||||
const size_t N = 8;
|
||||
const size_t memory_byte_size = sizeof(int) * N;
|
||||
|
||||
bool is_computation_correct = true;
|
||||
try {
|
||||
int *A_host = (int *)sycl::aligned_alloc_host(16, memory_byte_size, *queue);
|
||||
|
||||
for (size_t i = (size_t)0; i < N; i++) {
|
||||
A_host[i] = rand() % 32;
|
||||
}
|
||||
|
||||
int *A_device = (int *)sycl::malloc_device(memory_byte_size, *queue);
|
||||
int *B_device = (int *)sycl::malloc_device(memory_byte_size, *queue);
|
||||
|
||||
queue->memcpy(A_device, A_host, memory_byte_size);
|
||||
queue->wait_and_throw();
|
||||
|
||||
queue->submit([&](sycl::handler &cgh) {
|
||||
cgh.parallel_for(N, [=](sycl::id<1> idx) { B_device[idx] = A_device[idx] + idx.get(0); });
|
||||
});
|
||||
queue->wait_and_throw();
|
||||
|
||||
int *B_host = (int *)sycl::aligned_alloc_host(16, memory_byte_size, *queue);
|
||||
|
||||
queue->memcpy(B_host, B_device, memory_byte_size);
|
||||
queue->wait_and_throw();
|
||||
|
||||
for (size_t i = (size_t)0; i < N; i++) {
|
||||
const int expected_result = i + A_host[i];
|
||||
if (B_host[i] != expected_result) {
|
||||
is_computation_correct = false;
|
||||
if (s_error_cb) {
|
||||
s_error_cb(("Incorrect result in test kernel execution - expected " +
|
||||
std::to_string(expected_result) + ", got " + std::to_string(B_host[i]))
|
||||
.c_str(),
|
||||
s_error_user_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sycl::free(A_host, *queue);
|
||||
sycl::free(B_host, *queue);
|
||||
sycl::free(A_device, *queue);
|
||||
sycl::free(B_device, *queue);
|
||||
queue->wait_and_throw();
|
||||
}
|
||||
catch (const sycl::exception &e) {
|
||||
if (s_error_cb) {
|
||||
s_error_cb(e.what(), s_error_user_ptr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_computation_correct;
|
||||
}
|
||||
|
||||
bool oneapi_zero_memory_on_device(SyclQueue *queue_, void *device_pointer, const size_t num_bytes)
|
||||
{
|
||||
assert(queue_);
|
||||
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
|
||||
try {
|
||||
queue->memset(device_pointer, 0, num_bytes);
|
||||
queue->wait_and_throw();
|
||||
return true;
|
||||
}
|
||||
catch (const sycl::exception &e) {
|
||||
if (s_error_cb) {
|
||||
s_error_cb(e.what(), s_error_user_ptr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool oneapi_kernel_is_required_for_features(const std::string &kernel_name,
|
||||
const uint kernel_features)
|
||||
{
|
||||
/* Skip all non-Cycles kernels */
|
||||
if (kernel_name.find("oneapi_kernel_") == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) == 0 &&
|
||||
kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE)) !=
|
||||
std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((kernel_features & KERNEL_FEATURE_MNEE) == 0 &&
|
||||
kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_MNEE)) !=
|
||||
std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((kernel_features & KERNEL_FEATURE_VOLUME) == 0 &&
|
||||
kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK)) !=
|
||||
std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((kernel_features & (KERNEL_FEATURE_PATH_TRACING | KERNEL_FEATURE_BAKING)) == 0) &&
|
||||
((kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST)) !=
|
||||
std::string::npos) ||
|
||||
(kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW)) !=
|
||||
std::string::npos) ||
|
||||
(kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE)) !=
|
||||
std::string::npos) ||
|
||||
(kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_MNEE)) !=
|
||||
std::string::npos) ||
|
||||
(kernel_name.find(device_kernel_as_string(
|
||||
DEVICE_KERNEL_INTEGRATOR_INTERSECT_DEDICATED_LIGHT)) != std::string::npos)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oneapi_kernel_is_compatible_with_hardware_raytracing(const std::string &kernel_name)
|
||||
{
|
||||
/* MNEE and Ray-trace kernels work correctly with Hardware Ray-tracing starting with Embree 4.1.
|
||||
*/
|
||||
# if defined(RTC_VERSION) && RTC_VERSION < 40100
|
||||
return (kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_INTERSECT_MNEE)) ==
|
||||
std::string::npos) &&
|
||||
(kernel_name.find(device_kernel_as_string(
|
||||
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE)) == std::string::npos);
|
||||
# else
|
||||
return true;
|
||||
# endif
|
||||
}
|
||||
|
||||
bool oneapi_kernel_has_intersections(const std::string &kernel_name)
|
||||
{
|
||||
for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) {
|
||||
DeviceKernel kernel = (DeviceKernel)i;
|
||||
if (device_kernel_has_intersection(kernel)) {
|
||||
if (kernel_name.find(device_kernel_as_string(kernel)) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool oneapi_load_kernels(SyclQueue *queue_,
|
||||
const uint kernel_features,
|
||||
bool use_hardware_raytracing)
|
||||
{
|
||||
assert(queue_);
|
||||
sycl::queue *queue = reinterpret_cast<sycl::queue *>(queue_);
|
||||
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
/* For best performance, we always JIT compile the kernels that are using Embree. */
|
||||
if (use_hardware_raytracing) {
|
||||
try {
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> all_kernels_bundle =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(),
|
||||
{queue->get_device()});
|
||||
|
||||
for (const sycl::kernel_id &kernel_id : all_kernels_bundle.get_kernel_ids()) {
|
||||
const std::string &kernel_name = kernel_id.get_name();
|
||||
|
||||
if (!oneapi_kernel_is_required_for_features(kernel_name, kernel_features) ||
|
||||
!(oneapi_kernel_has_intersections(kernel_name) &&
|
||||
oneapi_kernel_is_compatible_with_hardware_raytracing(kernel_name)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle_input =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(
|
||||
queue->get_context(), {queue->get_device()}, {kernel_id});
|
||||
|
||||
const RTCFeatureFlags embree_features = oneapi_embree_features_from_kernel_features(
|
||||
kernel_features);
|
||||
one_kernel_bundle_input
|
||||
.set_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>(
|
||||
embree_features);
|
||||
sycl::build(one_kernel_bundle_input);
|
||||
}
|
||||
}
|
||||
catch (const sycl::exception &e) {
|
||||
if (s_error_cb) {
|
||||
s_error_cb(e.what(), s_error_user_ptr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
try {
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> all_kernels_bundle =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(),
|
||||
{queue->get_device()});
|
||||
|
||||
for (const sycl::kernel_id &kernel_id : all_kernels_bundle.get_kernel_ids()) {
|
||||
const std::string &kernel_name = kernel_id.get_name();
|
||||
|
||||
/* In case HWRT is on, compilation of kernels using Embree is already handled in previous
|
||||
* block. */
|
||||
if (!oneapi_kernel_is_required_for_features(kernel_name, kernel_features) ||
|
||||
(use_hardware_raytracing && oneapi_kernel_has_intersections(kernel_name) &&
|
||||
oneapi_kernel_is_compatible_with_hardware_raytracing(kernel_name)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
if (oneapi_kernel_has_intersections(kernel_name)) {
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle_input =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(
|
||||
queue->get_context(), {queue->get_device()}, {kernel_id});
|
||||
one_kernel_bundle_input
|
||||
.set_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>(
|
||||
RTC_FEATURE_FLAG_NONE);
|
||||
sycl::build(one_kernel_bundle_input);
|
||||
continue;
|
||||
}
|
||||
# endif
|
||||
/* This call will ensure that AoT or cached JIT binaries are available
|
||||
* for execution. It will trigger compilation if it is not already the case. */
|
||||
(void)sycl::get_kernel_bundle<sycl::bundle_state::executable>(
|
||||
queue->get_context(), {queue->get_device()}, {kernel_id});
|
||||
}
|
||||
}
|
||||
catch (const sycl::exception &e) {
|
||||
if (s_error_cb) {
|
||||
s_error_cb(e.what(), s_error_user_ptr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool oneapi_enqueue_kernel(KernelContext *kernel_context,
|
||||
const int kernel,
|
||||
const size_t global_size,
|
||||
const size_t local_size,
|
||||
const uint kernel_features,
|
||||
bool use_hardware_raytracing,
|
||||
void **args)
|
||||
{
|
||||
bool success = true;
|
||||
::DeviceKernel device_kernel = (::DeviceKernel)kernel;
|
||||
KernelGlobalsGPU *kg = (KernelGlobalsGPU *)kernel_context->kernel_globals;
|
||||
sycl::queue *queue = reinterpret_cast<sycl::queue *>(kernel_context->queue);
|
||||
assert(queue);
|
||||
if (!queue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Let the compiler throw an error if there are any kernels missing in this implementation. */
|
||||
# if defined(_WIN32)
|
||||
# pragma warning(error : 4062)
|
||||
# elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic error "-Wswitch"
|
||||
# endif
|
||||
|
||||
int max_shaders = 0;
|
||||
|
||||
if (device_kernel == DEVICE_KERNEL_INTEGRATOR_SORT_BUCKET_PASS ||
|
||||
device_kernel == DEVICE_KERNEL_INTEGRATOR_SORT_WRITE_PASS)
|
||||
{
|
||||
max_shaders = (kernel_context->scene_max_shaders);
|
||||
}
|
||||
|
||||
try {
|
||||
queue->submit([&](sycl::handler &cgh) {
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
/* Spec says it has no effect if the called kernel doesn't support the below specialization
|
||||
* constant but it can still trigger a recompilation, so we set it only if needed. */
|
||||
if (device_kernel_has_intersection(device_kernel)) {
|
||||
const RTCFeatureFlags embree_features = use_hardware_raytracing ?
|
||||
oneapi_embree_features_from_kernel_features(
|
||||
kernel_features) :
|
||||
RTC_FEATURE_FLAG_NONE;
|
||||
cgh.set_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>(
|
||||
embree_features);
|
||||
}
|
||||
# else
|
||||
(void)kernel_features;
|
||||
# endif
|
||||
switch (device_kernel) {
|
||||
case DEVICE_KERNEL_INTEGRATOR_RESET: {
|
||||
oneapi_call(kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_reset);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_init_from_camera);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_init_from_bake);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_intersect_closest);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_intersect_shadow);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_intersect_subsurface);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_intersect_volume_stack);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_DEDICATED_LIGHT: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_intersect_dedicated_light);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_INTERSECT_MNEE: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_intersect_mnee);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_shade_background);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT_NEE: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_shade_light_nee);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT_FORWARD: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_shade_light_forward);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_shade_shadow);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_shade_surface);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_shade_surface_raytrace);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_shade_volume);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME_RAY_MARCHING: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_shade_volume_ray_marching);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADE_DEDICATED_LIGHT: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_shade_dedicated_light);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_queued_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_queued_shadow_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_active_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_terminated_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_terminated_shadow_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_sorted_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SORT_BUCKET_PASS: {
|
||||
sycl::local_accessor<int> local_mem(max_shaders, cgh);
|
||||
oneapi_kernel_integrator_sort_bucket_pass(kg,
|
||||
global_size,
|
||||
local_size,
|
||||
cgh,
|
||||
*(int *)(args[0]),
|
||||
*(int *)(args[1]),
|
||||
*(int *)(args[2]),
|
||||
*(int **)(args[3]),
|
||||
*(int *)(args[4]),
|
||||
local_mem);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SORT_WRITE_PASS: {
|
||||
sycl::local_accessor<int> local_mem(max_shaders, cgh);
|
||||
oneapi_kernel_integrator_sort_write_pass(kg,
|
||||
global_size,
|
||||
local_size,
|
||||
cgh,
|
||||
*(int *)(args[0]),
|
||||
*(int *)(args[1]),
|
||||
*(int *)(args[2]),
|
||||
*(int **)(args[3]),
|
||||
*(int *)(args[4]),
|
||||
local_mem);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_compact_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_compact_shadow_paths_array);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_adaptive_sampling_convergence_check);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_adaptive_sampling_filter_x);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_adaptive_sampling_filter_y);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_SHADER_EVAL_DISPLACE: {
|
||||
oneapi_call(kg, cgh, global_size, local_size, args, oneapi_kernel_shader_eval_displace);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_SHADER_EVAL_BACKGROUND: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_shader_eval_background);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_shader_eval_curve_shadow_transparency);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_SHADER_EVAL_VOLUME_DENSITY: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_shader_eval_volume_density);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_PREFIX_SUM: {
|
||||
oneapi_call(kg, cgh, global_size, local_size, args, oneapi_kernel_prefix_sum);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_VOLUME_GUIDING_FILTER_X: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_volume_guiding_filter_x);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_VOLUME_GUIDING_FILTER_Y: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_volume_guiding_filter_y);
|
||||
break;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
# define DEVICE_KERNEL_FILM_CONVERT_PARTIAL(VARIANT, variant) \
|
||||
case DEVICE_KERNEL_FILM_CONVERT_##VARIANT: { \
|
||||
oneapi_call(kg, cgh, \
|
||||
global_size, \
|
||||
local_size, \
|
||||
args, \
|
||||
oneapi_kernel_film_convert_##variant); \
|
||||
break; \
|
||||
}
|
||||
|
||||
# define DEVICE_KERNEL_FILM_CONVERT(variant, VARIANT) \
|
||||
DEVICE_KERNEL_FILM_CONVERT_PARTIAL(VARIANT, variant) \
|
||||
DEVICE_KERNEL_FILM_CONVERT_PARTIAL(VARIANT##_HALF_RGBA, variant##_half_rgba)
|
||||
|
||||
DEVICE_KERNEL_FILM_CONVERT(depth, DEPTH);
|
||||
DEVICE_KERNEL_FILM_CONVERT(mist, MIST);
|
||||
DEVICE_KERNEL_FILM_CONVERT(volume_majorant, VOLUME_MAJORANT);
|
||||
DEVICE_KERNEL_FILM_CONVERT(sample_count, SAMPLE_COUNT);
|
||||
DEVICE_KERNEL_FILM_CONVERT(float, FLOAT);
|
||||
DEVICE_KERNEL_FILM_CONVERT(light_path, LIGHT_PATH);
|
||||
DEVICE_KERNEL_FILM_CONVERT(rgbe, RGBE);
|
||||
DEVICE_KERNEL_FILM_CONVERT(float3, FLOAT3);
|
||||
DEVICE_KERNEL_FILM_CONVERT(motion, MOTION);
|
||||
DEVICE_KERNEL_FILM_CONVERT(cryptomatte, CRYPTOMATTE);
|
||||
DEVICE_KERNEL_FILM_CONVERT(shadow_catcher, SHADOW_CATCHER);
|
||||
DEVICE_KERNEL_FILM_CONVERT(shadow_catcher_matte_with_shadow,
|
||||
SHADOW_CATCHER_MATTE_WITH_SHADOW);
|
||||
DEVICE_KERNEL_FILM_CONVERT(combined, COMBINED);
|
||||
DEVICE_KERNEL_FILM_CONVERT(float4, FLOAT4);
|
||||
|
||||
# undef DEVICE_KERNEL_FILM_CONVERT
|
||||
# undef DEVICE_KERNEL_FILM_CONVERT_PARTIAL
|
||||
/* clang-format on */
|
||||
|
||||
case DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_filter_guiding_preprocess);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_filter_guiding_set_fake_albedo);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_FILTER_COLOR_PREPROCESS: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_filter_color_preprocess);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_filter_color_postprocess);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_FILTER_COLOR_FLIP_Y: {
|
||||
oneapi_call(kg, cgh, global_size, local_size, args, oneapi_kernel_filter_color_flip_y);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_cryptomatte_postprocess);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES: {
|
||||
oneapi_call(
|
||||
kg, cgh, global_size, local_size, args, oneapi_kernel_integrator_compact_states);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_compact_shadow_states);
|
||||
break;
|
||||
}
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS: {
|
||||
oneapi_call(kg,
|
||||
cgh,
|
||||
global_size,
|
||||
local_size,
|
||||
args,
|
||||
oneapi_kernel_integrator_shadow_catcher_count_possible_splits);
|
||||
break;
|
||||
}
|
||||
/* Unsupported kernels */
|
||||
case DEVICE_KERNEL_NUM:
|
||||
case DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL:
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADOW_PATH_MNEE_PENDING:
|
||||
kernel_assert(0);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (const sycl::exception &e) {
|
||||
if (s_error_cb) {
|
||||
s_error_cb(e.what(), s_error_user_ptr);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
# if defined(_WIN32)
|
||||
# pragma warning(default : 4062)
|
||||
# elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif /* WITH_ONEAPI */
|
||||
67
blender-5.2.0/intern/cycles/kernel/device/oneapi/kernel.h
Normal file
67
blender-5.2.0/intern/cycles/kernel/device/oneapi/kernel.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_ONEAPI
|
||||
|
||||
# include <stddef.h>
|
||||
|
||||
/* NOTE(@nsirgien): Should match underlying type in the declaration inside "kernel/types.h"
|
||||
* TODO: use kernel/types.h directly. */
|
||||
enum DeviceKernel : int;
|
||||
|
||||
# ifndef CYCLES_KERNEL_ONEAPI_EXPORT
|
||||
# ifdef _WIN32
|
||||
# if defined(ONEAPI_EXPORT)
|
||||
# define CYCLES_KERNEL_ONEAPI_EXPORT extern __declspec(dllexport)
|
||||
# else
|
||||
# define CYCLES_KERNEL_ONEAPI_EXPORT extern __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define CYCLES_KERNEL_ONEAPI_EXPORT extern __attribute__((visibility("default")))
|
||||
# endif
|
||||
# endif
|
||||
|
||||
class SyclQueue;
|
||||
class SyclDevice;
|
||||
|
||||
typedef void (*OneAPIErrorCallback)(const char *error, void *user_ptr);
|
||||
|
||||
struct KernelContext {
|
||||
/* Queue, associated with selected device */
|
||||
SyclQueue *queue = nullptr;
|
||||
/* Pointer to USM device memory with all global/constant allocation on this device */
|
||||
void *kernel_globals = nullptr;
|
||||
/* We needs this additional data for some kernels. */
|
||||
int scene_max_shaders = 0;
|
||||
};
|
||||
|
||||
/* Use extern C linking so that the symbols can be easily load from the dynamic library at runtime.
|
||||
*/
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
CYCLES_KERNEL_ONEAPI_EXPORT bool oneapi_run_test_kernel(SyclQueue *queue_);
|
||||
CYCLES_KERNEL_ONEAPI_EXPORT bool oneapi_zero_memory_on_device(SyclQueue *queue_,
|
||||
void *device_pointer,
|
||||
size_t num_bytes);
|
||||
CYCLES_KERNEL_ONEAPI_EXPORT void oneapi_set_error_cb(OneAPIErrorCallback cb, void *user_ptr);
|
||||
CYCLES_KERNEL_ONEAPI_EXPORT size_t oneapi_suggested_gpu_kernel_size(const DeviceKernel kernel);
|
||||
CYCLES_KERNEL_ONEAPI_EXPORT bool oneapi_enqueue_kernel(KernelContext *context,
|
||||
const int kernel,
|
||||
const size_t global_size,
|
||||
const size_t local_size,
|
||||
const unsigned int kernel_features,
|
||||
bool use_hardware_raytracing,
|
||||
void **args);
|
||||
CYCLES_KERNEL_ONEAPI_EXPORT bool oneapi_load_kernels(SyclQueue *queue,
|
||||
const unsigned int kernel_features,
|
||||
bool use_hardware_raytracing);
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
|
||||
# endif
|
||||
#endif /* WITH_ONEAPI */
|
||||
@@ -0,0 +1,124 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Some macro magic to generate templates for kernel arguments.
|
||||
* The resulting oneapi_call() template allows to call a SYCL/C++ kernel
|
||||
* with typed arguments by only giving it a void `**args` as given by Cycles.
|
||||
* The template will automatically cast from void* to the expected type. */
|
||||
|
||||
/* When expanded by the preprocessor, the generated templates will look like this example: */
|
||||
#if 0
|
||||
template<typename T0, typename T1, typename T2>
|
||||
void oneapi_call(
|
||||
KernelGlobalsGPU *kg,
|
||||
sycl::handler &cgh,
|
||||
const size_t global_size,
|
||||
const size_t local_size,
|
||||
void **args,
|
||||
void (*func)(const KernelGlobalsGPU *, size_t, size_t, sycl::handler &, T0, T1, T2))
|
||||
{
|
||||
func(kg, global_size, local_size, cgh, *(T0 *)(args[0]), *(T1 *)(args[1]), *(T2 *)(args[2]));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
#define ONEAPI_TYP(x) typename T##x
|
||||
#define ONEAPI_CAST(x) *(T##x *)(args[x])
|
||||
#define ONEAPI_T(x) T##x
|
||||
|
||||
#define ONEAPI_GET_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, N, ...) N
|
||||
#define ONEAPI_0(_call, ...)
|
||||
#define ONEAPI_1(_call, x) _call(x)
|
||||
#define ONEAPI_2(_call, x, ...) _call(x), ONEAPI_1(_call, __VA_ARGS__)
|
||||
#define ONEAPI_3(_call, x, ...) _call(x), ONEAPI_2(_call, __VA_ARGS__)
|
||||
#define ONEAPI_4(_call, x, ...) _call(x), ONEAPI_3(_call, __VA_ARGS__)
|
||||
#define ONEAPI_5(_call, x, ...) _call(x), ONEAPI_4(_call, __VA_ARGS__)
|
||||
#define ONEAPI_6(_call, x, ...) _call(x), ONEAPI_5(_call, __VA_ARGS__)
|
||||
#define ONEAPI_7(_call, x, ...) _call(x), ONEAPI_6(_call, __VA_ARGS__)
|
||||
#define ONEAPI_8(_call, x, ...) _call(x), ONEAPI_7(_call, __VA_ARGS__)
|
||||
#define ONEAPI_9(_call, x, ...) _call(x), ONEAPI_8(_call, __VA_ARGS__)
|
||||
#define ONEAPI_10(_call, x, ...) _call(x), ONEAPI_9(_call, __VA_ARGS__)
|
||||
#define ONEAPI_11(_call, x, ...) _call(x), ONEAPI_10(_call, __VA_ARGS__)
|
||||
#define ONEAPI_12(_call, x, ...) _call(x), ONEAPI_11(_call, __VA_ARGS__)
|
||||
#define ONEAPI_13(_call, x, ...) _call(x), ONEAPI_12(_call, __VA_ARGS__)
|
||||
#define ONEAPI_14(_call, x, ...) _call(x), ONEAPI_13(_call, __VA_ARGS__)
|
||||
#define ONEAPI_15(_call, x, ...) _call(x), ONEAPI_14(_call, __VA_ARGS__)
|
||||
#define ONEAPI_16(_call, x, ...) _call(x), ONEAPI_15(_call, __VA_ARGS__)
|
||||
#define ONEAPI_17(_call, x, ...) _call(x), ONEAPI_16(_call, __VA_ARGS__)
|
||||
#define ONEAPI_18(_call, x, ...) _call(x), ONEAPI_17(_call, __VA_ARGS__)
|
||||
#define ONEAPI_19(_call, x, ...) _call(x), ONEAPI_18(_call, __VA_ARGS__)
|
||||
#define ONEAPI_20(_call, x, ...) _call(x), ONEAPI_19(_call, __VA_ARGS__)
|
||||
#define ONEAPI_21(_call, x, ...) _call(x), ONEAPI_20(_call, __VA_ARGS__)
|
||||
|
||||
#define ONEAPI_CALL_FOR(x, ...) \
|
||||
ONEAPI_GET_NTH_ARG("ignored", \
|
||||
##__VA_ARGS__, \
|
||||
ONEAPI_21, \
|
||||
ONEAPI_20, \
|
||||
ONEAPI_19, \
|
||||
ONEAPI_18, \
|
||||
ONEAPI_17, \
|
||||
ONEAPI_16, \
|
||||
ONEAPI_15, \
|
||||
ONEAPI_14, \
|
||||
ONEAPI_13, \
|
||||
ONEAPI_12, \
|
||||
ONEAPI_11, \
|
||||
ONEAPI_10, \
|
||||
ONEAPI_9, \
|
||||
ONEAPI_8, \
|
||||
ONEAPI_7, \
|
||||
ONEAPI_6, \
|
||||
ONEAPI_5, \
|
||||
ONEAPI_4, \
|
||||
ONEAPI_3, \
|
||||
ONEAPI_2, \
|
||||
ONEAPI_1, \
|
||||
ONEAPI_0) \
|
||||
(x, ##__VA_ARGS__)
|
||||
|
||||
/* This template automatically casts entries in the void **args array to the types requested by the kernel func.
|
||||
* Since kernel parameters are passed as void ** to the device, this is the closest that we have to type safety. */
|
||||
#define oneapi_template(...) \
|
||||
template<ONEAPI_CALL_FOR(ONEAPI_TYP, __VA_ARGS__)> \
|
||||
void oneapi_call( \
|
||||
KernelGlobalsGPU *kg, \
|
||||
sycl::handler &cgh, \
|
||||
size_t global_size, \
|
||||
size_t local_size, \
|
||||
void **args, \
|
||||
void (*func)(KernelGlobalsGPU*, size_t, size_t, sycl::handler &, ONEAPI_CALL_FOR(ONEAPI_T, __VA_ARGS__))) \
|
||||
{ \
|
||||
func(kg, \
|
||||
global_size, \
|
||||
local_size, \
|
||||
cgh, \
|
||||
ONEAPI_CALL_FOR(ONEAPI_CAST, __VA_ARGS__)); \
|
||||
}
|
||||
|
||||
oneapi_template(0)
|
||||
oneapi_template(0, 1)
|
||||
oneapi_template(0, 1, 2)
|
||||
oneapi_template(0, 1, 2, 3)
|
||||
oneapi_template(0, 1, 2, 3, 4)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
|
||||
oneapi_template(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
|
||||
|
||||
/* clang-format on */
|
||||
160
blender-5.2.0/intern/cycles/kernel/device/optix/CMakeLists.txt
Normal file
160
blender-5.2.0/intern/cycles/kernel/device/optix/CMakeLists.txt
Normal file
@@ -0,0 +1,160 @@
|
||||
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
../../..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC_KERNEL_DEVICE_OPTIX
|
||||
kernel.cu
|
||||
kernel_mnee.cu
|
||||
kernel_shader_raytrace.cu
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
math(EXPR OSL_LIBRARY_VERSION_CODE "${OSL_VERSION_MAJOR} * 10000 + ${OSL_VERSION_MINOR} * 100 + ${OSL_VERSION_PATCH}")
|
||||
|
||||
set(SRC_KERNEL_DEVICE_OPTIX
|
||||
${SRC_KERNEL_DEVICE_OPTIX}
|
||||
../../osl/services_optix.cu
|
||||
kernel_osl.cu
|
||||
kernel_osl_camera.cu
|
||||
kernel_osl_mnee.cu
|
||||
kernel_osl_shader_raytrace.cu
|
||||
kernel_osl_volume.cu
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SRC_KERNEL_DEVICE_OPTIX_HEADERS
|
||||
bvh.h
|
||||
compat.h
|
||||
globals.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_CYCLES_CUDA_BINARIES AND WITH_CYCLES_DEVICE_OPTIX)
|
||||
# CUDA version
|
||||
cuda_get_version(CUDA_VERSION)
|
||||
|
||||
# Modifies in parent scope:
|
||||
# - `optix_ptx`: appended with compressed ptx output.
|
||||
function(cycles_optix_kernel_add name input flags)
|
||||
set(output "${CMAKE_CURRENT_BINARY_DIR}/${name}.ptx")
|
||||
set(output_compressed "${output}.zst")
|
||||
|
||||
set(cuda_flags ${flags}
|
||||
-I "${OPTIX_INCLUDE_DIR}"
|
||||
-I "${CMAKE_CURRENT_SOURCE_DIR}/../../.."
|
||||
-o ${output})
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
set(cuda_flags ${cuda_flags}
|
||||
-D OSL_LIBRARY_VERSION_CODE=${OSL_LIBRARY_VERSION_CODE})
|
||||
endif()
|
||||
|
||||
set(arch compute_50)
|
||||
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
|
||||
set(cuda_version ${CUDA_VERSION})
|
||||
if("${CUDA_VERSION}" GREATER_EQUAL 130) # Support for Maxwell, Pascal and Volta was dropped in CUDA 13
|
||||
if(DEFINED CUDA11_NVCC_EXECUTABLE)
|
||||
# Use CUDA 11 for the OptiX PTX kernel, to retain support for older architectures.
|
||||
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
|
||||
set(cuda_version 110)
|
||||
else()
|
||||
set(arch compute_75)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cuda_add_common_flags(${cuda_version} ${arch} "${cuda_flags}" cuda_flags)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${output}
|
||||
DEPENDS
|
||||
${input}
|
||||
${SRC_KERNEL_DEVICE_OPTIX_HEADERS}
|
||||
$<TARGET_PROPERTY:cycles_kernel,INTERFACE_SOURCES>
|
||||
COMMAND
|
||||
${cuda_nvcc_executable}
|
||||
--ptx
|
||||
-arch=${arch}
|
||||
${cuda_flags}
|
||||
${input}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
unset(cuda_nvcc_executable)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${output_compressed}
|
||||
COMMAND "$<TARGET_FILE:zstd_compress>" ${output} ${output_compressed}
|
||||
DEPENDS ${output})
|
||||
|
||||
list(APPEND optix_ptx ${output_compressed})
|
||||
set(optix_ptx "${optix_ptx}" PARENT_SCOPE)
|
||||
|
||||
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${output_compressed}" ${CYCLES_INSTALL_PATH}/lib)
|
||||
endfunction()
|
||||
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix
|
||||
"kernel.cu"
|
||||
"")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_mnee
|
||||
"kernel_mnee.cu"
|
||||
"")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_shader_raytrace
|
||||
"kernel_shader_raytrace.cu"
|
||||
"--keep-device-functions")
|
||||
if(WITH_CYCLES_OSL)
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_osl
|
||||
"kernel_osl.cu"
|
||||
"--relocatable-device-code=true")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_osl_shader_raytrace
|
||||
"kernel_osl_shader_raytrace.cu"
|
||||
"--relocatable-device-code=true")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_osl_mnee
|
||||
"kernel_osl_mnee.cu"
|
||||
"--relocatable-device-code=true")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_osl_volume
|
||||
"kernel_osl_volume.cu"
|
||||
"--relocatable-device-code=true")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_osl_camera
|
||||
"kernel_osl_camera.cu"
|
||||
"--relocatable-device-code=true")
|
||||
cycles_optix_kernel_add(
|
||||
kernel_optix_osl_services
|
||||
"../../osl/services_optix.cu"
|
||||
"--relocatable-device-code=true")
|
||||
endif()
|
||||
|
||||
add_custom_target(cycles_kernel_optix
|
||||
ALL
|
||||
DEPENDS ${optix_ptx}
|
||||
SOURCES ${SRC_KERNEL_DEVICE_OPTIX} ${SRC_KERNEL_DEVICE_OPTIX_HEADERS}
|
||||
)
|
||||
cycles_set_solution_folder(cycles_kernel_optix)
|
||||
|
||||
source_group("device\\optix" FILES ${SRC_KERNEL_DEVICE_OPTIX} ${SRC_KERNEL_DEVICE_OPTIX_HEADERS})
|
||||
|
||||
add_dependencies(cycles_kernel cycles_kernel_optix)
|
||||
endif()
|
||||
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix)
|
||||
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix)
|
||||
644
blender-5.2.0/intern/cycles/kernel/device/optix/bvh.h
Normal file
644
blender-5.2.0/intern/cycles/kernel/device/optix/bvh.h
Normal file
@@ -0,0 +1,644 @@
|
||||
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* OptiX implementation of ray-scene intersection.
|
||||
*
|
||||
* Note on the payload registers.
|
||||
* Intersection and filtering functions might be sharing the same registers, even if it is not
|
||||
* very obvious from the trace/traverse call. The registers that have special meaning and are to
|
||||
* be kept "locked" to their meaning:
|
||||
* uint p4 = visibility;
|
||||
* uint p6 = pointer_pack_to_uint_0(ray);
|
||||
* uint p7 = pointer_pack_to_uint_1(ray); */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/bvh/types.h"
|
||||
#include "kernel/bvh/util.h"
|
||||
|
||||
#define OPTIX_DEFINE_ABI_VERSION_ONLY
|
||||
#include <optix_function_table.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Utilities. */
|
||||
|
||||
template<typename T> ccl_device_forceinline T *get_payload_ptr_0()
|
||||
{
|
||||
return pointer_unpack_from_uint<T>(optixGetPayload_0(), optixGetPayload_1());
|
||||
}
|
||||
template<typename T> ccl_device_forceinline T *get_payload_ptr_2()
|
||||
{
|
||||
return pointer_unpack_from_uint<T>(optixGetPayload_2(), optixGetPayload_3());
|
||||
}
|
||||
|
||||
template<typename T> ccl_device_forceinline T *get_payload_ptr_6()
|
||||
{
|
||||
return (T *)(((uint64_t)optixGetPayload_7() << 32) | optixGetPayload_6());
|
||||
}
|
||||
|
||||
ccl_device_forceinline int get_object_id()
|
||||
{
|
||||
#ifdef __OBJECT_MOTION__
|
||||
/* Always get the instance ID from the TLAS
|
||||
* There might be a motion transform node between TLAS and BLAS which does not have one. */
|
||||
return optixGetInstanceIdFromHandle(optixGetTransformListHandle(0));
|
||||
#else
|
||||
return optixGetInstanceId();
|
||||
#endif
|
||||
}
|
||||
|
||||
ccl_device_forceinline Intersection get_intersection()
|
||||
{
|
||||
Intersection isect;
|
||||
|
||||
isect.t = optixGetRayTmax();
|
||||
isect.prim = optixGetPrimitiveIndex();
|
||||
isect.object = get_object_id();
|
||||
|
||||
if (optixIsTriangleHit()) {
|
||||
/* Triangle. */
|
||||
const float2 barycentrics = optixGetTriangleBarycentrics();
|
||||
isect.u = barycentrics.x;
|
||||
isect.v = barycentrics.y;
|
||||
isect.type = kernel_data_fetch(objects, isect.object).primitive_type;
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
|
||||
/* Curve. */
|
||||
isect.u = __uint_as_float(optixGetAttribute_0());
|
||||
isect.v = __uint_as_float(optixGetAttribute_1());
|
||||
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, isect.prim);
|
||||
isect.type = segment.type;
|
||||
isect.prim = segment.prim;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
/* Point. */
|
||||
isect.u = 0.0f;
|
||||
isect.v = 0.0f;
|
||||
isect.type = kernel_data_fetch(objects, isect.object).primitive_type;
|
||||
}
|
||||
|
||||
return isect;
|
||||
}
|
||||
|
||||
/* Hit/miss functions. */
|
||||
|
||||
extern "C" __global__ void __miss__kernel_optix_miss()
|
||||
{
|
||||
/* 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss. */
|
||||
optixSetPayload_0(__float_as_uint(optixGetRayTmax()));
|
||||
optixSetPayload_5(PRIMITIVE_NONE);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __anyhit__kernel_optix_ignore()
|
||||
{
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
extern "C" __global__ void __closesthit__kernel_optix_ignore() {}
|
||||
|
||||
extern "C" __global__ void __anyhit__kernel_optix_local_hit()
|
||||
{
|
||||
#if defined(__HAIR__) || defined(__POINTCLOUD__)
|
||||
if (!optixIsTriangleHit()) {
|
||||
/* Ignore curves and points. */
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __BVH_LOCAL__
|
||||
const int object = get_object_id();
|
||||
if (object != optixGetPayload_4() /* local_object */) {
|
||||
/* Only intersect with matching object. */
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
if (intersection_skip_self_local(ray->self, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
const uint max_hits = optixGetPayload_5();
|
||||
if (max_hits == 0) {
|
||||
/* Special case for when no hit information is requested, just report that something was hit */
|
||||
optixSetPayload_5(true);
|
||||
return optixTerminateRay();
|
||||
}
|
||||
|
||||
const float isect_t = optixGetRayTmax();
|
||||
uint *const lcg_state = get_payload_ptr_0<uint>();
|
||||
LocalIntersection *const local_isect = get_payload_ptr_2<LocalIntersection>();
|
||||
|
||||
const int hit_index = local_intersect_get_record_index(
|
||||
local_isect, isect_t, lcg_state, max_hits);
|
||||
if (hit_index == -1) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
|
||||
Intersection *isect = &local_isect->hits[hit_index];
|
||||
isect->t = isect_t;
|
||||
isect->prim = prim;
|
||||
isect->object = get_object_id();
|
||||
isect->type = kernel_data_fetch(objects, isect->object).primitive_type;
|
||||
|
||||
const float2 barycentrics = optixGetTriangleBarycentrics();
|
||||
isect->u = barycentrics.x;
|
||||
isect->v = barycentrics.y;
|
||||
|
||||
/* Record geometric normal. */
|
||||
const int position_offset = kernel_data_fetch(objects, object).position_offset;
|
||||
const packed_uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
|
||||
const float3 tri_a = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
|
||||
const float3 tri_b = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
|
||||
const float3 tri_c = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
|
||||
|
||||
local_isect->Ng[hit_index] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
|
||||
|
||||
/* Continue tracing (without this the trace call would return after the first hit). */
|
||||
optixIgnoreIntersection();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
|
||||
{
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
ccl_private BVHShadowAllPayload *payload = get_payload_ptr_0<BVHShadowAllPayload>();
|
||||
const uint ray_visibility = optixGetPayload_4();
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
|
||||
Intersection isect = get_intersection();
|
||||
if (!bvh_shadow_all_anyhit_filter<ISECT_TEST_ALL>(
|
||||
kg, payload->state, *payload, ray->self, ray_visibility, isect))
|
||||
{
|
||||
optixTerminateRay();
|
||||
return;
|
||||
}
|
||||
|
||||
/* The idea here is to accept the hit, so that traversal won't consider any more hits beyond the
|
||||
* distance of the current hit anymore.
|
||||
*
|
||||
* We could accept the hit which is furthest away from the ones that are already recorded (for
|
||||
* this `>` needs to be replaced with `>=`). However, doing so has a performance impact in the
|
||||
* pabellon benchmark scene. The hypothesis here is that allowing to traverse one extra hit after
|
||||
* the array is filled allows to hit an opaque surface and do early exit from the shadow shading.
|
||||
*
|
||||
* Similar to this logic (allowing an extra hit) was in the original OptiX integration, so we
|
||||
* just keep following it to avoid performance regression. There is no the correct solution here,
|
||||
* as it depends on the scene. For example, if there are many transparent surfaces with no opaque
|
||||
* hit then it is faster to start accepting hits as soon as possible. However, if there are many
|
||||
* transparent surfaces, followed up with an opaque surface, it is faster to not accept any hit
|
||||
* and allow the opaque optimization to lead to an early output from the intersect-shade loop. */
|
||||
if (isect.t > payload->max_record_isect_t) {
|
||||
return;
|
||||
}
|
||||
|
||||
optixIgnoreIntersection();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" __global__ void __anyhit__kernel_optix_volume_test()
|
||||
{
|
||||
#if defined(__HAIR__) || defined(__POINTCLOUD__)
|
||||
if (!optixIsTriangleHit()) {
|
||||
/* Ignore curves. */
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
#endif
|
||||
|
||||
KernelGlobals kg = nullptr;
|
||||
|
||||
const int object = get_object_id();
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
const uint ray_visibility = optixGetPayload_4();
|
||||
|
||||
if (bvh_volume_anyhit_triangle_filter(kg, object, prim, ray->self, ray_visibility)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
|
||||
{
|
||||
const uint object = get_object_id();
|
||||
const uint visibility = optixGetPayload_4();
|
||||
#ifdef __VISIBILITY_FLAG__
|
||||
if ((kernel_data_fetch(objects, object).visibility & visibility) == 0) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
#endif
|
||||
|
||||
int prim = optixGetPrimitiveIndex();
|
||||
if (optixIsTriangleHit()) {
|
||||
/* Triangle. */
|
||||
}
|
||||
#ifdef __HAIR__
|
||||
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
|
||||
/* Curve. */
|
||||
prim = kernel_data_fetch(curve_segments, prim).prim;
|
||||
}
|
||||
#endif
|
||||
|
||||
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
|
||||
|
||||
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
|
||||
#ifdef __SHADOW_LINKING__
|
||||
if (intersection_skip_shadow_link(nullptr, ray->self, object)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (intersection_skip_self_shadow(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
else {
|
||||
/* Shadow ray early termination. */
|
||||
return optixTerminateRay();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (intersection_skip_self(ray->self, object, prim)) {
|
||||
return optixIgnoreIntersection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void __closesthit__kernel_optix_hit()
|
||||
{
|
||||
const int object = get_object_id();
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
|
||||
optixSetPayload_0(__float_as_uint(optixGetRayTmax())); /* Intersection distance */
|
||||
optixSetPayload_4(object);
|
||||
|
||||
if (optixIsTriangleHit()) {
|
||||
const float2 barycentrics = optixGetTriangleBarycentrics();
|
||||
optixSetPayload_1(__float_as_uint(barycentrics.x));
|
||||
optixSetPayload_2(__float_as_uint(barycentrics.y));
|
||||
optixSetPayload_3(prim);
|
||||
optixSetPayload_5(kernel_data_fetch(objects, object).primitive_type);
|
||||
}
|
||||
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
|
||||
optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */
|
||||
optixSetPayload_2(optixGetAttribute_1());
|
||||
optixSetPayload_3(segment.prim);
|
||||
optixSetPayload_5(segment.type);
|
||||
}
|
||||
else {
|
||||
optixSetPayload_1(0);
|
||||
optixSetPayload_2(0);
|
||||
optixSetPayload_3(prim);
|
||||
optixSetPayload_5(kernel_data_fetch(objects, object).primitive_type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom primitive intersection functions. */
|
||||
|
||||
#ifdef __HAIR__
|
||||
ccl_device_inline void optix_intersection_curve(const int prim, const int type)
|
||||
{
|
||||
const int object = get_object_id();
|
||||
|
||||
# ifdef __VISIBILITY_FLAG__
|
||||
const uint visibility = optixGetPayload_4();
|
||||
if ((kernel_data_fetch(objects, object).visibility & visibility) == 0) {
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
|
||||
const float3 ray_P = optixGetObjectRayOrigin();
|
||||
const float3 ray_D = optixGetObjectRayDirection();
|
||||
const float ray_tmin = optixGetRayTmin();
|
||||
|
||||
# ifdef __OBJECT_MOTION__
|
||||
const float time = optixGetRayTime();
|
||||
# else
|
||||
const float time = 0.0f;
|
||||
# endif
|
||||
|
||||
Intersection isect;
|
||||
isect.t = optixGetRayTmax();
|
||||
|
||||
if (curve_intersect(nullptr, &isect, ray_P, ray_D, ray_tmin, isect.t, object, prim, time, type))
|
||||
{
|
||||
static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use");
|
||||
optixReportIntersection(isect.t,
|
||||
type & PRIMITIVE_ALL,
|
||||
__float_as_int(isect.u), /* Attribute_0 */
|
||||
__float_as_int(isect.v)); /* Attribute_1 */
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void __intersection__curve_ribbon()
|
||||
{
|
||||
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, optixGetPrimitiveIndex());
|
||||
const int prim = segment.prim;
|
||||
const int type = segment.type;
|
||||
if ((type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
|
||||
optix_intersection_curve(prim, type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __POINTCLOUD__
|
||||
extern "C" __global__ void __intersection__point()
|
||||
{
|
||||
const int prim = optixGetPrimitiveIndex();
|
||||
const int object = get_object_id();
|
||||
const int type = kernel_data_fetch(objects, object).primitive_type;
|
||||
|
||||
# ifdef __VISIBILITY_FLAG__
|
||||
const uint visibility = optixGetPayload_4();
|
||||
if ((kernel_data_fetch(objects, object).visibility & visibility) == 0) {
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
|
||||
const float3 ray_P = optixGetObjectRayOrigin();
|
||||
const float3 ray_D = optixGetObjectRayDirection();
|
||||
const float ray_tmin = optixGetRayTmin();
|
||||
|
||||
# ifdef __OBJECT_MOTION__
|
||||
const float time = optixGetRayTime();
|
||||
# else
|
||||
const float time = 0.0f;
|
||||
# endif
|
||||
|
||||
Intersection isect;
|
||||
isect.t = optixGetRayTmax();
|
||||
|
||||
if (point_intersect(nullptr, &isect, ray_P, ray_D, ray_tmin, isect.t, object, prim, time, type))
|
||||
{
|
||||
static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use");
|
||||
optixReportIntersection(isect.t, type & PRIMITIVE_ALL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Scene intersection. */
|
||||
|
||||
ccl_device_intersect bool scene_intersect(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility,
|
||||
ccl_private Intersection *isect)
|
||||
{
|
||||
/* Note: some registers have hardcoded meaning.
|
||||
* Be careful when changing the values here. See the note at the top of this file for more
|
||||
* details. */
|
||||
uint p0 = 0;
|
||||
uint p1 = 0;
|
||||
uint p2 = 0;
|
||||
uint p3 = 0;
|
||||
uint p4 = visibility;
|
||||
uint p5 = PRIMITIVE_NONE;
|
||||
uint p6 = pointer_pack_to_uint_0(ray);
|
||||
uint p7 = pointer_pack_to_uint_1(ray);
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
else if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
|
||||
ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
|
||||
}
|
||||
|
||||
optixTrace(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
|
||||
ray->P,
|
||||
ray->D,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
ray->time,
|
||||
ray_mask,
|
||||
ray_flags,
|
||||
0, /* SBT offset for PG_HITD */
|
||||
0,
|
||||
0,
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
isect->t = __uint_as_float(p0);
|
||||
isect->u = __uint_as_float(p1);
|
||||
isect->v = __uint_as_float(p2);
|
||||
isect->prim = p3;
|
||||
isect->object = p4;
|
||||
isect->type = p5;
|
||||
|
||||
return p5 != PRIMITIVE_NONE;
|
||||
}
|
||||
|
||||
ccl_device_intersect bool scene_intersect_shadow(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
const uint visibility)
|
||||
{
|
||||
/* Note: some registers have hardcoded meaning.
|
||||
* Be careful when changing the values here. See the note at the top of this file for more
|
||||
* details. */
|
||||
uint p0 = 0;
|
||||
uint p1 = 0;
|
||||
uint p2 = 0;
|
||||
uint p3 = 0;
|
||||
uint p4 = visibility;
|
||||
uint p5 = PRIMITIVE_NONE;
|
||||
uint p6 = pointer_pack_to_uint_0(ray);
|
||||
uint p7 = pointer_pack_to_uint_1(ray);
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
else if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
|
||||
ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
|
||||
}
|
||||
|
||||
optixTraverse(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
|
||||
ray->P,
|
||||
ray->D,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
ray->time,
|
||||
ray_mask,
|
||||
ray_flags,
|
||||
0, /* SBT offset for PG_HITD */
|
||||
0,
|
||||
0,
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
return optixHitObjectIsHit();
|
||||
}
|
||||
|
||||
#ifdef __BVH_LOCAL__
|
||||
template<bool single_hit = false>
|
||||
ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private LocalIntersection *local_isect,
|
||||
const int local_object,
|
||||
ccl_private uint *lcg_state,
|
||||
const int max_hits)
|
||||
{
|
||||
/* Note: some registers have hardcoded meaning.
|
||||
* Be careful when changing the values here. See the note at the top of this file for more
|
||||
* details. */
|
||||
uint p0 = pointer_pack_to_uint_0(lcg_state);
|
||||
uint p1 = pointer_pack_to_uint_1(lcg_state);
|
||||
uint p2 = pointer_pack_to_uint_0(local_isect);
|
||||
uint p3 = pointer_pack_to_uint_1(local_isect);
|
||||
uint p4 = local_object;
|
||||
uint p6 = pointer_pack_to_uint_0(ray);
|
||||
uint p7 = pointer_pack_to_uint_1(ray);
|
||||
|
||||
/* Is set to zero on miss or if ray is aborted, so can be used as return value. */
|
||||
uint p5 = max_hits;
|
||||
|
||||
if (local_isect) {
|
||||
local_isect->num_hits = 0; /* Initialize hit count to zero. */
|
||||
}
|
||||
optixTraverse(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
|
||||
ray->P,
|
||||
ray->D,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
ray->time,
|
||||
0xFF,
|
||||
/* Need to always call into __anyhit__kernel_optix_local_hit. */
|
||||
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
|
||||
2, /* SBT offset for PG_HITL */
|
||||
0,
|
||||
0,
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
return p5;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
ccl_device_intersect void scene_intersect_shadow_all_optix(
|
||||
const ccl_private Ray *ccl_restrict ray,
|
||||
const uint ray_visibility,
|
||||
ccl_private BVHShadowAllPayload &ccl_restrict payload)
|
||||
{
|
||||
/* Note: some registers have hardcoded meaning.
|
||||
* Be careful when changing the values here. See the note at the top of this file for more
|
||||
* details. */
|
||||
uint p0 = pointer_pack_to_uint_0(&payload);
|
||||
uint p1 = pointer_pack_to_uint_1(&payload);
|
||||
uint p2 = 0;
|
||||
uint p3 = 0;
|
||||
uint p4 = ray_visibility;
|
||||
uint p5 = 0;
|
||||
uint p6 = pointer_pack_to_uint_0(ray);
|
||||
uint p7 = pointer_pack_to_uint_1(ray);
|
||||
|
||||
uint ray_mask = ray_visibility & 0xFF;
|
||||
if (0 == ray_mask && (ray_visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
|
||||
optixTraverse(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
|
||||
ray->P,
|
||||
ray->D,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
ray->time,
|
||||
ray_mask,
|
||||
/* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */
|
||||
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
|
||||
1, /* SBT offset for PG_HITS */
|
||||
0,
|
||||
0,
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __VOLUME__
|
||||
ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
||||
const ccl_private Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
const uint visibility)
|
||||
{
|
||||
/* Note: some registers have hardcoded meaning.
|
||||
* Be careful when changing the values here. See the note at the top of this file for more
|
||||
* details. */
|
||||
uint p0 = 0;
|
||||
uint p1 = 0;
|
||||
uint p2 = 0;
|
||||
uint p3 = 0;
|
||||
uint p4 = visibility;
|
||||
uint p5 = PRIMITIVE_NONE;
|
||||
uint p6 = pointer_pack_to_uint_0(ray);
|
||||
uint p7 = pointer_pack_to_uint_1(ray);
|
||||
|
||||
uint ray_mask = visibility & 0xFF;
|
||||
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
|
||||
ray_mask = 0xFF;
|
||||
}
|
||||
|
||||
optixTrace(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
|
||||
ray->P,
|
||||
ray->D,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
ray->time,
|
||||
ray_mask,
|
||||
/* Need to always call into __anyhit__kernel_optix_volume_test. */
|
||||
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
|
||||
3, /* SBT offset for PG_HITV */
|
||||
0,
|
||||
0,
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
p3,
|
||||
p4,
|
||||
p5,
|
||||
p6,
|
||||
p7);
|
||||
|
||||
isect->t = __uint_as_float(p0);
|
||||
isect->u = __uint_as_float(p1);
|
||||
isect->v = __uint_as_float(p2);
|
||||
isect->prim = p3;
|
||||
isect->object = p4;
|
||||
isect->type = p5;
|
||||
|
||||
return p5 != PRIMITIVE_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
100
blender-5.2.0/intern/cycles/kernel/device/optix/compat.h
Normal file
100
blender-5.2.0/intern/cycles/kernel/device/optix/compat.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2019 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define __KERNEL_GPU__
|
||||
#define __KERNEL_CUDA__ /* OptiX kernels are implicitly CUDA kernels too */
|
||||
#define __KERNEL_OPTIX__
|
||||
#define CCL_NAMESPACE_BEGIN
|
||||
#define CCL_NAMESPACE_END
|
||||
|
||||
#ifndef ATTR_FALLTHROUGH
|
||||
# define ATTR_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/* Manual definitions so we can compile without CUDA toolkit. */
|
||||
|
||||
#ifdef __CUDACC_RTC__
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
#else
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef CYCLES_CUBIN_CC
|
||||
# define FLT_MIN 1.175494350822287507969e-38f
|
||||
# define FLT_MAX 340282346638528859811704183484516925440.0f
|
||||
# define FLT_EPSILON 1.192092896e-07F
|
||||
#endif
|
||||
|
||||
#define ccl_device \
|
||||
static __device__ \
|
||||
__forceinline__ // Function calls are bad for OptiX performance, so inline everything
|
||||
#define ccl_device_extern extern "C" __device__
|
||||
#define ccl_device_inline ccl_device
|
||||
#define ccl_device_forceinline ccl_device
|
||||
#define ccl_device_inline_method __device__ __forceinline__
|
||||
#define ccl_device_template_spec template<> __device__ __forceinline__
|
||||
#define ccl_device_noinline static __device__ __noinline__
|
||||
#define ccl_device_noinline_cpu ccl_device
|
||||
#define ccl_global
|
||||
#define ccl_inline_constant static __constant__
|
||||
#define ccl_device_constant __constant__ __device__
|
||||
#define ccl_static_constexpr static constexpr
|
||||
#define ccl_constant const
|
||||
#define ccl_gpu_shared __shared__
|
||||
#define ccl_private
|
||||
#define ccl_ray_data ccl_private
|
||||
#define ccl_may_alias
|
||||
#define ccl_restrict __restrict__
|
||||
#define ccl_align(n) __align__(n)
|
||||
#define ccl_attr_maybe_unused [[maybe_unused]]
|
||||
|
||||
/* Zero initialize structs to help the compiler figure out scoping */
|
||||
#define ccl_optional_struct_init = {}
|
||||
|
||||
/* No assert supported for CUDA */
|
||||
|
||||
#define kernel_assert(cond)
|
||||
|
||||
/* GPU texture objects */
|
||||
|
||||
typedef unsigned long long CUtexObject;
|
||||
typedef CUtexObject ccl_gpu_image_object_2D;
|
||||
|
||||
template<typename T>
|
||||
ccl_device_forceinline T ccl_gpu_image_object_read_2D(const ccl_gpu_image_object_2D texobj,
|
||||
const float x,
|
||||
const float y)
|
||||
{
|
||||
return tex2D<T>(texobj, x, y);
|
||||
}
|
||||
|
||||
/* Half */
|
||||
|
||||
typedef unsigned short half;
|
||||
|
||||
ccl_device_forceinline half __float2half(const float f)
|
||||
{
|
||||
half val;
|
||||
asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f));
|
||||
return val;
|
||||
}
|
||||
|
||||
ccl_device_forceinline float __half2float(const half h)
|
||||
{
|
||||
float val;
|
||||
asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h));
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Types */
|
||||
|
||||
#include "util/half.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#define OPTIX_DONT_INCLUDE_CUDA
|
||||
#include <optix.h>
|
||||
65
blender-5.2.0/intern/cycles/kernel/device/optix/globals.h
Normal file
65
blender-5.2.0/intern/cycles/kernel/device/optix/globals.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Constant Globals */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/types.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/util/profiler.h"
|
||||
|
||||
#include "util/color.h"
|
||||
#include "util/types_image.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Not actually used, just a nullptr pointer that gets passed everywhere, which we
|
||||
* hope gets optimized out by the compiler. */
|
||||
struct KernelGlobalsGPU {
|
||||
int unused[1];
|
||||
};
|
||||
using KernelGlobals = const ccl_global KernelGlobalsGPU *ccl_restrict;
|
||||
|
||||
/* Launch parameters */
|
||||
struct KernelParamsOptiX {
|
||||
/* Kernel arguments */
|
||||
const int *path_index_array;
|
||||
float *render_buffer;
|
||||
uint *shader_eval_cache_miss;
|
||||
int shader_eval_offset;
|
||||
|
||||
/* Init kernel arguments */
|
||||
int num_tiles;
|
||||
int max_tile_work_size;
|
||||
|
||||
/* Global scene data and textures */
|
||||
KernelData data;
|
||||
#define KERNEL_DATA_ARRAY(type, name) const type *name;
|
||||
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) type *name;
|
||||
#include "kernel/data_arrays.h"
|
||||
|
||||
/* Integrator state */
|
||||
IntegratorStateGPU integrator_state;
|
||||
|
||||
void *osl_colorsystem;
|
||||
};
|
||||
|
||||
#ifdef __NVCC__
|
||||
extern "C"
|
||||
# ifndef __CUDACC_RDC__
|
||||
static
|
||||
# endif
|
||||
__constant__ KernelParamsOptiX kernel_params;
|
||||
#endif
|
||||
|
||||
/* Abstraction macros */
|
||||
#define kernel_data kernel_params.data
|
||||
#define kernel_data_array(name) kernel_params.name
|
||||
#define kernel_data_fetch(name, index) kernel_params.name[(index)]
|
||||
#define kernel_data_write(name, index, value) kernel_params.name[(index)] = (value)
|
||||
#define kernel_integrator_state kernel_params.integrator_state
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
68
blender-5.2.0/intern/cycles/kernel/device/optix/kernel.cu
Normal file
68
blender-5.2.0/intern/cycles/kernel/device/optix/kernel.cu
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2019, NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
// clang-format off
|
||||
#include "kernel/device/optix/compat.h"
|
||||
#include "kernel/device/optix/globals.h"
|
||||
|
||||
#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */
|
||||
|
||||
#include "kernel/tables.h"
|
||||
|
||||
#include "kernel/integrator/state.h"
|
||||
#include "kernel/integrator/state_flow.h"
|
||||
#include "kernel/integrator/state_util.h"
|
||||
|
||||
#include "kernel/integrator/intersect_closest.h"
|
||||
#include "kernel/integrator/intersect_shadow.h"
|
||||
#include "kernel/integrator/intersect_subsurface.h"
|
||||
#include "kernel/integrator/intersect_volume_stack.h"
|
||||
#include "kernel/integrator/intersect_dedicated_light.h"
|
||||
// clang-format on
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_closest(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_shadow(nullptr, path_index);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_subsurface()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_subsurface(nullptr, path_index);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_stack()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_volume_stack(nullptr, path_index);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_dedicated_light()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_dedicated_light(nullptr, path_index);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "kernel/device/optix/compat.h"
|
||||
#include "kernel/device/optix/globals.h"
|
||||
|
||||
#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */
|
||||
|
||||
#include "kernel/integrator/intersect_mnee.h"
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_mnee()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_intersect_mnee(nullptr, path_index);
|
||||
}
|
||||
108
blender-5.2.0/intern/cycles/kernel/device/optix/kernel_osl.cu
Normal file
108
blender-5.2.0/intern/cycles/kernel/device/optix/kernel_osl.cu
Normal file
@@ -0,0 +1,108 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#define WITH_OSL
|
||||
|
||||
/* Copy of the regular OptiX kernels with additional OSL support. */
|
||||
|
||||
#include "kernel/device/optix/kernel.cu"
|
||||
|
||||
#include "kernel/bake/bake.h"
|
||||
#include "kernel/integrator/shade_background.h"
|
||||
#include "kernel/integrator/shade_dedicated_light.h"
|
||||
#include "kernel/integrator/shade_light.h"
|
||||
#include "kernel/integrator/shade_shadow.h"
|
||||
#include "kernel/integrator/shade_surface.h"
|
||||
|
||||
#include "kernel/device/gpu/work_stealing.h"
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_background()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_shade_background(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_light_nee()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_shade_light_nee(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_light_forward()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_shade_light_forward(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_shade_surface(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_shadow()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_shade_shadow(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_dedicated_light()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
const int path_index = (kernel_params.path_index_array) ?
|
||||
kernel_params.path_index_array[global_index] :
|
||||
global_index;
|
||||
integrator_shade_dedicated_light(nullptr, path_index, kernel_params.render_buffer);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_shader_eval_displace()
|
||||
{
|
||||
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
|
||||
float *const output = kernel_params.render_buffer;
|
||||
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
|
||||
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
|
||||
kernel_displace_evaluate(nullptr, input, output, cache_miss, global_index);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_shader_eval_background()
|
||||
{
|
||||
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
|
||||
float *const output = kernel_params.render_buffer;
|
||||
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
|
||||
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
|
||||
kernel_background_evaluate(nullptr, input, output, cache_miss, global_index);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_shader_eval_curve_shadow_transparency()
|
||||
{
|
||||
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
|
||||
float *const output = kernel_params.render_buffer;
|
||||
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
|
||||
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
|
||||
kernel_curve_shadow_transparency_evaluate(nullptr, input, output, cache_miss, global_index);
|
||||
}
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_shader_eval_volume_density()
|
||||
{
|
||||
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
|
||||
float *const output = kernel_params.render_buffer;
|
||||
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
|
||||
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
|
||||
kernel_volume_density_evaluate(nullptr, input, output, cache_miss, global_index);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#define WITH_OSL
|
||||
|
||||
#include "kernel/device/optix/compat.h"
|
||||
#include "kernel/device/optix/globals.h"
|
||||
|
||||
#include "kernel/integrator/init_from_camera.h"
|
||||
|
||||
#include "kernel/device/gpu/work_stealing.h"
|
||||
|
||||
extern "C" __global__ void __raygen__kernel_optix_integrator_init_from_camera()
|
||||
{
|
||||
const int global_index = optixGetLaunchIndex().x;
|
||||
|
||||
const KernelWorkTile *tiles = (const KernelWorkTile *)kernel_params.path_index_array;
|
||||
|
||||
const int tile_index = global_index / kernel_params.max_tile_work_size;
|
||||
const int tile_work_index = global_index - tile_index * kernel_params.max_tile_work_size;
|
||||
|
||||
const KernelWorkTile *tile = &tiles[tile_index];
|
||||
|
||||
if (tile_work_index >= tile->work_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int path_index = tile->path_index_offset + tile_work_index;
|
||||
|
||||
uint x, y, sample;
|
||||
get_work_pixel(tile, tile_work_index, &x, &y, &sample);
|
||||
|
||||
integrator_init_from_camera(nullptr, path_index, tile, kernel_params.render_buffer, x, y, sample);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user