From 53d963245537b56db028e0f9e01c7fca8b0f2eee Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Fri, 30 Jan 2026 11:51:57 +0000 Subject: [PATCH 1/4] Added tests for batch query API with GPRT --- tests/CMakeLists.txt | 17 +++ tests/test_direct_ray_buffer_access.cpp | 124 ++++++++++++++++++ ..._direct_ray_buffer_access_deviceCode.slang | 24 ++++ tests/test_direct_ray_buffer_access_shared.h | 14 ++ tests/test_files | 2 +- tests/test_point_in_volume.cpp | 85 +++++++++++- tests/test_ray_fire.cpp | 82 +++++++++++- tests/util.h | 17 +++ vendor/GPRT | 2 +- 9 files changed, 362 insertions(+), 5 deletions(-) create mode 100644 tests/test_direct_ray_buffer_access.cpp create mode 100644 tests/test_direct_ray_buffer_access_deviceCode.slang create mode 100644 tests/test_direct_ray_buffer_access_shared.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d7286555..8166eee9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,6 +37,11 @@ if (XDG_ENABLE_MOAB) list(APPEND TEST_NAMES test_overlap_check) endif() +# This test really should be appended when any GPU library is enabled +if (XDG_ENABLE_GPRT) + list(APPEND TEST_NAMES test_direct_ray_buffer_access) +endif() + foreach(test ${TEST_NAMES}) add_executable(${test} ${test}.cpp) target_link_libraries(${test} xdg Catch2::Catch2WithMain) @@ -48,6 +53,18 @@ foreach(test ${TEST_NAMES}) TEST_PREFIX "${test}::") endforeach() +if (XDG_ENABLE_GPRT) + embed_devicecode( + OUTPUT_TARGET + test_direct_ray_buffer_access_deviceCode + HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/test_direct_ray_buffer_access_shared.h + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/test_direct_ray_buffer_access_deviceCode.slang + ) + target_link_libraries(test_direct_ray_buffer_access test_direct_ray_buffer_access_deviceCode) +endif() + set( TEST_FILES diff --git a/tests/test_direct_ray_buffer_access.cpp b/tests/test_direct_ray_buffer_access.cpp new file mode 100644 index 00000000..309ce9e9 --- /dev/null +++ b/tests/test_direct_ray_buffer_access.cpp @@ -0,0 +1,124 @@ +// for testing +#include +#include +#include +#include + +// xdg includes +#include "xdg/constants.h" +#include "xdg/mesh_manager_interface.h" +#include "xdg/gprt/ray_tracer.h" +#include "xdg/xdg.h" +#include "mesh_mock.h" +#include "test_direct_ray_buffer_access_shared.h" +#include "util.h" +#include "gprt.h" + +#include + +using namespace xdg; +using namespace xdg::test; + +extern GPRTProgram test_direct_ray_buffer_access_deviceCode; + +// This is a GPU only test - skip if no GPU ray tracing backends are enabled +TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", + GPRT_Raytracer) +{ + constexpr auto rt_backend = TestType::value; + + DYNAMIC_SECTION(fmt::format("Backend = {}", rt_backend)) { + check_ray_tracer_supported(rt_backend); + + auto rti = create_raytracer(rt_backend); + REQUIRE(rti); + + auto mm = std::make_shared(false); + mm->init(); + REQUIRE(mm->mesh_library() == MeshLibrary::MOCK); + + auto xdg = std::make_shared(); + xdg->set_mesh_manager_interface(mm); + xdg->set_ray_tracing_interface(rti); + xdg->prepare_raytracer(); + + std::vector origins; + std::vector directions; + size_t N = 64; + make_rays(N, origins, directions); + + auto gprt_rt = std::dynamic_pointer_cast(rti); + REQUIRE(gprt_rt); + + const MeshID volume_id = mm->volumes()[0]; + GPRTContext context = gprt_rt->context(); + GPRTModule module = gprtModuleCreate(context, test_direct_ray_buffer_access_deviceCode); + auto packRays = gprtComputeCreate(context, module, "pack_external_rays"); + + std::vector expected_distances(N, INFTY); + std::vector expected_surfaces(N, ID_NONE); + xdg->ray_fire(volume_id, + origins.data(), + directions.data(), + N, + expected_distances.data(), + expected_surfaces.data()); + + // Create callback to populate rays on device + RayPopulationCallback populate_callback = [&volume_id, &origins, &directions, &context, &packRays] + (const DeviceRayHitBuffers& buffer, size_t numRays) { + REQUIRE(origins.size() >= numRays); + REQUIRE(directions.size() >= numRays); + + // Convert to double3 for use on GPU + std::vector origins_device(numRays); + std::vector directions_device(numRays); + for (size_t i = 0; i < numRays; ++i) { + origins_device[i] = {origins[i].x, origins[i].y, origins[i].z}; + directions_device[i] = {directions[i].x, directions[i].y, directions[i].z}; + } + + auto origins_buffer = gprtDeviceBufferCreate(context, numRays, origins_device.data()); + auto directions_buffer = gprtDeviceBufferCreate(context, numRays, directions_device.data()); + + constexpr uint32_t threads_per_group = 256; + const uint32_t groups = static_cast((numRays + threads_per_group - 1) / threads_per_group); + + ExternalRayParams params = {}; + params.xdgRays = static_cast(buffer.rayDevPtr); + params.origins = gprtBufferGetDevicePointer(origins_buffer); + params.directions = gprtBufferGetDevicePointer(directions_buffer); + params.num_rays = static_cast(numRays); + params.total_threads = groups * threads_per_group; + params.volume_mesh_id = volume_id; + params.enabled = 1u; + + gprtComputeLaunch(packRays, + { groups, 1, 1 }, + { threads_per_group, 1, 1 }, + params); + gprtComputeSynchronize(context); + + gprtBufferDestroy(origins_buffer); + gprtBufferDestroy(directions_buffer); + }; + + // Populate rays via external API + xdg->populate_rays_external(N, populate_callback); + + xdg->ray_fire_prepared(volume_id, N); + std::vector hits; + xdg->transfer_hits_buffer_to_host(N, hits); + + REQUIRE(hits.size() == N); + for (size_t i = 0; i < N; ++i) { + REQUIRE(hits[i].surf_id == expected_surfaces[i]); + if (expected_surfaces[i] != ID_NONE) { + REQUIRE_THAT(hits[i].distance, Catch::Matchers::WithinAbs(expected_distances[i], 1e-6)); + } + } + + gprtComputeDestroy(packRays); + gprtModuleDestroy(module); + } +} diff --git a/tests/test_direct_ray_buffer_access_deviceCode.slang b/tests/test_direct_ray_buffer_access_deviceCode.slang new file mode 100644 index 00000000..a3e8729a --- /dev/null +++ b/tests/test_direct_ray_buffer_access_deviceCode.slang @@ -0,0 +1,24 @@ +#include "test_direct_ray_buffer_access_shared.h" + +[shader("compute")] +[numthreads(256, 1, 1)] +void pack_external_rays(uint3 DispatchThreadID: SV_DispatchThreadID, + uniform ExternalRayParams extParams) +{ + uint globalThreadID = DispatchThreadID.x; + uint stride = extParams.total_threads; // Groups * 256 + + // Grid-stride loop: each thread handles ray idx, idx+stride, idx+2*stride, ... + for (uint idx = globalThreadID; idx < extParams.num_rays; idx += stride) + { + xdg::dblRay r; + r.origin = extParams.origins[idx]; + r.direction = extParams.directions[idx]; + r.exclude_primitives = nullptr; + r.exclude_count = 0; + r.volume_mesh_id = extParams.volume_mesh_id; + r.enabled = extParams.enabled; + + extParams.xdgRays[idx] = r; + } +} diff --git a/tests/test_direct_ray_buffer_access_shared.h b/tests/test_direct_ray_buffer_access_shared.h new file mode 100644 index 00000000..a8e5d553 --- /dev/null +++ b/tests/test_direct_ray_buffer_access_shared.h @@ -0,0 +1,14 @@ +#include "gprt.h" + +#include "../include/xdg/gprt/ray.h" + +struct ExternalRayParams { + xdg::dblRay* xdgRays; + double3* origins; + double3* directions; + uint num_rays; + uint total_threads; + int volume_mesh_id; + uint enabled; +}; + diff --git a/tests/test_files b/tests/test_files index a3caf0af..ca579198 160000 --- a/tests/test_files +++ b/tests/test_files @@ -1 +1 @@ -Subproject commit a3caf0af3f128944c4d6eac93b481df6e4efd97c +Subproject commit ca57919851224047ef86fab177a0bfe9fa920127 diff --git a/tests/test_point_in_volume.cpp b/tests/test_point_in_volume.cpp index ae34e823..7e771499 100644 --- a/tests/test_point_in_volume.cpp +++ b/tests/test_point_in_volume.cpp @@ -12,6 +12,21 @@ using namespace xdg; using namespace xdg::test; +static void make_points(size_t N, + std::vector& points, + std::vector& directions) +{ + points.resize(N); + directions.resize(N); + for (size_t i = 0; i < N; ++i) { + // evens inside (origin), odds just outside +X + points[i] = (i % 2 == 0) ? xdg::Position{0,0,0} : xdg::Position{5.1,0,0}; + // alternate ±X directions + directions[i] = (i % 2 == 0) ? xdg::Direction{ 1,0,0} + : xdg::Direction{-1,0,0}; + } +} + // ---------- single test, sections per backend -------------------------------- TEMPLATE_TEST_CASE("Point-in-volume on MeshMock", "[piv][mock]", @@ -24,7 +39,6 @@ TEMPLATE_TEST_CASE("Point-in-volume on MeshMock", "[piv][mock]", check_ray_tracer_supported(rt_backend); // skip if backend not enabled at configuration time auto rti = create_raytracer(rt_backend); REQUIRE(rti); - rti->init(); // Keep MeshMock usage consistent across backends auto mm = std::make_shared(false); @@ -78,3 +92,72 @@ TEMPLATE_TEST_CASE("Point-in-volume on MeshMock", "[piv][mock]", REQUIRE(result == false); } } + +TEMPLATE_TEST_CASE("Batch API Point-in-volume on MeshMock", "[piv][mock][batch]", + Embree_Raytracer, + GPRT_Raytracer) +{ + constexpr auto rt_backend = TestType::value; + + DYNAMIC_SECTION(fmt::format("Backend = {}", rt_backend)) { + check_ray_tracer_supported(rt_backend); + if (rt_backend == RTLibrary::EMBREE) { + SKIP("Skipping PIV batch for Embree: batch API not implemented yet"); + } + + auto rti = create_raytracer(rt_backend); + REQUIRE(rti); + + auto mm = std::make_shared(false); + mm->init(); + REQUIRE(mm->mesh_library() == MeshLibrary::MOCK); + + auto [volume_tree, element_tree] = rti->register_volume(mm, mm->volumes()[0]); + REQUIRE(volume_tree != ID_NONE); + REQUIRE(element_tree == ID_NONE); + + rti->init(); + + std::vector points; + std::vector directions; + std::vector has_dir; + size_t N; + + SECTION("N=0 no-op") { + rti->point_in_volume(volume_tree, + nullptr, /*points*/ + 0, /*num_points*/ + nullptr /*results*/); + SUCCEED("N=0 completed without error"); + } + + SECTION("N=1") { + N = 1; + make_points(N, points, directions); + + auto scalar_result = static_cast(rti->point_in_volume(volume_tree, points[0], &directions[0])); + + std::vector batch_result(N, 0xFF); + rti->point_in_volume(volume_tree, points.data(), N, batch_result.data(), directions.data()); + REQUIRE(batch_result[0] == scalar_result); + } + + SECTION("N=64") { + N = 64; + make_points(N, points, directions); + + // Store results of scalar point_in_volume calls to verify batch against scalar + std::vector scalar_results(N, 0); + for (size_t i = 0; i < N; ++i) { + scalar_results[i] = static_cast(rti->point_in_volume(volume_tree, points[i], &directions[i])); + } + + std::vector batch_results(N, 0xFF); + rti->point_in_volume(volume_tree, points.data(), N, batch_results.data(), directions.data()); + for (size_t i = 0; i < points.size(); ++i) { + REQUIRE(batch_results[i] == scalar_results[i]); + } + } + } +} + diff --git a/tests/test_ray_fire.cpp b/tests/test_ray_fire.cpp index be816c36..552a4ed6 100644 --- a/tests/test_ray_fire.cpp +++ b/tests/test_ray_fire.cpp @@ -4,13 +4,14 @@ #include #include - // xdg includes #include "xdg/constants.h" #include "xdg/mesh_manager_interface.h" #include "mesh_mock.h" #include "util.h" +#include + using namespace xdg; using namespace xdg::test; @@ -109,4 +110,81 @@ TEMPLATE_TEST_CASE("Ray Fire on MeshMock (per-backend sections)", "[rayfire][moc intersection = rti->ray_fire(volume_tree, origin, direction, INFTY, HitOrientation::EXITING, &exclude_primitives); REQUIRE(intersection.second == ID_NONE); } -} \ No newline at end of file +} + +TEMPLATE_TEST_CASE("Batch API Ray Fire on MeshMock", "[rayfire][mock][batch]", + Embree_Raytracer, + GPRT_Raytracer) +{ + constexpr auto rt_backend = TestType::value; + + DYNAMIC_SECTION(fmt::format("Backend = {}", rt_backend)) { + check_ray_tracer_supported(rt_backend); + if (rt_backend == RTLibrary::EMBREE) { + SKIP("Skipping batch query mechanics test for Embree: batch API not implemented."); + } + + auto rti = create_raytracer(rt_backend); + REQUIRE(rti); + + auto mm = std::make_shared(false); + mm->init(); + REQUIRE(mm->mesh_library() == MeshLibrary::MOCK); + + auto [volume_tree, element_tree] = rti->register_volume(mm, mm->volumes()[0]); + REQUIRE(volume_tree != ID_NONE); + REQUIRE(element_tree == ID_NONE); + + rti->init(); + + std::vector origins; + std::vector directions; + size_t N; + + // ---- N = 0 ---- + SECTION("N=0 no-op") { + rti->ray_fire(volume_tree, nullptr, nullptr, 0, nullptr, nullptr, + INFTY, HitOrientation::EXITING, nullptr); + SUCCEED("N=0 completed without error"); + } + + // ---- N = 1 ---- + SECTION("N=1 equals scalar") { + N = 1; + make_rays(N, origins, directions); + + auto [dist_scalar, id_scalar] = rti->ray_fire(volume_tree, origins[0], directions[0], INFTY, HitOrientation::EXITING); + + double dist_batch = -1.0; + MeshID id_batch = ID_NONE; + rti->ray_fire(volume_tree, origins.data(), directions.data(), 1, + &dist_batch, &id_batch, INFTY, HitOrientation::EXITING, nullptr); + + REQUIRE(id_batch == id_scalar); + REQUIRE_THAT(dist_batch, Catch::Matchers::WithinAbs(dist_scalar, 1e-6)); + } + + // ---- N = 64 ---- + SECTION("N=64 matches scalar for all") { + N = 64; + make_rays(N, origins, directions); + + std::vector dist_scalar(64, INFTY); + std::vector id_scalar(64, ID_NONE); + for (size_t i = 0; i < 64; ++i) { + auto [d, id] = rti->ray_fire(volume_tree, origins[i], directions[i], INFTY, HitOrientation::EXITING); + dist_scalar[i] = d; id_scalar[i] = id; + } + + std::vector dist_batch(64, -1.0); + std::vector id_batch(64, ID_NONE); + rti->ray_fire(volume_tree, origins.data(), directions.data(), origins.size(), + dist_batch.data(), id_batch.data(), INFTY, HitOrientation::EXITING, nullptr); + + for (size_t i = 0; i < 64; ++i) { + REQUIRE(id_batch[i] == id_scalar[i]); + REQUIRE_THAT(dist_batch[i], Catch::Matchers::WithinAbs(dist_scalar[i], 1e-6)); + } + } + } +} diff --git a/tests/util.h b/tests/util.h index 579ff652..80964602 100644 --- a/tests/util.h +++ b/tests/util.h @@ -96,3 +96,20 @@ create_raytracer(xdg::RTLibrary rt) { return nullptr; } + +inline void make_rays(size_t N, std::vector& origins, std::vector& directions) +{ + origins.clear(); + directions.clear(); + origins.reserve(N); + directions.reserve(N); + for (size_t i = 0; i < N; ++i) { + int axis = static_cast(i % 3); + double s = (i % 2) ? 1.0 : -1.0; + origins.push_back({0.0, 0.0, 0.0}); + if (axis == 0) directions.push_back({s, 0.0, 0.0}); + else if (axis == 1) directions.push_back({0.0, s, 0.0}); + else directions.push_back({0.0, 0.0, s}); + } +} + diff --git a/vendor/GPRT b/vendor/GPRT index f1e95e41..405d9ee9 160000 --- a/vendor/GPRT +++ b/vendor/GPRT @@ -1 +1 @@ -Subproject commit f1e95e4188cde591547d6b4a33a70bf2afaeec59 +Subproject commit 405d9ee9f5ee8e1a0455f776f9e2c3adffb64160 From 2267795e4d661182dac73a6f91117ad8c268aae9 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Fri, 30 Jan 2026 12:33:01 +0000 Subject: [PATCH 2/4] Added test for point_in_volume_prepared() codepath --- tests/test_direct_ray_buffer_access.cpp | 146 ++++++++++++++++++------ tests/test_point_in_volume.cpp | 16 --- tests/util.h | 12 ++ 3 files changed, 121 insertions(+), 53 deletions(-) diff --git a/tests/test_direct_ray_buffer_access.cpp b/tests/test_direct_ray_buffer_access.cpp index 309ce9e9..e4c7edde 100644 --- a/tests/test_direct_ray_buffer_access.cpp +++ b/tests/test_direct_ray_buffer_access.cpp @@ -21,6 +21,50 @@ using namespace xdg::test; extern GPRTProgram test_direct_ray_buffer_access_deviceCode; +static RayPopulationCallback make_populate_callback(const std::vector& origins, + const std::vector& directions, + GPRTContext context, + GPRTComputeOf packRays, + MeshID volume_id) { + return [&origins, &directions, context, packRays, volume_id] + (const DeviceRayHitBuffers& buffer, size_t numRays) { + REQUIRE(origins.size() >= numRays); + REQUIRE(directions.size() >= numRays); + + // Convert to double3 for use on GPU + std::vector origins_device(numRays); + std::vector directions_device(numRays); + for (size_t i = 0; i < numRays; ++i) { + origins_device[i] = {origins[i].x, origins[i].y, origins[i].z}; + directions_device[i] = {directions[i].x, directions[i].y, directions[i].z}; + } + + auto origins_buffer = gprtDeviceBufferCreate(context, numRays, origins_device.data()); + auto directions_buffer = gprtDeviceBufferCreate(context, numRays, directions_device.data()); + + constexpr uint32_t threads_per_group = 256; + const uint32_t groups = static_cast((numRays + threads_per_group - 1) / threads_per_group); + + ExternalRayParams params = {}; + params.xdgRays = static_cast(buffer.rayDevPtr); + params.origins = gprtBufferGetDevicePointer(origins_buffer); + params.directions = gprtBufferGetDevicePointer(directions_buffer); + params.num_rays = static_cast(numRays); + params.total_threads = groups * threads_per_group; + params.volume_mesh_id = volume_id; + params.enabled = 1u; + + gprtComputeLaunch(packRays, + { groups, 1, 1 }, + { threads_per_group, 1, 1 }, + params); + gprtComputeSynchronize(context); + + gprtBufferDestroy(origins_buffer); + gprtBufferDestroy(directions_buffer); + }; +} + // This is a GPU only test - skip if no GPU ray tracing backends are enabled TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", GPRT_Raytracer) @@ -65,43 +109,11 @@ TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", expected_surfaces.data()); // Create callback to populate rays on device - RayPopulationCallback populate_callback = [&volume_id, &origins, &directions, &context, &packRays] - (const DeviceRayHitBuffers& buffer, size_t numRays) { - REQUIRE(origins.size() >= numRays); - REQUIRE(directions.size() >= numRays); - - // Convert to double3 for use on GPU - std::vector origins_device(numRays); - std::vector directions_device(numRays); - for (size_t i = 0; i < numRays; ++i) { - origins_device[i] = {origins[i].x, origins[i].y, origins[i].z}; - directions_device[i] = {directions[i].x, directions[i].y, directions[i].z}; - } - - auto origins_buffer = gprtDeviceBufferCreate(context, numRays, origins_device.data()); - auto directions_buffer = gprtDeviceBufferCreate(context, numRays, directions_device.data()); - - constexpr uint32_t threads_per_group = 256; - const uint32_t groups = static_cast((numRays + threads_per_group - 1) / threads_per_group); - - ExternalRayParams params = {}; - params.xdgRays = static_cast(buffer.rayDevPtr); - params.origins = gprtBufferGetDevicePointer(origins_buffer); - params.directions = gprtBufferGetDevicePointer(directions_buffer); - params.num_rays = static_cast(numRays); - params.total_threads = groups * threads_per_group; - params.volume_mesh_id = volume_id; - params.enabled = 1u; - - gprtComputeLaunch(packRays, - { groups, 1, 1 }, - { threads_per_group, 1, 1 }, - params); - gprtComputeSynchronize(context); - - gprtBufferDestroy(origins_buffer); - gprtBufferDestroy(directions_buffer); - }; + RayPopulationCallback populate_callback = make_populate_callback(origins, + directions, + context, + packRays, + volume_id); // Populate rays via external API xdg->populate_rays_external(N, populate_callback); @@ -122,3 +134,63 @@ TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", gprtModuleDestroy(module); } } + +TEMPLATE_TEST_CASE("Point-in-volume with external populated rays", "[piv][mock]", + GPRT_Raytracer) +{ + constexpr auto rt_backend = TestType::value; + + DYNAMIC_SECTION(fmt::format("Backend = {}", rt_backend)) { + check_ray_tracer_supported(rt_backend); + + auto rti = create_raytracer(rt_backend); + REQUIRE(rti); + + auto mm = std::make_shared(false); + mm->init(); + REQUIRE(mm->mesh_library() == MeshLibrary::MOCK); + + auto xdg = std::make_shared(); + xdg->set_mesh_manager_interface(mm); + xdg->set_ray_tracing_interface(rti); + xdg->prepare_raytracer(); + + std::vector points; + std::vector directions; + size_t N = 64; + make_points(N, points, directions); + + auto gprt_rt = std::dynamic_pointer_cast(rti); + REQUIRE(gprt_rt); + + const MeshID volume_id = mm->volumes()[0]; + GPRTContext context = gprt_rt->context(); + GPRTModule module = gprtModuleCreate(context, test_direct_ray_buffer_access_deviceCode); + auto packRays = gprtComputeCreate(context, module, "pack_external_rays"); + + std::vector expected_piv(N, 0); + for (size_t i = 0; i < N; ++i) { + expected_piv[i] = static_cast(xdg->point_in_volume(volume_id, points[i], &directions[i])); + } + + RayPopulationCallback populate_callback = make_populate_callback(points, + directions, + context, + packRays, + volume_id); + xdg->populate_rays_external(N, populate_callback); + + xdg->point_in_volume_prepared(N); + std::vector hits; + xdg->transfer_hits_buffer_to_host(N, hits); + + REQUIRE(hits.size() == N); + for (size_t i = 0; i < N; ++i) { + const auto expected = expected_piv[i] ? xdg::PointInVolume::INSIDE : xdg::PointInVolume::OUTSIDE; + REQUIRE(hits[i].piv == expected); + } + + gprtComputeDestroy(packRays); + gprtModuleDestroy(module); + } +} diff --git a/tests/test_point_in_volume.cpp b/tests/test_point_in_volume.cpp index 7e771499..acb62a41 100644 --- a/tests/test_point_in_volume.cpp +++ b/tests/test_point_in_volume.cpp @@ -12,21 +12,6 @@ using namespace xdg; using namespace xdg::test; -static void make_points(size_t N, - std::vector& points, - std::vector& directions) -{ - points.resize(N); - directions.resize(N); - for (size_t i = 0; i < N; ++i) { - // evens inside (origin), odds just outside +X - points[i] = (i % 2 == 0) ? xdg::Position{0,0,0} : xdg::Position{5.1,0,0}; - // alternate ±X directions - directions[i] = (i % 2 == 0) ? xdg::Direction{ 1,0,0} - : xdg::Direction{-1,0,0}; - } -} - // ---------- single test, sections per backend -------------------------------- TEMPLATE_TEST_CASE("Point-in-volume on MeshMock", "[piv][mock]", @@ -160,4 +145,3 @@ TEMPLATE_TEST_CASE("Batch API Point-in-volume on MeshMock", "[piv][mock][batch]" } } } - diff --git a/tests/util.h b/tests/util.h index 80964602..58d81d8f 100644 --- a/tests/util.h +++ b/tests/util.h @@ -113,3 +113,15 @@ inline void make_rays(size_t N, std::vector& origins, std::vector } } +inline void make_points(size_t N, std::vector& points, std::vector& directions) +{ + points.resize(N); + directions.resize(N); + for (size_t i = 0; i < N; ++i) { + // evens inside (origin), odds just outside +X + points[i] = (i % 2 == 0) ? xdg::Position{0.0, 0.0, 0.0} : xdg::Position{5.1, 0.0, 0.0}; + // alternate ±X directions + directions[i] = (i % 2 == 0) ? xdg::Direction{1.0, 0.0, 0.0} + : xdg::Direction{-1.0, 0.0, 0.0}; + } +} From 709b5c5c63af0c9f7f23e1556de7f16d848a7b02 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Tue, 3 Feb 2026 14:27:28 +0000 Subject: [PATCH 3/4] Updated direct_buffer_access test to also test multi-volume --- tests/test_direct_ray_buffer_access.cpp | 116 +++++++++--------- ..._direct_ray_buffer_access_deviceCode.slang | 10 +- tests/test_direct_ray_buffer_access_shared.h | 3 +- 3 files changed, 70 insertions(+), 59 deletions(-) diff --git a/tests/test_direct_ray_buffer_access.cpp b/tests/test_direct_ray_buffer_access.cpp index e4c7edde..2583815c 100644 --- a/tests/test_direct_ray_buffer_access.cpp +++ b/tests/test_direct_ray_buffer_access.cpp @@ -23,35 +23,38 @@ extern GPRTProgram test_direct_ray_buffer_access_deviceCode; static RayPopulationCallback make_populate_callback(const std::vector& origins, const std::vector& directions, + const std::vector& volume_ids, GPRTContext context, - GPRTComputeOf packRays, - MeshID volume_id) { - return [&origins, &directions, context, packRays, volume_id] - (const DeviceRayHitBuffers& buffer, size_t numRays) { - REQUIRE(origins.size() >= numRays); - REQUIRE(directions.size() >= numRays); + GPRTComputeOf packRays) { + return [&origins, &directions, volume_ids, context, packRays] + (const DeviceRayHitBuffers& buffer, size_t num_rays) { + + // When passing arrays to the callback, ensure they are the correct size + assert(origins.size() == num_rays); + assert(directions.size() == num_rays); // Convert to double3 for use on GPU - std::vector origins_device(numRays); - std::vector directions_device(numRays); - for (size_t i = 0; i < numRays; ++i) { + std::vector origins_device(num_rays); + std::vector directions_device(num_rays); + for (size_t i = 0; i < num_rays; ++i) { origins_device[i] = {origins[i].x, origins[i].y, origins[i].z}; directions_device[i] = {directions[i].x, directions[i].y, directions[i].z}; } - auto origins_buffer = gprtDeviceBufferCreate(context, numRays, origins_device.data()); - auto directions_buffer = gprtDeviceBufferCreate(context, numRays, directions_device.data()); + auto origins_buffer = gprtDeviceBufferCreate(context, num_rays, origins_device.data()); + auto directions_buffer = gprtDeviceBufferCreate(context, num_rays, directions_device.data()); + auto volume_ids_buffer = gprtDeviceBufferCreate(context, num_rays, volume_ids.data()); constexpr uint32_t threads_per_group = 256; - const uint32_t groups = static_cast((numRays + threads_per_group - 1) / threads_per_group); + const uint32_t groups = static_cast((num_rays + threads_per_group - 1) / threads_per_group); ExternalRayParams params = {}; params.xdgRays = static_cast(buffer.rayDevPtr); params.origins = gprtBufferGetDevicePointer(origins_buffer); params.directions = gprtBufferGetDevicePointer(directions_buffer); - params.num_rays = static_cast(numRays); + params.num_rays = static_cast(num_rays); params.total_threads = groups * threads_per_group; - params.volume_mesh_id = volume_id; + params.volume_mesh_ids = gprtBufferGetDevicePointer(volume_ids_buffer); params.enabled = 1u; gprtComputeLaunch(packRays, @@ -62,6 +65,9 @@ static RayPopulationCallback make_populate_callback(const std::vector& gprtBufferDestroy(origins_buffer); gprtBufferDestroy(directions_buffer); + if (volume_ids_buffer) { + gprtBufferDestroy(volume_ids_buffer); + } }; } @@ -72,18 +78,13 @@ TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", constexpr auto rt_backend = TestType::value; DYNAMIC_SECTION(fmt::format("Backend = {}", rt_backend)) { - check_ray_tracer_supported(rt_backend); - - auto rti = create_raytracer(rt_backend); - REQUIRE(rti); - - auto mm = std::make_shared(false); - mm->init(); - REQUIRE(mm->mesh_library() == MeshLibrary::MOCK); - - auto xdg = std::make_shared(); - xdg->set_mesh_manager_interface(mm); - xdg->set_ray_tracing_interface(rti); + check_ray_tracer_supported(rt_backend); // skip if backend not enabled at configuration time + std::shared_ptr xdg = XDG::create(MeshLibrary::MOAB, rt_backend); + REQUIRE(xdg->ray_tracing_interface()->library() == rt_backend); + REQUIRE(xdg->mesh_manager()->mesh_library() == MeshLibrary::MOAB); + const auto& mesh_manager = xdg->mesh_manager(); + mesh_manager->load_file("jezebel.h5m"); + mesh_manager->init(); xdg->prepare_raytracer(); std::vector origins; @@ -91,29 +92,34 @@ TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", size_t N = 64; make_rays(N, origins, directions); - auto gprt_rt = std::dynamic_pointer_cast(rti); + auto gprt_rt = std::dynamic_pointer_cast(xdg->ray_tracing_interface()); REQUIRE(gprt_rt); - const MeshID volume_id = mm->volumes()[0]; + std::vector volumes = mesh_manager->volumes(); + REQUIRE(volumes.size() >= 2); + const MeshID volume_id = volumes[0]; + const MeshID volume_id_alt = volumes[1]; GPRTContext context = gprt_rt->context(); GPRTModule module = gprtModuleCreate(context, test_direct_ray_buffer_access_deviceCode); auto packRays = gprtComputeCreate(context, module, "pack_external_rays"); std::vector expected_distances(N, INFTY); std::vector expected_surfaces(N, ID_NONE); - xdg->ray_fire(volume_id, - origins.data(), - directions.data(), - N, - expected_distances.data(), - expected_surfaces.data()); + + std::vector volume_ids(N, volume_id); + for (size_t i = 0; i < N; ++i) { + volume_ids[i] = (i % 2 == 0) ? volume_id : volume_id_alt; + const auto [dist, surf] = xdg->ray_fire(volume_ids[i], origins[i], directions[i]); + expected_distances[i] = dist; + expected_surfaces[i] = surf; + } // Create callback to populate rays on device RayPopulationCallback populate_callback = make_populate_callback(origins, - directions, - context, - packRays, - volume_id); + directions, + volume_ids, + context, + packRays); // Populate rays via external API xdg->populate_rays_external(N, populate_callback); @@ -141,18 +147,13 @@ TEMPLATE_TEST_CASE("Point-in-volume with external populated rays", "[piv][mock]" constexpr auto rt_backend = TestType::value; DYNAMIC_SECTION(fmt::format("Backend = {}", rt_backend)) { - check_ray_tracer_supported(rt_backend); - - auto rti = create_raytracer(rt_backend); - REQUIRE(rti); - - auto mm = std::make_shared(false); - mm->init(); - REQUIRE(mm->mesh_library() == MeshLibrary::MOCK); - - auto xdg = std::make_shared(); - xdg->set_mesh_manager_interface(mm); - xdg->set_ray_tracing_interface(rti); + check_ray_tracer_supported(rt_backend); // skip if backend not enabled at configuration time + std::shared_ptr xdg = XDG::create(MeshLibrary::MOAB, rt_backend); + REQUIRE(xdg->ray_tracing_interface()->library() == rt_backend); + REQUIRE(xdg->mesh_manager()->mesh_library() == MeshLibrary::MOAB); + const auto& mesh_manager = xdg->mesh_manager(); + mesh_manager->load_file("jezebel.h5m"); + mesh_manager->init(); xdg->prepare_raytracer(); std::vector points; @@ -160,24 +161,29 @@ TEMPLATE_TEST_CASE("Point-in-volume with external populated rays", "[piv][mock]" size_t N = 64; make_points(N, points, directions); - auto gprt_rt = std::dynamic_pointer_cast(rti); + auto gprt_rt = std::dynamic_pointer_cast(xdg->ray_tracing_interface()); REQUIRE(gprt_rt); - const MeshID volume_id = mm->volumes()[0]; + std::vector volumes = mesh_manager->volumes(); + REQUIRE(volumes.size() >= 2); + const MeshID volume_id = volumes[0]; + const MeshID volume_id_alt = volumes[1]; GPRTContext context = gprt_rt->context(); GPRTModule module = gprtModuleCreate(context, test_direct_ray_buffer_access_deviceCode); auto packRays = gprtComputeCreate(context, module, "pack_external_rays"); std::vector expected_piv(N, 0); + std::vector volume_ids(N, volume_id); for (size_t i = 0; i < N; ++i) { - expected_piv[i] = static_cast(xdg->point_in_volume(volume_id, points[i], &directions[i])); + volume_ids[i] = (i % 2 == 0) ? volume_id : volume_id_alt; + expected_piv[i] = static_cast(xdg->point_in_volume(volume_ids[i], points[i], &directions[i])); } RayPopulationCallback populate_callback = make_populate_callback(points, directions, + volume_ids, context, - packRays, - volume_id); + packRays); xdg->populate_rays_external(N, populate_callback); xdg->point_in_volume_prepared(N); diff --git a/tests/test_direct_ray_buffer_access_deviceCode.slang b/tests/test_direct_ray_buffer_access_deviceCode.slang index a3e8729a..8fae4b7c 100644 --- a/tests/test_direct_ray_buffer_access_deviceCode.slang +++ b/tests/test_direct_ray_buffer_access_deviceCode.slang @@ -16,9 +16,15 @@ void pack_external_rays(uint3 DispatchThreadID: SV_DispatchThreadID, r.direction = extParams.directions[idx]; r.exclude_primitives = nullptr; r.exclude_count = 0; - r.volume_mesh_id = extParams.volume_mesh_id; + r.volume_mesh_id = extParams.volume_mesh_ids[idx]; // Set volume mesh ID per ray r.enabled = extParams.enabled; - extParams.xdgRays[idx] = r; + printf("Ray %u: Origin=(%f, %f, %f), Direction=(%f, %f, %f), VolumeMeshID=%d\n", + idx, + r.origin.x, r.origin.y, r.origin.z, + r.direction.x, r.direction.y, r.direction.z, + r.volume_mesh_id); + + extParams.xdgRays[idx] = r; // Write to device ray buffer } } diff --git a/tests/test_direct_ray_buffer_access_shared.h b/tests/test_direct_ray_buffer_access_shared.h index a8e5d553..e8d5ceb3 100644 --- a/tests/test_direct_ray_buffer_access_shared.h +++ b/tests/test_direct_ray_buffer_access_shared.h @@ -8,7 +8,6 @@ struct ExternalRayParams { double3* directions; uint num_rays; uint total_threads; - int volume_mesh_id; + int32_t* volume_mesh_ids; uint enabled; }; - From 0c8f3d4a54eae15cdd99dd083016bbdb1bf38659 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Tue, 3 Feb 2026 15:46:01 +0000 Subject: [PATCH 4/4] Fixed multi-volume tests --- tests/test_direct_ray_buffer_access.cpp | 9 ++++----- tests/test_direct_ray_buffer_access_deviceCode.slang | 6 ------ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/test_direct_ray_buffer_access.cpp b/tests/test_direct_ray_buffer_access.cpp index 2583815c..97a96a11 100644 --- a/tests/test_direct_ray_buffer_access.cpp +++ b/tests/test_direct_ray_buffer_access.cpp @@ -54,7 +54,7 @@ static RayPopulationCallback make_populate_callback(const std::vector& params.directions = gprtBufferGetDevicePointer(directions_buffer); params.num_rays = static_cast(num_rays); params.total_threads = groups * threads_per_group; - params.volume_mesh_ids = gprtBufferGetDevicePointer(volume_ids_buffer); + params.volume_mesh_ids = gprtBufferGetDevicePointer(volume_ids_buffer); // Pass array of volume IDs to compute shader params.enabled = 1u; gprtComputeLaunch(packRays, @@ -105,10 +105,9 @@ TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", std::vector expected_distances(N, INFTY); std::vector expected_surfaces(N, ID_NONE); - std::vector volume_ids(N, volume_id); for (size_t i = 0; i < N; ++i) { - volume_ids[i] = (i % 2 == 0) ? volume_id : volume_id_alt; + volume_ids[i] = (i % 2 == 0) ? volume_id : volume_id_alt; // Volume IDs alternating between two volumes const auto [dist, surf] = xdg->ray_fire(volume_ids[i], origins[i], directions[i]); expected_distances[i] = dist; expected_surfaces[i] = surf; @@ -124,7 +123,7 @@ TEMPLATE_TEST_CASE("Ray Fire with external populated rays", "[rayfire][mock]", // Populate rays via external API xdg->populate_rays_external(N, populate_callback); - xdg->ray_fire_prepared(volume_id, N); + xdg->ray_fire_prepared(N); std::vector hits; xdg->transfer_hits_buffer_to_host(N, hits); @@ -192,7 +191,7 @@ TEMPLATE_TEST_CASE("Point-in-volume with external populated rays", "[piv][mock]" REQUIRE(hits.size() == N); for (size_t i = 0; i < N; ++i) { - const auto expected = expected_piv[i] ? xdg::PointInVolume::INSIDE : xdg::PointInVolume::OUTSIDE; + const auto expected = expected_piv[i] ? xdg::PointInVolume::INSIDE : xdg::PointInVolume::OUTSIDE; // convert back to enum REQUIRE(hits[i].piv == expected); } diff --git a/tests/test_direct_ray_buffer_access_deviceCode.slang b/tests/test_direct_ray_buffer_access_deviceCode.slang index 8fae4b7c..b61e6126 100644 --- a/tests/test_direct_ray_buffer_access_deviceCode.slang +++ b/tests/test_direct_ray_buffer_access_deviceCode.slang @@ -19,12 +19,6 @@ void pack_external_rays(uint3 DispatchThreadID: SV_DispatchThreadID, r.volume_mesh_id = extParams.volume_mesh_ids[idx]; // Set volume mesh ID per ray r.enabled = extParams.enabled; - printf("Ray %u: Origin=(%f, %f, %f), Direction=(%f, %f, %f), VolumeMeshID=%d\n", - idx, - r.origin.x, r.origin.y, r.origin.z, - r.direction.x, r.direction.y, r.direction.z, - r.volume_mesh_id); - extParams.xdgRays[idx] = r; // Write to device ray buffer } }