918 lines
35 KiB
C++
918 lines
35 KiB
C++
// Copyright 2009-2021 Intel Corporation
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "bvh_intersector_hybrid.h"
|
|
#include "bvh_traverser1.h"
|
|
#include "node_intersector1.h"
|
|
#include "node_intersector_packet.h"
|
|
|
|
#include "../geometry/intersector_iterators.h"
|
|
#include "../geometry/triangle_intersector.h"
|
|
#include "../geometry/trianglev_intersector.h"
|
|
#include "../geometry/trianglev_mb_intersector.h"
|
|
#include "../geometry/trianglei_intersector.h"
|
|
#include "../geometry/quadv_intersector.h"
|
|
#include "../geometry/quadi_intersector.h"
|
|
#include "../geometry/curveNv_intersector.h"
|
|
#include "../geometry/curveNi_intersector.h"
|
|
#include "../geometry/curveNi_mb_intersector.h"
|
|
#include "../geometry/linei_intersector.h"
|
|
#include "../geometry/subdivpatch1_intersector.h"
|
|
#include "../geometry/object_intersector.h"
|
|
#include "../geometry/instance_intersector.h"
|
|
#include "../geometry/subgrid_intersector.h"
|
|
#include "../geometry/subgrid_mb_intersector.h"
|
|
#include "../geometry/curve_intersector_virtual.h"
|
|
|
|
#define SWITCH_DURING_DOWN_TRAVERSAL 1
|
|
#define FORCE_SINGLE_MODE 0
|
|
|
|
#define ENABLE_FAST_COHERENT_CODEPATHS 1
|
|
|
|
namespace embree
|
|
{
|
|
namespace isa
|
|
{
|
|
template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
|
|
void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::intersect1(Accel::Intersectors* This,
|
|
const BVH* bvh,
|
|
NodeRef root,
|
|
size_t k,
|
|
Precalculations& pre,
|
|
RayHitK<K>& ray,
|
|
const TravRayK<K, robust>& tray,
|
|
IntersectContext* context)
|
|
{
|
|
/* stack state */
|
|
StackItemT<NodeRef> stack[stackSizeSingle]; // stack of nodes
|
|
StackItemT<NodeRef>* stackPtr = stack + 1; // current stack pointer
|
|
StackItemT<NodeRef>* stackEnd = stack + stackSizeSingle;
|
|
stack[0].ptr = root;
|
|
stack[0].dist = neg_inf;
|
|
|
|
/* load the ray into SIMD registers */
|
|
TravRay<N,robust> tray1;
|
|
tray1.template init<K>(k, tray.org, tray.dir, tray.rdir, tray.nearXYZ, tray.tnear[k], tray.tfar[k]);
|
|
|
|
/* pop loop */
|
|
while (true) pop:
|
|
{
|
|
/* pop next node */
|
|
if (unlikely(stackPtr == stack)) break;
|
|
stackPtr--;
|
|
NodeRef cur = NodeRef(stackPtr->ptr);
|
|
|
|
/* if popped node is too far, pop next one */
|
|
if (unlikely(*(float*)&stackPtr->dist > ray.tfar[k]))
|
|
continue;
|
|
|
|
/* downtraversal loop */
|
|
while (true)
|
|
{
|
|
/* intersect node */
|
|
size_t mask; vfloat<N> tNear;
|
|
STAT3(normal.trav_nodes, 1, 1, 1);
|
|
bool nodeIntersected = BVHNNodeIntersector1<N, types, robust>::intersect(cur, tray1, ray.time()[k], tNear, mask);
|
|
if (unlikely(!nodeIntersected)) { STAT3(normal.trav_nodes,-1,-1,-1); break; }
|
|
|
|
/* if no child is hit, pop next node */
|
|
if (unlikely(mask == 0))
|
|
goto pop;
|
|
|
|
/* select next child and push other children */
|
|
BVHNNodeTraverser1Hit<N, types>::traverseClosestHit(cur, mask, tNear, stackPtr, stackEnd);
|
|
}
|
|
|
|
/* this is a leaf node */
|
|
assert(cur != BVH::emptyNode);
|
|
STAT3(normal.trav_leaves, 1, 1, 1);
|
|
size_t num; Primitive* prim = (Primitive*)cur.leaf(num);
|
|
|
|
size_t lazy_node = 0;
|
|
PrimitiveIntersectorK::intersect(This, pre, ray, k, context, prim, num, tray1, lazy_node);
|
|
|
|
tray1.tfar = ray.tfar[k];
|
|
|
|
if (unlikely(lazy_node)) {
|
|
stackPtr->ptr = lazy_node;
|
|
stackPtr->dist = neg_inf;
|
|
stackPtr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
|
|
void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::intersect(vint<K>* __restrict__ valid_i,
|
|
Accel::Intersectors* __restrict__ This,
|
|
RayHitK<K>& __restrict__ ray,
|
|
IntersectContext* __restrict__ context)
|
|
{
|
|
BVH* __restrict__ bvh = (BVH*)This->ptr;
|
|
|
|
/* we may traverse an empty BVH in case all geometry was invalid */
|
|
if (bvh->root == BVH::emptyNode)
|
|
return;
|
|
|
|
#if ENABLE_FAST_COHERENT_CODEPATHS == 1
|
|
assert(context);
|
|
if (unlikely(types == BVH_AN1 && context->user && context->isCoherent()))
|
|
{
|
|
intersectCoherent(valid_i, This, ray, context);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* filter out invalid rays */
|
|
vbool<K> valid = *valid_i == -1;
|
|
#if defined(EMBREE_IGNORE_INVALID_RAYS)
|
|
valid &= ray.valid();
|
|
#endif
|
|
|
|
/* return if there are no valid rays */
|
|
size_t valid_bits = movemask(valid);
|
|
|
|
#if defined(__AVX__)
|
|
STAT3(normal.trav_hit_boxes[popcnt(movemask(valid))], 1, 1, 1);
|
|
#endif
|
|
|
|
if (unlikely(valid_bits == 0)) return;
|
|
|
|
/* verify correct input */
|
|
assert(all(valid, ray.valid()));
|
|
assert(all(valid, ray.tnear() >= 0.0f));
|
|
assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
|
|
Precalculations pre(valid, ray);
|
|
|
|
/* load ray */
|
|
TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
|
|
const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
|
|
const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
|
|
|
|
if (single)
|
|
{
|
|
tray.tnear = select(valid, org_ray_tnear, vfloat<K>(pos_inf));
|
|
tray.tfar = select(valid, org_ray_tfar , vfloat<K>(neg_inf));
|
|
|
|
for (; valid_bits!=0; ) {
|
|
const size_t i = bscf(valid_bits);
|
|
intersect1(This, bvh, bvh->root, i, pre, ray, tray, context);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* determine switch threshold based on flags */
|
|
const size_t switchThreshold = (context->user && context->isCoherent()) ? 2 : switchThresholdIncoherent;
|
|
|
|
vint<K> octant = ray.octant();
|
|
octant = select(valid, octant, vint<K>(0xffffffff));
|
|
|
|
/* test whether we have ray with opposing direction signs in the packet */
|
|
bool split = false;
|
|
{
|
|
size_t bits = valid_bits;
|
|
vbool<K> vsplit( false );
|
|
do
|
|
{
|
|
const size_t valid_index = bsf(bits);
|
|
vbool<K> octant_valid = octant[valid_index] == octant;
|
|
bits &= ~(size_t)movemask(octant_valid);
|
|
vsplit |= vint<K>(octant[valid_index]) == (octant^vint<K>(0x7));
|
|
} while (bits);
|
|
if (any(vsplit)) split = true;
|
|
}
|
|
|
|
do
|
|
{
|
|
const size_t valid_index = bsf(valid_bits);
|
|
const vint<K> diff_octant = vint<K>(octant[valid_index])^octant;
|
|
const vint<K> count_diff_octant = \
|
|
((diff_octant >> 2) & 1) +
|
|
((diff_octant >> 1) & 1) +
|
|
((diff_octant >> 0) & 1);
|
|
|
|
vbool<K> octant_valid = (count_diff_octant <= 1) & (octant != vint<K>(0xffffffff));
|
|
if (!single || !split) octant_valid = valid; // deactivate octant sorting in pure chunk mode, otherwise instance traversal performance goes down
|
|
|
|
|
|
octant = select(octant_valid,vint<K>(0xffffffff),octant);
|
|
valid_bits &= ~(size_t)movemask(octant_valid);
|
|
|
|
tray.tnear = select(octant_valid, org_ray_tnear, vfloat<K>(pos_inf));
|
|
tray.tfar = select(octant_valid, org_ray_tfar , vfloat<K>(neg_inf));
|
|
|
|
/* allocate stack and push root node */
|
|
vfloat<K> stack_near[stackSizeChunk];
|
|
NodeRef stack_node[stackSizeChunk];
|
|
stack_node[0] = BVH::invalidNode;
|
|
stack_near[0] = inf;
|
|
stack_node[1] = bvh->root;
|
|
stack_near[1] = tray.tnear;
|
|
NodeRef* stackEnd MAYBE_UNUSED = stack_node+stackSizeChunk;
|
|
NodeRef* __restrict__ sptr_node = stack_node + 2;
|
|
vfloat<K>* __restrict__ sptr_near = stack_near + 2;
|
|
|
|
while (1) pop:
|
|
{
|
|
/* pop next node from stack */
|
|
assert(sptr_node > stack_node);
|
|
sptr_node--;
|
|
sptr_near--;
|
|
NodeRef cur = *sptr_node;
|
|
if (unlikely(cur == BVH::invalidNode)) {
|
|
assert(sptr_node == stack_node);
|
|
break;
|
|
}
|
|
|
|
/* cull node if behind closest hit point */
|
|
vfloat<K> curDist = *sptr_near;
|
|
const vbool<K> active = curDist < tray.tfar;
|
|
if (unlikely(none(active)))
|
|
continue;
|
|
|
|
/* switch to single ray traversal */
|
|
#if (!defined(__WIN32__) || defined(__X86_64__)) && ((defined(__aarch64__)) || defined(__SSE4_2__))
|
|
#if FORCE_SINGLE_MODE == 0
|
|
if (single)
|
|
#endif
|
|
{
|
|
size_t bits = movemask(active);
|
|
#if FORCE_SINGLE_MODE == 0
|
|
if (unlikely(popcnt(bits) <= switchThreshold))
|
|
#endif
|
|
{
|
|
for (; bits!=0; ) {
|
|
const size_t i = bscf(bits);
|
|
intersect1(This, bvh, cur, i, pre, ray, tray, context);
|
|
}
|
|
tray.tfar = min(tray.tfar, ray.tfar);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
while (likely(!cur.isLeaf()))
|
|
{
|
|
/* process nodes */
|
|
const vbool<K> valid_node = tray.tfar > curDist;
|
|
STAT3(normal.trav_nodes, 1, popcnt(valid_node), K);
|
|
const NodeRef nodeRef = cur;
|
|
const BaseNode* __restrict__ const node = nodeRef.baseNode();
|
|
|
|
/* set cur to invalid */
|
|
cur = BVH::emptyNode;
|
|
curDist = pos_inf;
|
|
|
|
size_t num_child_hits = 0;
|
|
|
|
for (unsigned i = 0; i < N; i++)
|
|
{
|
|
const NodeRef child = node->children[i];
|
|
if (unlikely(child == BVH::emptyNode)) break;
|
|
vfloat<K> lnearP;
|
|
vbool<K> lhit = valid_node;
|
|
BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
|
|
|
|
/* if we hit the child we choose to continue with that child if it
|
|
is closer than the current next child, or we push it onto the stack */
|
|
if (likely(any(lhit)))
|
|
{
|
|
assert(sptr_node < stackEnd);
|
|
assert(child != BVH::emptyNode);
|
|
const vfloat<K> childDist = select(lhit, lnearP, inf);
|
|
/* push cur node onto stack and continue with hit child */
|
|
if (any(childDist < curDist))
|
|
{
|
|
if (likely(cur != BVH::emptyNode)) {
|
|
num_child_hits++;
|
|
*sptr_node = cur; sptr_node++;
|
|
*sptr_near = curDist; sptr_near++;
|
|
}
|
|
curDist = childDist;
|
|
cur = child;
|
|
}
|
|
|
|
/* push hit child onto stack */
|
|
else {
|
|
num_child_hits++;
|
|
*sptr_node = child; sptr_node++;
|
|
*sptr_near = childDist; sptr_near++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(__AVX__)
|
|
//STAT3(normal.trav_hit_boxes[num_child_hits], 1, 1, 1);
|
|
#endif
|
|
|
|
if (unlikely(cur == BVH::emptyNode))
|
|
goto pop;
|
|
|
|
/* improved distance sorting for 3 or more hits */
|
|
if (unlikely(num_child_hits >= 2))
|
|
{
|
|
if (any(sptr_near[-2] < sptr_near[-1]))
|
|
{
|
|
std::swap(sptr_near[-2],sptr_near[-1]);
|
|
std::swap(sptr_node[-2],sptr_node[-1]);
|
|
}
|
|
if (unlikely(num_child_hits >= 3))
|
|
{
|
|
if (any(sptr_near[-3] < sptr_near[-1]))
|
|
{
|
|
std::swap(sptr_near[-3],sptr_near[-1]);
|
|
std::swap(sptr_node[-3],sptr_node[-1]);
|
|
}
|
|
if (any(sptr_near[-3] < sptr_near[-2]))
|
|
{
|
|
std::swap(sptr_near[-3],sptr_near[-2]);
|
|
std::swap(sptr_node[-3],sptr_node[-2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if SWITCH_DURING_DOWN_TRAVERSAL == 1
|
|
if (single)
|
|
{
|
|
// seems to be the best place for testing utilization
|
|
if (unlikely(popcnt(tray.tfar > curDist) <= switchThreshold))
|
|
{
|
|
*sptr_node++ = cur;
|
|
*sptr_near++ = curDist;
|
|
goto pop;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* return if stack is empty */
|
|
if (unlikely(cur == BVH::invalidNode)) {
|
|
assert(sptr_node == stack_node);
|
|
break;
|
|
}
|
|
|
|
/* intersect leaf */
|
|
assert(cur != BVH::emptyNode);
|
|
const vbool<K> valid_leaf = tray.tfar > curDist;
|
|
STAT3(normal.trav_leaves, 1, popcnt(valid_leaf), K);
|
|
if (unlikely(none(valid_leaf))) continue;
|
|
size_t items; const Primitive* prim = (Primitive*)cur.leaf(items);
|
|
|
|
size_t lazy_node = 0;
|
|
PrimitiveIntersectorK::intersect(valid_leaf, This, pre, ray, context, prim, items, tray, lazy_node);
|
|
tray.tfar = select(valid_leaf, ray.tfar, tray.tfar);
|
|
|
|
if (unlikely(lazy_node)) {
|
|
*sptr_node = lazy_node; sptr_node++;
|
|
*sptr_near = neg_inf; sptr_near++;
|
|
}
|
|
}
|
|
} while(valid_bits);
|
|
}
|
|
|
|
|
|
template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
|
|
void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::intersectCoherent(vint<K>* __restrict__ valid_i,
|
|
Accel::Intersectors* __restrict__ This,
|
|
RayHitK<K>& __restrict__ ray,
|
|
IntersectContext* context)
|
|
{
|
|
BVH* __restrict__ bvh = (BVH*)This->ptr;
|
|
|
|
/* filter out invalid rays */
|
|
vbool<K> valid = *valid_i == -1;
|
|
#if defined(EMBREE_IGNORE_INVALID_RAYS)
|
|
valid &= ray.valid();
|
|
#endif
|
|
|
|
/* return if there are no valid rays */
|
|
size_t valid_bits = movemask(valid);
|
|
if (unlikely(valid_bits == 0)) return;
|
|
|
|
/* verify correct input */
|
|
assert(all(valid, ray.valid()));
|
|
assert(all(valid, ray.tnear() >= 0.0f));
|
|
assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
|
|
Precalculations pre(valid, ray);
|
|
|
|
/* load ray */
|
|
TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
|
|
const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
|
|
const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
|
|
|
|
vint<K> octant = ray.octant();
|
|
octant = select(valid, octant, vint<K>(0xffffffff));
|
|
|
|
do
|
|
{
|
|
const size_t valid_index = bsf(valid_bits);
|
|
const vbool<K> octant_valid = octant[valid_index] == octant;
|
|
valid_bits &= ~(size_t)movemask(octant_valid);
|
|
|
|
tray.tnear = select(octant_valid, org_ray_tnear, vfloat<K>(pos_inf));
|
|
tray.tfar = select(octant_valid, org_ray_tfar , vfloat<K>(neg_inf));
|
|
|
|
Frustum<robust> frustum;
|
|
frustum.template init<K>(octant_valid, tray.org, tray.rdir, tray.tnear, tray.tfar, N);
|
|
|
|
StackItemT<NodeRef> stack[stackSizeSingle]; // stack of nodes
|
|
StackItemT<NodeRef>* stackPtr = stack + 1; // current stack pointer
|
|
stack[0].ptr = bvh->root;
|
|
stack[0].dist = neg_inf;
|
|
|
|
while (1) pop:
|
|
{
|
|
/* pop next node from stack */
|
|
if (unlikely(stackPtr == stack)) break;
|
|
|
|
stackPtr--;
|
|
NodeRef cur = NodeRef(stackPtr->ptr);
|
|
|
|
/* cull node if behind closest hit point */
|
|
vfloat<K> curDist = *(float*)&stackPtr->dist;
|
|
const vbool<K> active = curDist < tray.tfar;
|
|
if (unlikely(none(active))) continue;
|
|
|
|
while (likely(!cur.isLeaf()))
|
|
{
|
|
/* process nodes */
|
|
//STAT3(normal.trav_nodes, 1, popcnt(valid_node), K);
|
|
const NodeRef nodeRef = cur;
|
|
const AABBNode* __restrict__ const node = nodeRef.getAABBNode();
|
|
|
|
vfloat<N> fmin;
|
|
size_t m_frustum_node = intersectNodeFrustum<N>(node, frustum, fmin);
|
|
|
|
if (unlikely(!m_frustum_node)) goto pop;
|
|
cur = BVH::emptyNode;
|
|
curDist = pos_inf;
|
|
|
|
#if defined(__AVX__)
|
|
//STAT3(normal.trav_hit_boxes[popcnt(m_frustum_node)], 1, 1, 1);
|
|
#endif
|
|
size_t num_child_hits = 0;
|
|
do {
|
|
const size_t i = bscf(m_frustum_node);
|
|
vfloat<K> lnearP;
|
|
vbool<K> lhit = false; // motion blur is not supported, so the initial value will be ignored
|
|
STAT3(normal.trav_nodes, 1, 1, 1);
|
|
BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
|
|
|
|
if (likely(any(lhit)))
|
|
{
|
|
const vfloat<K> childDist = fmin[i];
|
|
const NodeRef child = node->child(i);
|
|
BVHN<N>::prefetch(child);
|
|
if (any(childDist < curDist))
|
|
{
|
|
if (likely(cur != BVH::emptyNode)) {
|
|
num_child_hits++;
|
|
stackPtr->ptr = cur;
|
|
*(float*)&stackPtr->dist = toScalar(curDist);
|
|
stackPtr++;
|
|
}
|
|
curDist = childDist;
|
|
cur = child;
|
|
}
|
|
/* push hit child onto stack */
|
|
else {
|
|
num_child_hits++;
|
|
stackPtr->ptr = child;
|
|
*(float*)&stackPtr->dist = toScalar(childDist);
|
|
stackPtr++;
|
|
}
|
|
}
|
|
} while(m_frustum_node);
|
|
|
|
if (unlikely(cur == BVH::emptyNode)) goto pop;
|
|
|
|
/* improved distance sorting for 3 or more hits */
|
|
if (unlikely(num_child_hits >= 2))
|
|
{
|
|
if (stackPtr[-2].dist < stackPtr[-1].dist)
|
|
std::swap(stackPtr[-2],stackPtr[-1]);
|
|
if (unlikely(num_child_hits >= 3))
|
|
{
|
|
if (stackPtr[-3].dist < stackPtr[-1].dist)
|
|
std::swap(stackPtr[-3],stackPtr[-1]);
|
|
if (stackPtr[-3].dist < stackPtr[-2].dist)
|
|
std::swap(stackPtr[-3],stackPtr[-2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* intersect leaf */
|
|
assert(cur != BVH::invalidNode);
|
|
assert(cur != BVH::emptyNode);
|
|
const vbool<K> valid_leaf = tray.tfar > curDist;
|
|
STAT3(normal.trav_leaves, 1, popcnt(valid_leaf), K);
|
|
if (unlikely(none(valid_leaf))) continue;
|
|
size_t items; const Primitive* prim = (Primitive*)cur.leaf(items);
|
|
|
|
size_t lazy_node = 0;
|
|
PrimitiveIntersectorK::intersect(valid_leaf, This, pre, ray, context, prim, items, tray, lazy_node);
|
|
|
|
/* reduce max distance interval on successful intersection */
|
|
if (likely(any((ray.tfar < tray.tfar) & valid_leaf)))
|
|
{
|
|
tray.tfar = select(valid_leaf, ray.tfar, tray.tfar);
|
|
frustum.template updateMaxDist<K>(tray.tfar);
|
|
}
|
|
|
|
if (unlikely(lazy_node)) {
|
|
stackPtr->ptr = lazy_node;
|
|
stackPtr->dist = neg_inf;
|
|
stackPtr++;
|
|
}
|
|
}
|
|
|
|
} while(valid_bits);
|
|
}
|
|
|
|
// ===================================================================================================================================================================
|
|
// ===================================================================================================================================================================
|
|
// ===================================================================================================================================================================
|
|
|
|
template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
|
|
bool BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::occluded1(Accel::Intersectors* This,
|
|
const BVH* bvh,
|
|
NodeRef root,
|
|
size_t k,
|
|
Precalculations& pre,
|
|
RayK<K>& ray,
|
|
const TravRayK<K, robust>& tray,
|
|
IntersectContext* context)
|
|
{
|
|
/* stack state */
|
|
NodeRef stack[stackSizeSingle]; // stack of nodes that still need to get traversed
|
|
NodeRef* stackPtr = stack+1; // current stack pointer
|
|
NodeRef* stackEnd = stack+stackSizeSingle;
|
|
stack[0] = root;
|
|
|
|
/* load the ray into SIMD registers */
|
|
TravRay<N,robust> tray1;
|
|
tray1.template init<K>(k, tray.org, tray.dir, tray.rdir, tray.nearXYZ, tray.tnear[k], tray.tfar[k]);
|
|
|
|
/* pop loop */
|
|
while (true) pop:
|
|
{
|
|
/* pop next node */
|
|
if (unlikely(stackPtr == stack)) break;
|
|
stackPtr--;
|
|
NodeRef cur = (NodeRef)*stackPtr;
|
|
|
|
/* downtraversal loop */
|
|
while (true)
|
|
{
|
|
/* intersect node */
|
|
size_t mask; vfloat<N> tNear;
|
|
STAT3(shadow.trav_nodes, 1, 1, 1);
|
|
bool nodeIntersected = BVHNNodeIntersector1<N, types, robust>::intersect(cur, tray1, ray.time()[k], tNear, mask);
|
|
if (unlikely(!nodeIntersected)) { STAT3(shadow.trav_nodes,-1,-1,-1); break; }
|
|
|
|
/* if no child is hit, pop next node */
|
|
if (unlikely(mask == 0))
|
|
goto pop;
|
|
|
|
/* select next child and push other children */
|
|
BVHNNodeTraverser1Hit<N, types>::traverseAnyHit(cur, mask, tNear, stackPtr, stackEnd);
|
|
}
|
|
|
|
/* this is a leaf node */
|
|
assert(cur != BVH::emptyNode);
|
|
STAT3(shadow.trav_leaves, 1, 1, 1);
|
|
size_t num; Primitive* prim = (Primitive*)cur.leaf(num);
|
|
|
|
size_t lazy_node = 0;
|
|
if (PrimitiveIntersectorK::occluded(This, pre, ray, k, context, prim, num, tray1, lazy_node)) {
|
|
ray.tfar[k] = neg_inf;
|
|
return true;
|
|
}
|
|
|
|
if (unlikely(lazy_node)) {
|
|
*stackPtr = lazy_node;
|
|
stackPtr++;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
|
|
void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::occluded(vint<K>* __restrict__ valid_i,
|
|
Accel::Intersectors* __restrict__ This,
|
|
RayK<K>& __restrict__ ray,
|
|
IntersectContext* context)
|
|
{
|
|
BVH* __restrict__ bvh = (BVH*)This->ptr;
|
|
|
|
/* we may traverse an empty BVH in case all geometry was invalid */
|
|
if (bvh->root == BVH::emptyNode)
|
|
return;
|
|
|
|
#if ENABLE_FAST_COHERENT_CODEPATHS == 1
|
|
assert(context);
|
|
if (unlikely(types == BVH_AN1 && context->user && context->isCoherent()))
|
|
{
|
|
occludedCoherent(valid_i, This, ray, context);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* filter out already occluded and invalid rays */
|
|
vbool<K> valid = (*valid_i == -1) & (ray.tfar >= 0.0f);
|
|
#if defined(EMBREE_IGNORE_INVALID_RAYS)
|
|
valid &= ray.valid();
|
|
#endif
|
|
|
|
/* return if there are no valid rays */
|
|
const size_t valid_bits = movemask(valid);
|
|
if (unlikely(valid_bits == 0)) return;
|
|
|
|
/* verify correct input */
|
|
assert(all(valid, ray.valid()));
|
|
assert(all(valid, ray.tnear() >= 0.0f));
|
|
assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
|
|
Precalculations pre(valid, ray);
|
|
|
|
/* load ray */
|
|
TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
|
|
const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
|
|
const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
|
|
|
|
tray.tnear = select(valid, org_ray_tnear, vfloat<K>(pos_inf));
|
|
tray.tfar = select(valid, org_ray_tfar , vfloat<K>(neg_inf));
|
|
|
|
vbool<K> terminated = !valid;
|
|
const vfloat<K> inf = vfloat<K>(pos_inf);
|
|
|
|
/* determine switch threshold based on flags */
|
|
const size_t switchThreshold = (context->user && context->isCoherent()) ? 2 : switchThresholdIncoherent;
|
|
|
|
/* allocate stack and push root node */
|
|
vfloat<K> stack_near[stackSizeChunk];
|
|
NodeRef stack_node[stackSizeChunk];
|
|
stack_node[0] = BVH::invalidNode;
|
|
stack_near[0] = inf;
|
|
stack_node[1] = bvh->root;
|
|
stack_near[1] = tray.tnear;
|
|
NodeRef* stackEnd MAYBE_UNUSED = stack_node+stackSizeChunk;
|
|
NodeRef* __restrict__ sptr_node = stack_node + 2;
|
|
vfloat<K>* __restrict__ sptr_near = stack_near + 2;
|
|
|
|
while (1) pop:
|
|
{
|
|
/* pop next node from stack */
|
|
assert(sptr_node > stack_node);
|
|
sptr_node--;
|
|
sptr_near--;
|
|
NodeRef cur = *sptr_node;
|
|
if (unlikely(cur == BVH::invalidNode)) {
|
|
assert(sptr_node == stack_node);
|
|
break;
|
|
}
|
|
|
|
/* cull node if behind closest hit point */
|
|
vfloat<K> curDist = *sptr_near;
|
|
const vbool<K> active = curDist < tray.tfar;
|
|
if (unlikely(none(active)))
|
|
continue;
|
|
|
|
/* switch to single ray traversal */
|
|
#if (!defined(__WIN32__) || defined(__X86_64__)) && ((defined(__aarch64__)) || defined(__SSE4_2__))
|
|
#if FORCE_SINGLE_MODE == 0
|
|
if (single)
|
|
#endif
|
|
{
|
|
size_t bits = movemask(active);
|
|
#if FORCE_SINGLE_MODE == 0
|
|
if (unlikely(popcnt(bits) <= switchThreshold))
|
|
#endif
|
|
{
|
|
for (; bits!=0; ) {
|
|
const size_t i = bscf(bits);
|
|
if (occluded1(This, bvh, cur, i, pre, ray, tray, context))
|
|
set(terminated, i);
|
|
}
|
|
if (all(terminated)) break;
|
|
tray.tfar = select(terminated, vfloat<K>(neg_inf), tray.tfar);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while (likely(!cur.isLeaf()))
|
|
{
|
|
/* process nodes */
|
|
const vbool<K> valid_node = tray.tfar > curDist;
|
|
STAT3(shadow.trav_nodes, 1, popcnt(valid_node), K);
|
|
const NodeRef nodeRef = cur;
|
|
const BaseNode* __restrict__ const node = nodeRef.baseNode();
|
|
|
|
/* set cur to invalid */
|
|
cur = BVH::emptyNode;
|
|
curDist = pos_inf;
|
|
|
|
for (unsigned i = 0; i < N; i++)
|
|
{
|
|
const NodeRef child = node->children[i];
|
|
if (unlikely(child == BVH::emptyNode)) break;
|
|
vfloat<K> lnearP;
|
|
vbool<K> lhit = valid_node;
|
|
BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
|
|
|
|
/* if we hit the child we push the previously hit node onto the stack, and continue with the currently hit child */
|
|
if (likely(any(lhit)))
|
|
{
|
|
assert(sptr_node < stackEnd);
|
|
assert(child != BVH::emptyNode);
|
|
const vfloat<K> childDist = select(lhit, lnearP, inf);
|
|
|
|
/* push 'cur' node onto stack and continue with hit child */
|
|
if (likely(cur != BVH::emptyNode)) {
|
|
*sptr_node = cur; sptr_node++;
|
|
*sptr_near = curDist; sptr_near++;
|
|
}
|
|
curDist = childDist;
|
|
cur = child;
|
|
}
|
|
}
|
|
if (unlikely(cur == BVH::emptyNode))
|
|
goto pop;
|
|
|
|
#if SWITCH_DURING_DOWN_TRAVERSAL == 1
|
|
if (single)
|
|
{
|
|
// seems to be the best place for testing utilization
|
|
if (unlikely(popcnt(tray.tfar > curDist) <= switchThreshold))
|
|
{
|
|
*sptr_node++ = cur;
|
|
*sptr_near++ = curDist;
|
|
goto pop;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* return if stack is empty */
|
|
if (unlikely(cur == BVH::invalidNode)) {
|
|
assert(sptr_node == stack_node);
|
|
break;
|
|
}
|
|
|
|
|
|
/* intersect leaf */
|
|
assert(cur != BVH::emptyNode);
|
|
const vbool<K> valid_leaf = tray.tfar > curDist;
|
|
STAT3(shadow.trav_leaves, 1, popcnt(valid_leaf), K);
|
|
if (unlikely(none(valid_leaf))) continue;
|
|
size_t items; const Primitive* prim = (Primitive*) cur.leaf(items);
|
|
|
|
size_t lazy_node = 0;
|
|
terminated |= PrimitiveIntersectorK::occluded(!terminated, This, pre, ray, context, prim, items, tray, lazy_node);
|
|
if (all(terminated)) break;
|
|
tray.tfar = select(terminated, vfloat<K>(neg_inf), tray.tfar); // ignore node intersections for terminated rays
|
|
|
|
if (unlikely(lazy_node)) {
|
|
*sptr_node = lazy_node; sptr_node++;
|
|
*sptr_near = neg_inf; sptr_near++;
|
|
}
|
|
}
|
|
|
|
vfloat<K>::store(valid & terminated, &ray.tfar, neg_inf);
|
|
}
|
|
|
|
|
|
template<int N, int K, int types, bool robust, typename PrimitiveIntersectorK, bool single>
|
|
void BVHNIntersectorKHybrid<N, K, types, robust, PrimitiveIntersectorK, single>::occludedCoherent(vint<K>* __restrict__ valid_i,
|
|
Accel::Intersectors* __restrict__ This,
|
|
RayK<K>& __restrict__ ray,
|
|
IntersectContext* context)
|
|
{
|
|
BVH* __restrict__ bvh = (BVH*)This->ptr;
|
|
|
|
/* filter out invalid rays */
|
|
vbool<K> valid = *valid_i == -1;
|
|
#if defined(EMBREE_IGNORE_INVALID_RAYS)
|
|
valid &= ray.valid();
|
|
#endif
|
|
|
|
/* return if there are no valid rays */
|
|
size_t valid_bits = movemask(valid);
|
|
if (unlikely(valid_bits == 0)) return;
|
|
|
|
/* verify correct input */
|
|
assert(all(valid, ray.valid()));
|
|
assert(all(valid, ray.tnear() >= 0.0f));
|
|
assert(!(types & BVH_MB) || all(valid, (ray.time() >= 0.0f) & (ray.time() <= 1.0f)));
|
|
Precalculations pre(valid,ray);
|
|
|
|
/* load ray */
|
|
TravRayK<K, robust> tray(ray.org, ray.dir, single ? N : 0);
|
|
const vfloat<K> org_ray_tnear = max(ray.tnear(), 0.0f);
|
|
const vfloat<K> org_ray_tfar = max(ray.tfar , 0.0f);
|
|
|
|
vbool<K> terminated = !valid;
|
|
|
|
vint<K> octant = ray.octant();
|
|
octant = select(valid, octant, vint<K>(0xffffffff));
|
|
|
|
do
|
|
{
|
|
const size_t valid_index = bsf(valid_bits);
|
|
vbool<K> octant_valid = octant[valid_index] == octant;
|
|
valid_bits &= ~(size_t)movemask(octant_valid);
|
|
|
|
tray.tnear = select(octant_valid, org_ray_tnear, vfloat<K>(pos_inf));
|
|
tray.tfar = select(octant_valid, org_ray_tfar, vfloat<K>(neg_inf));
|
|
|
|
Frustum<robust> frustum;
|
|
frustum.template init<K>(octant_valid, tray.org, tray.rdir, tray.tnear, tray.tfar, N);
|
|
|
|
StackItemMaskT<NodeRef> stack[stackSizeSingle]; // stack of nodes
|
|
StackItemMaskT<NodeRef>* stackPtr = stack + 1; // current stack pointer
|
|
stack[0].ptr = bvh->root;
|
|
stack[0].mask = movemask(octant_valid);
|
|
|
|
while (1) pop:
|
|
{
|
|
/* pop next node from stack */
|
|
if (unlikely(stackPtr == stack)) break;
|
|
|
|
stackPtr--;
|
|
NodeRef cur = NodeRef(stackPtr->ptr);
|
|
|
|
/* cull node of active rays have already been terminated */
|
|
size_t m_active = (size_t)stackPtr->mask & (~(size_t)movemask(terminated));
|
|
|
|
if (unlikely(m_active == 0)) continue;
|
|
|
|
while (likely(!cur.isLeaf()))
|
|
{
|
|
/* process nodes */
|
|
//STAT3(normal.trav_nodes, 1, popcnt(valid_node), K);
|
|
const NodeRef nodeRef = cur;
|
|
const AABBNode* __restrict__ const node = nodeRef.getAABBNode();
|
|
|
|
vfloat<N> fmin;
|
|
size_t m_frustum_node = intersectNodeFrustum<N>(node, frustum, fmin);
|
|
|
|
if (unlikely(!m_frustum_node)) goto pop;
|
|
cur = BVH::emptyNode;
|
|
m_active = 0;
|
|
|
|
#if defined(__AVX__)
|
|
//STAT3(normal.trav_hit_boxes[popcnt(m_frustum_node)], 1, 1, 1);
|
|
#endif
|
|
size_t num_child_hits = 0;
|
|
do {
|
|
const size_t i = bscf(m_frustum_node);
|
|
vfloat<K> lnearP;
|
|
vbool<K> lhit = false; // motion blur is not supported, so the initial value will be ignored
|
|
STAT3(normal.trav_nodes, 1, 1, 1);
|
|
BVHNNodeIntersectorK<N, K, types, robust>::intersect(nodeRef, i, tray, ray.time(), lnearP, lhit);
|
|
|
|
if (likely(any(lhit)))
|
|
{
|
|
const NodeRef child = node->child(i);
|
|
assert(child != BVH::emptyNode);
|
|
BVHN<N>::prefetch(child);
|
|
if (likely(cur != BVH::emptyNode)) {
|
|
num_child_hits++;
|
|
stackPtr->ptr = cur;
|
|
stackPtr->mask = m_active;
|
|
stackPtr++;
|
|
}
|
|
cur = child;
|
|
m_active = movemask(lhit);
|
|
}
|
|
} while(m_frustum_node);
|
|
|
|
if (unlikely(cur == BVH::emptyNode)) goto pop;
|
|
}
|
|
|
|
/* intersect leaf */
|
|
assert(cur != BVH::invalidNode);
|
|
assert(cur != BVH::emptyNode);
|
|
#if defined(__AVX__)
|
|
STAT3(normal.trav_leaves, 1, popcnt(m_active), K);
|
|
#endif
|
|
if (unlikely(!m_active)) continue;
|
|
size_t items; const Primitive* prim = (Primitive*)cur.leaf(items);
|
|
|
|
size_t lazy_node = 0;
|
|
terminated |= PrimitiveIntersectorK::occluded(!terminated, This, pre, ray, context, prim, items, tray, lazy_node);
|
|
octant_valid &= !terminated;
|
|
if (unlikely(none(octant_valid))) break;
|
|
tray.tfar = select(terminated, vfloat<K>(neg_inf), tray.tfar); // ignore node intersections for terminated rays
|
|
|
|
if (unlikely(lazy_node)) {
|
|
stackPtr->ptr = lazy_node;
|
|
stackPtr->mask = movemask(octant_valid);
|
|
stackPtr++;
|
|
}
|
|
}
|
|
} while(valid_bits);
|
|
|
|
vfloat<K>::store(valid & terminated, &ray.tfar, neg_inf);
|
|
}
|
|
}
|
|
}
|