diff --git a/kernels/builders/bvh_builder_hair.h b/kernels/builders/bvh_builder_hair.h index d83e8918a1..e4544dd0a3 100644 --- a/kernels/builders/bvh_builder_hair.h +++ b/kernels/builders/bvh_builder_hair.h @@ -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) diff --git a/kernels/builders/bvh_builder_morton.h b/kernels/builders/bvh_builder_morton.h index 87d4786810..318001837a 100644 --- a/kernels/builders/bvh_builder_morton.h +++ b/kernels/builders/bvh_builder_morton.h @@ -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); } @@ -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& current, Allocator alloc) { diff --git a/kernels/builders/bvh_builder_msmblur_hair.h b/kernels/builders/bvh_builder_msmblur_hair.h index 397e8636b1..0d3e487535 100644 --- a/kernels/builders/bvh_builder_msmblur_hair.h +++ b/kernels/builders/bvh_builder_msmblur_hair.h @@ -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: diff --git a/kernels/common/rtcore_builder.cpp b/kernels/common/rtcore_builder.cpp index 29e3bdca20..cc064d3b2a 100644 --- a/kernels/common/rtcore_builder.cpp +++ b/kernels/common/rtcore_builder.cpp @@ -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(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e2828acb07..5f99ac211d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,8 @@ FOREACH(xml ${PRIMITIVE_TESTS}) ENDFOREACH() ENDFOREACH() +ADD_SUBDIRECTORY(regression) + IF (EMBREE_TESTING_INSTALL_TESTS) # test resources diff --git a/tests/integration/test_embree_release/test.cpp b/tests/integration/test_embree_release/test.cpp index 3585e5c3a2..ea5520676b 100644 --- a/tests/integration/test_embree_release/test.cpp +++ b/tests/integration/test_embree_release/test.cpp @@ -105,4 +105,3 @@ TEST_CASE("Minimal test", "[minimal]") REQUIRE(true); } - diff --git a/tests/regression/CMakeLists.txt b/tests/regression/CMakeLists.txt new file mode 100644 index 0000000000..80b0a23e80 --- /dev/null +++ b/tests/regression/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2009-2021 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +add_executable(embree_regression_morton_builder_clamp morton_builder_clamp_regression.cpp) +target_link_libraries(embree_regression_morton_builder_clamp PRIVATE embree) +set_property(TARGET embree_regression_morton_builder_clamp PROPERTY FOLDER tests/regression) + +if (BUILD_TESTING) + add_test(NAME regression_morton_builder_clamp COMMAND embree_regression_morton_builder_clamp) +endif() diff --git a/tests/regression/morton_builder_clamp_regression.cpp b/tests/regression/morton_builder_clamp_regression.cpp new file mode 100644 index 0000000000..d258c514bd --- /dev/null +++ b/tests/regression/morton_builder_clamp_regression.cpp @@ -0,0 +1,151 @@ +// Copyright 2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include +#include +#include +#include +#include + +constexpr unsigned int max_branching_factor = 8; + +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 bool buildProgress(void * /*userPtr*/, double /*f*/) +{ + return true; +} + +bool memoryMonitor(void * /*userPtr*/, ssize_t /*bytes*/, bool /*post*/) +{ + 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); + /* deliberately empty in regression test */ +} + +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 makeGridPrimitives(size_t primitiveCount) +{ + std::vector 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{}; + 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; + prims[i] = p; + } + return prims; +} + +static bool runCase(unsigned int maxBranchingFactor) +{ + RTCDevice device = rtcNewDevice(nullptr); + if (device == nullptr) + return false; + + RTCBVH bvh = rtcNewBVH(device); + if (bvh == nullptr) + { + rtcReleaseDevice(device); + return false; + } + + std::vector 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); + RTCError error = rtcGetDeviceError(device); + + rtcReleaseBVH(bvh); + rtcReleaseDevice(device); + return root == nullptr && error == RTC_ERROR_INVALID_ARGUMENT; +} + +int main() +{ + bool okOversized = runCase(64); + bool okExtreme = runCase(std::numeric_limits::max()); + + if (!okOversized) { + std::cerr << "Morton oversized maxBranchingFactor regression failed for maxBranchingFactor=64\n"; + } + if (!okExtreme) { + std::cerr << "Morton oversized maxBranchingFactor regression failed for maxBranchingFactor=UINT_MAX\n"; + } + + std::cout << "Morton oversized branching factor regression test completed.\n"; + return (okOversized && okExtreme) ? 0 : 1; +}