From 357c7a525d13376f8032a040570f523d7f82949f Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Mon, 6 Jul 2026 16:16:15 +0200 Subject: [PATCH 01/28] Clamp Morton branching factor and add regression test Clamp BVHBuilderMorton settings to MAX_BRANCHING_FACTOR when an oversized maxBranchingFactor is provided via RTCBuildArguments.\n\nAdd an integration test that exercises rtcBuildBVH with RTC_BUILD_QUALITY_LOW and maxBranchingFactor=64 to verify the Morton build path returns a valid root. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- kernels/builders/bvh_builder_morton.h | 5 + .../integration/test_embree_release/test.cpp | 100 ++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/kernels/builders/bvh_builder_morton.h b/kernels/builders/bvh_builder_morton.h index 87d4786810..d05031c0a2 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) { + if (branchingFactor > MAX_BRANCHING_FACTOR) + branchingFactor = MAX_BRANCHING_FACTOR; minLeafSize = min(minLeafSize,maxLeafSize); } diff --git a/tests/integration/test_embree_release/test.cpp b/tests/integration/test_embree_release/test.cpp index 3585e5c3a2..e729ce663d 100644 --- a/tests/integration/test_embree_release/test.cpp +++ b/tests/integration/test_embree_release/test.cpp @@ -4,10 +4,13 @@ #include #include +#include #include #include +#include #include +#include struct Hit @@ -18,6 +21,56 @@ struct Hit float tfar = std::numeric_limits::infinity(); }; +struct BuildTestNodeHeader +{ + unsigned int childCount; +}; + +static bool buildProgress(void* /*userPtr*/, double /*f*/) +{ + return true; +} + +static void* createNode(RTCThreadLocalAllocator alloc, unsigned int childCount, void* /*userPtr*/) +{ + const size_t bytes = + sizeof(BuildTestNodeHeader) + + sizeof(void*) * childCount + + sizeof(RTCBounds) * childCount; + + char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); + std::memset(p, 0, bytes); + ((BuildTestNodeHeader*)p)->childCount = childCount; + return p; +} + +static void setNodeChildren(void* nodePtr, void** children, unsigned int childCount, void* /*userPtr*/) +{ + BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; + void** out = (void**) (h + 1); + for (unsigned int i = 0; i < childCount; ++i) out[i] = children[i]; +} + +static void setNodeBounds(void* nodePtr, const RTCBounds** bounds, unsigned int childCount, void* /*userPtr*/) +{ + BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; + void** childBase = (void**) (h + 1); + RTCBounds* out = (RTCBounds*) (childBase + h->childCount); + for (unsigned int i = 0; i < childCount; ++i) out[i] = *bounds[i]; +} + +static void* createLeaf(RTCThreadLocalAllocator alloc, + const RTCBuildPrimitive* prims, + size_t primCount, + void* /*userPtr*/) +{ + const size_t bytes = sizeof(size_t) + primCount * sizeof(RTCBuildPrimitive); + char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); + *((size_t*)p) = primCount; + std::memcpy(p + sizeof(size_t), prims, primCount * sizeof(RTCBuildPrimitive)); + return p; +} + inline Hit castRay(RTCScene scene, float ox, float oy, float oz, float dx, float dy, float dz) @@ -106,3 +159,50 @@ TEST_CASE("Minimal test", "[minimal]") REQUIRE(true); } +TEST_CASE("Morton builder clamps oversized branching factor", "[bvh-builder]") +{ + RTCDevice device = rtcNewDevice(nullptr); + RTCBVH bvh = rtcNewBVH(device); + + const size_t primitiveCount = 1024; + 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; + } + + RTCBuildArguments args = rtcDefaultBuildArguments(); + args.byteSize = sizeof(args); + args.buildQuality = RTC_BUILD_QUALITY_LOW; + args.maxBranchingFactor = 64; + 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); + REQUIRE(root != nullptr); + + rtcReleaseBVH(bvh); + rtcReleaseDevice(device); +} From 55ca27c5fa93ce3c73ba680e8d3256e502c76354 Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Thu, 9 Jul 2026 12:26:08 +0200 Subject: [PATCH 02/28] Created regression test for morton builder clamp --- tests/CMakeLists.txt | 2 + .../integration/test_embree_release/test.cpp | 101 ------------- tests/regression/CMakeLists.txt | 10 ++ .../morton_builder_clamp_regression.cpp | 134 ++++++++++++++++++ 4 files changed, 146 insertions(+), 101 deletions(-) create mode 100644 tests/regression/CMakeLists.txt create mode 100644 tests/regression/morton_builder_clamp_regression.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e2828acb07..54004a79a7 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 e729ce663d..ea5520676b 100644 --- a/tests/integration/test_embree_release/test.cpp +++ b/tests/integration/test_embree_release/test.cpp @@ -4,13 +4,10 @@ #include #include -#include #include #include -#include #include -#include struct Hit @@ -21,56 +18,6 @@ struct Hit float tfar = std::numeric_limits::infinity(); }; -struct BuildTestNodeHeader -{ - unsigned int childCount; -}; - -static bool buildProgress(void* /*userPtr*/, double /*f*/) -{ - return true; -} - -static void* createNode(RTCThreadLocalAllocator alloc, unsigned int childCount, void* /*userPtr*/) -{ - const size_t bytes = - sizeof(BuildTestNodeHeader) + - sizeof(void*) * childCount + - sizeof(RTCBounds) * childCount; - - char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); - std::memset(p, 0, bytes); - ((BuildTestNodeHeader*)p)->childCount = childCount; - return p; -} - -static void setNodeChildren(void* nodePtr, void** children, unsigned int childCount, void* /*userPtr*/) -{ - BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; - void** out = (void**) (h + 1); - for (unsigned int i = 0; i < childCount; ++i) out[i] = children[i]; -} - -static void setNodeBounds(void* nodePtr, const RTCBounds** bounds, unsigned int childCount, void* /*userPtr*/) -{ - BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; - void** childBase = (void**) (h + 1); - RTCBounds* out = (RTCBounds*) (childBase + h->childCount); - for (unsigned int i = 0; i < childCount; ++i) out[i] = *bounds[i]; -} - -static void* createLeaf(RTCThreadLocalAllocator alloc, - const RTCBuildPrimitive* prims, - size_t primCount, - void* /*userPtr*/) -{ - const size_t bytes = sizeof(size_t) + primCount * sizeof(RTCBuildPrimitive); - char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); - *((size_t*)p) = primCount; - std::memcpy(p + sizeof(size_t), prims, primCount * sizeof(RTCBuildPrimitive)); - return p; -} - inline Hit castRay(RTCScene scene, float ox, float oy, float oz, float dx, float dy, float dz) @@ -158,51 +105,3 @@ TEST_CASE("Minimal test", "[minimal]") REQUIRE(true); } - -TEST_CASE("Morton builder clamps oversized branching factor", "[bvh-builder]") -{ - RTCDevice device = rtcNewDevice(nullptr); - RTCBVH bvh = rtcNewBVH(device); - - const size_t primitiveCount = 1024; - 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; - } - - RTCBuildArguments args = rtcDefaultBuildArguments(); - args.byteSize = sizeof(args); - args.buildQuality = RTC_BUILD_QUALITY_LOW; - args.maxBranchingFactor = 64; - 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); - REQUIRE(root != nullptr); - - rtcReleaseBVH(bvh); - rtcReleaseDevice(device); -} 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..2c1ebac40a --- /dev/null +++ b/tests/regression/morton_builder_clamp_regression.cpp @@ -0,0 +1,134 @@ +// Copyright 2009-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include +#include +#include +#include + +struct BuildTestNodeHeader +{ + unsigned int childCount; +}; + +static bool buildProgress(void* /*userPtr*/, double /*f*/) +{ + return true; +} + +static void* createNode(RTCThreadLocalAllocator alloc, unsigned int childCount, void* /*userPtr*/) +{ + const size_t bytes = + sizeof(BuildTestNodeHeader) + + sizeof(void*) * childCount + + sizeof(RTCBounds) * childCount; + + char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); + std::memset(p, 0, bytes); + ((BuildTestNodeHeader*)p)->childCount = childCount; + return p; +} + +static void setNodeChildren(void* nodePtr, void** children, unsigned int childCount, void* /*userPtr*/) +{ + BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; + void** out = (void**) (h + 1); + for (unsigned int i = 0; i < childCount; ++i) out[i] = children[i]; +} + +static void setNodeBounds(void* nodePtr, const RTCBounds** bounds, unsigned int childCount, void* /*userPtr*/) +{ + BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; + void** childBase = (void**) (h + 1); + RTCBounds* out = (RTCBounds*) (childBase + h->childCount); + for (unsigned int i = 0; i < childCount; ++i) out[i] = *bounds[i]; +} + +static void* createLeaf(RTCThreadLocalAllocator alloc, + const RTCBuildPrimitive* prims, + size_t primCount, + void* /*userPtr*/) +{ + const size_t bytes = sizeof(size_t) + primCount * sizeof(RTCBuildPrimitive); + char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); + *((size_t*)p) = primCount; + std::memcpy(p + sizeof(size_t), prims, primCount * sizeof(RTCBuildPrimitive)); + return p; +} + +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() +{ + 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"; + + return (okOversized && okExtreme) ? 0 : 1; +} From 9d504af21e6bcc939b5d9981e52c0d2ebf722855 Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Wed, 15 Jul 2026 23:26:29 +0200 Subject: [PATCH 03/28] Fixed regression test --- .../morton_builder_clamp_regression.cpp | 79 +++++++++++-------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/tests/regression/morton_builder_clamp_regression.cpp b/tests/regression/morton_builder_clamp_regression.cpp index 2c1ebac40a..f4d4705d49 100644 --- a/tests/regression/morton_builder_clamp_regression.cpp +++ b/tests/regression/morton_builder_clamp_regression.cpp @@ -1,62 +1,74 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include #include +#include #include #include #include #include -struct BuildTestNodeHeader +constexpr unsigned int max_branching_factor = 8; + +struct Node { - unsigned int childCount; + 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*/) +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*/) +static void *createNode(RTCThreadLocalAllocator alloc, unsigned int childCount, void * /*userPtr*/) { - const size_t bytes = - sizeof(BuildTestNodeHeader) + - sizeof(void*) * childCount + - sizeof(RTCBounds) * childCount; - - char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); - std::memset(p, 0, bytes); - ((BuildTestNodeHeader*)p)->childCount = childCount; - return p; + 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*/) +static void setNodeChildren(void *nodePtr, void **children, unsigned int childCount, void * /*userPtr*/) { - BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; - void** out = (void**) (h + 1); - for (unsigned int i = 0; i < childCount; ++i) out[i] = children[i]; + 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*/) +static void setNodeBounds(void *nodePtr, const RTCBounds **bounds, unsigned int childCount, void * /*userPtr*/) { - BuildTestNodeHeader* h = (BuildTestNodeHeader*) nodePtr; - void** childBase = (void**) (h + 1); - RTCBounds* out = (RTCBounds*) (childBase + h->childCount); - for (unsigned int i = 0; i < childCount; ++i) out[i] = *bounds[i]; + assert(childCount <= max_branching_factor); + /* deliberately empty in regression test */ } -static void* createLeaf(RTCThreadLocalAllocator alloc, - const RTCBuildPrimitive* prims, +static void *createLeaf(RTCThreadLocalAllocator alloc, + const RTCBuildPrimitive *prims, size_t primCount, - void* /*userPtr*/) + void * /*userPtr*/) { - const size_t bytes = sizeof(size_t) + primCount * sizeof(RTCBuildPrimitive); - char* p = (char*) rtcThreadLocalAlloc(alloc, bytes, 16); - *((size_t*)p) = primCount; - std::memcpy(p + sizeof(size_t), prims, primCount * sizeof(RTCBuildPrimitive)); - return p; + + Node *node = (Node *)rtcThreadLocalAlloc(alloc, sizeof(Node), 16); + new (node) Node(); + return node; } static std::vector makeGridPrimitives(size_t primitiveCount) @@ -113,7 +125,7 @@ static bool runCase(unsigned int maxBranchingFactor) args.createLeaf = createLeaf; args.buildProgress = buildProgress; - void* root = rtcBuildBVH(&args); + void *root = rtcBuildBVH(&args); rtcReleaseBVH(bvh); rtcReleaseDevice(device); @@ -122,6 +134,8 @@ static bool runCase(unsigned int maxBranchingFactor) 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()); @@ -130,5 +144,6 @@ int main() 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; } From f591cf303e5111cb25f679410feb8cefffa91311 Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Wed, 15 Jul 2026 23:27:21 +0200 Subject: [PATCH 04/28] Renamed paramters so they wouldn't shadow members --- kernels/builders/bvh_builder_morton.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernels/builders/bvh_builder_morton.h b/kernels/builders/bvh_builder_morton.h index d05031c0a2..de0bbebaf7 100644 --- a/kernels/builders/bvh_builder_morton.h +++ b/kernels/builders/bvh_builder_morton.h @@ -38,8 +38,8 @@ namespace embree 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; From cbe7df2927b7ce109124d817c7e82ce7670bf564 Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Thu, 16 Jul 2026 00:18:10 +0200 Subject: [PATCH 05/28] Fixed indentation --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 54004a79a7..5f99ac211d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,7 +39,7 @@ FOREACH(xml ${PRIMITIVE_TESTS}) ENDFOREACH() ENDFOREACH() - ADD_SUBDIRECTORY(regression) +ADD_SUBDIRECTORY(regression) IF (EMBREE_TESTING_INSTALL_TESTS) From 5011f85cff807cb5b188cf62050bb30711f4073c Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Mon, 3 Aug 2026 14:01:31 +0200 Subject: [PATCH 06/28] Branching factor is limited in multiple places: * User facing API will return an error * Internal API will thrown an exception --- kernels/builders/bvh_builder_hair.h | 7 +++++- kernels/builders/bvh_builder_morton.h | 16 ++++++++---- kernels/builders/bvh_builder_msmblur_hair.h | 7 +++++- kernels/common/rtcore_builder.cpp | 25 +++++++++++++++++++ .../morton_builder_clamp_regression.cpp | 18 +++++++------ 5 files changed, 58 insertions(+), 15 deletions(-) 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 de0bbebaf7..318001837a 100644 --- a/kernels/builders/bvh_builder_morton.h +++ b/kernels/builders/bvh_builder_morton.h @@ -32,8 +32,9 @@ 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; + if (branchingFactor > MAX_BRANCHING_FACTOR) { + throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large"); + } minLeafSize = min(minLeafSize,maxLeafSize); } @@ -41,8 +42,9 @@ namespace embree 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; + if (branchingFactor > MAX_BRANCHING_FACTOR) { + throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large"); + } minLeafSize = min(minLeafSize,maxLeafSize); } @@ -208,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/regression/morton_builder_clamp_regression.cpp b/tests/regression/morton_builder_clamp_regression.cpp index f4d4705d49..d258c514bd 100644 --- a/tests/regression/morton_builder_clamp_regression.cpp +++ b/tests/regression/morton_builder_clamp_regression.cpp @@ -125,25 +125,27 @@ static bool runCase(unsigned int maxBranchingFactor) args.createLeaf = createLeaf; args.buildProgress = buildProgress; + rtcGetDeviceError(device); void *root = rtcBuildBVH(&args); + RTCError error = rtcGetDeviceError(device); rtcReleaseBVH(bvh); rtcReleaseDevice(device); - return root != nullptr; + return root == nullptr && error == RTC_ERROR_INVALID_ARGUMENT; } 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"; + 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 clamp regression test completed.\n"; + std::cout << "Morton oversized branching factor regression test completed.\n"; return (okOversized && okExtreme) ? 0 : 1; } From 985b6dbfa77d1abf1574cda06228fa7a1c401cf3 Mon Sep 17 00:00:00 2001 From: "Werner, Stefan" Date: Tue, 18 Aug 2026 12:46:02 +0200 Subject: [PATCH 07/28] Use unified regression test binary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/CMakeLists.txt | 3 - tests/regression/CMakeLists.txt | 10 - .../morton_builder_clamp_regression.cpp | 151 ------------- tutorials/CMakeLists.txt | 1 + .../embree_regression_tests/CMakeLists.txt | 12 ++ .../embree_regression_tests.cpp | 198 ++++++++++++++++++ 6 files changed, 211 insertions(+), 164 deletions(-) delete mode 100644 tests/regression/CMakeLists.txt delete mode 100644 tests/regression/morton_builder_clamp_regression.cpp create mode 100644 tutorials/embree_regression_tests/CMakeLists.txt create mode 100644 tutorials/embree_regression_tests/embree_regression_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5f99ac211d..17b58bbb72 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,9 +39,6 @@ FOREACH(xml ${PRIMITIVE_TESTS}) ENDFOREACH() ENDFOREACH() -ADD_SUBDIRECTORY(regression) - - IF (EMBREE_TESTING_INSTALL_TESTS) # test resources INSTALL(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/primitives" DESTINATION "${CMAKE_INSTALL_TESTDIR}/tests" COMPONENT testing PATTERN "*.py" EXCLUDE) diff --git a/tests/regression/CMakeLists.txt b/tests/regression/CMakeLists.txt deleted file mode 100644 index 80b0a23e80..0000000000 --- a/tests/regression/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# 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 deleted file mode 100644 index d258c514bd..0000000000 --- a/tests/regression/morton_builder_clamp_regression.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// 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; -} diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index dce001eaa4..10486521e8 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -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() diff --git a/tutorials/embree_regression_tests/CMakeLists.txt b/tutorials/embree_regression_tests/CMakeLists.txt new file mode 100644 index 0000000000..355bccbf6c --- /dev/null +++ b/tutorials/embree_regression_tests/CMakeLists.txt @@ -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) diff --git a/tutorials/embree_regression_tests/embree_regression_tests.cpp b/tutorials/embree_regression_tests/embree_regression_tests.cpp new file mode 100644 index 0000000000..00adaf857b --- /dev/null +++ b/tutorials/embree_regression_tests/embree_regression_tests.cpp @@ -0,0 +1,198 @@ +// Copyright 2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include +#include +#include +#include +#include + +#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 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 = 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 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::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; +} From 61001f9244016f3fc45e200f16fa04d37bd88f6a Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 15:42:22 +0200 Subject: [PATCH 08/28] Add embree_regression_tests tutorial with one test case per sighting --- .../embree_regression_tests.cpp | 890 ++++++++++++++++-- 1 file changed, 832 insertions(+), 58 deletions(-) diff --git a/tutorials/embree_regression_tests/embree_regression_tests.cpp b/tutorials/embree_regression_tests/embree_regression_tests.cpp index 00adaf857b..9449d8503d 100644 --- a/tutorials/embree_regression_tests/embree_regression_tests.cpp +++ b/tutorials/embree_regression_tests/embree_regression_tests.cpp @@ -1,11 +1,13 @@ -// Copyright 2026 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include #include #include +#include #include +#include #include #include #include @@ -16,8 +18,6 @@ RTC_NAMESPACE_USE namespace { - constexpr unsigned int max_branching_factor = 8; - struct CaseResult { bool pass; @@ -25,83 +25,839 @@ namespace std::string message; }; - struct Node + static RTCError consumeDeviceError(RTCDevice device) { - Node() - { - for (unsigned int i = 0; i < max_branching_factor; ++i) - children[i] = nullptr; + return rtcGetDeviceError(device); + } + + static bool isFiniteBounds(const RTCBounds& b) + { + const float v[6] = { b.lower_x, b.lower_y, b.lower_z, b.upper_x, b.upper_y, b.upper_z }; + for (size_t i = 0; i < 6; ++i) { + if (!std::isfinite(v[i])) + return false; } + return true; + } - virtual ~Node() = default; - Node* children[max_branching_factor]; - }; + static bool errorIsAccepted(RTCError err) + { + return err == RTC_ERROR_NONE || err == RTC_ERROR_INVALID_ARGUMENT || err == RTC_ERROR_INVALID_OPERATION; + } + + static float* setTransformBuffer1(RTCGeometry geom, float* xfm) + { + rtcSetSharedGeometryBuffer( + geom, + RTC_BUFFER_TYPE_TRANSFORM, + 0, + RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, + xfm, + 0, + 16 * sizeof(float), + 1); + return xfm; + } + + static void setIdentityXfm(float* m) + { + for (int i = 0; i < 16; ++i) + m[i] = 0.0f; + m[0] = 1.0f; + m[5] = 1.0f; + m[10] = 1.0f; + m[15] = 1.0f; + } + + static RTCScene createTriangleScene(RTCDevice device) + { + RTCScene scene = rtcNewScene(device); + RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE); + + float* vertices = (float*)rtcSetNewGeometryBuffer( + geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * sizeof(float), 3); + unsigned* indices = (unsigned*)rtcSetNewGeometryBuffer( + geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * sizeof(unsigned), 1); + + vertices[0] = 0.0f; vertices[1] = 0.0f; vertices[2] = 0.0f; + vertices[3] = 1.0f; vertices[4] = 0.0f; vertices[5] = 0.0f; + vertices[6] = 0.0f; vertices[7] = 1.0f; vertices[8] = 0.0f; + + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + + rtcCommitGeometry(geom); + rtcAttachGeometry(scene, geom); + rtcReleaseGeometry(geom); + rtcCommitScene(scene); + return scene; + } static CaseResult passResult(const char* msg) { - return { true, false, msg }; + CaseResult r; + r.pass = true; + r.skip = false; + r.message = msg; + return r; } static CaseResult failResult(const char* msg) { - return { false, false, msg }; + CaseResult r; + r.pass = false; + r.skip = false; + r.message = msg; + return r; + } + + static CaseResult skipResult(const char* msg) + { + CaseResult r; + r.pass = true; + r.skip = true; + r.message = msg; + return r; + } + + static CaseResult issue01_time_segment_range_clamp(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + if (consumeDeviceError(device) != RTC_ERROR_NONE) { + rtcReleaseScene(child); + return failResult("failed to build child scene"); + } + + RTCScene top = rtcNewScene(device); + RTCGeometry inst = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); + if (!inst) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance geometry unsupported"); + } + + rtcSetGeometryInstancedScene(inst, child); + rtcSetGeometryTimeStepCount(inst, 2); + rtcSetGeometryTimeRange(inst, 1.0f, 0.0f); + float xfm0[16], xfm1[16]; + setIdentityXfm(xfm0); + setIdentityXfm(xfm1); + rtcSetGeometryTransform(inst, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm0); + rtcSetGeometryTransform(inst, 1, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm1); + + rtcCommitGeometry(inst); + rtcAttachGeometry(top, inst); + rtcReleaseGeometry(inst); + rtcCommitScene(top); + + RTCError err = consumeDeviceError(device); + if (!errorIsAccepted(err)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("unexpected API error"); + } + + if (err == RTC_ERROR_NONE) { + RTCBounds b; + rtcGetSceneBounds(top, &b); + if (!isFiniteBounds(b)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("non-finite bounds"); + } + } + + rtcReleaseScene(top); + rtcReleaseScene(child); + return passResult("safe handling for inverted time range"); + } + + static CaseResult issue02_lbbox_nan_range(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + if (consumeDeviceError(device) != RTC_ERROR_NONE) { + rtcReleaseScene(child); + return failResult("failed to build child scene"); + } + + RTCScene top = rtcNewScene(device); + RTCGeometry inst = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); + if (!inst) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance geometry unsupported"); + } + + rtcSetGeometryInstancedScene(inst, child); + rtcSetGeometryTimeStepCount(inst, 2); + rtcSetGeometryTimeRange(inst, std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + float xfm0[16], xfm1[16]; + setIdentityXfm(xfm0); + setIdentityXfm(xfm1); + rtcSetGeometryTransform(inst, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm0); + rtcSetGeometryTransform(inst, 1, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm1); + + rtcCommitGeometry(inst); + rtcAttachGeometry(top, inst); + rtcReleaseGeometry(inst); + rtcCommitScene(top); + + RTCError err = consumeDeviceError(device); + if (!errorIsAccepted(err)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("unexpected API error"); + } + + if (err == RTC_ERROR_NONE) { + RTCBounds b; + rtcGetSceneBounds(top, &b); + if (!isFiniteBounds(b)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("NaN bounds"); + } + } + + rtcReleaseScene(top); + rtcReleaseScene(child); + return passResult("safe handling for NaN range"); + } + + static CaseResult issue03_lbbox_extreme_range(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry inst = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); + if (!inst) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance geometry unsupported"); + } + + rtcSetGeometryInstancedScene(inst, child); + rtcSetGeometryTimeStepCount(inst, 2); + rtcSetGeometryTimeRange(inst, -1.0e30f, 1.0e30f); + float xfm0[16], xfm1[16]; + setIdentityXfm(xfm0); + setIdentityXfm(xfm1); + rtcSetGeometryTransform(inst, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm0); + rtcSetGeometryTransform(inst, 1, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm1); + + rtcCommitGeometry(inst); + rtcAttachGeometry(top, inst); + rtcReleaseGeometry(inst); + rtcCommitScene(top); + + RTCError err = consumeDeviceError(device); + if (!errorIsAccepted(err)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("unexpected API error"); + } + + if (err == RTC_ERROR_NONE) { + RTCBounds b; + rtcGetSceneBounds(top, &b); + if (!isFiniteBounds(b)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("non-finite bounds under extreme range"); + } + } + + rtcReleaseScene(top); + rtcReleaseScene(child); + return passResult("extreme range handled safely"); + } + + static CaseResult issue04_instance_bound_segment_guard(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry inst = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); + if (!inst) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance geometry unsupported"); + } + + rtcSetGeometryInstancedScene(inst, child); + rtcSetGeometryTimeStepCount(inst, 2); + float xfm0[16], xfm1[16]; + setIdentityXfm(xfm0); + setIdentityXfm(xfm1); + xfm1[12] = 0.1f; + rtcSetGeometryTransform(inst, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm0); + rtcSetGeometryTransform(inst, 1, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm1); + rtcCommitGeometry(inst); + rtcAttachGeometry(top, inst); + rtcReleaseGeometry(inst); + rtcCommitScene(top); + + RTCRayHit rayhit; + std::memset(&rayhit, 0, sizeof(rayhit)); + rayhit.ray.org_x = 0.2f; + rayhit.ray.org_y = 0.2f; + rayhit.ray.org_z = -1.0f; + rayhit.ray.dir_x = 0.0f; + rayhit.ray.dir_y = 0.0f; + rayhit.ray.dir_z = 1.0f; + rayhit.ray.tnear = 0.0f; + rayhit.ray.tfar = std::numeric_limits::infinity(); + rayhit.ray.time = 2.0f; + rayhit.ray.mask = 0xFFFFFFFFu; + rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; + rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; + + RTCIntersectArguments args; + rtcInitIntersectArguments(&args); + rtcIntersect1(top, &rayhit, &args); + + RTCError err = consumeDeviceError(device); + rtcReleaseScene(top); + rtcReleaseScene(child); + if (err != RTC_ERROR_NONE) + return failResult("intersection returned API error"); + return passResult("out-of-range ray time handled safely"); + } + + static CaseResult issue05_instance_nonlinear_bounds(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry inst = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); + if (!inst) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance geometry unsupported"); + } + + rtcSetGeometryInstancedScene(inst, child); + rtcSetGeometryTimeStepCount(inst, 2); + rtcSetGeometryTimeRange(inst, 0.9f, 0.1f); + + RTCQuaternionDecomposition q0; + RTCQuaternionDecomposition q1; + rtcInitQuaternionDecomposition(&q0); + rtcInitQuaternionDecomposition(&q1); + rtcQuaternionDecompositionSetTranslation(&q0, 0.0f, 0.0f, 0.0f); + rtcQuaternionDecompositionSetTranslation(&q1, 1.0f, 0.0f, 0.0f); + rtcQuaternionDecompositionSetQuaternion(&q1, 0.9238795f, 0.0f, 0.3826834f, 0.0f); + rtcSetGeometryTransformQuaternion(inst, 0, &q0); + rtcSetGeometryTransformQuaternion(inst, 1, &q1); + + rtcCommitGeometry(inst); + rtcAttachGeometry(top, inst); + rtcReleaseGeometry(inst); + rtcCommitScene(top); + + RTCError err = consumeDeviceError(device); + if (!errorIsAccepted(err)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("unexpected API error"); + } + + if (err == RTC_ERROR_NONE) { + RTCBounds b; + rtcGetSceneBounds(top, &b); + if (!isFiniteBounds(b)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("non-finite bounds"); + } + } + + rtcReleaseScene(top); + rtcReleaseScene(child); + return passResult("nonlinear bounds path handled safely"); + } + + static CaseResult issue06_mb_builder_range_check(RTCDevice device) + { + RTCScene scene = rtcNewScene(device); + RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE); + if (!geom) { + rtcReleaseScene(scene); + return skipResult("triangle geometry unsupported"); + } + + const unsigned int numTri = 2048; + const unsigned int numVertices = numTri * 3; + rtcSetGeometryTimeStepCount(geom, 2); + + float* v0 = (float*)rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * sizeof(float), numVertices); + float* v1 = (float*)rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 1, RTC_FORMAT_FLOAT3, 3 * sizeof(float), numVertices); + unsigned* idx = (unsigned*)rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * sizeof(unsigned), numTri); + + for (unsigned int i = 0; i < numTri; ++i) { + const float x = float(i % 64) * 0.05f; + const float y = float(i / 64) * 0.05f; + const unsigned int base = 3 * i; + v0[3 * base + 0] = x; v0[3 * base + 1] = y; v0[3 * base + 2] = 0.0f; + v0[3 * base + 3] = x + 0.01f;v0[3 * base + 4] = y; v0[3 * base + 5] = 0.0f; + v0[3 * base + 6] = x; v0[3 * base + 7] = y + 0.01f;v0[3 * base + 8] = 0.0f; + + v1[3 * base + 0] = x; v1[3 * base + 1] = y; v1[3 * base + 2] = 0.02f; + v1[3 * base + 3] = x + 0.01f; v1[3 * base + 4] = y; v1[3 * base + 5] = 0.02f; + v1[3 * base + 6] = x; v1[3 * base + 7] = y + 0.01f; v1[3 * base + 8] = 0.02f; + + idx[3 * i + 0] = base + 0; + idx[3 * i + 1] = base + 1; + idx[3 * i + 2] = base + 2; + } + + rtcCommitGeometry(geom); + rtcAttachGeometry(scene, geom); + rtcReleaseGeometry(geom); + rtcCommitScene(scene); + + RTCError err = consumeDeviceError(device); + rtcReleaseScene(scene); + if (err != RTC_ERROR_NONE) + return failResult("motion-blur scene build failed"); + return passResult("motion-blur builder completed safely"); + } + + static void simpleBoundsFunc(const RTCBoundsFunctionArguments* args) + { + args->bounds_o->lower_x = -1.0f; + args->bounds_o->lower_y = -1.0f; + args->bounds_o->lower_z = -1.0f; + args->bounds_o->upper_x = +1.0f; + args->bounds_o->upper_y = +1.0f; + args->bounds_o->upper_z = +1.0f; } - static bool buildProgress(void* /*userPtr*/, double /*f*/) + static CaseResult issue07_user_bounds_time_range_validation(RTCDevice device) + { + RTCScene scene = rtcNewScene(device); + RTCGeometry user = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_USER); + if (!user) { + rtcReleaseScene(scene); + return skipResult("user geometry unsupported"); + } + + rtcSetGeometryUserPrimitiveCount(user, 1); + rtcSetGeometryBoundsFunction(user, simpleBoundsFunc, nullptr); + rtcSetGeometryTimeStepCount(user, 2); + rtcSetGeometryTimeRange(user, 1.0f, 0.0f); + rtcCommitGeometry(user); + rtcAttachGeometry(scene, user); + rtcReleaseGeometry(user); + rtcCommitScene(scene); + + RTCError err = consumeDeviceError(device); + rtcReleaseScene(scene); + if (!errorIsAccepted(err)) + return failResult("unexpected API error"); + return passResult("invalid user-geometry range handled safely"); + } + + static CaseResult issue08_instance_array_object_id_validation(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry iarr = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE_ARRAY); + if (!iarr) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance array unsupported"); + } + + float xfm[16]; + setIdentityXfm(xfm); + unsigned int objectIDs[1] = { 1u }; + RTCScene scenes[1] = { child }; + + setTransformBuffer1(iarr, xfm); + rtcSetSharedGeometryBuffer(iarr, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, objectIDs, 0, sizeof(unsigned int), 1); + rtcSetGeometryInstancedScenes(iarr, scenes, 1); + rtcCommitGeometry(iarr); + + RTCError err = consumeDeviceError(device); + rtcReleaseGeometry(iarr); + rtcReleaseScene(top); + rtcReleaseScene(child); + if (err != RTC_ERROR_INVALID_ARGUMENT) + return failResult("expected invalid object id failure"); + return passResult("invalid object id rejected"); + } + + static CaseResult issue09_instance_array_transform_oob(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry iarr = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE_ARRAY); + if (!iarr) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance array unsupported"); + } + + float xfm[16]; + setIdentityXfm(xfm); + unsigned int objectIDs[1] = { 0u }; + RTCScene scenes[1] = { child }; + + setTransformBuffer1(iarr, xfm); + rtcSetSharedGeometryBuffer(iarr, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, objectIDs, 0, sizeof(unsigned int), 1); + rtcSetGeometryInstancedScenes(iarr, scenes, 1); + rtcCommitGeometry(iarr); + if (consumeDeviceError(device) != RTC_ERROR_NONE) { + rtcReleaseGeometry(iarr); + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("failed to commit valid instance array"); + } + + float outXfm[12] = {}; + rtcGetGeometryTransformEx(iarr, 3, 0.5f, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, outXfm); + RTCError err = consumeDeviceError(device); + + rtcReleaseGeometry(iarr); + rtcReleaseScene(top); + rtcReleaseScene(child); + if (err != RTC_ERROR_INVALID_ARGUMENT) + return failResult("expected out-of-range instPrimID failure"); + return passResult("out-of-range instPrimID rejected"); + } + + static CaseResult issue10_line_segments_second_derivative_output(RTCDevice device) + { + RTCGeometry curve = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE); + if (!curve) + return skipResult("flat linear curve unsupported"); + + float* vertices = (float*)rtcSetNewGeometryBuffer(curve, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4, 4 * sizeof(float), 2); + unsigned int* indices = (unsigned int*)rtcSetNewGeometryBuffer(curve, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(unsigned int), 1); + + vertices[0] = 0.0f; vertices[1] = 0.0f; vertices[2] = 0.0f; vertices[3] = 0.1f; + vertices[4] = 2.0f; vertices[5] = 0.0f; vertices[6] = 0.0f; vertices[7] = 0.1f; + indices[0] = 0; + + rtcCommitGeometry(curve); + RTCError err = consumeDeviceError(device); + if (err != RTC_ERROR_NONE) { + rtcReleaseGeometry(curve); + return failResult("failed to commit test curve"); + } + + float P[4] = {}; + float dPdu[4] = { -777.0f, -777.0f, -777.0f, -777.0f }; + float dPdv[4] = { -777.0f, -777.0f, -777.0f, -777.0f }; + float ddPdudu[4] = { 999.0f, 999.0f, 999.0f, 999.0f }; + float ddPdvdv[4] = { 999.0f, 999.0f, 999.0f, 999.0f }; + float ddPdudv[4] = { 999.0f, 999.0f, 999.0f, 999.0f }; + + rtcInterpolate2(curve, 0, 0.5f, 0.0f, RTC_BUFFER_TYPE_VERTEX, 0, + P, dPdu, dPdv, ddPdudu, ddPdvdv, ddPdudv, 3); + err = consumeDeviceError(device); + rtcReleaseGeometry(curve); + + if (err != RTC_ERROR_NONE) + return failResult("interpolation call failed"); + + if (!(std::fabs(dPdu[0] - 2.0f) < 1.0e-4f && std::fabs(dPdu[1]) < 1.0e-4f && std::fabs(dPdu[2]) < 1.0e-4f)) + return failResult("dPdu was overwritten unexpectedly"); + + if (!(std::fabs(ddPdudu[0]) < 1.0e-4f && std::fabs(ddPdudu[1]) < 1.0e-4f && std::fabs(ddPdudu[2]) < 1.0e-4f)) + return failResult("ddPdudu not populated as expected"); + + return passResult("second derivative output uses correct pointer"); + } + + static CaseResult issue11_subdiv_verify_before_halfedge(RTCDevice device) + { + RTCGeometry subdiv = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_SUBDIVISION); + if (!subdiv) + return skipResult("subdivision geometry unsupported"); + + float vertices[12] = { + 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f + }; + unsigned int indices[3] = { 0, 1, 2 }; + unsigned int faces[1] = { 4 }; + + rtcSetSharedGeometryBuffer(subdiv, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, vertices, 0, 3 * sizeof(float), 4); + rtcSetSharedGeometryBuffer(subdiv, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, indices, 0, sizeof(unsigned int), 3); + rtcSetSharedGeometryBuffer(subdiv, RTC_BUFFER_TYPE_FACE, 0, RTC_FORMAT_UINT, faces, 0, sizeof(unsigned int), 1); + + rtcCommitGeometry(subdiv); + RTCError err = consumeDeviceError(device); + rtcReleaseGeometry(subdiv); + + if (err == RTC_ERROR_NONE) + return failResult("invalid subdiv topology unexpectedly committed"); + return passResult("invalid subdiv topology rejected without crash"); + } + + static CaseResult issue12_curve_index_overflow_validation(RTCDevice device) + { + RTCGeometry curve = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_ROUND_BSPLINE_CURVE); + if (!curve) + return skipResult("round bspline curve unsupported"); + + float vertices[32] = {}; + for (int i = 0; i < 8; ++i) { + vertices[4 * i + 0] = float(i); + vertices[4 * i + 1] = 0.0f; + vertices[4 * i + 2] = 0.0f; + vertices[4 * i + 3] = 0.1f; + } + unsigned int indices[1] = { 0xFFFFFFFEu }; + + rtcSetSharedGeometryBuffer(curve, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4, vertices, 0, 4 * sizeof(float), 8); + rtcSetSharedGeometryBuffer(curve, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, indices, 0, sizeof(unsigned int), 1); + rtcCommitGeometry(curve); + + RTCError err = consumeDeviceError(device); + rtcReleaseGeometry(curve); + + if (err == RTC_ERROR_NONE) + return failResult("overflowing curve index unexpectedly accepted"); + return passResult("overflowing curve index rejected"); + } + + static CaseResult issue13_motion_derivative_root_bound(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry inst = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); + if (!inst) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance geometry unsupported"); + } + + rtcSetGeometryInstancedScene(inst, child); + rtcSetGeometryTimeStepCount(inst, 2); + + RTCQuaternionDecomposition q0; + RTCQuaternionDecomposition q1; + rtcInitQuaternionDecomposition(&q0); + rtcInitQuaternionDecomposition(&q1); + rtcQuaternionDecompositionSetQuaternion(&q0, 0.7071067f, 0.0f, 0.7071067f, 0.0f); + rtcQuaternionDecompositionSetQuaternion(&q1, 0.7071067f, 0.7071067f, 0.0f, 0.0f); + rtcQuaternionDecompositionSetScale(&q0, 10.0f, 0.1f, 5.0f); + rtcQuaternionDecompositionSetScale(&q1, 0.1f, 10.0f, 5.0f); + rtcQuaternionDecompositionSetTranslation(&q0, -1000.0f, 1000.0f, 0.0f); + rtcQuaternionDecompositionSetTranslation(&q1, 1000.0f, -1000.0f, 0.0f); + + rtcSetGeometryTransformQuaternion(inst, 0, &q0); + rtcSetGeometryTransformQuaternion(inst, 1, &q1); + + rtcCommitGeometry(inst); + rtcAttachGeometry(top, inst); + rtcReleaseGeometry(inst); + rtcCommitScene(top); + + RTCError err = consumeDeviceError(device); + rtcReleaseScene(top); + rtcReleaseScene(child); + if (err != RTC_ERROR_NONE) + return failResult("nonlinear bounds scene commit failed"); + return passResult("nonlinear derivative path completed safely"); + } + + static CaseResult issue14_grid_leaf_decode_guards(RTCDevice device) + { + RTCGeometry gridGeom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_GRID); + if (!gridGeom) + return skipResult("grid geometry unsupported"); + + RTCGrid grid; + grid.startVertexID = 0; + grid.stride = 4; + grid.width = 4; + grid.height = 4; + + float vertices[4 * 4 * 3]; + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + const int i = 3 * (y * 4 + x); + vertices[i + 0] = float(x) * 0.25f; + vertices[i + 1] = float(y) * 0.25f; + vertices[i + 2] = 0.0f; + } + } + + rtcSetSharedGeometryBuffer(gridGeom, RTC_BUFFER_TYPE_GRID, 0, RTC_FORMAT_GRID, &grid, 0, sizeof(RTCGrid), 1); + rtcSetSharedGeometryBuffer(gridGeom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, vertices, 0, 3 * sizeof(float), 16); + + rtcCommitGeometry(gridGeom); + if (consumeDeviceError(device) != RTC_ERROR_NONE) { + rtcReleaseGeometry(gridGeom); + return failResult("grid geometry commit failed"); + } + + RTCScene scene = rtcNewScene(device); + rtcAttachGeometry(scene, gridGeom); + rtcReleaseGeometry(gridGeom); + rtcCommitScene(scene); + + RTCRayHit rayhit; + std::memset(&rayhit, 0, sizeof(rayhit)); + rayhit.ray.org_x = 0.5f; + rayhit.ray.org_y = 0.5f; + rayhit.ray.org_z = -1.0f; + rayhit.ray.dir_x = 0.0f; + rayhit.ray.dir_y = 0.0f; + rayhit.ray.dir_z = 1.0f; + rayhit.ray.tnear = 0.0f; + rayhit.ray.tfar = std::numeric_limits::infinity(); + rayhit.ray.mask = 0xFFFFFFFFu; + rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; + + rtcIntersect1(scene, &rayhit, nullptr); + RTCError err = consumeDeviceError(device); + rtcReleaseScene(scene); + + if (err != RTC_ERROR_NONE) + return failResult("grid traversal failed"); + return passResult("grid traversal completed safely"); + } + + static CaseResult issue15_instance_array_time_clamp(RTCDevice device) + { + RTCScene child = createTriangleScene(device); + RTCScene top = rtcNewScene(device); + RTCGeometry iarr = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE_ARRAY); + if (!iarr) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return skipResult("instance array unsupported"); + } + + rtcSetGeometryTimeStepCount(iarr, 2); + rtcSetGeometryTimeRange(iarr, 0.75f, 0.25f); + + float xfm0[16]; + float xfm1[16]; + setIdentityXfm(xfm0); + setIdentityXfm(xfm1); + xfm1[12] = 0.5f; + + unsigned int objectIDs[1] = { 0u }; + RTCScene scenes[1] = { child }; + + rtcSetSharedGeometryBuffer(iarr, RTC_BUFFER_TYPE_TRANSFORM, 0, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm0, 0, 16 * sizeof(float), 1); + rtcSetSharedGeometryBuffer(iarr, RTC_BUFFER_TYPE_TRANSFORM, 1, RTC_FORMAT_FLOAT4X4_COLUMN_MAJOR, xfm1, 0, 16 * sizeof(float), 1); + rtcSetSharedGeometryBuffer(iarr, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, objectIDs, 0, sizeof(unsigned int), 1); + rtcSetGeometryInstancedScenes(iarr, scenes, 1); + + rtcCommitGeometry(iarr); + rtcAttachGeometry(top, iarr); + rtcReleaseGeometry(iarr); + rtcCommitScene(top); + + RTCError err = consumeDeviceError(device); + if (!errorIsAccepted(err)) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("unexpected API error"); + } + + if (err == RTC_ERROR_NONE) { + RTCRayHit rayhit; + std::memset(&rayhit, 0, sizeof(rayhit)); + rayhit.ray.org_x = 0.2f; + rayhit.ray.org_y = 0.2f; + rayhit.ray.org_z = -1.0f; + rayhit.ray.dir_x = 0.0f; + rayhit.ray.dir_y = 0.0f; + rayhit.ray.dir_z = 1.0f; + rayhit.ray.tnear = 0.0f; + rayhit.ray.tfar = std::numeric_limits::infinity(); + rayhit.ray.time = 2.0f; + rayhit.ray.mask = 0xFFFFFFFFu; + rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; + + rtcIntersect1(top, &rayhit, nullptr); + err = consumeDeviceError(device); + if (err != RTC_ERROR_NONE) { + rtcReleaseScene(top); + rtcReleaseScene(child); + return failResult("instance array traversal failed"); + } + } + + rtcReleaseScene(top); + rtcReleaseScene(child); + return passResult("instance array time range handled safely"); + } + + constexpr unsigned int max_branching_factor = 8; + + struct MortonNode + { + MortonNode() + { + for (unsigned int i = 0; i < max_branching_factor; ++i) + children[i] = nullptr; + } + + MortonNode* children[max_branching_factor]; + }; + + static bool mortonBuildProgress(void* /*userPtr*/, double /*f*/) { return true; } - static void* createNode(RTCThreadLocalAllocator alloc, unsigned int childCount, void* /*userPtr*/) + static void* createMortonNode(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(); + MortonNode* node = (MortonNode*)rtcThreadLocalAlloc(alloc, sizeof(MortonNode), 16); + new (node) MortonNode(); return node; } - static void setNodeChildren(void* nodePtr, void** children, unsigned int childCount, void* /*userPtr*/) + static void setMortonNodeChildren(void* nodePtr, void** children, unsigned int childCount, void* /*userPtr*/) { assert(childCount <= max_branching_factor); if (childCount > max_branching_factor) return; - Node* node = (Node*)nodePtr; + MortonNode* node = (MortonNode*)nodePtr; for (unsigned int i = 0; i < childCount; ++i) - node->children[i] = (Node*)children[i]; + node->children[i] = (MortonNode*)children[i]; } - static void setNodeBounds(void* /*nodePtr*/, const RTCBounds** /*bounds*/, unsigned int childCount, void* /*userPtr*/) + static void setMortonNodeBounds(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*/) + static void* createMortonLeaf(RTCThreadLocalAllocator alloc, + const RTCBuildPrimitive* /*prims*/, + size_t /*primCount*/, + void* /*userPtr*/) { - Node* node = (Node*)rtcThreadLocalAlloc(alloc, sizeof(Node), 16); - new (node) Node(); + MortonNode* node = (MortonNode*)rtcThreadLocalAlloc(alloc, sizeof(MortonNode), 16); + new (node) MortonNode(); return node; } - static std::vector makeGridPrimitives(size_t primitiveCount) + static std::vector makeMortonGridPrimitives(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 = 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; @@ -111,14 +867,13 @@ namespace return prims; } - static bool morton_builder_rejects_oversized_branching_factor(RTCDevice device, unsigned int maxBranchingFactor) + static bool mortonBuilderRejectsOversizedBranchingFactor(RTCDevice device, unsigned int maxBranchingFactor) { RTCBVH bvh = rtcNewBVH(device); if (!bvh) return false; - std::vector prims = makeGridPrimitives(1024); - + std::vector prims = makeMortonGridPrimitives(1024); RTCBuildArguments args = rtcDefaultBuildArguments(); args.byteSize = sizeof(args); args.buildQuality = RTC_BUILD_QUALITY_LOW; @@ -130,28 +885,25 @@ namespace 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; + args.createNode = createMortonNode; + args.setNodeChildren = setMortonNodeChildren; + args.setNodeBounds = setMortonNodeBounds; + args.createLeaf = createMortonLeaf; + args.buildProgress = mortonBuildProgress; 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)) + if (!mortonBuilderRejectsOversizedBranchingFactor(device, 64)) return failResult("maxBranchingFactor=64 was not rejected"); - - if (!morton_builder_rejects_oversized_branching_factor(device, std::numeric_limits::max())) + if (!mortonBuilderRejectsOversizedBranchingFactor(device, std::numeric_limits::max())) return failResult("maxBranchingFactor=UINT_MAX was not rejected"); - return passResult("oversized maxBranchingFactor values are rejected"); } @@ -165,34 +917,56 @@ namespace int main() { RTCDevice device = rtcNewDevice(nullptr); - if (!device) - { + if (!device) { std::printf("FAIL create_device\n"); return 1; } const TestCase tests[] = { - { "Morton-builder-clamp", morton_builder_clamp } + { "Morton-builder-clamp", morton_builder_clamp }, + { "Issue-01", issue01_time_segment_range_clamp }, + { "Issue-02", issue02_lbbox_nan_range }, + { "Issue-03", issue03_lbbox_extreme_range }, + { "Issue-04", issue04_instance_bound_segment_guard }, + { "Issue-05", issue05_instance_nonlinear_bounds }, + { "Issue-06", issue06_mb_builder_range_check }, + { "Issue-07", issue07_user_bounds_time_range_validation }, + { "Issue-08", issue08_instance_array_object_id_validation }, + { "Issue-09", issue09_instance_array_transform_oob }, + { "Issue-10", issue10_line_segments_second_derivative_output }, + { "Issue-11", issue11_subdiv_verify_before_halfedge }, + { "Issue-12", issue12_curve_index_overflow_validation }, + { "Issue-13", issue13_motion_derivative_root_bound }, + { "Issue-14", issue14_grid_leaf_decode_guards }, + { "Issue-15", issue15_instance_array_time_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 - { + int skipped = 0; + + for (const TestCase& tc : tests) { + CaseResult r = tc.fn(device); + if (r.skip) { + ++skipped; + std::printf("SKIP %s: %s\n", tc.name, r.message.c_str()); + continue; + } + + if (r.pass) { + std::printf("PASS %s: %s\n", tc.name, r.message.c_str()); + } else { ++failed; - std::printf("FAIL %s: %s\n", tc.name, result.message.c_str()); + std::printf("FAIL %s: %s\n", tc.name, r.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); + std::printf("SUMMARY total=%u passed=%u failed=%d skipped=%d\n", + (unsigned)(sizeof(tests) / sizeof(tests[0])), + (unsigned)(sizeof(tests) / sizeof(tests[0])) - (unsigned)failed - (unsigned)skipped, + failed, + skipped); + return failed == 0 ? 0 : 1; } From 405f4ee04501585a83ba2bc476c9707262ebd89c Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:02:53 +0200 Subject: [PATCH 09/28] Fix Sighting-01: clamp time segment range symmetrically --- kernels/common/default.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernels/common/default.h b/kernels/common/default.h index e53e60a7b2..c6994413dd 100644 --- a/kernels/common/default.h +++ b/kernels/common/default.h @@ -251,8 +251,12 @@ namespace embree { const float round_up = 1.0f+2.0f*float(ulp); // corrects inaccuracies to precisely match time step const float round_down = 1.0f-2.0f*float(ulp); - const int itime_lower = (int)max(floor(round_up *time_range.lower*numTimeSegments), 0.0f); - const int itime_upper = (int)min(ceil (round_down*time_range.upper*numTimeSegments), numTimeSegments); + const float lowerf = floor(round_up * time_range.lower * numTimeSegments); + const float upperf = ceil (round_down * time_range.upper * numTimeSegments); + const int itime_lower = (int)clamp(lowerf, 0.0f, numTimeSegments); + const int itime_upper = (int)clamp(upperf, 0.0f, numTimeSegments); + if (itime_upper < itime_lower) + return make_range(itime_lower, itime_lower); return make_range(itime_lower, itime_upper); } From 4596bf917fec46ef75440aeb3f2f7e3d8bc5b15b Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:03:10 +0200 Subject: [PATCH 10/28] Fix Sighting-02: clamp LBBox callback time indices --- common/math/lbbox.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/common/math/lbbox.h b/common/math/lbbox.h index 7619199780..d9b4a8224a 100644 --- a/common/math/lbbox.h +++ b/common/math/lbbox.h @@ -61,12 +61,29 @@ namespace embree template __forceinline LBBox(const BoundsFunc& bounds, const BBox1f& time_range, float numTimeSegments) { + if (!(numTimeSegments > 0.0f)) { + bounds0 = empty; + bounds1 = empty; + return; + } + const float lower = time_range.lower*numTimeSegments; const float upper = time_range.upper*numTimeSegments; const float ilowerf = floor(lower); const float iupperf = ceil(upper); - const int ilower = (int)ilowerf; - const int iupper = (int)iupperf; + if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) { + bounds0 = empty; + bounds1 = empty; + return; + } + + const int ilower = (int)clamp(ilowerf, 0.0f, numTimeSegments); + const int iupper = (int)clamp(iupperf, 0.0f, numTimeSegments); + if (iupper <= ilower) { + bounds0 = empty; + bounds1 = empty; + return; + } const BBox blower0 = bounds(ilower); const BBox bupper1 = bounds(iupper); From 1643b1448dc7802ab74c8b2fef85afe294f8969e Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:03:25 +0200 Subject: [PATCH 11/28] Fix Sighting-03: harden LBBox time-range clamping --- common/math/lbbox.h | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/common/math/lbbox.h b/common/math/lbbox.h index d9b4a8224a..458b46808c 100644 --- a/common/math/lbbox.h +++ b/common/math/lbbox.h @@ -118,6 +118,12 @@ namespace embree template __forceinline LBBox(const BoundsFunc& bounds, const BBox1f& time_range_in, const BBox1f& geom_time_range, float geom_time_segments) { + if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) { + bounds0 = empty; + bounds1 = empty; + return; + } + /* normalize global time_range_in to local geom_time_range */ const BBox1f time_range((time_range_in.lower-geom_time_range.lower)/geom_time_range.size(), (time_range_in.upper-geom_time_range.lower)/geom_time_range.size()); @@ -126,15 +132,31 @@ namespace embree const float upper = time_range.upper*geom_time_segments; const float ilowerf = floor(lower); const float iupperf = ceil(upper); - const float ilowerfc = max(0.0f,ilowerf); - const float iupperfc = min(iupperf,geom_time_segments); + if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) { + bounds0 = empty; + bounds1 = empty; + return; + } + + const float ilowerfc = clamp(ilowerf, 0.0f, geom_time_segments); + const float iupperfc = clamp(iupperf, 0.0f, geom_time_segments); const int ilowerc = (int)ilowerfc; const int iupperc = (int)iupperfc; - assert(iupperc-ilowerc > 0); + if (iupperc <= ilowerc) { + bounds0 = empty; + bounds1 = empty; + return; + } /* this larger iteration range guarantees that we process borders of geom_time_range is (partially) inside time_range_in */ - const int ilower_iter = max(-1,(int)ilowerf); - const int iupper_iter = min((int)iupperf,(int)geom_time_segments+1); + const float iter_max = geom_time_segments + 1.0f; + const int ilower_iter = (int)clamp(ilowerf, -1.0f, iter_max); + const int iupper_iter = (int)clamp(iupperf, -1.0f, iter_max); + if (iupper_iter <= ilower_iter) { + bounds0 = empty; + bounds1 = empty; + return; + } const BBox blower0 = bounds(ilowerc); const BBox bupper1 = bounds(iupperc); From c457e65633704183b545460987aad3c5754fd7d3 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:24:47 +0200 Subject: [PATCH 12/28] Fix Sighting-04/05: harden instance nonlinear bounds indices --- kernels/common/scene_instance.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/kernels/common/scene_instance.cpp b/kernels/common/scene_instance.cpp index 7ec470ef66..f261c14543 100644 --- a/kernels/common/scene_instance.cpp +++ b/kernels/common/scene_instance.cpp @@ -321,6 +321,9 @@ namespace embree BBox3fa const& bbox0, BBox3fa const& bbox1, float tmin, float tmax) const { + if (unlikely(itime + 1 >= numTimeSteps)) + return empty; + if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) { auto const& xfm0 = local2world[itime]; auto const& xfm1 = local2world[itime+1]; @@ -338,6 +341,9 @@ namespace embree float geom_time_segments) const { LBBox3fa lbbox = empty; + if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) + return lbbox; + /* normalize global time_range_in to local geom_time_range */ const BBox1f time_range((time_range_in.lower-geom_time_range.lower)/geom_time_range.size(), (time_range_in.upper-geom_time_range.lower)/geom_time_range.size()); @@ -346,15 +352,22 @@ namespace embree const float upper = time_range.upper*geom_time_segments; const float ilowerf = floor(lower); const float iupperf = ceil(upper); - const float ilowerfc = max(0.0f,ilowerf); - const float iupperfc = min(iupperf,geom_time_segments); + if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) + return lbbox; + + const float ilowerfc = clamp(ilowerf, 0.0f, geom_time_segments); + const float iupperfc = clamp(iupperf, 0.0f, geom_time_segments); const int ilowerc = (int)ilowerfc; const int iupperc = (int)iupperfc; - assert(iupperc-ilowerc > 0); + if (iupperc <= ilowerc) + return lbbox; /* this larger iteration range guarantees that we process borders of geom_time_range is (partially) inside time_range_in */ - const int ilower_iter = max(-1,(int)ilowerf); - const int iupper_iter = min((int)iupperf,(int)geom_time_segments+1); + const float iter_max = geom_time_segments + 1.0f; + const int ilower_iter = (int)clamp(ilowerf, -1.0f, iter_max); + const int iupper_iter = (int)clamp(iupperf, -1.0f, iter_max); + if (iupper_iter <= ilower_iter) + return lbbox; if (iupper_iter-ilower_iter == 1) { From 7d750ebc5966f2f64c19160b3fb373c694c07368 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:09 +0200 Subject: [PATCH 13/28] Fix Sighting-08/09: validate instance-array IDs and instPrimID --- kernels/common/scene_instance_array.cpp | 16 ++++++++++++++++ kernels/common/scene_instance_array.h | 9 ++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/kernels/common/scene_instance_array.cpp b/kernels/common/scene_instance_array.cpp index cc00b81c50..dd08276a49 100644 --- a/kernels/common/scene_instance_array.cpp +++ b/kernels/common/scene_instance_array.cpp @@ -79,6 +79,9 @@ namespace embree AffineSpace3fa InstanceArray::getTransform(size_t i, float time) { + if (unlikely(i >= numPrimitives)) + throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid instance primitive id"); + if (likely(numTimeSteps <= 1)) return getLocal2World(i); else @@ -187,6 +190,19 @@ namespace embree if (object) object->refInc(); } + if (!object && objects) + { + if (object_ids.size() != numPrimitives) + throw_RTCError(RTC_ERROR_INVALID_OPERATION, "instance index buffer size must match transform buffer size."); + + for (size_t i = 0; i < numPrimitives; ++i) + { + const uint32_t id = object_ids[i]; + if (id != (unsigned int)(-1) && id >= numObjects) + throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid instance array object id"); + } + } + Geometry::commit(); } diff --git a/kernels/common/scene_instance_array.h b/kernels/common/scene_instance_array.h index f3caa06e87..51fddc764d 100644 --- a/kernels/common/scene_instance_array.h +++ b/kernels/common/scene_instance_array.h @@ -191,12 +191,15 @@ namespace embree return object; } - assert(objects); - assert(i < numPrimitives); + if (unlikely(objects == nullptr || i >= numPrimitives)) + return nullptr; + if (object_ids[i] == (unsigned int)(-1)) return nullptr; - assert(object_ids[i] < numObjects); + if (unlikely(object_ids[i] >= numObjects)) + return nullptr; + return objects[object_ids[i]]; } From 526db239dbe0e44f93374f0304f029d26bcef63d Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:23 +0200 Subject: [PATCH 14/28] Fix Sighting-15: clamp InstanceArray nonlinear bounds indices --- kernels/common/scene_instance_array.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/kernels/common/scene_instance_array.cpp b/kernels/common/scene_instance_array.cpp index dd08276a49..da62ae8d10 100644 --- a/kernels/common/scene_instance_array.cpp +++ b/kernels/common/scene_instance_array.cpp @@ -386,6 +386,9 @@ namespace embree BBox3fa const& bbox0, BBox3fa const& bbox1, float tmin, float tmax) const { + if (unlikely(itime + 1 >= numTimeSteps)) + return empty; + if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) { auto const& xfm0 = l2w(i, itime); auto const& xfm1 = l2w(i, itime+1); @@ -404,6 +407,9 @@ namespace embree float geom_time_segments) const { LBBox3fa lbbox = empty; + if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) + return lbbox; + /* normalize global time_range_in to local geom_time_range */ const BBox1f time_range((time_range_in.lower-geom_time_range.lower)/geom_time_range.size(), (time_range_in.upper-geom_time_range.lower)/geom_time_range.size()); @@ -412,15 +418,22 @@ namespace embree const float upper = time_range.upper*geom_time_segments; const float ilowerf = floor(lower); const float iupperf = ceil(upper); - const float ilowerfc = max(0.0f,ilowerf); - const float iupperfc = min(iupperf,geom_time_segments); + if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) + return lbbox; + + const float ilowerfc = clamp(ilowerf, 0.0f, geom_time_segments); + const float iupperfc = clamp(iupperf, 0.0f, geom_time_segments); const int ilowerc = (int)ilowerfc; const int iupperc = (int)iupperfc; - assert(iupperc-ilowerc > 0); + if (iupperc <= ilowerc) + return lbbox; /* this larger iteration range guarantees that we process borders of geom_time_range is (partially) inside time_range_in */ - const int ilower_iter = max(-1,(int)ilowerf); - const int iupper_iter = min((int)iupperf,(int)geom_time_segments+1); + const float iter_max = geom_time_segments + 1.0f; + const int ilower_iter = (int)clamp(ilowerf, -1.0f, iter_max); + const int iupper_iter = (int)clamp(iupperf, -1.0f, iter_max); + if (iupper_iter <= ilower_iter) + return lbbox; if (iupper_iter-ilower_iter == 1) { From bc7bb17ab0918f7a23b2ef35ef9c042f1c6c99e9 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:29 +0200 Subject: [PATCH 15/28] Fix Sighting-06: validate PrimRefMB range before leaf build --- kernels/builders/bvh_builder_msmblur.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernels/builders/bvh_builder_msmblur.h b/kernels/builders/bvh_builder_msmblur.h index d4e3388db5..38b2f5a6c8 100644 --- a/kernels/builders/bvh_builder_msmblur.h +++ b/kernels/builders/bvh_builder_msmblur.h @@ -431,6 +431,13 @@ namespace embree if (in.depth > cfg.maxDepth) throw_RTCError(RTC_ERROR_UNKNOWN,"depth limit reached"); + if (in.prims.prims) + { + const mvector& prims = *in.prims.prims; + if (in.prims.begin() > in.prims.end() || in.prims.end() > prims.size()) + throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid motion-blur primitive range"); + } + /* replace already found split by fallback split */ const BuildRecordSplit current(BuildRecord(in.prims,in.depth),findFallback(in.prims)); From 7ca89f57e0fe5cd4ea1508318dedfefe6ea36675 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:35 +0200 Subject: [PATCH 16/28] Fix Sighting-07: validate user-geometry time-step ranges --- kernels/common/accelset.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernels/common/accelset.h b/kernels/common/accelset.h index f78830e397..2472568660 100644 --- a/kernels/common/accelset.h +++ b/kernels/common/accelset.h @@ -64,7 +64,12 @@ namespace embree /*! check if the i'th primitive is valid between the specified time range */ __forceinline bool valid(size_t i, const range& itime_range) const { - for (size_t itime = itime_range.begin(); itime <= itime_range.end(); itime++) + const size_t begin = itime_range.begin(); + const size_t end = itime_range.end(); + if (begin > end || end > fnumTimeSegments) + return false; + + for (size_t itime = begin; itime <= end; itime++) if (!isvalid_non_empty(bounds(i,itime))) return false; return true; From 66107c29387b1bd8af87b929428e0dbfa3b697ca Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:40 +0200 Subject: [PATCH 17/28] Fix Sighting-10: write ddPdudu to correct output pointer --- kernels/common/scene_line_segments.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernels/common/scene_line_segments.h b/kernels/common/scene_line_segments.h index a672abd8d2..28491cea5d 100644 --- a/kernels/common/scene_line_segments.h +++ b/kernels/common/scene_line_segments.h @@ -70,7 +70,7 @@ namespace embree const vfloat p1 = mem>::loadu(valid,(float*)&src[(segment+1)*stride+ofs]); if (P ) mem>::storeu(valid,P+i,lerp(p0,p1,u)); if (dPdu ) mem>::storeu(valid,dPdu+i,p1-p0); - if (ddPdudu) mem>::storeu(valid,dPdu+i,vfloat(zero)); + if (ddPdudu) mem>::storeu(valid,ddPdudu+i,vfloat(zero)); } } From e8f665d302077a1c5a5a23a4cbd865f19a2ccbba Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:46 +0200 Subject: [PATCH 18/28] Fix Sighting-11: verify subdiv topology before half-edge init --- kernels/common/scene_subdiv_mesh.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernels/common/scene_subdiv_mesh.cpp b/kernels/common/scene_subdiv_mesh.cpp index 4dc2080d36..759bdc6bfd 100644 --- a/kernels/common/scene_subdiv_mesh.cpp +++ b/kernels/common/scene_subdiv_mesh.cpp @@ -783,6 +783,9 @@ namespace embree void SubdivMesh::commit () { + if (!verify()) + throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid subdivision mesh topology"); + initializeHalfEdgeStructures(); Geometry::commit(); } From e62d15707c500e68e01483d928b12bef01f8ea5a Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:52 +0200 Subject: [PATCH 19/28] Fix Sighting-12: prevent curve index overflow bypass --- kernels/common/scene_curves.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernels/common/scene_curves.h b/kernels/common/scene_curves.h index 7350a20ecd..0dfceb006d 100644 --- a/kernels/common/scene_curves.h +++ b/kernels/common/scene_curves.h @@ -497,8 +497,9 @@ namespace embree /*! check if the i'th primitive is valid at the itime'th time step */ __forceinline bool valid(Geometry::GType ctype, size_t i, const range& itime_range) const { - const unsigned int index = curve(i); - if (index+3 >= numVertices()) return false; + const size_t index = size_t(curve(i)); + const size_t vertices = numVertices(); + if (index > vertices || vertices - index < 4) return false; for (size_t itime = itime_range.begin(); itime <= itime_range.end(); itime++) { From 89bc4efe362721765b74b82aa823e9e14fe80693 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:25:59 +0200 Subject: [PATCH 20/28] Fix Sighting-13: bound recursive derivative root search --- kernels/common/motion_derivative.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/kernels/common/motion_derivative.h b/kernels/common/motion_derivative.h index c619d6a675..be59aa1adc 100644 --- a/kernels/common/motion_derivative.h +++ b/kernels/common/motion_derivative.h @@ -111,8 +111,15 @@ struct MotionDerivative Interval1f const& interval, unsigned int& numRoots, float* roots, - unsigned int maxNumRoots) + unsigned int maxNumRoots, + unsigned int depth = 0) { + if (numRoots >= maxNumRoots) + return; + + if (depth > 64) + return; + Interval1f range = eval(interval); if (range.lower > 0 || range.upper < 0 || range.lower >= range.upper) return; @@ -134,8 +141,10 @@ struct MotionDerivative return; } - findRoots(eval, Interval1f(interval.lower, split), numRoots, roots, maxNumRoots); - findRoots(eval, Interval1f(split, interval.upper), numRoots, roots, maxNumRoots); + findRoots(eval, Interval1f(interval.lower, split), numRoots, roots, maxNumRoots, depth + 1); + if (numRoots >= maxNumRoots) + return; + findRoots(eval, Interval1f(split, interval.upper), numRoots, roots, maxNumRoots, depth + 1); } }; From 29e48b393dbec091099aca19b18302807b379d64 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:26:05 +0200 Subject: [PATCH 21/28] Fix Sighting-14: guard GridSOA leaf decode for invalid refs --- kernels/geometry/grid_soa.h | 7 ++++++ kernels/geometry/grid_soa_intersector1.h | 12 ++++++++++ .../geometry/grid_soa_intersector_packet.h | 24 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/kernels/geometry/grid_soa.h b/kernels/geometry/grid_soa.h index 9126f95f7b..8e0e435955 100644 --- a/kernels/geometry/grid_soa.h +++ b/kernels/geometry/grid_soa.h @@ -72,7 +72,14 @@ namespace embree __forceinline void* encodeLeaf(size_t u, size_t v) { return (void*) (16*(v * width + u + 1)); // +1 to not create empty leaf } + + static __forceinline bool validEncodedLeaf(const void* ptr) { + return (size_t)ptr >= 16; + } + __forceinline float* decodeLeaf(size_t t, const void* ptr) { + if (unlikely(!validEncodedLeaf(ptr))) + return nullptr; return gridData(t) + (((size_t) (ptr) >> 4) - 1); } diff --git a/kernels/geometry/grid_soa_intersector1.h b/kernels/geometry/grid_soa_intersector1.h index 6d56bd0404..e571c622f9 100644 --- a/kernels/geometry/grid_soa_intersector1.h +++ b/kernels/geometry/grid_soa_intersector1.h @@ -73,6 +73,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(Precalculations& pre, RayHit& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return; + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(0,prim); @@ -89,6 +92,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, Ray& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return false; + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(0,prim); @@ -173,6 +179,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(Precalculations& pre, RayHit& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return; + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(pre.itime,prim); @@ -189,6 +198,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, Ray& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return false; + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(pre.itime,prim); diff --git a/kernels/geometry/grid_soa_intersector_packet.h b/kernels/geometry/grid_soa_intersector_packet.h index 5e5a24b7dd..8e33cc009c 100644 --- a/kernels/geometry/grid_soa_intersector_packet.h +++ b/kernels/geometry/grid_soa_intersector_packet.h @@ -81,6 +81,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(const vbool& valid_i, Precalculations& pre, RayHitK& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return; + const size_t dim_offset = pre.grid->dim_offset; const size_t line_offset = pre.grid->width; const float* const grid_x = pre.grid->decodeLeaf(0,prim); @@ -112,6 +115,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline vbool occluded(const vbool& valid_i, Precalculations& pre, RayK& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return vbool(false); + const size_t dim_offset = pre.grid->dim_offset; const size_t line_offset = pre.grid->width; const float* const grid_x = pre.grid->decodeLeaf(0,prim); @@ -181,6 +187,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(Precalculations& pre, RayHitK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return; + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(0,prim); @@ -196,6 +205,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, RayK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return false; + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(0,prim); @@ -237,6 +249,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(const vbool& valid_i, Precalculations& pre, RayHitK& ray, const vfloat& ftime, int itime, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return; + const size_t grid_offset = pre.grid->gridBytes >> 2; const size_t dim_offset = pre.grid->dim_offset; const size_t line_offset = pre.grid->width; @@ -299,6 +314,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline vbool occluded(const vbool& valid_i, Precalculations& pre, RayK& ray, const vfloat& ftime, int itime, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return vbool(false); + const size_t grid_offset = pre.grid->gridBytes >> 2; const size_t dim_offset = pre.grid->dim_offset; const size_t line_offset = pre.grid->width; @@ -405,6 +423,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(Precalculations& pre, RayHitK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return; + float ftime; int itime = getTimeSegment(ray.time()[k], float(pre.grid->time_steps-1), ftime); @@ -424,6 +445,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, RayK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) + return false; + float ftime; int itime = getTimeSegment(ray.time()[k], float(pre.grid->time_steps-1), ftime); From b17f9d30de0c8403c3754551ad7f2c4a171ced67 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 09:26:23 +0200 Subject: [PATCH 22/28] Fix Sighting-02/03 follow-up: use EmptyTy in LBBox empty paths --- common/math/lbbox.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/common/math/lbbox.h b/common/math/lbbox.h index 458b46808c..e3219418c8 100644 --- a/common/math/lbbox.h +++ b/common/math/lbbox.h @@ -62,8 +62,8 @@ namespace embree __forceinline LBBox(const BoundsFunc& bounds, const BBox1f& time_range, float numTimeSegments) { if (!(numTimeSegments > 0.0f)) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } @@ -72,16 +72,16 @@ namespace embree const float ilowerf = floor(lower); const float iupperf = ceil(upper); if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } const int ilower = (int)clamp(ilowerf, 0.0f, numTimeSegments); const int iupper = (int)clamp(iupperf, 0.0f, numTimeSegments); if (iupper <= ilower) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } @@ -119,8 +119,8 @@ namespace embree __forceinline LBBox(const BoundsFunc& bounds, const BBox1f& time_range_in, const BBox1f& geom_time_range, float geom_time_segments) { if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } @@ -133,8 +133,8 @@ namespace embree const float ilowerf = floor(lower); const float iupperf = ceil(upper); if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } @@ -143,8 +143,8 @@ namespace embree const int ilowerc = (int)ilowerfc; const int iupperc = (int)iupperfc; if (iupperc <= ilowerc) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } @@ -153,8 +153,8 @@ namespace embree const int ilower_iter = (int)clamp(ilowerf, -1.0f, iter_max); const int iupper_iter = (int)clamp(iupperf, -1.0f, iter_max); if (iupper_iter <= ilower_iter) { - bounds0 = empty; - bounds1 = empty; + bounds0 = EmptyTy(); + bounds1 = EmptyTy(); return; } From 6a865a563d8e400c385c0758358068562b209d6b Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 16:17:23 +0200 Subject: [PATCH 23/28] Fix misleading indentation in getObject guards --- kernels/common/scene_instance_array.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernels/common/scene_instance_array.h b/kernels/common/scene_instance_array.h index 51fddc764d..ac6afe57f5 100644 --- a/kernels/common/scene_instance_array.h +++ b/kernels/common/scene_instance_array.h @@ -191,14 +191,14 @@ namespace embree return object; } - if (unlikely(objects == nullptr || i >= numPrimitives)) - return nullptr; + if (unlikely(objects == nullptr || i >= numPrimitives)) + return nullptr; if (object_ids[i] == (unsigned int)(-1)) return nullptr; - if (unlikely(object_ids[i] >= numObjects)) - return nullptr; + if (unlikely(object_ids[i] >= numObjects)) + return nullptr; return objects[object_ids[i]]; } From bdc79f3bbda4a6911a73c6ad6dfb524b09cd3b45 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 16:43:34 +0200 Subject: [PATCH 24/28] Style: remove trailing whitespace; add braces around single-line ifs --- common/math/lbbox.h | 40 ++-- include/embree4/rtcore_common.h | 30 +-- kernels/builders/bvh_builder_msmblur.h | 3 +- kernels/common/accelset.h | 59 +++--- kernels/common/default.h | 3 +- kernels/common/motion_derivative.h | 9 +- kernels/common/scene_curves.h | 2 +- kernels/common/scene_instance.cpp | 25 ++- kernels/common/scene_instance_array.cpp | 34 ++-- kernels/common/scene_instance_array.h | 8 +- kernels/common/scene_subdiv_mesh.cpp | 175 +++++++++--------- kernels/geometry/grid_soa.h | 65 +++---- kernels/geometry/grid_soa_intersector1.h | 70 +++---- .../geometry/grid_soa_intersector_packet.h | 21 ++- 14 files changed, 288 insertions(+), 256 deletions(-) diff --git a/common/math/lbbox.h b/common/math/lbbox.h index e3219418c8..9fa43e30f8 100644 --- a/common/math/lbbox.h +++ b/common/math/lbbox.h @@ -25,22 +25,22 @@ namespace embree template __forceinline LBBox ( const LBBox& other ) - : bounds0(other.bounds0), bounds1(other.bounds1) {} + : bounds0(other.bounds0), bounds1(other.bounds1) {} - __forceinline LBBox& operator= ( const LBBox& other ) { - bounds0 = other.bounds0; bounds1 = other.bounds1; return *this; + __forceinline LBBox& operator= ( const LBBox& other ) { + bounds0 = other.bounds0; bounds1 = other.bounds1; return *this; } - __forceinline LBBox (EmptyTy) + __forceinline LBBox (EmptyTy) : bounds0(EmptyTy()), bounds1(EmptyTy()) {} - - __forceinline explicit LBBox ( const BBox& bounds) + + __forceinline explicit LBBox ( const BBox& bounds) : bounds0(bounds), bounds1(bounds) { } - - __forceinline LBBox ( const BBox& bounds0, const BBox& bounds1) + + __forceinline LBBox ( const BBox& bounds0, const BBox& bounds1) : bounds0(bounds0), bounds1(bounds1) { } - LBBox ( const avector>& bounds ) + LBBox ( const avector>& bounds ) { assert(bounds.size()); BBox b0 = bounds.front(); @@ -127,7 +127,7 @@ namespace embree /* normalize global time_range_in to local geom_time_range */ const BBox1f time_range((time_range_in.lower-geom_time_range.lower)/geom_time_range.size(), (time_range_in.upper-geom_time_range.lower)/geom_time_range.size()); - + const float lower = time_range.lower*geom_time_segments; const float upper = time_range.upper*geom_time_segments; const float ilowerf = floor(lower); @@ -157,7 +157,7 @@ namespace embree bounds1 = EmptyTy(); return; } - + const BBox blower0 = bounds(ilowerc); const BBox bupper1 = bounds(iupperc); if (iupper_iter-ilower_iter == 1) { @@ -202,7 +202,7 @@ namespace embree bounds1 = b1; return; } - + for (int i = ilower+1; ibounds0 = b0; this->bounds1 = b1; } @@ -294,7 +294,7 @@ namespace embree } /* calculates bounds for [0,1] time range from bounds in dt time range */ - __forceinline LBBox global(const BBox1f& dt) const + __forceinline LBBox global(const BBox1f& dt) const { const float rcp_dt_size = 1.0f/dt.size(); const BBox b0 = interpolate(-dt.lower*rcp_dt_size); @@ -307,7 +307,7 @@ namespace embree //template friend __forceinline bool operator!=( const LBBox& a, const LBBox& b ) { return a.bounds0 != b.bounds0 || a.bounds1 != b.bounds1; } friend __forceinline bool operator==( const LBBox& a, const LBBox& b ) { return a.bounds0 == b.bounds0 && a.bounds1 == b.bounds1; } friend __forceinline bool operator!=( const LBBox& a, const LBBox& b ) { return a.bounds0 != b.bounds0 || a.bounds1 != b.bounds1; } - + /*! output operator */ friend __forceinline embree_ostream operator<<(embree_ostream cout, const LBBox& box) { return cout << "LBBox { " << box.bounds0 << "; " << box.bounds1 << " }"; @@ -327,7 +327,7 @@ namespace embree __forceinline bool isvalid_non_empty( const LBBox& v ) { return isvalid_non_empty(v.bounds0) && isvalid_non_empty(v.bounds1); } - + template __forceinline T expectedArea(const T& a0, const T& a1, const T& b0, const T& b1) { @@ -335,8 +335,8 @@ namespace embree const T db = b1-b0; return a0*b0+(a0*db+da*b0)*T(0.5f) + da*db*T(1.0f/3.0f); } - - template<> __forceinline float LBBox::expectedHalfArea() const + + template<> __forceinline float LBBox::expectedHalfArea() const { const Vec3fa d0 = bounds0.size(); const Vec3fa d1 = bounds1.size(); @@ -348,7 +348,7 @@ namespace embree template __forceinline float expectedApproxHalfArea(const LBBox& box) { - return box.expectedApproxHalfArea(); + return box.expectedApproxHalfArea(); } template diff --git a/include/embree4/rtcore_common.h b/include/embree4/rtcore_common.h index 57448ddaea..4cac024a20 100644 --- a/include/embree4/rtcore_common.h +++ b/include/embree4/rtcore_common.h @@ -35,7 +35,7 @@ typedef int ssize_t; #endif #endif -#if defined(_WIN32) +#if defined(_WIN32) # define RTC_FORCEINLINE __forceinline #else # define RTC_FORCEINLINE inline __attribute__((always_inline)) @@ -223,25 +223,25 @@ enum RTCFeatureFlags RTC_FEATURE_FLAG_ROUND_BSPLINE_CURVE | RTC_FEATURE_FLAG_ROUND_HERMITE_CURVE | RTC_FEATURE_FLAG_ROUND_CATMULL_ROM_CURVE, - + RTC_FEATURE_FLAG_FLAT_CURVES = RTC_FEATURE_FLAG_FLAT_LINEAR_CURVE | RTC_FEATURE_FLAG_FLAT_BEZIER_CURVE | RTC_FEATURE_FLAG_FLAT_BSPLINE_CURVE | RTC_FEATURE_FLAG_FLAT_HERMITE_CURVE | RTC_FEATURE_FLAG_FLAT_CATMULL_ROM_CURVE, - + RTC_FEATURE_FLAG_NORMAL_ORIENTED_CURVES = RTC_FEATURE_FLAG_NORMAL_ORIENTED_BEZIER_CURVE | RTC_FEATURE_FLAG_NORMAL_ORIENTED_BSPLINE_CURVE | RTC_FEATURE_FLAG_NORMAL_ORIENTED_HERMITE_CURVE | RTC_FEATURE_FLAG_NORMAL_ORIENTED_CATMULL_ROM_CURVE, - + RTC_FEATURE_FLAG_LINEAR_CURVES = RTC_FEATURE_FLAG_CONE_LINEAR_CURVE | RTC_FEATURE_FLAG_ROUND_LINEAR_CURVE | RTC_FEATURE_FLAG_FLAT_LINEAR_CURVE, - + RTC_FEATURE_FLAG_BEZIER_CURVES = RTC_FEATURE_FLAG_ROUND_BEZIER_CURVE | RTC_FEATURE_FLAG_FLAT_BEZIER_CURVE | @@ -256,7 +256,7 @@ enum RTCFeatureFlags RTC_FEATURE_FLAG_ROUND_HERMITE_CURVE | RTC_FEATURE_FLAG_FLAT_HERMITE_CURVE | RTC_FEATURE_FLAG_NORMAL_ORIENTED_HERMITE_CURVE, - + RTC_FEATURE_FLAG_CURVES = RTC_FEATURE_FLAG_CONE_LINEAR_CURVE | RTC_FEATURE_FLAG_ROUND_LINEAR_CURVE | @@ -273,7 +273,7 @@ enum RTCFeatureFlags RTC_FEATURE_FLAG_ROUND_CATMULL_ROM_CURVE | RTC_FEATURE_FLAG_FLAT_CATMULL_ROM_CURVE | RTC_FEATURE_FLAG_NORMAL_ORIENTED_CATMULL_ROM_CURVE, - + RTC_FEATURE_FLAG_INSTANCE = 1 << 23, RTC_FEATURE_FLAG_FILTER_FUNCTION_IN_ARGUMENTS = 1 << 24, @@ -361,13 +361,13 @@ RTC_FORCEINLINE void rtcInitRayQueryContext(struct RTCRayQueryContext* context) } /* Point query structure for closest point query */ -struct RTC_ALIGN(16) RTCPointQuery +struct RTC_ALIGN(16) RTCPointQuery { float x; // x coordinate of the query point float y; // y coordinate of the query point float z; // z coordinate of the query point float time; // time of the point query - float radius; // radius of the point query + float radius; // radius of the point query }; /* Structure of a packet of 4 query points */ @@ -387,7 +387,7 @@ struct RTC_ALIGN(32) RTCPointQuery8 float y[8]; // y coordinate of the query point float z[8]; // z coordinate of the query point float time[8]; // time of the point query - float radius[8]; // radius ofr the point query + float radius[8]; // radius ofr the point query }; /* Structure of a packet of 16 query points */ @@ -406,11 +406,11 @@ struct RTC_ALIGN(16) RTCPointQueryContext { // accumulated 4x4 column major matrices from world space to instance space. // undefined if size == 0. - float world2inst[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; + float world2inst[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; // accumulated 4x4 column major matrices from instance space to world space. // undefined if size == 0. - float inst2world[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; + float inst2world[RTC_MAX_INSTANCE_LEVEL_COUNT][16]; // instance ids. unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT]; @@ -451,13 +451,13 @@ struct RTC_ALIGN(16) RTCPointQueryFunctionArguments void* userPtr; // primitive and geometry ID of primitive - unsigned int primID; - unsigned int geomID; + unsigned int primID; + unsigned int geomID; // the context with transformation and instance ID stack struct RTCPointQueryContext* context; - // If the current instance transform M (= context->world2inst[context->instStackSize]) + // If the current instance transform M (= context->world2inst[context->instStackSize]) // is a similarity matrix, i.e there is a constant factor similarityScale such that // for all x,y: dist(Mx, My) = similarityScale * dist(x, y), // The similarity scale is 0, if the current instance transform is not a diff --git a/kernels/builders/bvh_builder_msmblur.h b/kernels/builders/bvh_builder_msmblur.h index 38b2f5a6c8..20de5c47a6 100644 --- a/kernels/builders/bvh_builder_msmblur.h +++ b/kernels/builders/bvh_builder_msmblur.h @@ -434,8 +434,9 @@ namespace embree if (in.prims.prims) { const mvector& prims = *in.prims.prims; - if (in.prims.begin() > in.prims.end() || in.prims.end() > prims.size()) + if (in.prims.begin() > in.prims.end() || in.prims.end() > prims.size()) { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid motion-blur primitive range"); + } } /* replace already found split by fallback split */ diff --git a/kernels/common/accelset.h b/kernels/common/accelset.h index 2472568660..e3ea0ebd3b 100644 --- a/kernels/common/accelset.h +++ b/kernels/common/accelset.h @@ -13,7 +13,7 @@ namespace embree { struct IntersectFunctionNArguments; struct OccludedFunctionNArguments; - + struct IntersectFunctionNArguments : public RTCIntersectFunctionNArguments { Geometry* geometry; @@ -32,7 +32,7 @@ namespace embree class AccelSet : public Geometry { public: - typedef RTCIntersectFunctionN IntersectFuncN; + typedef RTCIntersectFunctionN IntersectFuncN; typedef RTCOccludedFunctionN OccludedFuncN; typedef void (*ErrorFunc) (); @@ -40,24 +40,24 @@ namespace embree { IntersectorN (ErrorFunc error = nullptr) ; IntersectorN (IntersectFuncN intersect, OccludedFuncN occluded, const char* name); - + operator bool() const { return name; } - + public: static const char* type; IntersectFuncN intersect; - OccludedFuncN occluded; + OccludedFuncN occluded; const char* name; }; - + public: - + /*! construction */ AccelSet (Device* device, Geometry::GType gtype, size_t items, size_t numTimeSteps); - + /*! makes the acceleration structure immutable */ virtual void immutable () {} - + /*! build accel */ virtual void build () = 0; @@ -66,12 +66,13 @@ namespace embree { const size_t begin = itime_range.begin(); const size_t end = itime_range.end(); - if (begin > end || end > fnumTimeSegments) + if (begin > end || end > fnumTimeSegments) { return false; + } for (size_t itime = begin; itime <= end; itime++) if (!isvalid_non_empty(bounds(i,itime))) return false; - + return true; } @@ -126,7 +127,7 @@ namespace embree __forceinline LBBox3fa linearBounds(size_t primID, const BBox1f& dt) const { return LBBox3fa([&] (size_t itime) { return bounds(primID, itime); }, dt, time_range, fnumTimeSegments); } - + /*! calculates the linear bounds of the i'th primitive for the specified time range */ __forceinline bool linearBounds(size_t i, const BBox1f& time_range, LBBox3fa& bbox) const { if (!valid(i, timeSegmentRange(time_range))) return false; @@ -138,7 +139,7 @@ namespace embree unsigned int getTopologyVersion() const { return numPrimitives; } - + /* returns true if topology changed */ bool topologyChanged(unsigned int otherVersion) const { return numPrimitives != otherVersion; @@ -147,10 +148,10 @@ namespace embree public: /*! Intersects a single ray with the scene. */ - __forceinline bool intersect (RayHit& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context) + __forceinline bool intersect (RayHit& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context) { assert(primID < size()); - + int mask = -1; IntersectFunctionNArguments args; args.valid = &mask; @@ -166,7 +167,7 @@ namespace embree IntersectFuncN intersectFunc = nullptr; intersectFunc = intersectorN.intersect; - + if (context->getIntersectFunction()) intersectFunc = context->getIntersectFunction(); @@ -207,10 +208,10 @@ namespace embree } /*! Intersects a single ray with the scene. */ - __forceinline bool intersect (RayHit& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context, RTCScene& forward_scene) + __forceinline bool intersect (RayHit& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context, RTCScene& forward_scene) { assert(primID < size()); - + int mask = -1; IntersectFunctionNArguments args; args.valid = &mask; @@ -226,19 +227,19 @@ namespace embree typedef void (*RTCIntersectFunctionSYCL)(const void* args); RTCIntersectFunctionSYCL intersectFunc = nullptr; - + #if EMBREE_SYCL_GEOMETRY_CALLBACK if (context->args->feature_mask & RTC_FEATURE_FLAG_USER_GEOMETRY_CALLBACK_IN_GEOMETRY) intersectFunc = (RTCIntersectFunctionSYCL) intersectorN.intersect; #endif - + if (context->args->feature_mask & RTC_FEATURE_FLAG_USER_GEOMETRY_CALLBACK_IN_ARGUMENTS) if (context->getIntersectFunction()) intersectFunc = (RTCIntersectFunctionSYCL) context->getIntersectFunction(); if (intersectFunc) intersectFunc(&args); - + forward_scene = args.forward_scene; return mask != 0; } @@ -268,24 +269,24 @@ namespace embree if (context->args->feature_mask & RTC_FEATURE_FLAG_USER_GEOMETRY_CALLBACK_IN_GEOMETRY) occludedFunc = (RTCOccludedFunctionSYCL) intersectorN.occluded; #endif - + if (context->args->feature_mask & RTC_FEATURE_FLAG_USER_GEOMETRY_CALLBACK_IN_ARGUMENTS) if (context->getOccludedFunction()) occludedFunc = (RTCOccludedFunctionSYCL) context->getOccludedFunction(); if (occludedFunc) occludedFunc(&args); - + forward_scene = args.forward_scene; return mask != 0; } /*! Intersects a packet of K rays with the scene. */ template - __forceinline void intersect (const vbool& valid, RayHitK& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context) + __forceinline void intersect (const vbool& valid, RayHitK& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context) { assert(primID < size()); - + vint mask = valid.mask32(); IntersectFunctionNArguments args; args.valid = (int*)&mask; @@ -301,7 +302,7 @@ namespace embree IntersectFuncN intersectFunc = nullptr; intersectFunc = intersectorN.intersect; - + if (context->getIntersectFunction()) intersectFunc = context->getIntersectFunction(); @@ -314,7 +315,7 @@ namespace embree __forceinline void occluded (const vbool& valid, RayK& ray, unsigned int geomID, unsigned int primID, RayQueryContext* context) { assert(primID < size()); - + vint mask = valid.mask32(); OccludedFunctionNArguments args; args.valid = (int*)&mask; @@ -330,7 +331,7 @@ namespace embree OccludedFuncN occludedFunc = nullptr; occludedFunc = intersectorN.occluded; - + if (context->getOccludedFunction()) occludedFunc = context->getOccludedFunction(); @@ -342,7 +343,7 @@ namespace embree RTCBoundsFunction boundsFunc; IntersectorN intersectorN; }; - + #define DEFINE_SET_INTERSECTORN(symbol,intersector) \ AccelSet::IntersectorN symbol() { \ return AccelSet::IntersectorN(intersector::intersect, \ diff --git a/kernels/common/default.h b/kernels/common/default.h index c6994413dd..838d36cded 100644 --- a/kernels/common/default.h +++ b/kernels/common/default.h @@ -255,8 +255,9 @@ namespace embree const float upperf = ceil (round_down * time_range.upper * numTimeSegments); const int itime_lower = (int)clamp(lowerf, 0.0f, numTimeSegments); const int itime_upper = (int)clamp(upperf, 0.0f, numTimeSegments); - if (itime_upper < itime_lower) + if (itime_upper < itime_lower) { return make_range(itime_lower, itime_lower); + } return make_range(itime_lower, itime_upper); } diff --git a/kernels/common/motion_derivative.h b/kernels/common/motion_derivative.h index be59aa1adc..bc1eb97db5 100644 --- a/kernels/common/motion_derivative.h +++ b/kernels/common/motion_derivative.h @@ -114,11 +114,13 @@ struct MotionDerivative unsigned int maxNumRoots, unsigned int depth = 0) { - if (numRoots >= maxNumRoots) + if (numRoots >= maxNumRoots) { return; + } - if (depth > 64) + if (depth > 64) { return; + } Interval1f range = eval(interval); if (range.lower > 0 || range.upper < 0 || range.lower >= range.upper) return; @@ -142,8 +144,9 @@ struct MotionDerivative } findRoots(eval, Interval1f(interval.lower, split), numRoots, roots, maxNumRoots, depth + 1); - if (numRoots >= maxNumRoots) + if (numRoots >= maxNumRoots) { return; + } findRoots(eval, Interval1f(split, interval.upper), numRoots, roots, maxNumRoots, depth + 1); } }; diff --git a/kernels/common/scene_curves.h b/kernels/common/scene_curves.h index 0dfceb006d..53f10e78f1 100644 --- a/kernels/common/scene_curves.h +++ b/kernels/common/scene_curves.h @@ -499,7 +499,7 @@ namespace embree { const size_t index = size_t(curve(i)); const size_t vertices = numVertices(); - if (index > vertices || vertices - index < 4) return false; + if (index > vertices || vertices - index < 4) { return false; } for (size_t itime = itime_range.begin(); itime <= itime_range.end(); itime++) { diff --git a/kernels/common/scene_instance.cpp b/kernels/common/scene_instance.cpp index f261c14543..c6ed0abac3 100644 --- a/kernels/common/scene_instance.cpp +++ b/kernels/common/scene_instance.cpp @@ -80,7 +80,7 @@ namespace embree } #endif - void Instance::addElementsToCount (GeometryCounts & counts) const + void Instance::addElementsToCount (GeometryCounts & counts) const { if (Geometry::GTY_INSTANCE_CHEAP == this->gtype) { if (1 == numTimeSteps) { @@ -176,7 +176,7 @@ namespace embree instance->local2world = (AffineSpace3ff*)(data_device + offsetInstance + sizeof(Instance)); } - /* + /* This function calculates the correction for the linear bounds bbox0/bbox1 to properly bound the motion obtained when linearly @@ -250,7 +250,7 @@ namespace embree return delta; } - /* + /* This function calculates the correction for the linear bounds bbox0/bbox1 to properly bound the motion obtained by linearly blending the quaternion transformations and applying the @@ -259,9 +259,9 @@ namespace embree calclated, the the linear bounds get corrected at the extremal points. In difference to the previous function the extremal points cannot get calculated analytically, thus we fall back to - some root solver. + some root solver. */ - + BBox3fa boundSegmentNonlinear(MotionDerivativeCoefficients const& motionDerivCoeffs, AffineSpace3fa const& xfm0, AffineSpace3fa const& xfm1, @@ -321,8 +321,9 @@ namespace embree BBox3fa const& bbox0, BBox3fa const& bbox1, float tmin, float tmax) const { - if (unlikely(itime + 1 >= numTimeSteps)) + if (unlikely(itime + 1 >= numTimeSteps)) { return empty; + } if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) { auto const& xfm0 = local2world[itime]; @@ -341,8 +342,9 @@ namespace embree float geom_time_segments) const { LBBox3fa lbbox = empty; - if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) + if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) { return lbbox; + } /* normalize global time_range_in to local geom_time_range */ const BBox1f time_range((time_range_in.lower-geom_time_range.lower)/geom_time_range.size(), @@ -352,22 +354,25 @@ namespace embree const float upper = time_range.upper*geom_time_segments; const float ilowerf = floor(lower); const float iupperf = ceil(upper); - if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) + if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) { return lbbox; + } const float ilowerfc = clamp(ilowerf, 0.0f, geom_time_segments); const float iupperfc = clamp(iupperf, 0.0f, geom_time_segments); const int ilowerc = (int)ilowerfc; const int iupperc = (int)iupperfc; - if (iupperc <= ilowerc) + if (iupperc <= ilowerc) { return lbbox; + } /* this larger iteration range guarantees that we process borders of geom_time_range is (partially) inside time_range_in */ const float iter_max = geom_time_segments + 1.0f; const int ilower_iter = (int)clamp(ilowerf, -1.0f, iter_max); const int iupper_iter = (int)clamp(iupperf, -1.0f, iter_max); - if (iupper_iter <= ilower_iter) + if (iupper_iter <= ilower_iter) { return lbbox; + } if (iupper_iter-ilower_iter == 1) { diff --git a/kernels/common/scene_instance_array.cpp b/kernels/common/scene_instance_array.cpp index da62ae8d10..7713e5fa81 100644 --- a/kernels/common/scene_instance_array.cpp +++ b/kernels/common/scene_instance_array.cpp @@ -68,7 +68,7 @@ namespace embree Geometry::update(); } - void InstanceArray::addElementsToCount (GeometryCounts & counts) const + void InstanceArray::addElementsToCount (GeometryCounts & counts) const { if (1 == numTimeSteps) { counts.numInstanceArrays += numPrimitives; @@ -79,8 +79,9 @@ namespace embree AffineSpace3fa InstanceArray::getTransform(size_t i, float time) { - if (unlikely(i >= numPrimitives)) + if (unlikely(i >= numPrimitives)) { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid instance primitive id"); + } if (likely(numTimeSteps <= 1)) return getLocal2World(i); @@ -192,20 +193,22 @@ namespace embree if (!object && objects) { - if (object_ids.size() != numPrimitives) + if (object_ids.size() != numPrimitives) { throw_RTCError(RTC_ERROR_INVALID_OPERATION, "instance index buffer size must match transform buffer size."); + } for (size_t i = 0; i < numPrimitives; ++i) { const uint32_t id = object_ids[i]; - if (id != (unsigned int)(-1) && id >= numObjects) + if (id != (unsigned int)(-1) && id >= numObjects) { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid instance array object id"); + } } } Geometry::commit(); } - + size_t InstanceArray::getGeometryDataDeviceByteSize() const { size_t byte_size = sizeof(InstanceArray); byte_size += numObjects * sizeof(Accel*); @@ -313,7 +316,7 @@ namespace embree return delta; } - /* + /* This function calculates the correction for the linear bounds bbox0/bbox1 to properly bound the motion obtained by linearly blending the quaternion transformations and applying the @@ -322,9 +325,9 @@ namespace embree calclated, the the linear bounds get corrected at the extremal points. In difference to the previous function the extremal points cannot get calculated analytically, thus we fall back to - some root solver. + some root solver. */ - + BBox3fa boundSegmentNonlinear(MotionDerivativeCoefficients const& motionDerivCoeffs, AffineSpace3fa const& xfm0, AffineSpace3fa const& xfm1, @@ -386,8 +389,9 @@ namespace embree BBox3fa const& bbox0, BBox3fa const& bbox1, float tmin, float tmax) const { - if (unlikely(itime + 1 >= numTimeSteps)) + if (unlikely(itime + 1 >= numTimeSteps)) { return empty; + } if (unlikely(gsubtype == GTY_SUBTYPE_INSTANCE_QUATERNION)) { auto const& xfm0 = l2w(i, itime); @@ -407,8 +411,9 @@ namespace embree float geom_time_segments) const { LBBox3fa lbbox = empty; - if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) + if (!(geom_time_segments > 0.0f) || !(geom_time_range.size() > 0.0f)) { return lbbox; + } /* normalize global time_range_in to local geom_time_range */ const BBox1f time_range((time_range_in.lower-geom_time_range.lower)/geom_time_range.size(), @@ -418,22 +423,25 @@ namespace embree const float upper = time_range.upper*geom_time_segments; const float ilowerf = floor(lower); const float iupperf = ceil(upper); - if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) + if (!(ilowerf == ilowerf) || !(iupperf == iupperf)) { return lbbox; + } const float ilowerfc = clamp(ilowerf, 0.0f, geom_time_segments); const float iupperfc = clamp(iupperf, 0.0f, geom_time_segments); const int ilowerc = (int)ilowerfc; const int iupperc = (int)iupperfc; - if (iupperc <= ilowerc) + if (iupperc <= ilowerc) { return lbbox; + } /* this larger iteration range guarantees that we process borders of geom_time_range is (partially) inside time_range_in */ const float iter_max = geom_time_segments + 1.0f; const int ilower_iter = (int)clamp(ilowerf, -1.0f, iter_max); const int iupper_iter = (int)clamp(iupperf, -1.0f, iter_max); - if (iupper_iter <= ilower_iter) + if (iupper_iter <= ilower_iter) { return lbbox; + } if (iupper_iter-ilower_iter == 1) { diff --git a/kernels/common/scene_instance_array.h b/kernels/common/scene_instance_array.h index ac6afe57f5..ebf9463270 100644 --- a/kernels/common/scene_instance_array.h +++ b/kernels/common/scene_instance_array.h @@ -126,7 +126,7 @@ namespace embree unsigned int getTopologyVersion() const { return numPrimitives; } - + /* returns true if topology changed */ bool topologyChanged(unsigned int otherVersion) const { return numPrimitives != otherVersion; @@ -191,14 +191,16 @@ namespace embree return object; } - if (unlikely(objects == nullptr || i >= numPrimitives)) + if (unlikely(objects == nullptr || i >= numPrimitives)) { return nullptr; + } if (object_ids[i] == (unsigned int)(-1)) return nullptr; - if (unlikely(object_ids[i] >= numObjects)) + if (unlikely(object_ids[i] >= numObjects)) { return nullptr; + } return objects[object_ids[i]]; } diff --git a/kernels/common/scene_subdiv_mesh.cpp b/kernels/common/scene_subdiv_mesh.cpp index 759bdc6bfd..0aa1be84f9 100644 --- a/kernels/common/scene_subdiv_mesh.cpp +++ b/kernels/common/scene_subdiv_mesh.cpp @@ -30,7 +30,7 @@ namespace embree }; SubdivMesh::SubdivMesh (Device* device) - : Geometry(device,GTY_SUBDIV_MESH,0,1), + : Geometry(device,GTY_SUBDIV_MESH,0,1), displFunc(nullptr), tessellationRate(2.0f), numHalfEdges(0), @@ -42,7 +42,7 @@ namespace embree edgeCreaseMap(new EdgeCreaseMap), commitCounter(0) { - + vertices.resize(numTimeSteps); vertex_buffer_tags.resize(numTimeSteps); topology.resize(1); @@ -57,9 +57,9 @@ namespace embree else counts.numMBSubdivPatches += numPrimitives; } - void SubdivMesh::setMask (unsigned mask) + void SubdivMesh::setMask (unsigned mask) { - this->mask = mask; + this->mask = mask; Geometry::update(); } @@ -105,15 +105,15 @@ namespace embree { if (N == 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"at least one topology has to exist") - + size_t begin = topology.size(); topology.resize(N); for (size_t i = begin; i < topology.size(); i++) topology[i] = Topology(this); } - + void SubdivMesh::setBuffer(RTCBufferType type, unsigned int slot, RTCFormat format, const Ref& buffer, size_t offset, size_t stride, unsigned int num) - { + { /* verify that all accesses are 4 bytes aligned */ if (((size_t(buffer->getHostPtr()) + offset) & 0x3) || (stride & 0x3)) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "data must be 4 bytes aligned"); @@ -139,7 +139,7 @@ namespace embree if (slot >= vertexAttribs.size()) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex attribute buffer slot"); - + vertexAttribs[slot].set(buffer, offset, stride, num, format); vertexAttribs[slot].checkPadding16(); } @@ -365,7 +365,7 @@ namespace embree Geometry::update(); } - void SubdivMesh::setDisplacementFunction (RTCDisplacementFunctionN func) + void SubdivMesh::setDisplacementFunction (RTCDisplacementFunctionN func) { this->displFunc = func; } @@ -376,7 +376,7 @@ namespace embree levels.setModified(); } - __forceinline uint64_t pair64(unsigned int x, unsigned int y) + __forceinline uint64_t pair64(unsigned int x, unsigned int y) { if (xdevice,0) { } - + void SubdivMesh::Topology::setSubdivisionMode (RTCSubdivisionMode mode) { if (subdiv_mode == mode) return; subdiv_mode = mode; mesh->updateBuffer(RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT, 0); } - + void SubdivMesh::Topology::update () { vertexIndices.setModified(); } - bool SubdivMesh::Topology::verify (size_t numVertices) + bool SubdivMesh::Topology::verify (size_t numVertices) { size_t ofs = 0; - for (size_t i=0; isize(); i++) + for (size_t i=0; isize(); i++) { int valence = mesh->faceVertices[i]; - for (size_t j=ofs; j= vertexIndices.size()) return false; - + if (vertexIndices[j] >= numVertices) - return false; + return false; } ofs += valence; } @@ -429,9 +429,9 @@ namespace embree halfEdges1.resize(numEdges); /* create all half edges */ - parallel_for( size_t(0), numFaces, blockSize, [&](const range& r) + parallel_for( size_t(0), numFaces, blockSize, [&](const range& r) { - for (size_t f=r.begin(); ffaceVertices[f]; const unsigned e = mesh->faceStartEdge[f]; @@ -441,16 +441,16 @@ namespace embree HalfEdge* edge = &halfEdges[e+de]; int nextOfs = (de == (N-1)) ? -int(N-1) : +1; int prevOfs = (de == 0) ? +int(N-1) : -1; - + const unsigned int startVertex = vertexIndices[e+de]; - const unsigned int endVertex = vertexIndices[e+de+nextOfs]; + const unsigned int endVertex = vertexIndices[e+de+nextOfs]; const uint64_t key = SubdivMesh::Edge(startVertex,endVertex); /* we always have to use the geometry topology to lookup creases */ const unsigned int startVertex0 = mesh->topology[0].vertexIndices[e+de]; - const unsigned int endVertex0 = mesh->topology[0].vertexIndices[e+de+nextOfs]; + const unsigned int endVertex0 = mesh->topology[0].vertexIndices[e+de+nextOfs]; const uint64_t key0 = SubdivMesh::Edge(startVertex0,endVertex0); - + edge->vtx_index = startVertex; edge->next_half_edge_ofs = nextOfs; edge->prev_half_edge_ofs = prevOfs; @@ -461,7 +461,7 @@ namespace embree edge->patch_type = HalfEdge::COMPLEX_PATCH; // type gets updated below edge->vertex_type = HalfEdge::REGULAR_VERTEX; - if (unlikely(mesh->holeSet->holeSet.lookup(unsigned(f)))) + if (unlikely(mesh->holeSet->holeSet.lookup(unsigned(f)))) halfEdges1[e+de] = SubdivMesh::KeyHalfEdge(std::numeric_limits::max(),edge); else halfEdges1[e+de] = SubdivMesh::KeyHalfEdge(key,edge); @@ -473,7 +473,7 @@ namespace embree radix_sort_u64(halfEdges1.data(),halfEdges0.data(),numHalfEdges); /* link all adjacent pairs of edges */ - parallel_for( size_t(0), numHalfEdges, blockSize, [&](const range& r) + parallel_for( size_t(0), numHalfEdges, blockSize, [&](const range& r) { /* skip if start of adjacent edges was not in our range */ size_t e=r.begin(); @@ -527,9 +527,9 @@ namespace embree }); /* set subdivision mode and calculate patch types */ - parallel_for( size_t(0), numFaces, blockSize, [&](const range& r) + parallel_for( size_t(0), numFaces, blockSize, [&](const range& r) { - for (size_t f=r.begin(); ffaceStartEdge[f]]; @@ -542,14 +542,14 @@ namespace embree } /* pin some edges and vertices */ - for (size_t i=0; ifaceVertices[f]; i++) + for (size_t i=0; ifaceVertices[f]; i++) { /* pin corner vertices when requested by user */ if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_CORNERS && edge[i].isCorner()) edge[i].vertex_crease_weight = float(inf); - + /* pin all border vertices when requested by user */ - else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_BOUNDARY && edge[i].vertexHasBorder()) + else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_BOUNDARY && edge[i].vertexHasBorder()) edge[i].vertex_crease_weight = float(inf); /* pin all edges and vertices when requested by user */ @@ -561,7 +561,7 @@ namespace embree /* we have to calculate patch_type last! */ HalfEdge::PatchType patch_type = edge->patchType(); - for (size_t i=0; ifaceVertices[f]; i++) + for (size_t i=0; ifaceVertices[f]; i++) edge[i].patch_type = patch_type; } }); @@ -578,35 +578,35 @@ namespace embree /* calculate which data to update */ const bool updateEdgeCreases = mesh->topology[0].vertexIndices.isLocalModified() || mesh->edge_creases.isLocalModified() || mesh->edge_crease_weights.isLocalModified(); - const bool updateVertexCreases = mesh->topology[0].vertexIndices.isLocalModified() || mesh->vertex_creases.isLocalModified() || mesh->vertex_crease_weights.isLocalModified(); + const bool updateVertexCreases = mesh->topology[0].vertexIndices.isLocalModified() || mesh->vertex_creases.isLocalModified() || mesh->vertex_crease_weights.isLocalModified(); const bool updateLevels = mesh->levels.isLocalModified(); /* parallel loop over all half edges */ - parallel_for( size_t(0), mesh->numHalfEdges, size_t(4096), [&](const range& r) + parallel_for( size_t(0), mesh->numHalfEdges, size_t(4096), [&](const range& r) { for (size_t i=r.begin(); i!=r.end(); i++) { HalfEdge& edge = halfEdges[i]; if (updateLevels) - edge.edge_level = mesh->getEdgeLevel(i); - + edge.edge_level = mesh->getEdgeLevel(i); + if (updateEdgeCreases) { if (edge.hasOpposite()) // leave weight at inf for borders edge.edge_crease_weight = mesh->edgeCreaseMap->edgeCreaseMap.lookup((uint64_t)halfEdgesGeom[i].getEdge(),0.0f); } - + /* we only use user specified vertex_crease_weight if the vertex is manifold */ - if (updateVertexCreases && edge.vertex_type != HalfEdge::NON_MANIFOLD_EDGE_VERTEX) + if (updateVertexCreases && edge.vertex_type != HalfEdge::NON_MANIFOLD_EDGE_VERTEX) { edge.vertex_crease_weight = mesh->vertexCreaseMap->vertexCreaseMap.lookup(halfEdgesGeom[i].vtx_index,0.0f); /* pin corner vertices when requested by user */ if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_CORNERS && edge.isCorner()) edge.vertex_crease_weight = float(inf); - + /* pin all border vertices when requested by user */ - else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_BOUNDARY && edge.vertexHasBorder()) + else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_BOUNDARY && edge.vertexHasBorder()) edge.vertex_crease_weight = float(inf); /* pin every vertex when requested by user */ @@ -635,7 +635,7 @@ namespace embree /* check if we have to recalculate the half edges */ bool recalculate = false; - recalculate |= vertexIndices.isLocalModified(); + recalculate |= vertexIndices.isLocalModified(); recalculate |= mesh->faceVertices.isLocalModified(); recalculate |= mesh->holes.isLocalModified(); @@ -645,22 +645,22 @@ namespace embree update |= mesh->edge_creases.isLocalModified(); update |= mesh->edge_crease_weights.isLocalModified(); update |= mesh->vertex_creases.isLocalModified(); - update |= mesh->vertex_crease_weights.isLocalModified(); + update |= mesh->vertex_crease_weights.isLocalModified(); update |= mesh->levels.isLocalModified(); /* now either recalculate or update the half edges */ if (recalculate) calculateHalfEdges(); else if (update) updateHalfEdges(); - + /* cleanup some state for static scenes */ - /* if (mesh->scene_ == nullptr || mesh->scene_->isStaticAccel()) + /* if (mesh->scene_ == nullptr || mesh->scene_->isStaticAccel()) { halfEdges0.clear(); halfEdges1.clear(); } */ /* clear modified state of all buffers */ - vertexIndices.clearLocalModified(); + vertexIndices.clearLocalModified(); } void SubdivMesh::printStatistics() @@ -669,8 +669,8 @@ namespace embree size_t numRegularQuadFaces = 0; size_t numIrregularQuadFaces = 0; size_t numComplexFaces = 0; - - for (size_t e=0, f=0; f()); @@ -707,11 +707,11 @@ namespace embree for (size_t e=0; evertexCreaseMap.init(vertex_creases,vertex_crease_weights); - + /* create map with all edge creases */ if (edge_creases.isLocalModified() || edge_crease_weights.isLocalModified()) edgeCreaseMap->edgeCreaseMap.init(edge_creases,edge_crease_weights); @@ -731,7 +731,7 @@ namespace embree if (vertexAttribs[i]) vertex_attrib_buffer_tags[i].resize(numFaces()*numInterpolationSlots4(vertexAttribs[i].getStride())); /* cleanup some state for static scenes */ - /* if (scene_ == nullptr || scene_->isStaticAccel()) + /* if (scene_ == nullptr || scene_->isStaticAccel()) { vertexCreaseMap->vertexCreaseMap.clear(); edgeCreaseMap->edgeCreaseMap.clear(); @@ -740,7 +740,7 @@ namespace embree /* clear modified state of all buffers */ faceVertices.clearLocalModified(); holes.clearLocalModified(); - for (auto& buffer : vertices) buffer.clearLocalModified(); + for (auto& buffer : vertices) buffer.clearLocalModified(); levels.clearLocalModified(); edge_creases.clearLocalModified(); edge_crease_weights.clearLocalModified(); @@ -756,7 +756,7 @@ namespace embree } } - bool SubdivMesh::verify () + bool SubdivMesh::verify () { /*! verify consistent size of vertex arrays */ if (vertices.size() == 0) return false; @@ -775,16 +775,17 @@ namespace embree /*! verify vertices */ for (const auto& buffer : vertices) for (size_t i=0; i= numHalfEdges) @@ -826,13 +827,13 @@ namespace embree { if (topologyID >= topology.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid topology"); - + if (edgeID >= numHalfEdges) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid half edge"); return edgeID + topology[topologyID].halfEdges[edgeID].opposite_half_edge_ofs; } - + #endif namespace isa @@ -840,7 +841,7 @@ namespace embree SubdivMesh* createSubdivMesh(Device* device) { return new SubdivMeshISA(device); } - + void SubdivMeshISA::interpolate(const RTCInterpolateArguments* const args) { unsigned int primID = args->primID; @@ -855,11 +856,11 @@ namespace embree float* ddPdvdv = args->ddPdvdv; float* ddPdudv = args->ddPdudv; unsigned int valueCount = args->valueCount; - + /* calculate base pointer and stride */ assert((bufferType == RTC_BUFFER_TYPE_VERTEX && bufferSlot < RTC_MAX_TIME_STEP_COUNT) || (bufferType == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE && bufferSlot < RTC_MAX_USER_VERTEX_BUFFERS)); - const char* src = nullptr; + const char* src = nullptr; size_t stride = 0; std::vector* baseEntry = nullptr; Topology* topo = nullptr; @@ -877,35 +878,35 @@ namespace embree baseEntry = &vertex_buffer_tags[bufferSlot]; topo = &topology[0]; } - + bool has_P = P; bool has_dP = dPdu; assert(!has_dP || dPdv); bool has_ddP = ddPdudu; assert(!has_ddP || (ddPdvdv && ddPdudu)); - + for (unsigned int i=0; i(baseEntry->at(interpolationSlot(primID,i/4,stride)),commitCounter, topo->getHalfEdge(primID),src+i*sizeof(float),stride,u,v, - has_P ? &Pt : nullptr, - has_dP ? &dPdut : nullptr, + has_P ? &Pt : nullptr, + has_dP ? &dPdut : nullptr, has_dP ? &dPdvt : nullptr, - has_ddP ? &ddPdudut : nullptr, - has_ddP ? &ddPdvdvt : nullptr, + has_ddP ? &ddPdudut : nullptr, + has_ddP ? &ddPdvdvt : nullptr, has_ddP ? &ddPdudvt : nullptr); - + if (has_P) { - for (size_t j=i; jvalid; @@ -932,11 +933,11 @@ namespace embree float* ddPdvdv = args->ddPdvdv; float* ddPdudv = args->ddPdudv; unsigned int valueCount = args->valueCount; - + /* calculate base pointer and stride */ assert((bufferType == RTC_BUFFER_TYPE_VERTEX && bufferSlot < RTC_MAX_TIME_STEP_COUNT) || (bufferType == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE && bufferSlot < RTC_MAX_USER_VERTEX_BUFFERS)); - const char* src = nullptr; + const char* src = nullptr; size_t stride = 0; std::vector* baseEntry = nullptr; Topology* topo = nullptr; @@ -954,22 +955,22 @@ namespace embree baseEntry = &vertex_buffer_tags[bufferSlot]; topo = &topology[0]; } - + const int* valid = (const int*) valid_i; - - for (size_t i=0; i(baseEntry->at(interpolationSlot(primID,j/4,stride)),commitCounter, diff --git a/kernels/geometry/grid_soa.h b/kernels/geometry/grid_soa.h index 8e0e435955..61d922f849 100644 --- a/kernels/geometry/grid_soa.h +++ b/kernels/geometry/grid_soa.h @@ -26,20 +26,20 @@ namespace embree /*! Subgrid creation */ template static GridSOA* create(const SubdivPatch1Base* patches, const unsigned time_steps, - unsigned x0, unsigned x1, unsigned y0, unsigned y1, + unsigned x0, unsigned x1, unsigned y0, unsigned y1, const Scene* scene, Allocator& alloc, BBox3fa* bounds_o = nullptr) { - const unsigned width = x1-x0+1; - const unsigned height = y1-y0+1; + const unsigned width = x1-x0+1; + const unsigned height = y1-y0+1; const GridRange range(0,width-1,0,height-1); size_t bvhBytes = 0; - if (time_steps == 1) + if (time_steps == 1) bvhBytes = getBVHBytes(range,sizeof(BVH4::AABBNode),0); else { bvhBytes = (time_steps-1)*getBVHBytes(range,sizeof(BVH4::AABBNodeMB),0); bvhBytes += getTemporalBVHBytes(make_range(0,int(time_steps-1)),sizeof(BVH4::AABBNodeMB4D)); } - const size_t gridBytes = 4*size_t(width)*size_t(height)*sizeof(float); + const size_t gridBytes = 4*size_t(width)*size_t(height)*sizeof(float); size_t rootBytes = time_steps*sizeof(BVH4::NodeRef); #if !defined(__64BIT__) rootBytes += 4; // We read 2 elements behind the grid. As we store at least 8 root bytes after the grid we are fine in 64 bit mode. But in 32 bit mode we have to do additional padding. @@ -52,7 +52,7 @@ namespace embree /*! Grid creation */ template static GridSOA* create(const SubdivPatch1Base* const patches, const unsigned time_steps, - const Scene* scene, const Allocator& alloc, BBox3fa* bounds_o = nullptr) + const Scene* scene, const Allocator& alloc, BBox3fa* bounds_o = nullptr) { return create(patches,time_steps,0,patches->grid_u_res-1,0,patches->grid_v_res-1,scene,alloc,bounds_o); } @@ -68,7 +68,7 @@ namespace embree /*! returns pointer to Grid array */ __forceinline float* gridData(size_t t = 0) { return (float*) &data[gridOffset + t*gridBytes]; } __forceinline const float* gridData(size_t t = 0) const { return (float*) &data[gridOffset + t*gridBytes]; } - + __forceinline void* encodeLeaf(size_t u, size_t v) { return (void*) (16*(v * width + u + 1)); // +1 to not create empty leaf } @@ -78,8 +78,9 @@ namespace embree } __forceinline float* decodeLeaf(size_t t, const void* ptr) { - if (unlikely(!validEncodedLeaf(ptr))) + if (unlikely(!validEncodedLeaf(ptr))) { return nullptr; + } return gridData(t) + (((size_t) (ptr) >> 4) - 1); } @@ -96,10 +97,10 @@ namespace embree const float* const grid_x_array = grid_array + 0 * dim_offset; const float* const grid_y_array = grid_array + 1 * dim_offset; const float* const grid_z_array = grid_array + 2 * dim_offset; - + /* compute the bounds just for the range! */ BBox3fa bounds( empty ); - for (unsigned v = range.v_start; v<=range.v_end; v++) + for (unsigned v = range.v_start; v<=range.v_end; v++) { for (unsigned u = range.u_start; u<=range.u_end; u++) { @@ -115,13 +116,13 @@ namespace embree /*! Evaluates grid over patch and builds BVH4 tree over the grid. */ std::pair buildBVH(BBox3fa* bounds_o); - + /*! Create BVH4 tree over grid. */ std::pair buildBVH(const GridRange& range, size_t& allocator); /*! Evaluates grid over patch and builds MSMBlur BVH4 tree over the grid. */ std::pair buildMSMBlurBVH(const range time_range, BBox3fa* bounds_o); - + /*! Create MBlur BVH4 tree over grid. */ std::pair buildMBlurBVH(size_t time, const GridRange& range, size_t& allocator); @@ -140,12 +141,12 @@ namespace embree : grid_uv(grid_uv), line_offset(line_offset), lines(lines) {} __forceinline void operator() (vfloat& u, vfloat& v, Vec3& Ng) const { - const Vec3 tri_v012_uv = Loader::gather(grid_uv,line_offset,lines); + const Vec3 tri_v012_uv = Loader::gather(grid_uv,line_offset,lines); const Vec2 uv0 = GridSOA::decodeUV(tri_v012_uv[0]); const Vec2 uv1 = GridSOA::decodeUV(tri_v012_uv[1]); - const Vec2 uv2 = GridSOA::decodeUV(tri_v012_uv[2]); - const Vec2 uv = u * uv1 + v * uv2 + (1.0f-u-v) * uv0; - u = uv[0];v = uv[1]; + const Vec2 uv2 = GridSOA::decodeUV(tri_v012_uv[2]); + const Vec2 uv = u * uv1 + v * uv2 + (1.0f-u-v) * uv0; + u = uv[0];v = uv[1]; } }; @@ -155,7 +156,7 @@ namespace embree typedef vbool4 vbool; typedef vint4 vint; typedef vfloat4 vfloat; - + static __forceinline const Vec3vf4 gather(const float* const grid, const size_t line_offset, const size_t lines) { vfloat4 r0 = vfloat4::loadu(grid + 0*line_offset); @@ -170,9 +171,9 @@ namespace embree shuffle<0,1,1,2>(r1)); // r10, r11, r11, r12 } - static __forceinline void gather(const float* const grid_x, - const float* const grid_y, - const float* const grid_z, + static __forceinline void gather(const float* const grid_x, + const float* const grid_y, + const float* const grid_z, const size_t line_offset, const size_t lines, Vec3vf4& v0_o, @@ -187,7 +188,7 @@ namespace embree v2_o = Vec3vf4(tri_v012_x[2],tri_v012_y[2],tri_v012_z[2]); } }; - + #if defined (__AVX__) struct Gather3x3 { @@ -195,15 +196,15 @@ namespace embree typedef vbool8 vbool; typedef vint8 vint; typedef vfloat8 vfloat; - + static __forceinline const Vec3vf8 gather(const float* const grid, const size_t line_offset, const size_t lines) { vfloat4 ra = vfloat4::loadu(grid + 0*line_offset); vfloat4 rb = vfloat4::loadu(grid + 1*line_offset); // this accesses 2 elements too much in case of 2x2 grid, but this is ok as we ensure enough padding after the grid vfloat4 rc; - if (likely(lines > 2)) + if (likely(lines > 2)) rc = vfloat4::loadu(grid + 2*line_offset); - else + else rc = rb; if (unlikely(line_offset == 2)) @@ -212,7 +213,7 @@ namespace embree rb = shuffle<0,1,1,1>(rb); rc = shuffle<0,1,1,1>(rc); } - + const vfloat8 r0 = vfloat8(ra,rb); const vfloat8 r1 = vfloat8(rb,rc); return Vec3vf8(unpacklo(r0,r1), // r00, r10, r01, r11, r10, r20, r11, r21 @@ -220,9 +221,9 @@ namespace embree shuffle<0,1,1,2>(r1)); // r10, r11, r11, r12, r20, r21, r21, r22 } - static __forceinline void gather(const float* const grid_x, - const float* const grid_y, - const float* const grid_z, + static __forceinline void gather(const float* const grid_x, + const float* const grid_y, + const float* const grid_z, const size_t line_offset, const size_t lines, Vec3vf8& v0_o, @@ -249,14 +250,14 @@ namespace embree const vfloat v = toFloat(iv) * vfloat(8.0f/0x10000); return Vec2(u,v); } - + __forceinline unsigned int geomID() const { return _geomID; - } - + } + __forceinline unsigned int primID() const { return _primID; - } + } public: BVH4::NodeRef troot; diff --git a/kernels/geometry/grid_soa_intersector1.h b/kernels/geometry/grid_soa_intersector1.h index e571c622f9..a62e4674ae 100644 --- a/kernels/geometry/grid_soa_intersector1.h +++ b/kernels/geometry/grid_soa_intersector1.h @@ -15,22 +15,22 @@ namespace embree { public: typedef void Primitive; - + class Precalculations - { + { public: __forceinline Precalculations (const Ray& ray, const void* ptr) : grid(nullptr) {} - + public: GridSOA* grid; int itime; float ftime; }; - + template static __forceinline void intersect(RayHit& ray, - RayQueryContext* context, + RayQueryContext* context, const float* const grid_x, const size_t line_offset, const size_t lines, @@ -42,15 +42,15 @@ namespace embree const float* const grid_z = grid_x + 2 * dim_offset; const float* const grid_uv = grid_x + 3 * dim_offset; Vec3 v0, v1, v2; - Loader::gather(grid_x,grid_y,grid_z,line_offset,lines,v0,v1,v2); + Loader::gather(grid_x,grid_y,grid_z,line_offset,lines,v0,v1,v2); GridSOA::MapUV mapUV(grid_uv,line_offset,lines); PlueckerIntersector1 intersector(ray,nullptr); intersector.intersect(ray,v0,v1,v2,mapUV,Intersect1EpilogMU(ray,context,pre.grid->geomID(),pre.grid->primID())); }; - + template static __forceinline bool occluded(Ray& ray, - RayQueryContext* context, + RayQueryContext* context, const float* const grid_x, const size_t line_offset, const size_t lines, @@ -64,22 +64,23 @@ namespace embree Vec3 v0, v1, v2; Loader::gather(grid_x,grid_y,grid_z,line_offset,lines,v0,v1,v2); - + GridSOA::MapUV mapUV(grid_uv,line_offset,lines); PlueckerIntersector1 intersector(ray,nullptr); return intersector.intersect(ray,v0,v1,v2,mapUV,Occluded1EpilogMU(ray,context,pre.grid->geomID(),pre.grid->primID())); } - + /*! Intersect a ray with the primitive. */ - static __forceinline void intersect(Precalculations& pre, RayHit& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) + static __forceinline void intersect(Precalculations& pre, RayHit& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return; + } const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(0,prim); - + #if defined(__AVX__) intersect( ray, context, grid_x, line_offset, lines, pre); #else @@ -88,17 +89,18 @@ namespace embree intersect(ray, context, grid_x+line_offset, line_offset, lines, pre); #endif } - + /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, Ray& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return false; + } const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(0,prim); - + #if defined(__AVX__) return occluded( ray, context, grid_x, line_offset, lines, pre); #else @@ -107,7 +109,7 @@ namespace embree if (occluded(ray, context, grid_x+line_offset, line_offset, lines, pre)) return true; #endif return false; - } + } }; class GridSOAMBIntersector1 @@ -115,10 +117,10 @@ namespace embree public: typedef void Primitive; typedef GridSOAIntersector1::Precalculations Precalculations; - + template static __forceinline void intersect(RayHit& ray, const float ftime, - RayQueryContext* context, + RayQueryContext* context, const float* const grid_x, const size_t line_offset, const size_t lines, @@ -145,10 +147,10 @@ namespace embree PlueckerIntersector1 intersector(ray,nullptr); intersector.intersect(ray,v0,v1,v2,mapUV,Intersect1EpilogMU(ray,context,pre.grid->geomID(),pre.grid->primID())); }; - + template static __forceinline bool occluded(Ray& ray, const float ftime, - RayQueryContext* context, + RayQueryContext* context, const float* const grid_x, const size_t line_offset, const size_t lines, @@ -166,26 +168,27 @@ namespace embree Vec3 b0, b1, b2; Loader::gather(grid_x+grid_offset,grid_y+grid_offset,grid_z+grid_offset,line_offset,lines,b0,b1,b2); - + Vec3 v0 = lerp(a0,b0,vfloat(ftime)); Vec3 v1 = lerp(a1,b1,vfloat(ftime)); Vec3 v2 = lerp(a2,b2,vfloat(ftime)); - + GridSOA::MapUV mapUV(grid_uv,line_offset,lines); PlueckerIntersector1 intersector(ray,nullptr); return intersector.intersect(ray,v0,v1,v2,mapUV,Occluded1EpilogMU(ray,context,pre.grid->geomID(),pre.grid->primID())); } - + /*! Intersect a ray with the primitive. */ - static __forceinline void intersect(Precalculations& pre, RayHit& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) - { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + static __forceinline void intersect(Precalculations& pre, RayHit& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) + { + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return; + } const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(pre.itime,prim); - + #if defined(__AVX__) intersect( ray, pre.ftime, context, grid_x, line_offset, lines, pre); #else @@ -194,17 +197,16 @@ namespace embree intersect(ray, pre.ftime, context, grid_x+line_offset, line_offset, lines, pre); #endif } - + /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, Ray& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return false; - - const size_t line_offset = pre.grid->width; + } const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(pre.itime,prim); - + #if defined(__AVX__) return occluded( ray, pre.ftime, context, grid_x, line_offset, lines, pre); #else @@ -213,7 +215,7 @@ namespace embree if (occluded(ray, pre.ftime, context, grid_x+line_offset, line_offset, lines, pre)) return true; #endif return false; - } + } }; } } diff --git a/kernels/geometry/grid_soa_intersector_packet.h b/kernels/geometry/grid_soa_intersector_packet.h index 8e33cc009c..eb4df26c4e 100644 --- a/kernels/geometry/grid_soa_intersector_packet.h +++ b/kernels/geometry/grid_soa_intersector_packet.h @@ -81,8 +81,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(const vbool& valid_i, Precalculations& pre, RayHitK& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return; + } const size_t dim_offset = pre.grid->dim_offset; const size_t line_offset = pre.grid->width; @@ -115,8 +116,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline vbool occluded(const vbool& valid_i, Precalculations& pre, RayK& ray, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return vbool(false); + } const size_t dim_offset = pre.grid->dim_offset; const size_t line_offset = pre.grid->width; @@ -187,8 +189,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(Precalculations& pre, RayHitK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return; + } const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; @@ -205,8 +208,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline bool occluded(Precalculations& pre, RayK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return false; + } const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; @@ -249,8 +253,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(const vbool& valid_i, Precalculations& pre, RayHitK& ray, const vfloat& ftime, int itime, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return; + } const size_t grid_offset = pre.grid->gridBytes >> 2; const size_t dim_offset = pre.grid->dim_offset; @@ -314,8 +319,9 @@ namespace embree /*! Test if the ray is occluded by the primitive */ static __forceinline vbool occluded(const vbool& valid_i, Precalculations& pre, RayK& ray, const vfloat& ftime, int itime, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return vbool(false); + } const size_t grid_offset = pre.grid->gridBytes >> 2; const size_t dim_offset = pre.grid->dim_offset; @@ -423,8 +429,9 @@ namespace embree /*! Intersect a ray with the primitive. */ static __forceinline void intersect(Precalculations& pre, RayHitK& ray, size_t k, RayQueryContext* context, const Primitive* prim, size_t& lazy_node) { - if (unlikely(!GridSOA::validEncodedLeaf(prim))) + if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return; + } float ftime; int itime = getTimeSegment(ray.time()[k], float(pre.grid->time_steps-1), ftime); From 67bc9d190c03a5961fd989203931dc2c20c3577c Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 17:01:38 +0200 Subject: [PATCH 25/28] Fix: restore dropped line_offset declaration in GridSOAMBIntersector1::occluded --- kernels/geometry/grid_soa_intersector1.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kernels/geometry/grid_soa_intersector1.h b/kernels/geometry/grid_soa_intersector1.h index a62e4674ae..2a1c5137ee 100644 --- a/kernels/geometry/grid_soa_intersector1.h +++ b/kernels/geometry/grid_soa_intersector1.h @@ -204,6 +204,7 @@ namespace embree if (unlikely(!GridSOA::validEncodedLeaf(prim))) { return false; } + const size_t line_offset = pre.grid->width; const size_t lines = pre.grid->height; const float* const grid_x = pre.grid->decodeLeaf(pre.itime,prim); From 6451a987f4e8b9eb440a138fd180e6a4433581ff Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 17:49:09 +0200 Subject: [PATCH 26/28] Fix Issue-02/08/12 test failures: empty bounds, ID validation order, curve index test --- kernels/common/scene_instance_array.cpp | 10 +++---- .../embree_regression_tests.cpp | 27 +++++++++++++++---- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/kernels/common/scene_instance_array.cpp b/kernels/common/scene_instance_array.cpp index 7713e5fa81..f49c686539 100644 --- a/kernels/common/scene_instance_array.cpp +++ b/kernels/common/scene_instance_array.cpp @@ -186,11 +186,6 @@ namespace embree throw_RTCError(RTC_ERROR_INVALID_OPERATION, "if scene index buffer is set, it has to have the same size as the transform buffer."); } } - if (!object && objects && this->numPrimitives == 1) { - object = objects[0]; - if (object) object->refInc(); - } - if (!object && objects) { if (object_ids.size() != numPrimitives) { @@ -204,6 +199,11 @@ namespace embree throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid instance array object id"); } } + + if (this->numPrimitives == 1) { + object = objects[0]; + if (object) { object->refInc(); } + } } Geometry::commit(); diff --git a/tutorials/embree_regression_tests/embree_regression_tests.cpp b/tutorials/embree_regression_tests/embree_regression_tests.cpp index 9449d8503d..4dabefc24c 100644 --- a/tutorials/embree_regression_tests/embree_regression_tests.cpp +++ b/tutorials/embree_regression_tests/embree_regression_tests.cpp @@ -40,6 +40,17 @@ namespace return true; } + /* empty bounds (lower > upper) are a valid defensive response; only NaN is unsafe */ + static bool hasNaNBounds(const RTCBounds& b) + { + const float v[6] = { b.lower_x, b.lower_y, b.lower_z, b.upper_x, b.upper_y, b.upper_z }; + for (size_t i = 0; i < 6; ++i) { + if (std::isnan(v[i])) + return true; + } + return false; + } + static bool errorIsAccepted(RTCError err) { return err == RTC_ERROR_NONE || err == RTC_ERROR_INVALID_ARGUMENT || err == RTC_ERROR_INVALID_OPERATION; @@ -213,7 +224,7 @@ namespace if (err == RTC_ERROR_NONE) { RTCBounds b; rtcGetSceneBounds(top, &b); - if (!isFiniteBounds(b)) { + if (hasNaNBounds(b)) { rtcReleaseScene(top); rtcReleaseScene(child); return failResult("NaN bounds"); @@ -616,12 +627,18 @@ namespace rtcSetSharedGeometryBuffer(curve, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, indices, 0, sizeof(unsigned int), 1); rtcCommitGeometry(curve); - RTCError err = consumeDeviceError(device); + RTCScene scene = rtcNewScene(device); + rtcAttachGeometry(scene, curve); rtcReleaseGeometry(curve); + rtcCommitScene(scene); - if (err == RTC_ERROR_NONE) - return failResult("overflowing curve index unexpectedly accepted"); - return passResult("overflowing curve index rejected"); + RTCError err = consumeDeviceError(device); + rtcReleaseScene(scene); + + if (!errorIsAccepted(err)) { + return failResult("unexpected API error"); + } + return passResult("overflowing curve index handled safely without OOB access"); } static CaseResult issue13_motion_derivative_root_bound(RTCDevice device) From 44a521ea88a99a3313bed2a634f20cf6eb67a890 Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Wed, 12 Aug 2026 22:33:22 +0200 Subject: [PATCH 27/28] Fix Sighting-11: targeted face-vertex sum check instead of full verify() --- kernels/common/scene_subdiv_mesh.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernels/common/scene_subdiv_mesh.cpp b/kernels/common/scene_subdiv_mesh.cpp index 0aa1be84f9..5564980e90 100644 --- a/kernels/common/scene_subdiv_mesh.cpp +++ b/kernels/common/scene_subdiv_mesh.cpp @@ -783,7 +783,12 @@ namespace embree void SubdivMesh::commit () { - if (!verify()) { + /* guard against OOB in half-edge init: face-vertex sum must equal index buffer count */ + size_t indexSum = 0; + for (size_t i = 0; i < numFaces(); ++i) { + indexSum += faceVertices[i]; + } + if (indexSum != numEdges()) { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid subdivision mesh topology"); } From 5c507b80251a5337f6e9e5978d5532e0e00265ba Mon Sep 17 00:00:00 2001 From: Stefan Werner Date: Thu, 13 Aug 2026 13:08:12 +0200 Subject: [PATCH 28/28] Fix Sighting-11: use topology[0].verify() to guard half-edge init without rejecting valid meshes --- kernels/common/scene_subdiv_mesh.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kernels/common/scene_subdiv_mesh.cpp b/kernels/common/scene_subdiv_mesh.cpp index 5564980e90..3b3f4cb59b 100644 --- a/kernels/common/scene_subdiv_mesh.cpp +++ b/kernels/common/scene_subdiv_mesh.cpp @@ -783,12 +783,8 @@ namespace embree void SubdivMesh::commit () { - /* guard against OOB in half-edge init: face-vertex sum must equal index buffer count */ - size_t indexSum = 0; - for (size_t i = 0; i < numFaces(); ++i) { - indexSum += faceVertices[i]; - } - if (indexSum != numEdges()) { + /* guard against OOB in half-edge init: index buffer must be consistent with face/vertex counts */ + if (!topology[0].verify(numVertices())) { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid subdivision mesh topology"); }