// Copyright 2009-2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "bvh_intersector1.h" #include "node_intersector1.h" #include "bvh_traverser1.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" namespace embree { namespace isa { template void BVHNIntersector1::intersect(const Accel::Intersectors* __restrict__ This, RayHit& __restrict__ ray, IntersectContext* __restrict__ context) { const BVH* __restrict__ bvh = (const BVH*)This->ptr; /* we may traverse an empty BVH in case all geometry was invalid */ if (bvh->root == BVH::emptyNode) return; /* perform per ray precalculations required by the primitive intersector */ Precalculations pre(ray, bvh); /* stack state */ StackItemT stack[stackSize]; // stack of nodes StackItemT* stackPtr = stack+1; // current stack pointer StackItemT* stackEnd = stack+stackSize; stack[0].ptr = bvh->root; stack[0].dist = neg_inf; if (bvh->root == BVH::emptyNode) return; /* filter out invalid rays */ #if defined(EMBREE_IGNORE_INVALID_RAYS) if (!ray.valid()) return; #endif /* verify correct input */ assert(ray.valid()); assert(ray.tnear() >= 0.0f); assert(!(types & BVH_MB) || (ray.time() >= 0.0f && ray.time() <= 1.0f)); /* load the ray into SIMD registers */ TravRay tray(ray.org, ray.dir, max(ray.tnear(), 0.0f), max(ray.tfar, 0.0f)); /* initialize the node traverser */ BVHNNodeTraverser1Hit nodeTraverser; /* 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 defined(__AVX512ER__) /* much faster on KNL */ if (unlikely(any(vfloat(*(float*)&stackPtr->dist) > tray.tfar))) continue; #else if (unlikely(*(float*)&stackPtr->dist > ray.tfar)) continue; #endif /* downtraversal loop */ while (true) { /* intersect node */ size_t mask; vfloat tNear; STAT3(normal.trav_nodes,1,1,1); bool nodeIntersected = BVHNNodeIntersector1::intersect(cur, tray, ray.time(), 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 */ nodeTraverser.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; PrimitiveIntersector1::intersect(This, pre, ray, context, prim, num, tray, lazy_node); tray.tfar = ray.tfar; /* push lazy node onto stack */ if (unlikely(lazy_node)) { stackPtr->ptr = lazy_node; stackPtr->dist = neg_inf; stackPtr++; } } } template void BVHNIntersector1::occluded(const Accel::Intersectors* __restrict__ This, Ray& __restrict__ ray, IntersectContext* __restrict__ context) { const BVH* __restrict__ bvh = (const BVH*)This->ptr; /* we may traverse an empty BVH in case all geometry was invalid */ if (bvh->root == BVH::emptyNode) return; /* early out for already occluded rays */ if (unlikely(ray.tfar < 0.0f)) return; /* perform per ray precalculations required by the primitive intersector */ Precalculations pre(ray, bvh); /* stack state */ NodeRef stack[stackSize]; // stack of nodes that still need to get traversed NodeRef* stackPtr = stack+1; // current stack pointer NodeRef* stackEnd = stack+stackSize; stack[0] = bvh->root; /* filter out invalid rays */ #if defined(EMBREE_IGNORE_INVALID_RAYS) if (!ray.valid()) return; #endif /* verify correct input */ assert(ray.valid()); assert(ray.tnear() >= 0.0f); assert(!(types & BVH_MB) || (ray.time() >= 0.0f && ray.time() <= 1.0f)); /* load the ray into SIMD registers */ TravRay tray(ray.org, ray.dir, max(ray.tnear(), 0.0f), max(ray.tfar, 0.0f)); /* initialize the node traverser */ BVHNNodeTraverser1Hit nodeTraverser; /* 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 tNear; STAT3(shadow.trav_nodes,1,1,1); bool nodeIntersected = BVHNNodeIntersector1::intersect(cur, tray, ray.time(), 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 */ nodeTraverser.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 (PrimitiveIntersector1::occluded(This, pre, ray, context, prim, num, tray, lazy_node)) { ray.tfar = neg_inf; break; } /* push lazy node onto stack */ if (unlikely(lazy_node)) { *stackPtr = (NodeRef)lazy_node; stackPtr++; } } } template struct PointQueryDispatch { typedef typename PrimitiveIntersector1::Precalculations Precalculations; typedef typename PrimitiveIntersector1::Primitive Primitive; typedef BVHN BVH; typedef typename BVH::NodeRef NodeRef; typedef typename BVH::AABBNode AABBNode; typedef typename BVH::AABBNodeMB4D AABBNodeMB4D; static const size_t stackSize = 1+(N-1)*BVH::maxDepth+3; // +3 due to 16-wide store /* right now AVX512KNL SIMD extension only for standard node types */ static const size_t Nx = (types == BVH_AN1 || types == BVH_QN1) ? vextend::size : N; static __forceinline bool pointQuery(const Accel::Intersectors* This, PointQuery* query, PointQueryContext* context) { const BVH* __restrict__ bvh = (const BVH*)This->ptr; /* we may traverse an empty BVH in case all geometry was invalid */ if (bvh->root == BVH::emptyNode) return false; /* stack state */ StackItemT stack[stackSize]; // stack of nodes StackItemT* stackPtr = stack+1; // current stack pointer StackItemT* stackEnd = stack+stackSize; stack[0].ptr = bvh->root; stack[0].dist = neg_inf; /* verify correct input */ assert(!(types & BVH_MB) || (query->time >= 0.0f && query->time <= 1.0f)); /* load the point query into SIMD registers */ TravPointQuery tquery(query->p, context->query_radius); /* initialize the node traverser */ BVHNNodeTraverser1Hit nodeTraverser; bool changed = false; float cull_radius = context->query_type == POINT_QUERY_TYPE_SPHERE ? query->radius * query->radius : dot(context->query_radius, context->query_radius); /* 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 > cull_radius)) continue; /* downtraversal loop */ while (true) { /* intersect node */ size_t mask; vfloat tNear; STAT3(point_query.trav_nodes,1,1,1); bool nodeIntersected; if (likely(context->query_type == POINT_QUERY_TYPE_SPHERE)) { nodeIntersected = BVHNNodePointQuerySphere1::pointQuery(cur, tquery, query->time, tNear, mask); } else { nodeIntersected = BVHNNodePointQueryAABB1 ::pointQuery(cur, tquery, query->time, tNear, mask); } if (unlikely(!nodeIntersected)) { STAT3(point_query.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 */ nodeTraverser.traverseClosestHit(cur, mask, tNear, stackPtr, stackEnd); } /* this is a leaf node */ assert(cur != BVH::emptyNode); STAT3(point_query.trav_leaves,1,1,1); size_t num; Primitive* prim = (Primitive*)cur.leaf(num); size_t lazy_node = 0; if (PrimitiveIntersector1::pointQuery(This, query, context, prim, num, tquery, lazy_node)) { changed = true; tquery.rad = context->query_radius; cull_radius = context->query_type == POINT_QUERY_TYPE_SPHERE ? query->radius * query->radius : dot(context->query_radius, context->query_radius); } /* push lazy node onto stack */ if (unlikely(lazy_node)) { stackPtr->ptr = lazy_node; stackPtr->dist = neg_inf; stackPtr++; } } return changed; } }; /* disable point queries for not yet supported geometry types */ template struct PointQueryDispatch { static __forceinline bool pointQuery(const Accel::Intersectors* This, PointQuery* query, PointQueryContext* context) { return false; } }; template struct PointQueryDispatch { static __forceinline bool pointQuery(const Accel::Intersectors* This, PointQuery* query, PointQueryContext* context) { return false; } }; template struct PointQueryDispatch { static __forceinline bool pointQuery(const Accel::Intersectors* This, PointQuery* query, PointQueryContext* context) { return false; } }; template bool BVHNIntersector1::pointQuery( const Accel::Intersectors* This, PointQuery* query, PointQueryContext* context) { return PointQueryDispatch::pointQuery(This, query, context); } } }