Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion kernels/builders/bvh_builder_hair.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ namespace embree
createLeaf(createLeaf),
progressMonitor(progressMonitor),
reportFinishedRange(reportFinishedRange),
alignedHeuristic(prims), unalignedHeuristic(scene,prims), strandHeuristic(scene,prims) {}
alignedHeuristic(prims), unalignedHeuristic(scene,prims), strandHeuristic(scene,prims)
{
if (cfg.branchingFactor > MAX_BRANCHING_FACTOR) {
throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large");
}
}

/*! checks if all primitives are from the same geometry */
__forceinline bool sameGeometry(const PrimInfoRange& range)
Expand Down
17 changes: 14 additions & 3 deletions kernels/builders/bvh_builder_morton.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ namespace embree
if (RTC_BUILD_ARGUMENTS_HAS(settings,minLeafSize )) minLeafSize = settings.minLeafSize;
if (RTC_BUILD_ARGUMENTS_HAS(settings,maxLeafSize )) maxLeafSize = settings.maxLeafSize;

if (branchingFactor > MAX_BRANCHING_FACTOR) {
throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large");
}

minLeafSize = min(minLeafSize,maxLeafSize);
}

Settings (size_t branchingFactor, size_t maxDepth, size_t minLeafSize, size_t maxLeafSize, size_t singleThreadThreshold)
: branchingFactor(branchingFactor), maxDepth(maxDepth), minLeafSize(minLeafSize), maxLeafSize(maxLeafSize), singleThreadThreshold(singleThreadThreshold)
Settings (size_t branchingFactor_, size_t maxDepth_, size_t minLeafSize_, size_t maxLeafSize_, size_t singleThreadThreshold_)
: branchingFactor(branchingFactor_), maxDepth(maxDepth_), minLeafSize(minLeafSize_), maxLeafSize(maxLeafSize_), singleThreadThreshold(singleThreadThreshold_)
{
if (branchingFactor > MAX_BRANCHING_FACTOR) {
throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large");
}
minLeafSize = min(minLeafSize,maxLeafSize);
Comment thread
johguenther marked this conversation as resolved.
}

Expand Down Expand Up @@ -203,7 +210,11 @@ namespace embree
createLeaf(createLeaf),
calculateBounds(calculateBounds),
progressMonitor(progressMonitor),
morton(nullptr) {}
morton(nullptr)
{
if (branchingFactor > MAX_BRANCHING_FACTOR)
throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large");
}

ReductionTy createLargeLeaf(size_t depth, const range<unsigned>& current, Allocator alloc)
{
Expand Down
7 changes: 6 additions & 1 deletion kernels/builders/bvh_builder_msmblur_hair.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ namespace embree
createLeaf(createLeaf),
progressMonitor(progressMonitor),
unalignedHeuristic(scene),
temporalSplitHeuristic(scene->device,recalculatePrimRef) {}
temporalSplitHeuristic(scene->device,recalculatePrimRef)
{
if (cfg.branchingFactor > MAX_BRANCHING_FACTOR) {
throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large");
}
}

private:

Expand Down
25 changes: 25 additions & 0 deletions kernels/common/rtcore_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,31 @@ RTC_NAMESPACE_BEGIN
if (arguments->primitiveArrayCapacity < arguments->primitiveCount)
throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"primitiveArrayCapacity must be greater or equal to primitiveCount")

if (RTC_BUILD_ARGUMENTS_HAS((*arguments),maxLeafSize) && arguments->maxLeafSize > RTC_BUILD_MAX_PRIMITIVES_PER_LEAF) {
throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"maxLeafSize must be smaller or equal to RTC_BUILD_MAX_PRIMITIVES_PER_LEAF")
}

if (RTC_BUILD_ARGUMENTS_HAS((*arguments),maxBranchingFactor))
{
const unsigned int branchingFactor = arguments->maxBranchingFactor;
if (branchingFactor < 2) {
throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"maxBranchingFactor must be greater or equal to 2");
}

if (arguments->buildQuality == RTC_BUILD_QUALITY_LOW)
{
if (branchingFactor > BVHBuilderMorton::MAX_BRANCHING_FACTOR) {
throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"maxBranchingFactor too large for RTC_BUILD_QUALITY_LOW (maximum is 8)")
}
}
else if (arguments->buildQuality == RTC_BUILD_QUALITY_MEDIUM || arguments->buildQuality == RTC_BUILD_QUALITY_HIGH)
{
if (branchingFactor > GeneralBVHBuilder::MAX_BRANCHING_FACTOR) {
throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"maxBranchingFactor too large for this build quality (maximum is 16)")
}
}
}

/* initialize the allocator */
bvh->allocator.init_estimate(arguments->primitiveCount*sizeof(BBox3fa));
bvh->allocator.reset();
Expand Down
1 change: 0 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ FOREACH(xml ${PRIMITIVE_TESTS})
ENDFOREACH()
ENDFOREACH()


IF (EMBREE_TESTING_INSTALL_TESTS)
# test resources
INSTALL(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/primitives" DESTINATION "${CMAKE_INSTALL_TESTDIR}/tests" COMPONENT testing PATTERN "*.py" EXCLUDE)
Expand Down
1 change: 0 additions & 1 deletion tests/integration/test_embree_release/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,3 @@ TEST_CASE("Minimal test", "[minimal]")

REQUIRE(true);
}

1 change: 1 addition & 0 deletions tutorials/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ ADD_SUBDIRECTORY(ray_mask)
ADD_SUBDIRECTORY(forest)
ADD_SUBDIRECTORY(host_device_memory)
ADD_SUBDIRECTORY(embree_tests)
ADD_SUBDIRECTORY(embree_regression_tests)


ENDIF()
12 changes: 12 additions & 0 deletions tutorials/embree_regression_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Copyright 2009-2021 Intel Corporation
## SPDX-License-Identifier: Apache-2.0

ADD_EXECUTABLE(embree_regression_tests ../../kernels/embree.rc embree_regression_tests.cpp)
TARGET_LINK_LIBRARIES(embree_regression_tests embree)
SET_PROPERTY(TARGET embree_regression_tests PROPERTY FOLDER tutorials)
SET_PROPERTY(TARGET embree_regression_tests APPEND PROPERTY COMPILE_FLAGS " ${FLAGS_LOWEST}")
INSTALL(TARGETS embree_regression_tests DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT examples)
SIGN_TARGET(embree_regression_tests)

ADD_EMBREE_TEST_ECS(embree_regression_tests embree_regression_tests NO_REFERENCE NO_ISPC NO_SYCL)
SET_EMBREE_TEST_PROPERTIES(embree_regression_tests PROPERTIES TIMEOUT 7000)
198 changes: 198 additions & 0 deletions tutorials/embree_regression_tests/embree_regression_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#include <embree4/rtcore.h>
#include <embree4/rtcore_builder.h>

#include <cassert>
#include <cstdio>
#include <limits>
#include <string>
#include <vector>

#if defined(RTC_NAMESPACE_USE)
RTC_NAMESPACE_USE
#endif

namespace
{
constexpr unsigned int max_branching_factor = 8;

struct CaseResult
{
bool pass;
bool skip;
std::string message;
};

struct Node
{
Node()
{
for (unsigned int i = 0; i < max_branching_factor; ++i)
children[i] = nullptr;
}

virtual ~Node() = default;
Node* children[max_branching_factor];
};

static CaseResult passResult(const char* msg)
{
return { true, false, msg };
}

static CaseResult failResult(const char* msg)
{
return { false, false, msg };
}

static bool buildProgress(void* /*userPtr*/, double /*f*/)
{
return true;
}

static void* createNode(RTCThreadLocalAllocator alloc, unsigned int childCount, void* /*userPtr*/)
{
assert(childCount <= max_branching_factor);
if (childCount > max_branching_factor)
return nullptr;

Node* node = (Node*)rtcThreadLocalAlloc(alloc, sizeof(Node), 16);
new (node) Node();
return node;
}

static void setNodeChildren(void* nodePtr, void** children, unsigned int childCount, void* /*userPtr*/)
{
assert(childCount <= max_branching_factor);
if (childCount > max_branching_factor)
return;

Node* node = (Node*)nodePtr;
for (unsigned int i = 0; i < childCount; ++i)
node->children[i] = (Node*)children[i];
}

static void setNodeBounds(void* /*nodePtr*/, const RTCBounds** /*bounds*/, unsigned int childCount, void* /*userPtr*/)
{
assert(childCount <= max_branching_factor);
}

static void* createLeaf(RTCThreadLocalAllocator alloc,
const RTCBuildPrimitive* /*prims*/,
size_t /*primCount*/,
void* /*userPtr*/)
{
Node* node = (Node*)rtcThreadLocalAlloc(alloc, sizeof(Node), 16);
new (node) Node();
return node;
}

static std::vector<RTCBuildPrimitive> makeGridPrimitives(size_t primitiveCount)
{
std::vector<RTCBuildPrimitive> prims(primitiveCount);
for (size_t i = 0; i < primitiveCount; ++i)
{
const float x = float(i % 32);
const float y = float((i / 32) % 32);

RTCBuildPrimitive& p = prims[i];
p = {};
p.lower_x = x * 2.0f;
p.lower_y = y * 2.0f;
p.lower_z = 0.0f;
p.upper_x = p.lower_x + 0.5f;
p.upper_y = p.lower_y + 0.5f;
p.upper_z = 0.5f;
p.geomID = 0;
p.primID = (unsigned int)i;
}
return prims;
}

static bool morton_builder_rejects_oversized_branching_factor(RTCDevice device, unsigned int maxBranchingFactor)
{
RTCBVH bvh = rtcNewBVH(device);
if (!bvh)
return false;

std::vector<RTCBuildPrimitive> prims = makeGridPrimitives(1024);

RTCBuildArguments args = rtcDefaultBuildArguments();
args.byteSize = sizeof(args);
args.buildQuality = RTC_BUILD_QUALITY_LOW;
args.maxBranchingFactor = maxBranchingFactor;
args.maxDepth = 1024;
args.minLeafSize = 1;
args.maxLeafSize = 1;
args.bvh = bvh;
args.primitives = prims.data();
args.primitiveCount = prims.size();
args.primitiveArrayCapacity = prims.size();
args.createNode = createNode;
args.setNodeChildren = setNodeChildren;
args.setNodeBounds = setNodeBounds;
args.createLeaf = createLeaf;
args.buildProgress = buildProgress;

rtcGetDeviceError(device);
void* root = rtcBuildBVH(&args);
const RTCError error = rtcGetDeviceError(device);

rtcReleaseBVH(bvh);
return root == nullptr && error == RTC_ERROR_INVALID_ARGUMENT;
}

static CaseResult morton_builder_clamp(RTCDevice device)
{
if (!morton_builder_rejects_oversized_branching_factor(device, 64))
return failResult("maxBranchingFactor=64 was not rejected");

if (!morton_builder_rejects_oversized_branching_factor(device, std::numeric_limits<unsigned int>::max()))
return failResult("maxBranchingFactor=UINT_MAX was not rejected");

return passResult("oversized maxBranchingFactor values are rejected");
}

struct TestCase
{
const char* name;
CaseResult (*fn)(RTCDevice);
};
}

int main()
{
RTCDevice device = rtcNewDevice(nullptr);
if (!device)
{
std::printf("FAIL create_device\n");
return 1;
}

const TestCase tests[] = {
{ "Morton-builder-clamp", morton_builder_clamp }
};

int failed = 0;
for (const TestCase& tc : tests)
{
const CaseResult result = tc.fn(device);
if (result.pass)
std::printf("PASS %s: %s\n", tc.name, result.message.c_str());
else
{
++failed;
std::printf("FAIL %s: %s\n", tc.name, result.message.c_str());
}
}

rtcReleaseDevice(device);

std::printf("SUMMARY total=%u passed=%u failed=%d\n",
(unsigned)(sizeof(tests) / sizeof(tests[0])),
(unsigned)(sizeof(tests) / sizeof(tests[0])) - (unsigned)failed,
failed);
return failed == 0 ? 0 : 1;
}
Loading