diff --git a/kernels/builders/bvh_builder_morton.h b/kernels/builders/bvh_builder_morton.h index 87d4786810..de0bbebaf7 100644 --- a/kernels/builders/bvh_builder_morton.h +++ b/kernels/builders/bvh_builder_morton.h @@ -32,12 +32,17 @@ 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) + branchingFactor = MAX_BRANCHING_FACTOR; + 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) + branchingFactor = MAX_BRANCHING_FACTOR; minLeafSize = min(minLeafSize,maxLeafSize); } 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..f4d4705d49 --- /dev/null +++ b/tests/regression/morton_builder_clamp_regression.cpp @@ -0,0 +1,149 @@ +// 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; + + void *root = rtcBuildBVH(&args); + + rtcReleaseBVH(bvh); + rtcReleaseDevice(device); + return root != nullptr; +} + +int main() +{ + /* In the failure case, this test should assert or result in a segfault from stack overflow. */ + + bool okOversized = runCase(64); + bool okExtreme = runCase(std::numeric_limits::max()); + + if (!okOversized) + std::cerr << "Morton clamp regression failed for maxBranchingFactor=64\n"; + if (!okExtreme) + std::cerr << "Morton clamp regression failed for maxBranchingFactor=UINT_MAX\n"; + + std::cout << "Morton clamp regression test completed.\n"; + return (okOversized && okExtreme) ? 0 : 1; +}