230 lines
8.1 KiB
C++
230 lines
8.1 KiB
C++
// Copyright 2009-2021 Intel Corporation
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#pragma once
|
|
|
|
#include "qnode.h"
|
|
#include "statistics.h"
|
|
#include "rtbuild.h"
|
|
|
|
namespace embree
|
|
{
|
|
/*
|
|
|
|
The QBVH6 structure defines the bounding volume hierarchy (BVH)
|
|
that is used by the hardware. It is a BVH with 6-wide branching
|
|
factor, and quantized bounding boxes. At the leaf level quads
|
|
(QuadLeaf type), procedural geometries (ProceduralLeaf
|
|
type), and instances (InstanceLeaf type) can get referenced.
|
|
|
|
*/
|
|
|
|
inline constexpr size_t roundOffsetTo128(size_t offset) {
|
|
return 2 * ((offset + 127) / 128);
|
|
}
|
|
|
|
struct QBVH6
|
|
{
|
|
typedef NodeRef Node;
|
|
typedef InternalNode<InternalNode6Data> InternalNode6;
|
|
|
|
static constexpr uint64_t rootNodeOffset = 128;
|
|
|
|
static_assert(sizeof(InternalNode6) == 64, "InternalNode6 must be 64 bytes large");
|
|
|
|
/* structure used to initialize the memory allocator inside the BVH */
|
|
struct SizeEstimate
|
|
{
|
|
SizeEstimate ()
|
|
: nodeBytes(0), leafBytes(0), proceduralBytes(0) {}
|
|
|
|
SizeEstimate (size_t nodeBytes, size_t leafBytes, size_t proceduralBytes)
|
|
: nodeBytes(nodeBytes), leafBytes(leafBytes), proceduralBytes(proceduralBytes) {}
|
|
|
|
size_t bytes() const {
|
|
return sizeof(QBVH6) + nodeBytes + leafBytes + proceduralBytes;
|
|
}
|
|
|
|
friend bool operator<= (SizeEstimate a, SizeEstimate b)
|
|
{
|
|
if (a.nodeBytes > b.nodeBytes) return false;
|
|
if (a.leafBytes > b.leafBytes) return false;
|
|
if (a.proceduralBytes > b.proceduralBytes) return false;
|
|
return true;
|
|
}
|
|
|
|
friend SizeEstimate operator+ (const SizeEstimate& a, const SizeEstimate& b)
|
|
{
|
|
return SizeEstimate(a.nodeBytes + b.nodeBytes,
|
|
a.leafBytes + b.leafBytes,
|
|
a.proceduralBytes + b.proceduralBytes);
|
|
}
|
|
|
|
/* output operator */
|
|
friend inline std::ostream& operator<<(std::ostream& cout, const SizeEstimate& estimate)
|
|
{
|
|
cout << "SizeEstimate {" << std::endl;
|
|
cout << " nodeBytes = " << estimate.nodeBytes << ", " << std::endl;
|
|
cout << " leafBytes = " << estimate.leafBytes << ", " << std::endl;
|
|
cout << " proceduralBytes = " << estimate.proceduralBytes << ", " << std::endl;
|
|
return cout << "}";
|
|
}
|
|
|
|
public:
|
|
size_t nodeBytes; // bytes required to store internal nodes
|
|
size_t leafBytes; // bytes required to store leaf nodes
|
|
size_t proceduralBytes; // bytes required to store procedural leaf nodes
|
|
};
|
|
|
|
/* Initializes a QBVH6 node with its provided size. The memory for
|
|
* the QBVH6 structure is overallocated and the allocation size is
|
|
* provided to the constructor, such that the allocator of the BVH
|
|
* can get initialized properly. */
|
|
|
|
QBVH6(SizeEstimate size)
|
|
: nodeDataStart((uint32_t)roundOffsetTo128(sizeof(QBVH6))), nodeDataCur(nodeDataStart),
|
|
leafDataStart(nodeDataCur + (uint32_t)(size.nodeBytes / 64)), leafDataCur(leafDataStart),
|
|
proceduralDataStart(leafDataCur + (uint32_t)(size.leafBytes / 64)), proceduralDataCur(proceduralDataStart),
|
|
backPointerDataStart(proceduralDataCur + (uint32_t)(size.proceduralBytes/64)), backPointerDataEnd(backPointerDataStart)
|
|
{
|
|
assert(size.nodeBytes % 64 == 0);
|
|
assert(size.leafBytes % 64 == 0);
|
|
assert(size.proceduralBytes % 64 == 0);
|
|
assert(size.bytes() <= (64LL << 32));
|
|
|
|
bounds = embree::empty;
|
|
}
|
|
|
|
/* Returns the root node of the BVH */
|
|
Node root() const {
|
|
return Node(rootNodeOffset,(uint64_t)this);
|
|
}
|
|
|
|
/* sets root not offset to point to this specified node */
|
|
void setRootNodeOffset(Node node) {
|
|
assert(node.cur_prim == 0);
|
|
uint64_t MAYBE_UNUSED rootNodeOffset1 = (uint64_t)node - (uint64_t)this;
|
|
assert(rootNodeOffset == rootNodeOffset1);
|
|
}
|
|
|
|
/* check if BVH is empty */
|
|
bool empty() const {
|
|
return root().type == NODE_TYPE_INVALID;
|
|
}
|
|
|
|
/* pretty printing */
|
|
template<typename QInternalNode>
|
|
static void printInternalNodeStatistics(std::ostream& cout, QBVH6::Node node, uint32_t depth, uint32_t numChildren = 6);
|
|
static void print(std::ostream& cout, QBVH6::Node node, uint32_t depth, uint32_t numChildren=6);
|
|
void print(std::ostream& cout = std::cout) const;
|
|
|
|
/* output operator */
|
|
friend inline std::ostream& operator<<(std::ostream& cout, const QBVH6& qbvh) {
|
|
qbvh.print(cout); return cout;
|
|
}
|
|
|
|
/* calculates BVH statistics */
|
|
BVHStatistics computeStatistics() const;
|
|
|
|
/*
|
|
This section implements a simple allocator for BVH data. The
|
|
BVH data is separated into two section, a section where nodes
|
|
and leaves in mixed mode are allocated, and a section where
|
|
only leaves are allocate in fat-leaf mode.
|
|
|
|
*/
|
|
public:
|
|
|
|
/* allocate data in the node memory section */
|
|
char* allocNode(size_t bytes)
|
|
{
|
|
assert(bytes % 64 == 0);
|
|
uint32_t blocks = (uint32_t)bytes / 64;
|
|
assert(nodeDataCur + blocks <= leafDataStart);
|
|
char* ptr = (char*)this + 64 * (size_t)nodeDataCur;
|
|
nodeDataCur += blocks;
|
|
return ptr;
|
|
}
|
|
|
|
/* allocate memory in the leaf memory section */
|
|
char* allocLeaf(size_t bytes)
|
|
{
|
|
assert(bytes % 64 == 0);
|
|
uint32_t blocks = (uint32_t)bytes / 64;
|
|
assert(leafDataCur + blocks <= proceduralDataStart);
|
|
char* ptr = (char*)this + 64 * (size_t)leafDataCur;
|
|
leafDataCur += blocks;
|
|
return ptr;
|
|
}
|
|
|
|
/* allocate memory in procedural leaf memory section */
|
|
char* allocProceduralLeaf(size_t bytes)
|
|
{
|
|
assert(bytes % 64 == 0);
|
|
uint32_t blocks = (uint32_t)bytes / 64;
|
|
assert(proceduralDataCur + blocks <= backPointerDataStart);
|
|
char* ptr = (char*)this + 64 * (size_t)proceduralDataCur;
|
|
proceduralDataCur += blocks;
|
|
return ptr;
|
|
}
|
|
|
|
/* returns pointer to node address */
|
|
char* nodePtr(size_t ofs) {
|
|
return (char*)this + 64 * size_t(nodeDataStart) + ofs;
|
|
}
|
|
/* returns pointer to address for next leaf allocation */
|
|
char* leafPtr() {
|
|
return (char*)this + 64 * (size_t)leafDataCur;
|
|
}
|
|
|
|
/* returns the total number of bytes of the BVH */
|
|
size_t getTotalBytes() const {
|
|
return 64 * (size_t)backPointerDataEnd;
|
|
}
|
|
|
|
/* returns number of bytes available for node allocations */
|
|
size_t getFreeNodeBytes() const {
|
|
return 64 * (size_t)(leafDataStart - nodeDataCur);
|
|
}
|
|
|
|
/* returns number of bytes available for leaf allocations */
|
|
size_t getFreeLeafBytes() const {
|
|
return 64 * (size_t)(proceduralDataStart - leafDataCur);
|
|
}
|
|
|
|
/* returns number of bytes available for procedural leaf allocations */
|
|
size_t getFreeProceduralLeafBytes() const {
|
|
return 64 * (size_t)(backPointerDataStart - proceduralDataCur);
|
|
}
|
|
|
|
/* returns the bytes used by allocations */
|
|
size_t getUsedBytes() const {
|
|
return getTotalBytes() - getFreeNodeBytes() - getFreeLeafBytes() - getFreeProceduralLeafBytes();
|
|
}
|
|
|
|
bool hasBackPointers() const {
|
|
return backPointerDataStart < backPointerDataEnd;
|
|
}
|
|
|
|
public:
|
|
ze_raytracing_accel_format_internal_t rtas_format = ZE_RTAS_DEVICE_FORMAT_EXP_VERSION_1;
|
|
uint32_t reserved1;
|
|
BBox3f bounds; // bounding box of the BVH
|
|
|
|
uint32_t nodeDataStart; // first 64 byte block of node data
|
|
uint32_t nodeDataCur; // next free 64 byte block for node allocations
|
|
uint32_t leafDataStart; // first 64 byte block of leaf data
|
|
uint32_t leafDataCur; // next free 64 byte block for leaf allocations
|
|
uint32_t proceduralDataStart; // first 64 byte block for procedural leaf data
|
|
uint32_t proceduralDataCur; // next free 64 byte block for procedural leaf allocations
|
|
uint32_t backPointerDataStart; // first 64 byte block for back pointers
|
|
uint32_t backPointerDataEnd; // end of back pointer array
|
|
uint32_t numTimeSegments = 1;
|
|
uint32_t numPrims = 0; // number of primitives in this BVH
|
|
uint32_t reserved[12];
|
|
uint64_t dispatchGlobalsPtr;
|
|
};
|
|
|
|
static_assert(sizeof(QBVH6) == 128, "QBVH6 must be 128 bytes large");
|
|
}
|
|
|