Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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