1
1

Compare commits

...

34 Commits

Author SHA1 Message Date
MATILLAT Quentin
421159e620 Fix the building of empty tree 2019-10-15 08:10:18 +02:00
d9d1faef01 deps: fix building embree on windows 2019-10-10 21:06:21 -06:00
MATILLAT Quentin
4963fc8a70 Update embree version 2019-10-10 16:40:17 +02:00
MATILLAT Quentin
4b1c544e56 Merge branch 'master' into soc-2019-embree-gpu-rebased 2019-10-03 21:11:34 +02:00
MATILLAT Quentin
9d602cb3e9 Add motion blur for unaligned box 2019-10-03 21:00:51 +02:00
MATILLAT Quentin
648c053885 Cycles: Improve convert time from Embree 2019-09-17 15:52:45 +02:00
MATILLAT Quentin
8495f9e73d Allow to build empty tree 2019-08-27 16:47:31 +02:00
MATILLAT Quentin
7629463638 Add path to make deps 2019-08-27 16:30:18 +02:00
MATILLAT Quentin
137754bba3 Update embree to support all kind of curve 2019-08-20 15:07:23 +02:00
MATILLAT Quentin
cc6c338b25 Cycles: Remove some debug flags 2019-08-19 18:35:20 +02:00
MATILLAT Quentin
b11e093bd6 Cycles: Patch embree version 2019-08-18 16:10:54 +02:00
MATILLAT Quentin
46c0763187 Cycles: Remove unused BVH Builder using Embree BVH 2019-08-18 15:56:51 +02:00
MATILLAT Quentin
e4b2ae0fb6 Add support for curve and unaligned nodes 2019-08-16 16:31:12 +02:00
MATILLAT Quentin
209cf2f890 Remove embree header 2019-08-11 13:17:20 +02:00
MATILLAT Quentin
b0f08e8220 Cycle: Add linear boundbox interpolation on CPU 2019-08-11 12:17:17 +02:00
MATILLAT Quentin
c6bc309d0c Add initial support for curve 2019-08-09 16:19:56 +02:00
MATILLAT Quentin
e0cc40f93c Move most of the export code to Embree
Need to be compiled with a modified version of Embree available on
github: https://github.com/tinou98/embree
2019-08-02 18:49:45 +02:00
MATILLAT Quentin
ad6db67771 Add support for linear interpolation on bounding box 2019-07-26 12:16:10 +02:00
MATILLAT Quentin
12ad52c887 Pre-reserve memory to reduce reallocation 2019-07-19 11:32:02 +02:00
MATILLAT Quentin
0317d3083d Directly fill packed structure
Lead to slightly better performance than internal builder on scene that
use motion blur
2019-07-18 15:16:34 +02:00
MATILLAT Quentin
e013a24d07 Add a visibility flag for time node
This should help to reduce computation overhead for non time node
2019-07-13 12:51:37 +02:00
MATILLAT Quentin
6f25eda4d7 Update timing information
Timing information are now stored using the same system as visibility :
Each node store information from his children
2019-07-11 15:05:32 +02:00
MATILLAT Quentin
4510849e3d Add time limits for each nodes 2019-07-10 17:37:16 +02:00
MATILLAT Quentin
1480ef3107 Add support for Triangle4i and InstancedPrimitive 2019-07-04 13:15:05 +02:00
MATILLAT Quentin
1671d033d8 Add missing header 2019-07-02 18:37:28 +02:00
MATILLAT Quentin
3f0a43db2d Code refactor & Performance improvement 2019-07-01 18:57:41 +02:00
MATILLAT Quentin
43b1fc97bd Add basic support for instanced mesh 2019-06-28 21:38:08 +02:00
MATILLAT Quentin
bc98b74829 Add Embree access on GPU
Add shrink BVH4 to BVH2 to use Embree access on GPU
2019-06-21 19:12:27 +02:00
MATILLAT Quentin
68d764f93c Add GUI option to use all options
Current options are :
- bvh_builder: Ask embree to build the BVH tree from bounding boxes
- Raw embree: Use all of embree features (cannot be used on GPU)
- bvh_access: Give all the object to Embree, then use introspection to
              extract the tree
2019-06-19 15:36:59 +02:00
MATILLAT Quentin
2ac560181c Convert Embree's internal data to Blender BVH4 2019-06-16 16:17:02 +02:00
MATILLAT Quentin
db229b2380 Add basic access to Embree's internal data 2019-06-14 22:20:21 +02:00
MATILLAT Quentin
d40ca519f3 Add support for instancied mesh 2019-06-07 10:24:09 +02:00
MATILLAT Quentin
b9928e843d Fix crash when empty scene 2019-05-31 18:12:35 +02:00
MATILLAT Quentin
6532a2e695 Initial implementation for Embree GPU 2019-05-31 15:55:05 +02:00
25 changed files with 2029 additions and 68 deletions

View File

@@ -41,6 +41,7 @@ ExternalProject_Add(external_embree
DOWNLOAD_DIR ${DOWNLOAD_DIR}
URL_HASH MD5=${EMBREE_HASH}
PREFIX ${BUILD_DIR}/embree
PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/embree/src/external_embree < ${PATCH_DIR}/embree.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/embree ${DEFAULT_CMAKE_FLAGS} ${EMBREE_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/embree
)

View File

@@ -2519,7 +2519,7 @@ compile_Embree() {
fi
# To be changed each time we make edits that would modify the compiled results!
embree_magic=9
embree_magic=11
_init_embree
# Clean install if needed!
@@ -2554,6 +2554,9 @@ compile_Embree() {
git reset --hard
fi
INFO "Applying patch on to of Embree sources"
git apply $SCRIPT_DIR/patches/embree.diff
# Always refresh the whole build!
if [ -d build ]; then
rm -rf build

View File

@@ -0,0 +1,651 @@
diff --git a/doc/src/api.md b/doc/src/api.md
index f190dc160..e77d85cf3 100644
--- a/doc/src/api.md
+++ b/doc/src/api.md
@@ -955,6 +955,11 @@ Embree API Reference
```
\pagebreak
+## rtcExtractBVH
+``` {include=src/api/rtcExtractBVH.md}
+```
+\pagebreak
+
Performance Recommendations
===========================
diff --git a/doc/src/api/rtcExtractBVH.md b/doc/src/api/rtcExtractBVH.md
new file mode 100644
index 000000000..3c34c0ce2
--- /dev/null
+++ b/doc/src/api/rtcExtractBVH.md
@@ -0,0 +1,94 @@
+% rtcExtractBVH(3) | Embree Ray Tracing Kernels 3
+
+#### NAME
+
+ rtcExtractBVH - Extract BVH from a scene
+
+#### SYNOPSIS
+
+ #include <embree3/rtcore_builder.h>
+
+ struct BVHPrimitive
+ {
+ unsigned int geomID;
+ unsigned int primID;
+ };
+
+ struct RTCBVHExtractFunction
+ {
+ void (*expectedSize) (unsigned int num_leaf, unsigned int num_tri, void *userData);
+
+ void* (*createLeaf) (unsigned int nbPrim, const BVHPrimitive prims[], void *userData);
+ void* (*createInstance) (unsigned int nbPrim, const unsigned int geomID[], void *userData);
+ void* (*createCurve) (unsigned int nbPrim, const BVHPrimitive prims[], void *userData);
+
+ void* (*createInnerNode) (unsigned int nbChild, void* children[], void *userData);
+
+ void (*setAlignedBounds) (void *node, const RTCBounds &bounds, void *userData);
+ void (*setLinearBounds) (void *node, const RTCLinearBounds &lbounds, void *userData);
+ void (*setUnalignedBounds) (void *node, const RTCAffineSpace &affSpace, void *userData);
+ void (*setUnalignedLinearBounds) (void *node, const RTCAffineSpace &affSpace, const RTCBounds &bounds, void *userData);
+ };
+
+ void* rtcExtractBVH(
+ RTCScene hscene,
+ RTCBVHExtractFunction args,
+ void *userData
+ );
+
+#### DESCRIPTION
+
+The `rtcExtractBVH` function can be used to extract the BVH tree from a scene
+that has already been built.
+
+The `rtcExtractBVH` take the scene to export as parameter as well as a
+structure that contain callback function pointers and a user-defined pointer
+that is passed to all callback functions when invoked.
+All callback functions are typically called from a multiple threads, thus
+their implementation must be thread-safe.
+
+At least 8 callback functions must be registered, which are invoked during
+build to create BVH leaf (`createLeaf`, `createInstance` and `createCurve`
+member), to create a BVH inner node (`createInnerNode` member) and to set the
+bounding boxes of a node (`setAlignedBounds`, `setLinearBounds`,
+`setUnalignedBounds` and `setUnalignedLinearBounds` member).
+
+The function pointer used to preallocate buffer (`expectedSize` member) is
+optional and may be `NULL`.
+
+The `createLeaf`, `createInstance` and `createCurve` callback also gets the
+number of primitives for this leaf, and an array of size `nbPrim` of
+primitive objects.
+
+The `createInnerNode` callback gets the number of child, as well as an array
+of `nbChild` child previously returned by either `createLeaf`,
+`createInstance` or `createCurve`.
+
+The `setAlignedBounds`, `setLinearBounds`, `setUnalignedBounds` and
+`setUnalignedLinearBounds` callback function gets a pointer to the node as
+input (`node` argument) as well as the bounds for this node.
+
+The `setAlignedBounds` callback function take bounds of type `RTCBounds` which
+represent an aligned bound box.
+
+The `setLinearBounds` callback function take bounds of type `RTCLinearBounds`
+which represent an aligned bound box that can be linearly interpolated over
+time.
+
+The `setUnalignedBounds` callback function take bounds of type `RTCAffineSpace`
+which represent an affine space in which the bound are ((0, 0, 0), (1, 1, 1)).
+
+The `setUnalignedLinearBounds` callback function take bounds of type
+`RTCAffineSpace` and `RTCBounds`. The bound box in the affine space described
+by `affSpace` are the linear interpolation between ((0, 0, 0), (1, 1, 1)) and
+`bounds`.
+
+#### EXIT STATUS
+
+Return the root node, returned by the root `createInnerNode`, or `NULL` if an
+error occurred.
+On failure an error code is set that can be queried using `rtcDeviceGetError`.
+
+#### SEE ALSO
+
+[rtcCommitScene]
diff --git a/include/embree3/rtcore_builder.h b/include/embree3/rtcore_builder.h
index af84035b0..3f2c4eddf 100644
--- a/include/embree3/rtcore_builder.h
+++ b/include/embree3/rtcore_builder.h
@@ -131,6 +131,35 @@ RTC_API void rtcRetainBVH(RTCBVH bvh);
/* Releases the BVH (decrements reference count). */
RTC_API void rtcReleaseBVH(RTCBVH bvh);
+struct BVHPrimitive {
+ unsigned int geomID;
+ unsigned int primID;
+};
+
+struct RTCBVHExtractFunction
+{
+ /**
+ * Allow to preallocate buffer, is called before everything else
+ * Can be NULL if not used
+ */
+ void (*expectedSize) (unsigned int num_leaf, unsigned int num_tri, void *userData);
+
+ // Leaf creator function
+ void* (*createLeaf) (unsigned int nbPrim, const BVHPrimitive prims[], void *userData);
+ void* (*createInstance) (unsigned int nbPrim, const unsigned int geomID[], void *userData);
+ void* (*createCurve) (unsigned int nbPrim, const BVHPrimitive prims[], void *userData);
+
+ // InnerNode creator function
+ void* (*createInnerNode) (unsigned int nbChild, void* children[], void *userData);
+
+ void (*setAlignedBounds) (void *node, const RTCBounds &bounds, void *userData);
+ void (*setLinearBounds) (void *node, const RTCLinearBounds &lbounds, void *userData);
+ void (*setUnalignedBounds) (void *node, const RTCAffineSpace &affSpace, void *userData);
+ void (*setUnalignedLinearBounds) (void *node, const RTCAffineSpace &affSpace, const RTCBounds &bounds, void *userData);
+};
+
+RTC_API void *rtcExtractBVH(RTCScene hscene, RTCBVHExtractFunction args, void *userData);
+
#if defined(__cplusplus)
}
#endif
diff --git a/include/embree3/rtcore_common.h b/include/embree3/rtcore_common.h
index 29dfcba11..7eb21c28c 100644
--- a/include/embree3/rtcore_common.h
+++ b/include/embree3/rtcore_common.h
@@ -191,6 +191,13 @@ struct RTC_ALIGN(16) RTCLinearBounds
struct RTCBounds bounds1;
};
+/* AffineSpace representation */
+struct RTC_ALIGN(16) RTCAffineSpace
+{
+ float linear[3*3];
+ float affine[3];
+};
+
/* Intersection context flags */
enum RTCIntersectContextFlags
{
diff --git a/kernels/bvh/bvh_builder_hair.cpp b/kernels/bvh/bvh_builder_hair.cpp
index 14273787f..cad238123 100644
--- a/kernels/bvh/bvh_builder_hair.cpp
+++ b/kernels/bvh/bvh_builder_hair.cpp
@@ -135,8 +135,9 @@ namespace embree
#if defined(__AVX__)
Builder* BVH8Curve8vBuilder_OBB_New (void* bvh, Scene* scene, size_t mode) { return new BVHNHairBuilderSAH<8,Curve8v,Line8i>((BVH8*)bvh,scene); }
Builder* BVH4Curve8iBuilder_OBB_New (void* bvh, Scene* scene, size_t mode) { return new BVHNHairBuilderSAH<4,Curve8i,Line8i>((BVH4*)bvh,scene); }
-#endif
+ unsigned int getLine8iPrimId(Line8i *line, unsigned int i) { return line->primID(i); }
+#endif
}
}
#endif
diff --git a/kernels/common/rtcore_builder.cpp b/kernels/common/rtcore_builder.cpp
index 56858294c..6a154881b 100644
--- a/kernels/common/rtcore_builder.cpp
+++ b/kernels/common/rtcore_builder.cpp
@@ -29,8 +29,18 @@
#include "../builders/bvh_builder_sah.h"
#include "../builders/bvh_builder_morton.h"
+#include "../bvh/bvh.h"
+#include "../geometry/instance.h"
+#include "../geometry/trianglev.h"
+#include "../geometry/trianglei.h"
+#include "../geometry/curveNi.h"
+#include "../geometry/curveNi_mb.h"
+#include "../geometry/linei.h"
+
namespace embree
{
+ DECLARE_ISA_FUNCTION(unsigned int, getLine8iPrimId, Line8i* COMMA unsigned int);
+
namespace isa // FIXME: support more ISAs for builders
{
struct BVH : public RefCount
@@ -334,92 +344,362 @@ namespace embree
return root;
}
- RTC_API void* rtcBuildBVH(const RTCBuildArguments* arguments)
- {
- BVH* bvh = (BVH*) arguments->bvh;
- RTC_CATCH_BEGIN;
- RTC_TRACE(rtcBuildBVH);
- RTC_VERIFY_HANDLE(bvh);
- RTC_VERIFY_HANDLE(arguments);
- RTC_VERIFY_HANDLE(arguments->createNode);
- RTC_VERIFY_HANDLE(arguments->setNodeChildren);
- RTC_VERIFY_HANDLE(arguments->setNodeBounds);
- RTC_VERIFY_HANDLE(arguments->createLeaf);
-
- if (arguments->primitiveArrayCapacity < arguments->primitiveCount)
- throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"primitiveArrayCapacity must be greater or equal to primitiveCount")
-
- /* initialize the allocator */
- bvh->allocator.init_estimate(arguments->primitiveCount*sizeof(BBox3fa));
- bvh->allocator.reset();
-
- /* switch between differnet builders based on quality level */
- if (arguments->buildQuality == RTC_BUILD_QUALITY_LOW)
- return rtcBuildBVHMorton(arguments);
- else if (arguments->buildQuality == RTC_BUILD_QUALITY_MEDIUM)
- return rtcBuildBVHBinnedSAH(arguments);
- else if (arguments->buildQuality == RTC_BUILD_QUALITY_HIGH) {
- if (arguments->splitPrimitive == nullptr || arguments->primitiveArrayCapacity <= arguments->primitiveCount)
- return rtcBuildBVHBinnedSAH(arguments);
- else
- return rtcBuildBVHSpatialSAH(arguments);
- }
- else
- throw_RTCError(RTC_ERROR_INVALID_OPERATION,"invalid build quality");
+ template<int N>
+ void* createLeaf(const typename BVHN<N>::NodeRef node,
+ const PrimitiveType *leafType,
+ const RTCBVHExtractFunction args,
+ void *userData) {
+ size_t nb;
+ if(leafType == &Triangle4v::type) {
+ Triangle4v *prims = reinterpret_cast<Triangle4v *>(node.leaf(nb));
+ BVHPrimitive *primsArray = (BVHPrimitive *)alloca(4 * nb * sizeof(BVHPrimitive));
+ unsigned int realNum = 0;
+ for(int i = 0; i < nb; ++i) {
+ for(size_t j = 0; j < prims[i].size(); j++) {
+ primsArray[realNum].geomID = prims[i].geomID(j);
+ primsArray[realNum].primID = prims[i].primID(j);
+ ++realNum;
+ }
+ }
+
+ return args.createLeaf(realNum, primsArray, userData);
+ } else if(leafType == &Triangle4i::type) {
+ Triangle4i *prims = reinterpret_cast<Triangle4i *>(node.leaf(nb));
+ BVHPrimitive *primsArray = (BVHPrimitive *)alloca(4 * nb * sizeof(BVHPrimitive));
+ unsigned int realNum = 0;
+ for(int i = 0; i < nb; ++i) {
+ for(size_t j = 0; j < prims[i].size(); j++) {
+ primsArray[realNum].geomID = prims[i].geomID(j);
+ primsArray[realNum].primID = prims[i].primID(j);
+ ++realNum;
+ }
+ }
+
+ return args.createLeaf(realNum, primsArray, userData);
+ } else if(leafType == &InstancePrimitive::type) {
+ InstancePrimitive *prims = reinterpret_cast<InstancePrimitive *>(node.leaf(nb));
+ unsigned int *geomIDs = (unsigned int *)alloca(sizeof(unsigned int)*nb);
+ for(int i = 0; i < nb; ++i)
+ geomIDs[i] = prims[i].instance->geomID;
+
+ return args.createInstance(nb, geomIDs, userData);
+ } else if(leafType == &Curve8i::type) {
+ typedef unsigned char Primitive;
+
+ Primitive* prim = (Primitive*)node.leaf(nb);
+ if(nb == 0) return nullptr;
+
+ assert(nb == 1);
+ Geometry::GType ty = (Geometry::GType)(*prim);
+
+ BVHPrimitive primsArray[8];
+ unsigned int realNum = 0;
+
+ switch(ty) {
+ case Geometry::GTY_FLAT_LINEAR_CURVE: {
+ // Access to PrimID from right ISA, otherwise lead to allignement issue
+ DEFINE_ISA_FUNCTION(unsigned int, getLine8iPrimId, Line8i* COMMA unsigned int)
+ SELECT_SYMBOL_INIT_AVX(getCPUFeatures(), getLine8iPrimId)
+
+ Line8i *line = reinterpret_cast<Line8i*>(prim);
+
+ for(size_t i = 0; i < line->m; i++) {
+ primsArray[realNum].geomID = line->geomID();
+ primsArray[realNum].primID = getLine8iPrimId(line, i);
+ ++realNum;
+ }
+ } break;
+ case Geometry::GTY_FLAT_BEZIER_CURVE:
+ case Geometry::GTY_ROUND_BEZIER_CURVE:
+ case Geometry::GTY_ORIENTED_BEZIER_CURVE:
+ case Geometry::GTY_FLAT_BSPLINE_CURVE:
+ case Geometry::GTY_ROUND_BSPLINE_CURVE:
+ case Geometry::GTY_ORIENTED_BSPLINE_CURVE:
+ case Geometry::GTY_FLAT_HERMITE_CURVE:
+ case Geometry::GTY_ROUND_HERMITE_CURVE:
+ case Geometry::GTY_ORIENTED_HERMITE_CURVE: {
+ Curve8i *curve = reinterpret_cast<Curve8i*>(prim);
+ const auto Nb = curve->N;
+ for(size_t i = 0; i < Nb; i++) {
+ primsArray[realNum].geomID = curve->geomID(Nb);
+ primsArray[realNum].primID = curve->primID(Nb)[i];
+ ++realNum;
+ }
+ } break;
+ default:
+ throw_RTCError(RTC_ERROR_INVALID_OPERATION, "Unexpected curve geom type");
+ }
+
+ return args.createCurve(realNum, primsArray, userData);
+ } else if(leafType == &Curve8iMB::type) {
+ typedef unsigned char Primitive;
+
+ Primitive* prim = (Primitive*)node.leaf(nb);
+ if(nb == 0) return nullptr;
+
+ assert(nb == 1);
+ Geometry::GType ty = (Geometry::GType)(*prim);
+
+ BVHPrimitive primsArray[8];
+ unsigned int realNum = 0;
+
+ switch(ty) {
+ case Geometry::GTY_FLAT_LINEAR_CURVE: {
+ // Access to PrimID from right ISA, otherwise lead to allignement issue
+ DEFINE_ISA_FUNCTION(unsigned int, getLine8iPrimId, Line8i* COMMA unsigned int)
+ SELECT_SYMBOL_INIT_AVX(getCPUFeatures(), getLine8iPrimId)
+
+ Line8i *line = reinterpret_cast<Line8i*>(prim);
+
+ for(size_t i = 0; i < line->m; i++) {
+ primsArray[realNum].geomID = line->geomID();
+ primsArray[realNum].primID = getLine8iPrimId(line, i);
+ ++realNum;
+ }
+ } break;
+ case Geometry::GTY_FLAT_BEZIER_CURVE:
+ case Geometry::GTY_ROUND_BEZIER_CURVE:
+ case Geometry::GTY_ORIENTED_BEZIER_CURVE:
+ case Geometry::GTY_FLAT_BSPLINE_CURVE:
+ case Geometry::GTY_ROUND_BSPLINE_CURVE:
+ case Geometry::GTY_ORIENTED_BSPLINE_CURVE:
+ case Geometry::GTY_FLAT_HERMITE_CURVE:
+ case Geometry::GTY_ROUND_HERMITE_CURVE:
+ case Geometry::GTY_ORIENTED_HERMITE_CURVE: {
+ Curve8iMB *curve = reinterpret_cast<Curve8iMB*>(prim);
+ const auto Nb = curve->N;
+ for(size_t i = 0; i < Nb; i++) {
+ primsArray[realNum].geomID = curve->geomID(Nb);
+ primsArray[realNum].primID = curve->primID(Nb)[i];
+ ++realNum;
+ }
+ } break;
+ default:
+ throw_RTCError(RTC_ERROR_INVALID_OPERATION, "Unexpected curve geom type");
+ }
+
+ return args.createCurve(realNum, primsArray, userData);
+ } else {
+ throw_RTCError(RTC_ERROR_INVALID_OPERATION, "Unsupported primitive");
+ }
+ }
- /* if we are in dynamic mode, then do not clear temporary data */
- if (!(arguments->buildFlags & RTC_BUILD_FLAG_DYNAMIC))
- {
- bvh->morton_src.clear();
- bvh->morton_tmp.clear();
- }
+ inline RTCBounds boundsToRTC(const BBox3fa &bounds) {
+ RTCBounds bb;
+ bb.lower_x = bounds.lower.x;
+ bb.lower_y = bounds.lower.y;
+ bb.lower_z = bounds.lower.z;
- RTC_CATCH_END(bvh->device);
- return nullptr;
+ bb.upper_x = bounds.upper.x;
+ bb.upper_y = bounds.upper.y;
+ bb.upper_z = bounds.upper.z;
+
+ bb.align0 = 0;
+ bb.align1 = 1;
+
+ return bb;
}
- RTC_API void* rtcThreadLocalAlloc(RTCThreadLocalAllocator localAllocator, size_t bytes, size_t align)
- {
- FastAllocator::CachedAllocator* alloc = (FastAllocator::CachedAllocator*) localAllocator;
- RTC_CATCH_BEGIN;
- RTC_TRACE(rtcThreadLocalAlloc);
- return alloc->malloc0(bytes,align);
- RTC_CATCH_END(alloc->alloc->getDevice());
- return nullptr;
+ template <unsigned int N>
+ inline RTCAffineSpace affineSpaceToRTC(const AffineSpace3vf<N> affSpaces, unsigned int i) {
+ RTCAffineSpace affSpace;
+
+ affSpace.affine[0] = affSpaces.p.x[i];
+ affSpace.affine[1] = affSpaces.p.y[i];
+ affSpace.affine[2] = affSpaces.p.z[i];
+
+ affSpace.linear[0] = affSpaces.l.vx.x[i];
+ affSpace.linear[1] = affSpaces.l.vx.y[i];
+ affSpace.linear[2] = affSpaces.l.vx.z[i];
+ affSpace.linear[3] = affSpaces.l.vy.x[i];
+ affSpace.linear[4] = affSpaces.l.vy.y[i];
+ affSpace.linear[5] = affSpaces.l.vy.z[i];
+ affSpace.linear[6] = affSpaces.l.vz.x[i];
+ affSpace.linear[7] = affSpaces.l.vz.y[i];
+ affSpace.linear[8] = affSpaces.l.vz.z[i];
+
+ return affSpace;
}
- RTC_API void rtcMakeStaticBVH(RTCBVH hbvh)
- {
- BVH* bvh = (BVH*) hbvh;
- RTC_CATCH_BEGIN;
- RTC_TRACE(rtcStaticBVH);
- RTC_VERIFY_HANDLE(hbvh);
- bvh->morton_src.clear();
- bvh->morton_tmp.clear();
- RTC_CATCH_END(bvh->device);
+ template<int N>
+ void* recurse(const typename BVHN<N>::NodeRef node,
+ const PrimitiveType *leafType,
+ const RTCBVHExtractFunction args,
+ void *userData) {
+ if(node.isLeaf())
+ return createLeaf<N>(node, leafType, args, userData);
+
+ const typename BVHN<N>::BaseNode *bnode = nullptr;
+ const typename BVHN<N>::AlignedNode *anode = nullptr;
+ const typename BVHN<N>::AlignedNodeMB *anodeMB = nullptr;
+ const typename BVHN<N>::AlignedNodeMB4D *anodeMB4D = nullptr;
+
+ const typename BVHN<N>::UnalignedNode *unanode = nullptr;
+ const typename BVHN<N>::UnalignedNodeMB *unanodeMB = nullptr;
+
+ if(node.isAlignedNode()) {
+ anode = node.alignedNode();
+ bnode = anode;
+ } else if(node.isAlignedNodeMB()) {
+ anodeMB = node.alignedNodeMB();
+ bnode = anodeMB;
+ } else if (node.isAlignedNodeMB4D()) {
+ anodeMB4D = node.alignedNodeMB4D();
+ anodeMB = anodeMB4D;
+ bnode = anodeMB;
+ } else if (node.isUnalignedNode()) {
+ unanode = node.unalignedNode();
+ bnode = unanode;
+ } else if (node.isUnalignedNodeMB()) {
+ unanodeMB = node.unalignedNodeMB();
+ bnode = unanodeMB;
+ } else {
+ throw_RTCError(RTC_ERROR_INVALID_OPERATION, "Unknown node type");
+ }
+
+ unsigned int nb = 0;
+ void *children[4];
+ for(unsigned int i = 0; i < 4; i++) {
+ void *child = recurse<N>(bnode->child(i), leafType, args, userData);
+ if(child == nullptr) continue;
+
+ if(anode != nullptr) {
+ args.setAlignedBounds(child, boundsToRTC(anode->bounds(i)), userData);
+ } else if (anodeMB != nullptr) {
+ RTCLinearBounds lb;
+ lb.bounds0 = boundsToRTC(anodeMB->bounds0(i));
+ lb.bounds1 = boundsToRTC(anodeMB->bounds1(i) - anodeMB->bounds0(i));
+
+ if (anodeMB4D != nullptr) {
+ lb.bounds0.align0 = anodeMB4D->timeRange(i).lower;
+ lb.bounds0.align1 = anodeMB4D->timeRange(i).upper;
+ }
+
+ args.setLinearBounds(child, lb, userData);
+ } else if(unanode != nullptr) {
+ RTCAffineSpace affSpace = affineSpaceToRTC<N>(unanode->naabb, i);
+ args.setUnalignedBounds(child, affSpace, userData);
+ } else if(unanodeMB != nullptr) {
+ RTCAffineSpace affSpace = affineSpaceToRTC<N>(unanodeMB->space0, i);
+
+ RTCBounds bounds;
+ bounds.lower_x = unanodeMB->b1.lower.x[i];
+ bounds.lower_y = unanodeMB->b1.lower.y[i];
+ bounds.lower_z = unanodeMB->b1.lower.z[i];
+
+ bounds.upper_x = unanodeMB->b1.upper.x[i];
+ bounds.upper_y = unanodeMB->b1.upper.y[i];
+ bounds.upper_z = unanodeMB->b1.upper.z[i];
+
+ args.setUnalignedLinearBounds(child, affSpace, bounds, userData);
+ }
+
+ children[nb++] = child;
+ }
+
+ return args.createInnerNode(nb, children, userData);
}
- RTC_API void rtcRetainBVH(RTCBVH hbvh)
- {
- BVH* bvh = (BVH*) hbvh;
- Device* device = bvh ? bvh->device : nullptr;
- RTC_CATCH_BEGIN;
- RTC_TRACE(rtcRetainBVH);
- RTC_VERIFY_HANDLE(hbvh);
- bvh->refInc();
- RTC_CATCH_END(device);
+ std::vector<void*> prerecurse(Accel *a, RTCBVHExtractFunction args, void *userData) {
+ std::vector<void*> nodes;
+
+ AccelData *ad = a->intersectors.ptr;
+ switch(ad->type) {
+ case AccelData::TY_BVH4: {
+ BVH4 *bvh = dynamic_cast<BVH4 *>(ad);
+ BVH4::NodeRef root = bvh->root;
+
+ void* node = recurse<4>(root, bvh->primTy, args, userData);
+ args.setAlignedBounds(node, boundsToRTC(bvh->bounds.bounds()), userData);
+ nodes.push_back(node);
+ } break;
+ case AccelData::TY_BVH8: {
+ BVH8 *bvh = dynamic_cast<BVH8 *>(ad);
+ BVH8::NodeRef root = bvh->root;
+
+ void *node = recurse<8>(root, bvh->primTy, args, userData);
+ args.setAlignedBounds(node, boundsToRTC(bvh->bounds.bounds()), userData);
+ nodes.push_back(node);
+ } break;
+ case AccelData::TY_ACCELN: {
+ AccelN *acceln = dynamic_cast<AccelN *>(ad);
+ for (Accel *acc : acceln->accels) {
+ auto newNodes = prerecurse(acc, args, userData);
+ nodes.insert(nodes.end(), newNodes.begin(), newNodes.end());
+ }
+ } break;
+ default:
+ throw_RTCError(RTC_ERROR_INVALID_OPERATION, "Unable to extract something else than BVH4/8 tree");
+ }
+
+ return nodes;
}
-
- RTC_API void rtcReleaseBVH(RTCBVH hbvh)
- {
- BVH* bvh = (BVH*) hbvh;
- Device* device = bvh ? bvh->device : nullptr;
+
+ RTC_API void *rtcExtractBVH(RTCScene hscene, RTCBVHExtractFunction args, void *userData) {
+ Scene* scene = (Scene*) hscene;
RTC_CATCH_BEGIN;
- RTC_TRACE(rtcReleaseBVH);
- RTC_VERIFY_HANDLE(hbvh);
- bvh->refDec();
- RTC_CATCH_END(device);
+ RTC_TRACE(rtcSampleTry);
+#if defined(DEBUG)
+ RTC_VERIFY_HANDLE(hscene);
+#endif
+
+ if (args.expectedSize != nullptr) {
+ /* Defines size and helper macro */
+ struct Size {
+ unsigned int num_prim = 0;
+ unsigned int num_tri = 0;
+ };
+
+#define SIZE(ops) \
+ Size* size = reinterpret_cast<Size*>(userData); \
+ ops \
+ return nullptr;
+
+#define INC(var, val) size->var += (val);
+
+
+ Size size;
+ RTCBVHExtractFunction param;
+
+ param.createLeaf = [](unsigned int nbPrim, const BVHPrimitive[], void* userData) -> void* {
+ SIZE(INC(num_prim, nbPrim) INC(num_tri, 3 * nbPrim))
+ };
+ param.createInstance = [](unsigned int nbPrim, const unsigned int[], void* userData) -> void* {
+ SIZE(INC(num_prim, nbPrim))
+ };
+ param.createCurve = [](unsigned int nbPrim, const BVHPrimitive[], void* userData) -> void* {
+ SIZE(INC(num_prim, nbPrim))
+ };
+ param.createInnerNode = [](unsigned int, void*[], void* ) -> void* { return nullptr; };
+ param.setAlignedBounds = [](void*, const RTCBounds &, void*) {};
+ param.setLinearBounds = [](void*, const RTCLinearBounds &, void*) {};
+ param.setUnalignedBounds = [](void*, const RTCAffineSpace &, void*) {};
+ param.setUnalignedLinearBounds = [](void*, const RTCAffineSpace &, const RTCBounds &, void*) {};
+ param.expectedSize = nullptr;
+
+ prerecurse(scene, param, &size);
+ args.expectedSize(size.num_prim, size.num_tri, userData);
+ }
+
+ std::vector<void*> nodes;
+
+ nodes = prerecurse(scene, args, userData);
+
+ if(nodes.size() == 0)
+ return nullptr;
+
+ if(nodes.size() == 1)
+ return nodes[0];
+
+ RTCBounds bounds;
+ rtcGetSceneBounds(hscene, &bounds);
+
+ void *root = args.createInnerNode(nodes.size(), nodes.data(), userData);
+ args.setAlignedBounds(root, bounds, userData);
+ return root;
+
+ RTC_CATCH_END2(scene);
+ return nullptr;
}
}
}
-

View File

@@ -57,6 +57,12 @@ enum_bvh_layouts = (
('BVH8', "BVH8", "", 4),
)
enum_bvh_builder = (
('INTERNAL', "Internal", "Use old internal BVH builder"),
('EMBREE', "Embree", "Use full featured embree (only on CPU)"),
('EMBREE_CONVERT', "Embree, then convert", "Use embree, then convert it to BVH2"),
)
enum_bvh_types = (
('DYNAMIC_BVH', "Dynamic BVH", "Objects can be individually updated, at the cost of slower render time"),
('STATIC_BVH', "Static BVH", "Any object modification requires a complete BVH rebuild, but renders faster"),
@@ -537,11 +543,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
items=enum_bvh_types,
default='DYNAMIC_BVH',
)
use_bvh_embree: BoolProperty(
name="Use Embree",
description="Use Embree as ray accelerator",
default=False,
bvh_builder: EnumProperty(
name="BVH Builder",
description="Choose the BVH builder that will be used",
items=enum_bvh_builder,
default='INTERNAL',
)
debug_use_spatial_splits: BoolProperty(
name="Use Spatial Splits",
description="Use BVH spatial splits: longer builder time, faster render",

View File

@@ -647,17 +647,13 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa
cscene = scene.cycles
col = layout.column()
if _cycles.with_embree:
row = col.row()
row.active = use_cpu(context)
row.prop(cscene, "use_bvh_embree")
col.prop(cscene, "bvh_builder")
col.prop(cscene, "debug_use_spatial_splits")
sub = col.column()
sub.active = not cscene.use_bvh_embree or not _cycles.with_embree
sub.active = cscene.bvh_builder != 'EMBREE' or not _cycles.with_embree
sub.prop(cscene, "debug_use_hair_bvh")
sub = col.column()
sub.active = not cscene.debug_use_spatial_splits and not cscene.use_bvh_embree
sub.active = not cscene.debug_use_spatial_splits and cscene.bvh_builder != 'EMBREE'
sub.prop(cscene, "debug_bvh_time_steps")

View File

@@ -701,8 +701,14 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
}
#ifdef WITH_EMBREE
params.bvh_layout = RNA_boolean_get(&cscene, "use_bvh_embree") ? BVH_LAYOUT_EMBREE :
params.bvh_layout;
switch (RNA_enum_get(&cscene, "bvh_builder")) {
case 1:
params.bvh_layout = BVH_LAYOUT_EMBREE;
break;
case 2:
params.bvh_layout = BVH_LAYOUT_EMBREE_CONVERTED;
break;
}
#endif
return params;
}

View File

@@ -18,6 +18,7 @@ set(SRC
bvh_sort.cpp
bvh_split.cpp
bvh_unaligned.cpp
bvh_embree_converter.cpp
)
set(SRC_HEADERS
@@ -33,6 +34,7 @@ set(SRC_HEADERS
bvh_sort.h
bvh_split.h
bvh_unaligned.h
bvh_embree_converter.h
)
set(LIB

View File

@@ -51,6 +51,8 @@ const char *bvh_layout_name(BVHLayout layout)
return "NONE";
case BVH_LAYOUT_EMBREE:
return "EMBREE";
case BVH_LAYOUT_EMBREE_CONVERTED:
return "EMBREE_CONVERTED";
case BVH_LAYOUT_ALL:
return "ALL";
}
@@ -105,6 +107,7 @@ BVH *BVH::create(const BVHParams &params, const vector<Object *> &objects)
case BVH_LAYOUT_BVH8:
return new BVH8(params, objects);
case BVH_LAYOUT_EMBREE:
case BVH_LAYOUT_EMBREE_CONVERTED:
#ifdef WITH_EMBREE
return new BVHEmbree(params, objects);
#endif

View File

@@ -94,7 +94,7 @@ void BVH2::pack_aligned_node(int idx,
int4 data[BVH_NODE_SIZE] = {
make_int4(
visibility0 & ~PATH_RAY_NODE_UNALIGNED, visibility1 & ~PATH_RAY_NODE_UNALIGNED, c0, c1),
visibility0 & ~PATH_RAY_NODE_CLEAR, visibility1 & ~PATH_RAY_NODE_CLEAR, c0, c1),
make_int4(__float_as_int(b0.min.x),
__float_as_int(b1.min.x),
__float_as_int(b0.max.x),
@@ -144,8 +144,8 @@ void BVH2::pack_unaligned_node(int idx,
float4 data[BVH_UNALIGNED_NODE_SIZE];
Transform space0 = BVHUnaligned::compute_node_transform(bounds0, aligned_space0);
Transform space1 = BVHUnaligned::compute_node_transform(bounds1, aligned_space1);
data[0] = make_float4(__int_as_float(visibility0 | PATH_RAY_NODE_UNALIGNED),
__int_as_float(visibility1 | PATH_RAY_NODE_UNALIGNED),
data[0] = make_float4(__int_as_float((visibility0 & ~PATH_RAY_NODE_CLEAR) | PATH_RAY_NODE_UNALIGNED),
__int_as_float((visibility1 & ~PATH_RAY_NODE_CLEAR) | PATH_RAY_NODE_UNALIGNED),
__int_as_float(c0),
__int_as_float(c1));
@@ -208,6 +208,7 @@ void BVH2::pack_nodes(const BVHNode *root)
pack_leaf(e, leaf);
}
else {
assert(e.node->num_children() == 2);
/* inner node */
int idx[2];
for (int i = 0; i < 2; ++i) {
@@ -280,7 +281,10 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility)
idx, aligned_space, aligned_space, bbox0, bbox1, c0, c1, visibility0, visibility1);
}
else {
pack_aligned_node(idx, bbox0, bbox1, c0, c1, visibility0, visibility1);
pack_aligned_node(idx,
bbox0, bbox1,
c0, c1,
visibility0, visibility1);
}
bbox.grow(bbox0);

View File

@@ -54,6 +54,7 @@
# include "util/util_foreach.h"
# include "util/util_logging.h"
# include "util/util_progress.h"
# include "bvh_embree_converter.h"
CCL_NAMESPACE_BEGIN
@@ -286,7 +287,8 @@ int BVHEmbree::rtc_shared_users = 0;
thread_mutex BVHEmbree::rtc_shared_mutex;
BVHEmbree::BVHEmbree(const BVHParams &params_, const vector<Object *> &objects_)
: BVH(params_, objects_),
: bvh_layout(params_.bvh_layout),
BVH2(params_, objects_),
scene(NULL),
mem_used(0),
top_level(NULL),
@@ -301,7 +303,11 @@ BVHEmbree::BVHEmbree(const BVHParams &params_, const vector<Object *> &objects_)
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
thread_scoped_lock lock(rtc_shared_mutex);
if (rtc_shared_users == 0) {
rtc_shared_device = rtcNewDevice("verbose=0");
if(bvh_layout == BVH_LAYOUT_EMBREE_CONVERTED) {
rtc_shared_device = rtcNewDevice("verbose=0;hair_accel_mb=bvh4.virtualcurve8imb;");
} else {
rtc_shared_device = rtcNewDevice("verbose=0;");
}
/* Check here if Embree was built with the correct flags. */
ssize_t ret = rtcGetDeviceProperty(rtc_shared_device, RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED);
if (ret != 1) {
@@ -483,16 +489,21 @@ void BVHEmbree::build(Progress &progress, Stats *stats_)
rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress);
rtcCommitScene(scene);
pack_primitives();
if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return;
}
progress.set_substatus("Packing geometry");
pack_nodes(NULL);
if(this->bvh_layout == BVH_LAYOUT_EMBREE_CONVERTED) {
BVHEmbreeConverter conv(this->scene, this->objects, this->params);
conv.fillPack(this->pack);
} else {
pack_primitives();
if (progress.get_cancel()) {
delete_rtcScene();
stats = NULL;
return;
}
pack_nodes(NULL);
}
stats = NULL;
}

View File

@@ -23,6 +23,7 @@
# include <embree3/rtcore_scene.h>
# include "bvh/bvh.h"
# include "bvh/bvh2.h"
# include "bvh/bvh_params.h"
# include "util/util_thread.h"
@@ -33,10 +34,10 @@ CCL_NAMESPACE_BEGIN
class Mesh;
class BVHEmbree : public BVH {
class BVHEmbree : public BVH2 {
public:
virtual void build(Progress &progress, Stats *stats) override;
virtual ~BVHEmbree();
virtual ~BVHEmbree() override;
RTCScene scene;
static void destroy(RTCScene);
@@ -64,6 +65,8 @@ class BVHEmbree : public BVH {
BVHEmbree *top_level;
private:
BVHLayout bvh_layout;
void delete_rtcScene();
void update_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh);
void update_curve_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh);

View File

@@ -0,0 +1,897 @@
/*
* Copyright 2018, Blender Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef WITH_EMBREE
#include "bvh_embree_converter.h"
#include "bvh_node.h"
#include "render/mesh.h"
CCL_NAMESPACE_BEGIN
/* Utility functions */
void packPush(PackedBVH *pack, const size_t packIdx,
const int object_id, const int prim_id,
const int prim_type, const uint visibility,
const uint tri_index)
{
pack->prim_index.resize(packIdx + 1);
pack->prim_type.resize(packIdx + 1);
pack->prim_object.resize(packIdx + 1);
pack->prim_visibility.resize(packIdx + 1);
pack->prim_tri_index.resize(packIdx + 1);
pack->prim_index[packIdx] = prim_id;
pack->prim_type[packIdx] = prim_type;
pack->prim_object[packIdx] = object_id;
pack->prim_visibility[packIdx] = visibility;
pack->prim_tri_index[packIdx] = tri_index;
}
BoundBox RTCBoundBoxToCCL(const RTCBounds &bound)
{
return BoundBox(make_float3(bound.lower_x, bound.lower_y, bound.lower_z),
make_float3(bound.upper_x, bound.upper_y, bound.upper_z));
}
Transform transformSpaceFromBound(const BoundBox &bounds) {
Transform space = transform_identity();
space.x.w -= bounds.min.x;
space.y.w -= bounds.min.y;
space.z.w -= bounds.min.z;
float3 dim = bounds.max - bounds.min;
return transform_scale(1.0f / max(1e-18f, dim.x),
1.0f / max(1e-18f, dim.y),
1.0f / max(1e-18f, dim.z)) * space;
}
BVHNode *merge(const BVHNode *oldRoot, BVHNode *n0, BVHNode *n1) {
BoundBox childBB = merge(n0->bounds, n1->bounds);
BVHNode *root = new InnerNode(childBB, n0, n1);
/* If one of the child has linear bound, take the parents bound */
if(n0->deltaBounds != nullptr || n1->deltaBounds != nullptr || n0->is_unaligned || n1->is_unaligned) {
root->bounds = oldRoot->bounds;
if(oldRoot->deltaBounds != nullptr) root->deltaBounds = new BoundBox(*oldRoot->deltaBounds);
if(oldRoot->is_unaligned) root->set_aligned_space(*oldRoot->aligned_space);
}
return root;
}
BVHNode *makeBVHTreeFromList(std::deque<BVHNode *> nodes)
{
BVHNode *ret = nullptr;
while(!nodes.empty()) {
if(ret == nullptr) {
ret = nodes.front();
nodes.pop_front();
continue;
}
/* If it's a leaf or a full node -> create a new parrent */
if(ret->is_leaf() || ret->num_children() == 4) {
ret = new InnerNode(ret->bounds, &ret, 1);
}
InnerNode *innerNode = dynamic_cast<InnerNode*>(ret);
innerNode->children[innerNode->num_children_++] = nodes.front();
innerNode->bounds.grow(nodes.front()->bounds);
nodes.pop_front();
if(ret->num_children() == 4) {
nodes.push_back(ret);
ret = nullptr;
}
}
return ret;
}
BVHNode *bvh_shrink(BVHNode *root)
{
if(root->is_leaf()) {
if(root->num_triangles() == 0) // Remove empty leafs
return nullptr;
else
return root;
}
InnerNode *node = dynamic_cast<InnerNode*>(root);
int num_children = 0;
BVHNode* children[4];
for(int i = 0; i < node->num_children(); ++i) {
BVHNode *child = bvh_shrink(node->get_child(i));
if(child != nullptr)
children[num_children++] = child;
}
if(num_children == 0) {
delete root;
return nullptr;
}
if(num_children == 1) {
delete root;
return children[0];
}
// We have 2 node or more, we'll pack them into 2 nodes (to respect BVH2)
node->num_children_ = 2;
if(num_children == 2) {
node->children[0] = children[0];
node->children[1] = children[1];
return node;
}
node->children[0] = merge(root, children[0], children[1]);
if(num_children == 3) {
node->children[1] = children[2];
} else {
node->children[1] = merge(root, children[2], children[3]);
}
return node;
}
BVHEmbreeConverter::BVHEmbreeConverter(RTCScene scene,
std::vector<Object *> objects,
const BVHParams &params)
: s(scene),
objects(objects),
params(params)
{}
BVHNode* BVHEmbreeConverter::createLeaf(unsigned int nbPrim,
const BVHPrimitive prims[])
{
const unsigned int from = this->packIdx;
uint visibility = 0;
for(unsigned int i = 0; i < nbPrim; i++) {
const unsigned int geom_id = prims[i].geomID;
const unsigned int prim_id = prims[i].primID;
const unsigned int object_id = geom_id / 2;
Object *obj = this->objects.at(object_id);
Mesh::Triangle tri = obj->mesh->get_triangle(prim_id);
int prim_type = obj->mesh->has_motion_blur()
? PRIMITIVE_MOTION_TRIANGLE
: PRIMITIVE_TRIANGLE;
visibility |= obj->visibility;
packPush(this->pack, this->packIdx++,
object_id, prim_id,
prim_type, obj->visibility,
this->pack->prim_tri_verts.size());
array<float4> *prim_tri_verts = &this->pack->prim_tri_verts;
prim_tri_verts->reserve(this->pack->prim_tri_verts.size());
float3 *verts = obj->mesh->verts.data();
prim_tri_verts->reserve(this->pack->prim_tri_verts.size() + 3);
for(int i = 0; i < 3; i++)
prim_tri_verts->push_back_reserved(float3_to_float4(verts[tri.v[i]]));
}
return new LeafNode(BoundBox::empty, visibility, from, from + nbPrim);
}
BVHNode* BVHEmbreeConverter::createInstance(unsigned int nbPrim,
const unsigned int geomID[])
{
std::deque<BVHNode *> nodes;
for(size_t i = 0; i < nbPrim; i++) {
uint id = geomID[i] / 2;
Object *obj = this->objects.at(id);
LeafNode *leafNode = new LeafNode(obj->bounds, obj->visibility,
this->packIdx, this->packIdx + 1);
packPush(this->pack, this->packIdx++,
id, -1,
PRIMITIVE_NONE, obj->visibility,
-1);
nodes.push_back(leafNode);
}
return makeBVHTreeFromList(nodes);
}
BVHNode* BVHEmbreeConverter::createCurve(unsigned int nbPrim,
const BVHPrimitive prims[])
{
std::deque<BVHNode *> nodes;
for(unsigned int i = 0; i < nbPrim; i++) {
const auto geom_id = prims[i].geomID;
const auto prim_id = prims[i].primID;
const unsigned int object_id = geom_id / 2;
Object *obj = this->objects.at(object_id);
unsigned int curve_id = 0;
int segment_id = prim_id;
Mesh::Curve curve = obj->mesh->get_curve(0);
while(segment_id >= curve.num_segments()) {
curve = obj->mesh->get_curve(++curve_id);
segment_id -= curve.num_segments();
}
int prim_type = PRIMITIVE_PACK_SEGMENT(obj->mesh->has_motion_blur()
? PRIMITIVE_MOTION_CURVE
: PRIMITIVE_CURVE, segment_id);
LeafNode *leafNode = new LeafNode(BoundBox::empty, obj->visibility,
this->packIdx, this->packIdx + 1);
int nbStep = 1;
const Attribute *curve_attr_mP = nullptr;
if (obj->mesh->has_motion_blur()) {
curve_attr_mP = obj->mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
nbStep = obj->mesh->motion_steps;
}
for(int i = 0; i < nbStep; i++) {
float4 keys[4];
curve.cardinal_keys_for_step(
obj->mesh->curve_keys.data(),
obj->mesh->curve_radius.data(),
curve_attr_mP == nullptr ? nullptr : curve_attr_mP->data_float3(),
obj->mesh->curve_keys.size(),
nbStep, i,
max(curve.first_key + segment_id - 1, curve.first_key) - curve.first_key,
segment_id, segment_id + 1,
min(curve.first_key + segment_id + 2, curve.first_key + curve.num_keys - 1) - curve.first_key,
keys
);
curve.bounds_grow(keys, leafNode->bounds);
}
packPush(this->pack, this->packIdx++,
object_id, curve_id,
prim_type, obj->visibility, -1);
nodes.push_back(leafNode);
}
return makeBVHTreeFromList(nodes);
}
BVHNode *BVHEmbreeConverter::createInnerNode(unsigned int nbChild,
BVHNode *children[])
{
return new InnerNode(BoundBox::empty, children, nbChild);
}
void BVHEmbreeConverter::setAlignedBounds(BVHNode *node, BoundBox bounds)
{
node->bounds = bounds;
}
void BVHEmbreeConverter::setLinearBounds(BVHNode *node, BoundBox bounds, BoundBox deltaBounds)
{
node->bounds = bounds;
node->deltaBounds = new BoundBox(deltaBounds);
}
void BVHEmbreeConverter::setUnalignedBounds(BVHNode *node, Transform affSpace)
{
Transform inv = transform_inverse(affSpace);
BoundBox bb(transform_point(&inv, make_float3(0)));
bb.grow(transform_point(&inv, make_float3(1)));
node->bounds = bb;
node->set_aligned_space(affSpace);
}
void BVHEmbreeConverter::setUnalignedLinearBounds(BVHNode *node, Transform affSpace, BoundBox bounds)
{
Transform inv = transform_inverse(affSpace);
BoundBox bb(transform_point(&inv, make_float3(0)));
bb.grow(transform_point(&inv, make_float3(1)));
bb.grow(transform_point(&inv, bounds.min));
bb.grow(transform_point(&inv, bounds.max));
node->bounds = bb;
node->set_aligned_space(affSpace);
node->deltaBounds = new BoundBox(bounds.min,
bounds.max - make_float3(1));
}
void BVHEmbreeConverter::set_time_bounds(BVHNode *node, float2 time_range)
{
node->time_from = time_range.x;
node->time_to = time_range.y;
}
#define SET_THIS \
BVHEmbreeConverter* This = reinterpret_cast<BVHEmbreeConverter*>(userData)
#define SET_NODE \
BVHNode* Node = reinterpret_cast<BVHNode*>(node)
BVHNode* BVHEmbreeConverter::getBVH4()
{
RTCBVHExtractFunction param;
param.createLeaf = [](unsigned int nbPrim, const BVHPrimitive prims[], void* userData) -> void* {
SET_THIS;
return This->createLeaf(nbPrim, prims);
};
param.createInstance = [](unsigned int nbPrim, const unsigned int geomID[], void* userData) -> void* {
SET_THIS;
return This->createInstance(nbPrim, geomID);
};
param.createCurve = [](unsigned int nbPrim, const BVHPrimitive prims[], void* userData) -> void* {
SET_THIS;
return This->createCurve(nbPrim, prims);
};
param.createInnerNode = [](unsigned int nbChild, void* children[], void* userData) -> void* {
SET_THIS;
return This->createInnerNode(nbChild, reinterpret_cast<BVHNode**>(children));
};
param.setAlignedBounds = [](void* node, const RTCBounds &bounds, void* userData) {
SET_THIS; SET_NODE;
This->setAlignedBounds(Node, RTCBoundBoxToCCL(bounds));
};
param.setLinearBounds = [](void* node, const RTCLinearBounds &lbounds, void* userData) {
SET_THIS; SET_NODE;
This->setLinearBounds(Node,
RTCBoundBoxToCCL(lbounds.bounds0),
RTCBoundBoxToCCL(lbounds.bounds1));
This->set_time_bounds(Node, make_float2(lbounds.bounds0.align0,
lbounds.bounds0.align1));
};
param.setUnalignedBounds = [](void* node, const RTCAffineSpace &affSpace, void* userData) {
SET_THIS; SET_NODE;
Transform tr;
tr.x.x = affSpace.linear[0];
tr.y.x = affSpace.linear[1];
tr.z.x = affSpace.linear[2];
tr.x.y = affSpace.linear[3];
tr.y.y = affSpace.linear[4];
tr.z.y = affSpace.linear[5];
tr.x.z = affSpace.linear[6];
tr.y.z = affSpace.linear[7];
tr.z.z = affSpace.linear[8];
tr.x.w = affSpace.affine[0];
tr.y.w = affSpace.affine[1];
tr.z.w = affSpace.affine[2];
This->setUnalignedBounds(Node, tr);
};
param.setUnalignedLinearBounds = [](void* node, const RTCAffineSpace &affSpace, const RTCBounds &bounds, void* userData) {
SET_THIS; SET_NODE;
Transform tr;
tr.x.x = affSpace.linear[0];
tr.y.x = affSpace.linear[1];
tr.z.x = affSpace.linear[2];
tr.x.y = affSpace.linear[3];
tr.y.y = affSpace.linear[4];
tr.z.y = affSpace.linear[5];
tr.x.z = affSpace.linear[6];
tr.y.z = affSpace.linear[7];
tr.z.z = affSpace.linear[8];
tr.x.w = affSpace.affine[0];
tr.y.w = affSpace.affine[1];
tr.z.w = affSpace.affine[2];
This->setUnalignedLinearBounds(Node, tr, RTCBoundBoxToCCL(bounds));
};
param.expectedSize = [](unsigned int num_prim, unsigned int num_tri, void* userData) {
SET_THIS;
This->pack->prim_visibility.reserve(num_prim);
This->pack->prim_object.reserve(num_prim);
This->pack->prim_type.reserve(num_prim);
This->pack->prim_index.reserve(num_prim);
This->pack->prim_tri_index.reserve(num_prim);
This->pack->prim_tri_verts.reserve(num_tri);
};
return reinterpret_cast<BVHNode *>(rtcExtractBVH(this->s, param, this));
}
BVHNode* BVHEmbreeConverter::getBVH2()
{
BVHNode *root = this->getBVH4();
if(root == nullptr) return nullptr;
return bvh_shrink(root);
}
void BVHEmbreeConverter::pack_instances(size_t nodes_size, size_t leaf_nodes_size, PackedBVH &pack)
{
/* Adjust primitive index to point to the triangle in the global array, for
* meshes with transform applied and already in the top level BVH.
*/
for (size_t i = 0; i < pack.prim_index.size(); i++)
if (pack.prim_index[i] != -1) {
if (pack.prim_type[i] & PRIMITIVE_ALL_CURVE)
pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->curve_offset;
else
pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_offset;
}
/* track offsets of instanced BVH data in global array */
size_t prim_offset = pack.prim_index.size();
size_t nodes_offset = nodes_size;
size_t nodes_leaf_offset = leaf_nodes_size;
/* clear array that gives the node indexes for instanced objects */
pack.object_node.clear();
/* reserve */
size_t prim_index_size = pack.prim_index.size();
size_t prim_tri_verts_size = pack.prim_tri_verts.size();
size_t pack_prim_index_offset = prim_index_size;
size_t pack_prim_tri_verts_offset = prim_tri_verts_size;
size_t pack_nodes_offset = nodes_size;
size_t pack_leaf_nodes_offset = leaf_nodes_size;
size_t object_offset = 0;
map<Mesh *, int> mesh_map;
foreach (Object *ob, objects) {
Mesh *mesh = ob->mesh;
BVH *bvh = mesh->bvh;
if (mesh->need_build_bvh()) {
if (mesh_map.find(mesh) == mesh_map.end()) {
prim_index_size += bvh->pack.prim_index.size();
prim_tri_verts_size += bvh->pack.prim_tri_verts.size();
nodes_size += bvh->pack.nodes.size();
leaf_nodes_size += bvh->pack.leaf_nodes.size();
mesh_map[mesh] = 1;
}
}
}
mesh_map.clear();
pack.prim_index.resize(prim_index_size);
pack.prim_type.resize(prim_index_size);
pack.prim_object.resize(prim_index_size);
pack.prim_visibility.resize(prim_index_size);
pack.prim_tri_verts.resize(prim_tri_verts_size);
pack.prim_tri_index.resize(prim_index_size);
pack.nodes.resize(nodes_size);
pack.leaf_nodes.resize(leaf_nodes_size);
pack.object_node.resize(objects.size());
if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
pack.prim_time.resize(prim_index_size);
}
int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : nullptr;
int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : nullptr;
int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : nullptr;
uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : nullptr;
float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : nullptr;
uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : nullptr;
int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : nullptr;
int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : nullptr;
float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : nullptr;
/* merge */
foreach (Object *ob, objects) {
Mesh *mesh = ob->mesh;
/* We assume that if mesh doesn't need own BVH it was already included
* into a top-level BVH and no packing here is needed.
*/
if (!mesh->need_build_bvh()) {
pack.object_node[object_offset++] = 0;
continue;
}
/* if mesh already added once, don't add it again, but used set
* node offset for this object */
map<Mesh *, int>::iterator it = mesh_map.find(mesh);
if (mesh_map.find(mesh) != mesh_map.end()) {
int noffset = it->second;
pack.object_node[object_offset++] = noffset;
continue;
}
BVH *bvh = mesh->bvh;
int noffset = nodes_offset;
int noffset_leaf = nodes_leaf_offset;
int mesh_tri_offset = mesh->tri_offset;
int mesh_curve_offset = mesh->curve_offset;
/* fill in node indexes for instances */
if (bvh->pack.root_index == -1)
pack.object_node[object_offset++] = -noffset_leaf - 1;
else
pack.object_node[object_offset++] = noffset;
mesh_map[mesh] = pack.object_node[object_offset - 1];
/* merge primitive, object and triangle indexes */
if (bvh->pack.prim_index.size()) {
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
int *bvh_prim_index = &bvh->pack.prim_index[0];
int *bvh_prim_type = &bvh->pack.prim_type[0];
uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : nullptr;
for (size_t i = 0; i < bvh_prim_index_size; i++) {
if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_curve_offset;
pack_prim_tri_index[pack_prim_index_offset] = -1;
}
else {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset;
pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] +
pack_prim_tri_verts_offset;
}
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
if (bvh_prim_time != nullptr) {
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
}
pack_prim_index_offset++;
}
}
/* Merge triangle vertices data. */
if (bvh->pack.prim_tri_verts.size()) {
const size_t prim_tri_size = bvh->pack.prim_tri_verts.size();
memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset,
&bvh->pack.prim_tri_verts[0],
prim_tri_size * sizeof(float4));
pack_prim_tri_verts_offset += prim_tri_size;
}
/* merge nodes */
if (bvh->pack.leaf_nodes.size()) {
int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0];
size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size();
for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_SIZE_LEAF, j++) {
int4 data = leaf_nodes_offset[i];
data.x += prim_offset;
data.y += prim_offset;
pack_leaf_nodes[pack_leaf_nodes_offset] = data;
for (int j = 1; j < BVH_NODE_SIZE_LEAF; ++j) {
pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j];
}
pack_leaf_nodes_offset += BVH_NODE_SIZE_LEAF;
}
}
if (bvh->pack.nodes.size()) {
int4 *bvh_nodes = &bvh->pack.nodes[0];
size_t bvh_nodes_size = bvh->pack.nodes.size();
for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) {
size_t nsize = BVH_NODE_SIZE_BASE;
if (bvh_nodes[i].x & PATH_RAY_NODE_4D) nsize += BVH_NODE_SIZE_4D;
if (bvh_nodes[i].x & PATH_RAY_NODE_MB) nsize += BVH_NODE_SIZE_MOTION_BLUR;
if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) nsize += BVH_NODE_SIZE_UNALIGNED;
/* Modify offsets into arrays */
int4 data = bvh_nodes[i];
data.z += (data.z < 0) ? -noffset_leaf : noffset;
data.w += (data.w < 0) ? -noffset_leaf : noffset;
pack_nodes[pack_nodes_offset] = data;
memcpy(&pack_nodes[pack_nodes_offset + 1],
&bvh_nodes[i + 1],
sizeof(int4) * (nsize - 1));
pack_nodes_offset += nsize;
i += nsize;
}
}
nodes_offset += bvh->pack.nodes.size();
nodes_leaf_offset += bvh->pack.leaf_nodes.size();
prim_offset += bvh->pack.prim_index.size();
}
}
void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf, PackedBVH &pack)
{
assert(e.idx + BVH_NODE_SIZE_LEAF <= pack.leaf_nodes.size());
float4 data[BVH_NODE_SIZE_LEAF];
memset(data, 0, sizeof(data));
if (leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
/* object */
data[0].x = __int_as_float(~(leaf->lo));
data[0].y = __int_as_float(0);
}
else {
/* triangle */
data[0].x = __int_as_float(leaf->lo);
data[0].y = __int_as_float(leaf->hi);
}
data[0].z = __uint_as_float(leaf->visibility);
data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4) * BVH_NODE_SIZE_LEAF);
}
void pack_base_node(const int idx, const BVHStackEntry &c0, const BVHStackEntry &c1, uint visibilityFlag, PackedBVH &pack)
{
assert(idx + BVH_NODE_SIZE_BASE <= pack.nodes.size());
// Clear visibility flags used for node type, and apply relevant ones
uint visibility0 = (c0.node->visibility & ~PATH_RAY_NODE_CLEAR) | visibilityFlag;
uint visibility1 = (c1.node->visibility & ~PATH_RAY_NODE_CLEAR) | visibilityFlag;
int4 data[BVH_NODE_SIZE_BASE] = {
make_int4(visibility0, visibility1, c0.encodeIdx(), c1.encodeIdx()),
make_int4(__float_as_int(c0.node->bounds.min.x),
__float_as_int(c1.node->bounds.min.x),
__float_as_int(c0.node->bounds.max.x),
__float_as_int(c1.node->bounds.max.x)),
make_int4(__float_as_int(c0.node->bounds.min.y),
__float_as_int(c1.node->bounds.min.y),
__float_as_int(c0.node->bounds.max.y),
__float_as_int(c1.node->bounds.max.y)),
make_int4(__float_as_int(c0.node->bounds.min.z),
__float_as_int(c1.node->bounds.min.z),
__float_as_int(c0.node->bounds.max.z),
__float_as_int(c1.node->bounds.max.z)),
};
memcpy(&pack.nodes[idx], data, sizeof(int4) * BVH_NODE_SIZE_BASE);
}
void pack_unaligned_node(const int idx, const BVHStackEntry &c0, const BVHStackEntry &c1, uint visibilityFlag, PackedBVH &pack) {
assert(idx + BVH_NODE_SIZE_BASE + BVH_NODE_SIZE_UNALIGNED <= pack.nodes.size());
// Clear visibility flags used for node type, and apply relevant ones
uint visibility0 = (c0.node->visibility & ~PATH_RAY_NODE_CLEAR) | visibilityFlag;
uint visibility1 = (c1.node->visibility & ~PATH_RAY_NODE_CLEAR) | visibilityFlag;
float4 data[BVH_NODE_SIZE_BASE + BVH_NODE_SIZE_UNALIGNED];
data[0] = make_float4(__int_as_float(visibility0),
__int_as_float(visibility1),
__int_as_float(c0.encodeIdx()),
__int_as_float(c1.encodeIdx()));
Transform bb0 = c0.node->is_unaligned ? *c0.node->aligned_space
: transformSpaceFromBound(c0.node->bounds);
Transform bb1 = c1.node->is_unaligned ? *c1.node->aligned_space
: transformSpaceFromBound(c1.node->bounds);
data[1] = bb0.x;
data[2] = bb0.y;
data[3] = bb0.z;
data[4] = bb1.x;
data[5] = bb1.y;
data[6] = bb1.z;
memcpy(&pack.nodes[idx], data, sizeof(float4) * (BVH_NODE_SIZE_BASE + BVH_NODE_SIZE_UNALIGNED));
}
void pack_4D_node(const int idx, const BVHNode *c0, const BVHNode *c1, PackedBVH &pack)
{
assert(idx + BVH_NODE_SIZE_4D <= pack.nodes.size());
assert(BVH_NODE_SIZE_4D == 1);
pack.nodes[idx] = make_int4(__float_as_int(c0->time_from),
__float_as_int(c1->time_from),
__float_as_int(c0->time_to),
__float_as_int(c1->time_to));
};
void pack_MB_node(const int idx, const BoundBox *bb0, const BoundBox *bb1, PackedBVH &pack)
{
assert(idx + BVH_NODE_SIZE_MOTION_BLUR <= pack.nodes.size());
if(bb0 == nullptr) bb0 = new BoundBox(make_float3(0));
if(bb1 == nullptr) bb1 = new BoundBox(make_float3(0));
int4 data[BVH_NODE_SIZE_MOTION_BLUR] = {
make_int4(__float_as_int(bb0->min.x),
__float_as_int(bb1->min.x),
__float_as_int(bb0->max.x),
__float_as_int(bb1->max.x)),
make_int4(__float_as_int(bb0->min.y),
__float_as_int(bb1->min.y),
__float_as_int(bb0->max.y),
__float_as_int(bb1->max.y)),
make_int4(__float_as_int(bb0->min.z),
__float_as_int(bb1->min.z),
__float_as_int(bb0->max.z),
__float_as_int(bb1->max.z)),
};
memcpy(&pack.nodes[idx], data, sizeof(int4) * BVH_NODE_SIZE_MOTION_BLUR);
}
int getSize(const BVHNode *e) {
int size = BVH_NODE_SIZE_BASE;
if(e->has_time_limited()) size += BVH_NODE_SIZE_4D;
if(e->has_motion_blur()) size += BVH_NODE_SIZE_MOTION_BLUR;
if(e->has_unaligned()) size += BVH_NODE_SIZE_UNALIGNED;
return size;
}
void pack_inner(const BVHStackEntry &e, const BVHStackEntry &c0, const BVHStackEntry &c1, PackedBVH &pack)
{
uint visibility = 0;
if(e.node->has_time_limited())
visibility |= PATH_RAY_NODE_4D;
if(e.node->has_motion_blur())
visibility |= PATH_RAY_NODE_MB;
if(e.node->has_unaligned())
visibility |= PATH_RAY_NODE_UNALIGNED;
int idx = e.idx;
if(e.node->has_unaligned()) {
pack_unaligned_node(idx, c0, c1, visibility, pack);
idx += BVH_NODE_SIZE_BASE + BVH_NODE_SIZE_UNALIGNED;
} else {
pack_base_node(idx, c0, c1, visibility, pack);
idx += BVH_NODE_SIZE_BASE;
}
if(visibility & PATH_RAY_NODE_MB) {
pack_MB_node(idx, c0.node->deltaBounds, c1.node->deltaBounds, pack);
idx += BVH_NODE_SIZE_MOTION_BLUR;
}
if(visibility & PATH_RAY_NODE_4D) {
pack_4D_node(idx, c0.node, c1.node, pack);
idx += BVH_NODE_SIZE_4D;
}
assert(e.idx + getSize(e.node) == idx);
}
void BVHEmbreeConverter::fillPack(PackedBVH &pack) {
pack.prim_visibility.clear();
pack.prim_object.clear();
pack.prim_type.clear();
pack.prim_index.clear();
pack.prim_tri_index.clear();
pack.prim_tri_verts.clear();
this->pack = &pack;
BVHNode *root = this->getBVH2();
if(root == nullptr) {
pack.nodes.clear();
pack.leaf_nodes.resize(1);
pack.leaf_nodes[0].x = 0;
pack.leaf_nodes[0].y = 0;
pack.leaf_nodes[0].z = 0;
pack.leaf_nodes[0].w = 0;
pack.root_index = -1;
return;
}
const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
assert(num_leaf_nodes <= num_nodes);
const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
size_t node_size = num_inner_nodes * BVH_NODE_SIZE_BASE;
// Additional size for unaligned nodes
if (params.use_unaligned_nodes) {
const size_t num_unaligned_nodes = root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
node_size += num_unaligned_nodes * BVH_NODE_SIZE_UNALIGNED;
}
// Additional size for linear bound
node_size += root->getSubtreeSize(BVH_STAT_MOTION_BLURED_NODE_COUNT) * BVH_NODE_SIZE_MOTION_BLUR;
// Additional size for time limits
node_size += root->getSubtreeSize(BVH_STAT_4D_NODE_COUNT) * BVH_NODE_SIZE_4D;
/* Resize arrays */
pack.nodes.clear();
pack.leaf_nodes.clear();
/* For top level BVH, first merge existing BVH's so we know the offsets. */
if (params.top_level) {
pack_instances(node_size, num_leaf_nodes * BVH_NODE_SIZE_LEAF, pack);
}
else {
pack.nodes.resize(node_size);
pack.leaf_nodes.resize(num_leaf_nodes * BVH_NODE_SIZE_LEAF);
}
int nextNodeIdx = 0, nextLeafNodeIdx = 0;
vector<BVHStackEntry> stack;
stack.reserve(BVHParams::MAX_DEPTH * 2);
if (root->is_leaf()) {
stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
}
else {
stack.push_back(BVHStackEntry(root, nextNodeIdx));
nextNodeIdx += getSize(root);
}
while (stack.size()) {
BVHStackEntry e = stack.back();
stack.pop_back();
if (e.node->is_leaf()) {
/* leaf node */
const LeafNode *leaf = reinterpret_cast<const LeafNode *>(e.node);
pack_leaf(e, leaf, pack);
}
else {
assert(e.node->num_children() == 2);
/* inner node */
int idx[2];
for (int i = 0; i < 2; ++i) {
if (e.node->get_child(i)->is_leaf()) {
idx[i] = nextLeafNodeIdx++;
}
else {
idx[i] = nextNodeIdx;
nextNodeIdx += getSize(e.node->get_child(i));
}
}
stack.push_back(BVHStackEntry(e.node->get_child(0), idx[0]));
stack.push_back(BVHStackEntry(e.node->get_child(1), idx[1]));
pack_inner(e, stack[stack.size() - 2], stack[stack.size() - 1], pack);
}
}
assert(node_size == nextNodeIdx);
/* root index to start traversal at, to handle case of single leaf node */
pack.root_index = (root->is_leaf()) ? -1 : 0;
root->deleteSubtree();
}
CCL_NAMESPACE_END
#endif /* WITH_EMBREE */

View File

@@ -0,0 +1,222 @@
/*
* Copyright 2018, Blender Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __BVH_EMBREE_CONVERTER_H__
#define __BVH_EMBREE_CONVERTER_H__
#ifdef WITH_EMBREE
#include <deque>
#include <stack>
#include "render/object.h"
#include <embree3/rtcore_builder.h>
#include "bvh_node.h"
#include "bvh.h"
CCL_NAMESPACE_BEGIN
#define BVH_NODE_SIZE_LEAF 1
#define BVH_NODE_SIZE_BASE 4
#define BVH_NODE_SIZE_4D 1
#define BVH_NODE_SIZE_MOTION_BLUR 3
#define BVH_NODE_SIZE_UNALIGNED 3
/**
* @brief This class implements a converter from Embree internal data structure
* to Blender's internal structure
*/
class BVHEmbreeConverter
{
public:
BVHEmbreeConverter(RTCScene scene,
std::vector<Object *> objects,
const BVHParams &params);
BVHNode *getBVH4();
BVHNode *getBVH2();
void fillPack(PackedBVH &pack);
private:
RTCScene s;
std::vector<Object *> objects;
const BVHParams &params;
PackedBVH *pack;
unsigned int packIdx = 0;
private:
BVHNode* createLeaf(unsigned int nbPrim, const BVHPrimitive prims[]);
BVHNode* createInstance(unsigned int nbPrim, const unsigned int geomID[]);
BVHNode *createCurve(unsigned int nbPrim, const BVHPrimitive prims[]);
BVHNode *createInnerNode(unsigned int nbChild, BVHNode *children[]);
void setAlignedBounds(BVHNode* node, BoundBox bounds);
void setLinearBounds(BVHNode* node, BoundBox bounds, BoundBox deltaBounds);
void setUnalignedBounds(BVHNode* node, Transform affSpace);
void setUnalignedLinearBounds(BVHNode *node, Transform affSpace, BoundBox bounds);
void set_time_bounds(BVHNode* node, float2 time_range);
void pack_instances(size_t nodes_size,
size_t leaf_nodes_size,
PackedBVH &pack);
};
/**
* @brief Merge two BVHNode into a tree that contain both of them
* @param oldRoot A common ancestor of the two nodes
* @param n0 First child
* @param n1 Second child
* @return A node that contains both child
*/
BVHNode *merge(const BVHNode *oldRoot, BVHNode *n0, BVHNode *n1);
/**
* @brief Insert a new primitive in packed data
* @param pack Pack structure
* @param packIdx Index to insert into
* @param object_id Object ID
* @param prim_id Primitive ID
* @param prim_type Primitive type
* @param visibility Visibility flag of the primitive
* @param tri_index Index for the array containing triangle information
*/
void packPush(PackedBVH *pack, const size_t packIdx,
const int object_id, const int prim_id,
const int prim_type, const uint visibility, const uint tri_index);
/**
* @brief Convert embree Bound Box to cycle Bound Box
* @param bound Embree Bound Box
* @return Cycle Bound Box
*/
BoundBox RTCBoundBoxToCCL(const RTCBounds &bound);
/**
* @brief Create unaligned space from aligned bounds
* @param bounds Aligne bounds box
* @return A space in which bounds are [0; 1] on all axis
*/
Transform transformSpaceFromBound(const BoundBox &bounds);
/**
* @brief Convert a list of BVHNode to a BVH2
* Create new nodes if required
* @param nodes A list of nodes to insert in the tree
* @return A BVH2 containing all provided nodes
*/
BVHNode *makeBVHTreeFromList(std::deque<BVHNode *> nodes);
/**
* @brief Convert a tree (up to BVH4) to a BVH2
* Do not copy node, but update child list: thus every pointer to anywhere in
* this tree remain valid
* @param root The source BVH tree
* @return A BVH2 containg at least all node from the source
*/
BVHNode *bvh_shrink(BVHNode *root);
/**
* @brief Copute the size for a node
* @param e The node to get the size
* @return The size used in memory by the node
*/
int getSize(const BVHNode *e);
/**
* @brief Pack a leaf node
* @param e Node entry
* @param leaf Leaf node to insert
* @param pack Pack structure to fill
*/
void pack_leaf(const BVHStackEntry &e,
const LeafNode *leaf,
PackedBVH &pack);
/**
* @brief Pack a generic aligned node
* @param idx Index of the node to pack
* @param c0 First child of the node
* @param c1 Second child of the node
* @param visibilityFlag Visibility mask to apply to both node
* @param pack Pack structure to fill
*
* @see pack_unaligned_node
*/
void pack_base_node(const int idx,
const BVHStackEntry &c0,
const BVHStackEntry &c1,
uint visibilityFlag,
PackedBVH &pack);
/**
* @brief Pack an unaligned node
* @param idx Index of the node to pack
* @param c0 First child of the node
* @param c1 Second child of the node
* @param visibilityFlag Visibility mask to apply to both node
* @param pack Pack structure to fill
*
* @see pack_unaligned_node
*/
void pack_unaligned_node(const int idx,
const BVHStackEntry &c0,
const BVHStackEntry &c1,
uint visibilityFlag,
PackedBVH &pack);
/**
* @brief Pack additional data for 4D node
* @param idx Index to insert data to (should be baseIdx + BVH_NODE_SIZE_BASE)
* @param c0 First child of the node
* @param c1 Second child of the node
* @param pack Pack structure to fill
*/
void pack_4D_node(const int idx,
const BVHNode *c0,
const BVHNode *c1,
PackedBVH &pack);
/**
* @brief Pack additional data for MB node
* Store linear bound box of both children of the node
* @param idx Index to insert data to (should be baseIdx + BVH_NODE_SIZE_BASE)
* @param c0 First child of the node
* @param c1 Second child of the node
* @param pack Pack structure to fill
*/
void pack_MB_node(const int idx,
const BoundBox *bb0,
const BoundBox *bb1,
PackedBVH &pack);
/**
* @brief Pack an inner node
* @param e Root node to store
* @param c0 First child of the node
* @param c1 Second child of the node
* @param pack Pack structure to fill
*/
void pack_inner(const BVHStackEntry &e,
const BVHStackEntry &c0,
const BVHStackEntry &c1,
PackedBVH &pack);
CCL_NAMESPACE_END
#endif /* WITH_EMBREE */
#endif /* __BVH_EMBREE_CONVERTER_H__ */

View File

@@ -91,6 +91,14 @@ int BVHNode::getSubtreeSize(BVH_STAT stat) const
cnt += 1;
}
return cnt;
case BVH_STAT_4D_NODE_COUNT:
if(this->has_time_limited())
cnt = 1;
break;
case BVH_STAT_MOTION_BLURED_NODE_COUNT:
if(this->has_motion_blur())
cnt = 1;
break;
default:
assert(0); /* unknown mode */
}
@@ -118,7 +126,7 @@ float BVHNode::computeSubtreeSAHCost(const BVHParams &p, float probability) cons
for (int i = 0; i < num_children(); i++) {
BVHNode *child = get_child(i);
SAH += child->computeSubtreeSAHCost(
p, probability * child->bounds.safe_area() / bounds.safe_area());
p, probability * child->bounds.safe_area() / bounds.safe_area() * (child->time_to - child->time_from) / (time_to - time_from));
}
return SAH;

View File

@@ -36,6 +36,8 @@ enum BVH_STAT {
BVH_STAT_ALIGNED_LEAF_COUNT,
BVH_STAT_UNALIGNED_LEAF_COUNT,
BVH_STAT_DEPTH,
BVH_STAT_4D_NODE_COUNT,
BVH_STAT_MOTION_BLURED_NODE_COUNT,
};
class BVHParams;
@@ -45,6 +47,7 @@ class BVHNode {
virtual ~BVHNode()
{
delete aligned_space;
delete deltaBounds;
}
virtual bool is_leaf() const = 0;
@@ -88,6 +91,40 @@ class BVHNode {
return false;
}
inline bool is_time_limited() const {
return this->time_from > 0 || this->time_to < 1;
}
inline bool has_time_limited() const
{
if (is_leaf()) {
return false;
}
for (int i = 0; i < num_children(); ++i) {
if (get_child(i)->is_time_limited()) {
return true;
}
}
return false;
}
inline bool is_motion_blur() const {
return this->deltaBounds != nullptr;
}
inline bool has_motion_blur() const
{
if (is_leaf()) {
return false;
}
for (int i = 0; i < num_children(); ++i) {
if (get_child(i)->is_motion_blur()) {
return true;
}
}
return false;
}
// Subtree functions
int getSubtreeSize(BVH_STAT stat = BVH_STAT_NODE_COUNT) const;
float computeSubtreeSAHCost(const BVHParams &p, float probability = 1.0f) const;
@@ -110,6 +147,8 @@ class BVHNode {
*/
Transform *aligned_space;
BoundBox *deltaBounds;
float time_from, time_to;
protected:
@@ -118,6 +157,7 @@ class BVHNode {
visibility(0),
is_unaligned(false),
aligned_space(NULL),
deltaBounds(NULL),
time_from(0.0f),
time_to(1.0f)
{
@@ -128,6 +168,7 @@ class BVHNode {
visibility(other.visibility),
is_unaligned(other.is_unaligned),
aligned_space(NULL),
deltaBounds(NULL),
time_from(other.time_from),
time_to(other.time_to)
{

View File

@@ -338,7 +338,7 @@ class CPUDevice : public Device {
}
#endif
#ifdef WITH_EMBREE
bvh_layout_mask |= BVH_LAYOUT_EMBREE;
bvh_layout_mask |= BVH_LAYOUT_EMBREE | BVH_LAYOUT_EMBREE_CONVERTED;
#endif /* WITH_EMBREE */
return bvh_layout_mask;
}

View File

@@ -185,7 +185,7 @@ class CUDADevice : public Device {
virtual BVHLayoutMask get_bvh_layout_mask() const
{
return BVH_LAYOUT_BVH2;
return BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE_CONVERTED;
}
/*#ifdef NDEBUG

View File

@@ -127,6 +127,7 @@ ccl_device_inline
isect_t,
node_addr,
PATH_RAY_ALL_VISIBILITY,
ray->time,
dist);
#else // __KERNEL_SSE2__
traverse_mask = NODE_INTERSECT(kg,
@@ -142,6 +143,7 @@ ccl_device_inline
shufflexyz,
node_addr,
PATH_RAY_ALL_VISIBILITY,
ray->time,
dist);
#endif // __KERNEL_SSE2__

View File

@@ -33,39 +33,69 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg,
const float3 P,
const float3 idir,
const float t,
const int node_addr,
int node_addr,
const uint visibility,
const float rayTime,
float dist[2])
{
/* fetch node data */
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
float4 node0 = kernel_tex_fetch(__bvh_nodes, node_addr + 1);
float4 node1 = kernel_tex_fetch(__bvh_nodes, node_addr + 2);
float4 node2 = kernel_tex_fetch(__bvh_nodes, node_addr + 3);
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr++);
float4 node0 = kernel_tex_fetch(__bvh_nodes, node_addr++);
float4 node1 = kernel_tex_fetch(__bvh_nodes, node_addr++);
float4 node2 = kernel_tex_fetch(__bvh_nodes, node_addr++);
float3 c0lo = make_float3(node0.x, node1.x, node2.x);
float3 c0hi = make_float3(node0.z, node1.z, node2.z);
float3 c1lo = make_float3(node0.y, node1.y, node2.y);
float3 c1hi = make_float3(node0.w, node1.w, node2.w);
if (__float_as_int(cnodes.x) & PATH_RAY_NODE_MB) {
float4 node3 = kernel_tex_fetch(__bvh_nodes, node_addr++);
float4 node4 = kernel_tex_fetch(__bvh_nodes, node_addr++);
float4 node5 = kernel_tex_fetch(__bvh_nodes, node_addr++);
float3 c0dlo = make_float3(node3.x, node4.x, node5.x);
float3 c0dhi = make_float3(node3.z, node4.z, node5.z);
float3 c1dlo = make_float3(node3.y, node4.y, node5.y);
float3 c1dhi = make_float3(node3.w, node4.w, node5.w);
c0lo += rayTime * c0dlo;
c0hi += rayTime * c0dhi;
c1lo += rayTime * c1dlo;
c1hi += rayTime * c1dhi;
}
/* 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 c0lox = (c0lo.x - P.x) * idir.x;
float c0hix = (c0hi.x - P.x) * idir.x;
float c0loy = (c0lo.y - P.y) * idir.y;
float c0hiy = (c0hi.y - P.y) * idir.y;
float c0loz = (c0lo.z - P.z) * idir.z;
float c0hiz = (c0hi.z - P.z) * idir.z;
float c0min = max4(0.0f, min(c0lox, c0hix), min(c0loy, c0hiy), min(c0loz, c0hiz));
float c0max = min4(t, 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 c1lox = (c1lo.x - P.x) * idir.x;
float c1hix = (c1hi.x - P.x) * idir.x;
float c1loy = (c1lo.y - P.y) * idir.y;
float c1hiy = (c1hi.y - P.y) * idir.y;
float c1loz = (c1lo.z - P.z) * idir.z;
float c1hiz = (c1hi.z - P.z) * idir.z;
float c1min = max4(0.0f, min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz));
float c1max = min4(t, max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz));
dist[0] = c0min;
dist[1] = c1min;
if (__float_as_int(cnodes.x) & PATH_RAY_NODE_4D) {
float4 timeLimits = kernel_tex_fetch(__bvh_nodes, node_addr++);
if(timeLimits.x > rayTime || timeLimits.z < rayTime)
c0max = -1.0f;
if(timeLimits.y > rayTime || timeLimits.w < rayTime)
c1max = -1.0f;
}
# 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) |
@@ -81,14 +111,38 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals *kg
const float t,
int node_addr,
int child,
const float rayTime,
float dist[2])
{
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr);
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;
float3 nrdir = bvh_inverse_direction(aligned_dir);
float3 lower_xyz;
float3 upper_xyz;
if (__float_as_int(cnodes.x) & PATH_RAY_NODE_MB) {
float4 node3 = kernel_tex_fetch(__bvh_nodes, node_addr + 7);
float4 node4 = kernel_tex_fetch(__bvh_nodes, node_addr + 8);
float4 node5 = kernel_tex_fetch(__bvh_nodes, node_addr + 9);
float3 c0dlo = make_float3(node3.x, node4.x, node5.x);
float3 c0dhi = make_float3(node3.z, node4.z, node5.z);
float3 c1dlo = make_float3(node3.y, node4.y, node5.y);
float3 c1dhi = make_float3(node3.w, node4.w, node5.w);
float3 lowBounds = ((child == 0) ? c0dlo : c1dlo) * rayTime,
uppBounds = make_float3(1, 1, 1) + rayTime * (child == 0 ? c0dhi : c1dhi);
lower_xyz = (lowBounds - aligned_P) * nrdir;
upper_xyz = (uppBounds - aligned_P) * nrdir;
} else {
lower_xyz = aligned_P * -nrdir;
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);
@@ -108,11 +162,12 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg,
const float t,
const int node_addr,
const uint visibility,
const float rayTime,
float dist[2])
{
int mask = 0;
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 0, &dist[0])) {
if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 0, rayTime, &dist[0])) {
# ifdef __VISIBILITY_FLAG__
if ((__float_as_uint(cnodes.x) & visibility))
# endif
@@ -120,7 +175,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg,
mask |= 1;
}
}
if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 1, &dist[1])) {
if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 1, rayTime, &dist[1])) {
# ifdef __VISIBILITY_FLAG__
if ((__float_as_uint(cnodes.y) & visibility))
# endif
@@ -138,14 +193,15 @@ ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg,
const float t,
const int node_addr,
const uint visibility,
const float rayTime,
float dist[2])
{
float4 node = kernel_tex_fetch(__bvh_nodes, node_addr);
if (__float_as_uint(node.x) & PATH_RAY_NODE_UNALIGNED) {
return bvh_unaligned_node_intersect(kg, P, dir, idir, t, node_addr, visibility, dist);
return bvh_unaligned_node_intersect(kg, P, dir, idir, t, node_addr, visibility, rayTime, dist);
}
else {
return bvh_aligned_node_intersect(kg, P, idir, t, node_addr, visibility, dist);
return bvh_aligned_node_intersect(kg, P, idir, t, node_addr, visibility, rayTime, dist);
}
}
@@ -158,34 +214,59 @@ int ccl_device_forceinline bvh_aligned_node_intersect(KernelGlobals *kg,
const ssef Psplat[3],
const ssef idirsplat[3],
const shuffle_swap_t shufflexyz[3],
const int node_addr,
int node_addr,
const uint visibility,
const float rayTime,
float dist[2])
{
/* Intersect two child bounding boxes, SSE3 version adapted from Embree */
const ssef pn = cast(ssei(0, 0, 0x80000000, 0x80000000));
/* fetch node data */
const ssef *bvh_nodes = (ssef *)kg->__bvh_nodes.data + node_addr;
const ssef *bvh_nodes = (ssef *)kg->__bvh_nodes.data;
/* intersect ray against child nodes */
const ssef tminmaxx = (shuffle_swap(bvh_nodes[1], shufflexyz[0]) - Psplat[0]) * idirsplat[0];
const ssef tminmaxy = (shuffle_swap(bvh_nodes[2], shufflexyz[1]) - Psplat[1]) * idirsplat[1];
const ssef tminmaxz = (shuffle_swap(bvh_nodes[3], shufflexyz[2]) - Psplat[2]) * idirsplat[2];
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr++);
ssef x = bvh_nodes[node_addr++];
ssef y = bvh_nodes[node_addr++];
ssef z = bvh_nodes[node_addr++];
if (__float_as_uint(cnodes.x) & PATH_RAY_NODE_MB) {
const ssef dx = bvh_nodes[node_addr++];
const ssef dy = bvh_nodes[node_addr++];
const ssef dz = bvh_nodes[node_addr++];
x += rayTime * dx;
y += rayTime * dy;
z += rayTime * dz;
}
/* intersect ray against child nodes */
const ssef tminmaxx = (shuffle_swap(x, shufflexyz[0]) - Psplat[0]) * idirsplat[0];
const ssef tminmaxy = (shuffle_swap(y, shufflexyz[1]) - Psplat[1]) * idirsplat[1];
const ssef tminmaxz = (shuffle_swap(z, shufflexyz[2]) - Psplat[2]) * idirsplat[2];
/* calculate { c0min, c1min, -c0max, -c1max} */
ssef minmax = max(max(tminmaxx, tminmaxy), max(tminmaxz, tsplat));
const ssef tminmax = minmax ^ pn;
const sseb lrhit = tminmax <= shuffle<2, 3, 0, 1>(tminmax);
sseb lrhit = tminmax <= shuffle<2, 3, 0, 1>(tminmax);
dist[0] = tminmax[0];
dist[1] = tminmax[1];
if (__float_as_uint(cnodes.x) & PATH_RAY_NODE_4D) {
const ssef timeLimit = bvh_nodes[node_addr++];
const sseb timeMin = timeLimit < rayTime;
const sseb timeMax = timeLimit > rayTime;
lrhit &= timeMin & shuffle<2, 3, 0, 1>(timeMax);
}
int mask = movemask(lrhit);
# ifdef __VISIBILITY_FLAG__
/* this visibility test gives a 5% performance hit, how to solve? */
float4 cnodes = kernel_tex_fetch(__bvh_nodes, node_addr + 0);
int cmask = (((mask & 1) && (__float_as_uint(cnodes.x) & visibility)) ? 1 : 0) |
(((mask & 2) && (__float_as_uint(cnodes.y) & visibility)) ? 2 : 0);
return cmask;
@@ -257,6 +338,7 @@ ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg,
const shuffle_swap_t shufflexyz[3],
const int node_addr,
const uint visibility,
const float rayTime,
float dist[2])
{
float4 node = kernel_tex_fetch(__bvh_nodes, node_addr);
@@ -266,7 +348,7 @@ ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg,
}
else {
return bvh_aligned_node_intersect(
kg, P, dir, tsplat, Psplat, idirsplat, shufflexyz, node_addr, visibility, dist);
kg, P, dir, tsplat, Psplat, idirsplat, shufflexyz, node_addr, visibility, rayTime, dist);
}
}
#endif /* !defined(__KERNEL_SSE2__) */

View File

@@ -122,6 +122,7 @@ ccl_device_inline
isect_t,
node_addr,
visibility,
ray->time,
dist);
#else // __KERNEL_SSE2__
traverse_mask = NODE_INTERSECT(kg,
@@ -137,6 +138,7 @@ ccl_device_inline
shufflexyz,
node_addr,
visibility,
ray->time,
dist);
#endif // __KERNEL_SSE2__

View File

@@ -117,6 +117,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
isect->t,
node_addr,
visibility,
ray->time,
dist);
}
#else // __KERNEL_SSE2__
@@ -134,6 +135,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
shufflexyz,
node_addr,
visibility,
ray->time,
dist);
}
#endif // __KERNEL_SSE2__

View File

@@ -118,6 +118,7 @@ ccl_device_inline
isect->t,
node_addr,
visibility,
ray->time,
dist);
#else // __KERNEL_SSE2__
traverse_mask = NODE_INTERSECT(kg,
@@ -133,6 +134,7 @@ ccl_device_inline
shufflexyz,
node_addr,
visibility,
ray->time,
dist);
#endif // __KERNEL_SSE2__

View File

@@ -122,6 +122,7 @@ ccl_device_inline
isect_t,
node_addr,
visibility,
ray->time,
dist);
#else // __KERNEL_SSE2__
traverse_mask = NODE_INTERSECT(kg,
@@ -137,6 +138,7 @@ ccl_device_inline
shufflexyz,
node_addr,
visibility,
ray->time,
dist);
#endif // __KERNEL_SSE2__

View File

@@ -319,7 +319,15 @@ enum PathRayFlag {
/* Ray is to be terminated. */
PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_IMMEDIATE | PATH_RAY_TERMINATE_AFTER_TRANSPARENT),
/* Path and shader is being evaluated for direct lighting emission. */
PATH_RAY_EMISSION = (1 << 22)
PATH_RAY_EMISSION = (1 << 22),
/* Special flag to tag 4D BVH nodes (they also carry time bounds). */
PATH_RAY_NODE_4D = (1 << 23),
/* Special flag to tag nodes that can be linearly interpolated */
PATH_RAY_NODE_MB = (1 << 24),
PATH_RAY_NODE_CLEAR = (PATH_RAY_NODE_4D | PATH_RAY_NODE_UNALIGNED | PATH_RAY_NODE_MB),
};
/* Closure Label */
@@ -1338,6 +1346,7 @@ typedef enum KernelBVHLayout {
BVH_LAYOUT_BVH4 = (1 << 1),
BVH_LAYOUT_BVH8 = (1 << 2),
BVH_LAYOUT_EMBREE = (1 << 3),
BVH_LAYOUT_EMBREE_CONVERTED = (1 << 4),
BVH_LAYOUT_DEFAULT = BVH_LAYOUT_BVH8,
BVH_LAYOUT_ALL = (unsigned int)(-1),
} KernelBVHLayout;

View File

@@ -1995,6 +1995,10 @@ void MeshManager::device_update_bvh(Device *device,
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
#ifdef WITH_EMBREE
if(bparams.bvh_layout == BVH_LAYOUT_EMBREE_CONVERTED) {
dscene->data.bvh.bvh_layout = BVH_LAYOUT_BVH2;
}
if (bparams.bvh_layout == BVH_LAYOUT_EMBREE) {
dscene->data.bvh.scene = ((BVHEmbree *)bvh)->scene;
}