xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/ray_query/vktRayQueryStressTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2015 The Khronos Group Inc.
6  * Copyright (c) 2020 Advanced Micro Devices, Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21 * \file
22 * \brief ray query stress tests for VK_KHR_ray_query utility functions
23 *//*--------------------------------------------------------------------*/
24 
25 #include "vkRayTracingUtil.hpp"
26 #include "tcuTestCase.hpp"
27 #include "tcuSurface.hpp"
28 #include "vktTestCase.hpp"
29 #include "vktTestCaseUtil.hpp"
30 #include "vktRayQueryStressTests.hpp"
31 #include <iostream>
32 #include <cmath>
33 #ifndef M_PI
34 #define M_PI 3.1415926535897932384626433832795
35 #endif
36 
37 namespace vkt
38 {
39 namespace RayQuery
40 {
41 
42 namespace
43 {
44 
45 using namespace vk;
46 constexpr float MAX_T_VALUE = 10000000.0;
47 
48 enum class TestType
49 {
50     TRIANGLES,
51     AABBS,
52 };
53 
54 struct StressTestParams
55 {
56     TestType testType;
57 };
58 
59 struct ResultData
60 {
ResultDatavkt::RayQuery::__anon7a987cf30111::ResultData61     ResultData() : x(0.f), y(0.f), z(0.f), w(0.f)
62     {
63     }
ResultDatavkt::RayQuery::__anon7a987cf30111::ResultData64     ResultData(const float ix) : x(ix), y(ix), z(ix), w(ix)
65     {
66     }
ResultDatavkt::RayQuery::__anon7a987cf30111::ResultData67     ResultData(const float ix, const float iy, const float iz, const float iw) : x(ix), y(iy), z(iz), w(iw)
68     {
69     }
equalvkt::RayQuery::__anon7a987cf30111::ResultData70     bool equal(ResultData other)
71     {
72         const float epsilon = 0.000001f;
73         return ((abs(other.x - x) < epsilon) && (abs(other.y - y) < epsilon) && (abs(other.z - z) < epsilon) &&
74                 (abs(other.w - w) < epsilon));
75     }
76 
77     float x;
78     float y;
79     float z;
80     float w;
81 };
82 
83 class RayQueryStressCase : public TestCase
84 {
85 public:
86     RayQueryStressCase(tcu::TestContext &testCtx, const std::string &name, const RayQueryTestParams &rayQueryParams,
87                        const StressTestParams &stressParams);
~RayQueryStressCase(void)88     virtual ~RayQueryStressCase(void)
89     {
90     }
91 
92     virtual void checkSupport(Context &context) const;
93     virtual void initPrograms(vk::SourceCollections &programCollection) const;
94     virtual TestInstance *createInstance(Context &context) const;
95 
96 protected:
97     RayQueryTestParams m_rayQueryParams;
98     StressTestParams m_stressParams;
99 };
100 
101 class RayQueryStressInstance : public TestInstance
102 {
103 public:
104     RayQueryStressInstance(Context &context, const RayQueryTestParams &rayQueryParams,
105                            const StressTestParams &stressParams);
~RayQueryStressInstance(void)106     virtual ~RayQueryStressInstance(void)
107     {
108     }
109 
110     virtual tcu::TestStatus iterate(void);
111 
112 protected:
113     RayQueryTestParams m_rayQueryParams;
114     StressTestParams m_stressParams;
115 };
116 
RayQueryStressCase(tcu::TestContext & testCtx,const std::string & name,const RayQueryTestParams & rayQueryParams,const StressTestParams & stressParams)117 RayQueryStressCase::RayQueryStressCase(tcu::TestContext &testCtx, const std::string &name,
118                                        const RayQueryTestParams &rayQueryParams, const StressTestParams &stressParams)
119     : TestCase(testCtx, name)
120     , m_rayQueryParams(rayQueryParams)
121     , m_stressParams(stressParams)
122 {
123 }
124 
checkSupport(Context & context) const125 void RayQueryStressCase::checkSupport(Context &context) const
126 {
127     context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
128     context.requireDeviceFunctionality("VK_KHR_ray_query");
129 
130     const VkPhysicalDeviceRayQueryFeaturesKHR &rayQueryFeaturesKHR = context.getRayQueryFeatures();
131     if (rayQueryFeaturesKHR.rayQuery == false)
132         TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceRayQueryFeaturesKHR.rayQuery");
133 
134     const VkPhysicalDeviceAccelerationStructureFeaturesKHR &accelerationStructureFeaturesKHR =
135         context.getAccelerationStructureFeatures();
136     if (accelerationStructureFeaturesKHR.accelerationStructure == false)
137         TCU_THROW(TestError,
138                   "VK_KHR_ray_query requires VkPhysicalDeviceAccelerationStructureFeaturesKHR.accelerationStructure");
139 
140     const VkPhysicalDeviceFeatures2 &features2 = context.getDeviceFeatures2();
141 
142     if ((m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::TESSELLATION_CONTROL ||
143          m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::TESSELLATION_EVALUATION) &&
144         features2.features.tessellationShader == false)
145         TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceFeatures2.tessellationShader");
146 
147     if (m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::GEOMETRY &&
148         features2.features.geometryShader == false)
149         TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceFeatures2.geometryShader");
150 
151     switch (m_rayQueryParams.shaderSourceType)
152     {
153     case RayQueryShaderSourceType::VERTEX:
154     case RayQueryShaderSourceType::TESSELLATION_CONTROL:
155     case RayQueryShaderSourceType::TESSELLATION_EVALUATION:
156     case RayQueryShaderSourceType::GEOMETRY:
157         context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
158         break;
159     default:
160         break;
161     }
162 
163     if (m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::RAY_GENERATION ||
164         m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::RAY_GENERATION_RT ||
165         m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::INTERSECTION ||
166         m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::ANY_HIT ||
167         m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::CLOSEST_HIT ||
168         m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::MISS ||
169         m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::CALLABLE)
170     {
171         context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
172 
173         const VkPhysicalDeviceRayTracingPipelineFeaturesKHR &rayTracingPipelineFeaturesKHR =
174             context.getRayTracingPipelineFeatures();
175 
176         if (rayTracingPipelineFeaturesKHR.rayTracingPipeline == false)
177             TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceRayTracingPipelineFeaturesKHR.rayTracingPipeline");
178     }
179 }
180 
initPrograms(vk::SourceCollections & programCollection) const181 void RayQueryStressCase::initPrograms(vk::SourceCollections &programCollection) const
182 {
183     std::string rayQueryPart = "";
184     std::string ifPart       = "";
185     std::string elsePart     = "";
186     std::ostringstream src;
187 
188     if (m_rayQueryParams.shaderSourceType != RayQueryShaderSourceType::RAY_GENERATION_RT)
189     {
190         src << "   Ray ray = rays[index];\n"
191                "   float x = "
192             << MAX_T_VALUE * 2
193             << ";\n"
194                "   float y = "
195             << MAX_T_VALUE * 2
196             << ";\n"
197                "   float z = index;\n"
198                "   float w = ray.pos.z;\n"
199                "   rayQueryEXT rayQuery;\n"
200                "   rayQueryInitializeEXT(rayQuery, scene, "
201             << m_rayQueryParams.rayFlags
202             << ", 0xFF, ray.pos, ray.tmin, ray.dir, ray.tmax);\n"
203                "   while (rayQueryProceedEXT(rayQuery))\n"
204                "   {\n"
205                "       if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == "
206                "gl_RayQueryCandidateIntersectionTriangleEXT)\n"
207                "       {\n"
208                "           rayQueryConfirmIntersectionEXT(rayQuery);\n"
209                "       }\n"
210                "       else if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == "
211                "gl_RayQueryCandidateIntersectionAABBEXT)\n"
212                "       {\n"
213                "           float t = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, false) - index + 0.5f;\n"
214                "           if (t < rayQueryGetIntersectionTEXT(rayQuery, true))"
215                "           {\n"
216                "                rayQueryGenerateIntersectionEXT(rayQuery, t);\n"
217                "           }\n"
218                "       }\n"
219                "   }\n"
220                "\n"
221                "   if ((rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT) "
222                "||\n"
223                "       (rayQueryGetIntersectionTypeEXT(rayQuery, true) == "
224                "gl_RayQueryCommittedIntersectionGeneratedEXT))\n"
225                "   {\n"
226                "       x = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);\n"
227                "       y = rayQueryGetIntersectionTEXT(rayQuery, true);\n"
228                "   }\n"
229                "   rayQueryTerminateEXT(rayQuery);\n";
230     }
231     else if (m_rayQueryParams.shaderSourceType == RayQueryShaderSourceType::RAY_GENERATION_RT)
232     {
233         src << "   Ray ray = rays[index];\n"
234                "   float x = "
235             << MAX_T_VALUE * 2
236             << ";\n"
237                "   float y = "
238             << MAX_T_VALUE * 2
239             << ";\n"
240                "   float z = 0;\n"
241                "   float w = 0;\n"
242                "   traceRayEXT(scene, 0, 0xFF, 0, 0, 0, ray.pos, ray.tmin, ray.dir, ray.tmax, 0);\n"
243                "   x = payload.x;\n"
244                "   y = payload.y;\n"
245                "   z = payload.z;\n"
246                "   w = payload.w;\n";
247     }
248 
249     rayQueryPart = src.str();
250     generateRayQueryShaders(programCollection, m_rayQueryParams, rayQueryPart, MAX_T_VALUE);
251 }
252 
createInstance(Context & context) const253 TestInstance *RayQueryStressCase::createInstance(Context &context) const
254 {
255     return new RayQueryStressInstance(context, m_rayQueryParams, m_stressParams);
256 }
257 
RayQueryStressInstance(Context & context,const RayQueryTestParams & rayQueryParams,const StressTestParams & stressParams)258 RayQueryStressInstance::RayQueryStressInstance(Context &context, const RayQueryTestParams &rayQueryParams,
259                                                const StressTestParams &stressParams)
260     : vkt::TestInstance(context)
261     , m_rayQueryParams(rayQueryParams)
262     , m_stressParams(stressParams)
263 {
264 }
265 
iterate(void)266 tcu::TestStatus RayQueryStressInstance::iterate(void)
267 {
268     std::vector<tcu::Vec3> emptyVerts;
269 
270     /*    TestType::TRIANGLES
271         instance 0
272         PrimitiveIds and intersectionTs are used to verify correct intersection
273 
274         Triangle composed of triangles at increasing z-values - sort of triangular shaped stairs
275         This structure is repeated for 20000 levels with increasing z-values for each triangle.
276 
277         *   X-------------------X   *
278         *    \ \_    z=2      //    *
279         *     \   \_       /  /     *
280         *      \     \  /    /      *
281         *       \     O     /       *
282         *        \ z=0|z=1 /        *
283         *         \   |   /         *
284         *          \  |  /          *
285         *           \ | /           *
286         *            \|/            *
287         *             X             *
288 
289         The rays are shot at the center of gravity of the triangles in the xy plane and originated at z minus epsilon to hit the triangle at z.
290 
291         TestType::AABBS
292         each level is composed 0f 3 aabbs
293         there are 20000 levels at increasing z-values
294         60000 rays are shot at the center of each aabb originated at z minus epsilon to hit the aabb at (cx, cy, z)
295 
296               X-----------------X
297               |                 |
298               |       z=2       |
299               X--------O--------X
300               |        |        |
301               |  z=0   |  z=1   |
302               |        |        |
303               X--------X--------X
304     */
305 
306     const uint32_t numLevels        = 20000;
307     const uint32_t numPrimsPerLevel = 3;
308     const float alfa                = 2.0f * static_cast<float>(M_PI) / static_cast<float>(numPrimsPerLevel);
309     const uint32_t totalNumPrims    = numPrimsPerLevel * numLevels;
310     const float incrZ               = 1.0f;
311     const float epsilon             = incrZ / 10.f;
312     const float cosAlfa             = cos(alfa);
313     const float sinAlfa             = sin(alfa);
314     const float tanAlfaOver2        = abs(tan(alfa / 2.0f));
315     tcu::Vec2 p1(tanAlfaOver2, 1);
316     tcu::Vec2 p2(-tanAlfaOver2, 1);
317     float z = 0;
318 
319     m_rayQueryParams.rays.resize(totalNumPrims);
320     std::vector<ResultData> expectedResults(totalNumPrims);
321     std::vector<tcu::Vec3> instance1(m_stressParams.testType == TestType::TRIANGLES ? totalNumPrims * 3 :
322                                                                                       totalNumPrims * 2);
323 
324     for (uint32_t idx = 0; idx < totalNumPrims; ++idx)
325     {
326         tcu::Vec2 center;
327 
328         float x = p1[0] * cosAlfa - p1[1] * sinAlfa;
329         float y = p1[0] * sinAlfa + p1[1] * cosAlfa;
330         p1      = tcu::Vec2(x, y);
331         x       = p2[0] * cosAlfa - p2[1] * sinAlfa;
332         y       = p2[0] * sinAlfa + p2[1] * cosAlfa;
333         p2      = tcu::Vec2(x, y);
334 
335         if (m_stressParams.testType == TestType::TRIANGLES)
336         {
337             tcu::Vec3 v0(0, 0, z);
338             tcu::Vec3 v1(p1[0], p1[1], z);
339             tcu::Vec3 v2(p2[0], p2[1], z);
340 
341             center[0] = (p1[0] + p2[0]) / 3.f;
342             center[1] = (p1[1] + p2[1]) / 3.f;
343 
344             instance1[idx * 3]     = v0;
345             instance1[idx * 3 + 1] = v1;
346             instance1[idx * 3 + 2] = v2;
347 
348             expectedResults[idx] = ResultData(static_cast<float>(idx), epsilon, 0, 0);
349         }
350         else
351         {
352             tcu::Vec3 v0(de::min<float>(p1[0], p2[0]), de::min<float>(p1[1], p2[1]), z);
353             tcu::Vec3 v1(de::max<float>(p1[0], p2[0]), de::max<float>(p1[1], p2[1]), z);
354 
355             if (p1.y() > 0 && p2.y() > 0)
356             {
357                 //top box
358                 v0 = {v0.x(), de::min<float>(v1.y(), 0), z};
359             }
360             else
361             {
362                 //bottom boxes
363                 v1 = {v1.x(), de::min<float>(v1.y(), 0), z};
364             }
365 
366             center[0] = (v0[0] + v1[0]) / 2.f;
367             center[1] = (v0[1] + v1[1]) / 2.f;
368 
369             instance1[idx * 2]     = v0;
370             instance1[idx * 2 + 1] = v1;
371 
372             expectedResults[idx] = ResultData(static_cast<float>(idx), 0.5f, 0, 0);
373         }
374 
375         m_rayQueryParams.rays[idx] =
376             Ray{tcu::Vec3(center[0], center[1], z - epsilon), 0.0f, tcu::Vec3(0.0f, 0.0f, 1.0f), MAX_T_VALUE};
377 
378         z += incrZ;
379     }
380 
381     if (m_stressParams.testType == TestType::TRIANGLES)
382     {
383         m_rayQueryParams.verts.push_back(instance1);
384         m_rayQueryParams.aabbs.push_back(emptyVerts);
385     }
386     else
387     {
388         m_rayQueryParams.verts.push_back(emptyVerts);
389         m_rayQueryParams.aabbs.push_back(instance1);
390     }
391 
392     std::vector<ResultData> resultData;
393 
394     switch (m_rayQueryParams.pipelineType)
395     {
396     case RayQueryShaderSourcePipeline::COMPUTE:
397     {
398         resultData = rayQueryComputeTestSetup<ResultData>(
399             m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(),
400             m_context.getInstanceInterface(), m_context.getPhysicalDevice(), m_context.getBinaryCollection(),
401             m_context.getUniversalQueue(), m_context.getUniversalQueueFamilyIndex(), m_rayQueryParams);
402         break;
403     }
404     case RayQueryShaderSourcePipeline::RAYTRACING:
405     {
406         resultData = rayQueryRayTracingTestSetup<ResultData>(
407             m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(),
408             m_context.getInstanceInterface(), m_context.getPhysicalDevice(), m_context.getBinaryCollection(),
409             m_context.getUniversalQueue(), m_context.getUniversalQueueFamilyIndex(), m_rayQueryParams);
410         break;
411     }
412     case RayQueryShaderSourcePipeline::GRAPHICS:
413     {
414         resultData = rayQueryGraphicsTestSetup<ResultData>(
415             m_context.getDeviceInterface(), m_context.getDevice(), m_context.getUniversalQueueFamilyIndex(),
416             m_context.getDefaultAllocator(), m_context.getBinaryCollection(), m_context.getUniversalQueue(),
417             m_context.getInstanceInterface(), m_context.getPhysicalDevice(), m_rayQueryParams);
418         break;
419     }
420     default:
421     {
422         TCU_FAIL("Invalid shader type!");
423     }
424     }
425 
426     const uint32_t width  = numPrimsPerLevel;
427     const uint32_t height = numLevels;
428 
429     uint32_t index      = 0;
430     uint32_t mismatched = 0;
431 
432     tcu::Surface resultImage(width, height);
433     for (uint32_t x = 0; x < static_cast<uint32_t>(resultImage.getWidth()); ++x)
434     {
435         for (uint32_t y = 0; y < static_cast<uint32_t>(resultImage.getHeight()); ++y)
436         {
437             if ((resultData[index].x == expectedResults[index].x) &&
438                 (abs(resultData[index].y - expectedResults[index].y) < 0.2))
439             {
440                 resultImage.setPixel(x, y, tcu::RGBA(255, 0, 0, 255));
441             }
442             else
443             {
444                 mismatched++;
445                 resultImage.setPixel(x, y, tcu::RGBA(0, 0, 0, 255));
446             }
447             index++;
448         }
449     }
450 
451     // Write Image
452     m_context.getTestContext().getLog() << tcu::TestLog::ImageSet("Result of rendering", "Result of rendering")
453                                         << tcu::TestLog::Image("Result", "Result", resultImage)
454                                         << tcu::TestLog::EndImageSet;
455 
456     if (mismatched > 0)
457         TCU_FAIL("Result data did not match expected output");
458 
459     return tcu::TestStatus::pass("pass");
460 }
461 
462 } // anonymous namespace
463 
createRayQueryStressTests(tcu::TestContext & testCtx)464 tcu::TestCaseGroup *createRayQueryStressTests(tcu::TestContext &testCtx)
465 {
466     struct ShaderSourceTypeData
467     {
468         RayQueryShaderSourceType shaderSourceType;
469         RayQueryShaderSourcePipeline shaderSourcePipeline;
470         const char *name;
471     } shaderSourceTypes[] = {
472         {RayQueryShaderSourceType::VERTEX, RayQueryShaderSourcePipeline::GRAPHICS, "vertex_shader"},
473         {RayQueryShaderSourceType::TESSELLATION_CONTROL, RayQueryShaderSourcePipeline::GRAPHICS, "tess_control_shader"},
474         {RayQueryShaderSourceType::TESSELLATION_EVALUATION, RayQueryShaderSourcePipeline::GRAPHICS,
475          "tess_evaluation_shader"},
476         {
477             RayQueryShaderSourceType::GEOMETRY,
478             RayQueryShaderSourcePipeline::GRAPHICS,
479             "geometry_shader",
480         },
481         {
482             RayQueryShaderSourceType::FRAGMENT,
483             RayQueryShaderSourcePipeline::GRAPHICS,
484             "fragment_shader",
485         },
486         {
487             RayQueryShaderSourceType::COMPUTE,
488             RayQueryShaderSourcePipeline::COMPUTE,
489             "compute_shader",
490         },
491         {
492             RayQueryShaderSourceType::RAY_GENERATION,
493             RayQueryShaderSourcePipeline::RAYTRACING,
494             "rgen_shader",
495         },
496         {
497             RayQueryShaderSourceType::RAY_GENERATION_RT,
498             RayQueryShaderSourcePipeline::RAYTRACING,
499             "rgen_rt_shader",
500         },
501         {
502             RayQueryShaderSourceType::INTERSECTION,
503             RayQueryShaderSourcePipeline::RAYTRACING,
504             "isect_shader",
505         },
506         {
507             RayQueryShaderSourceType::ANY_HIT,
508             RayQueryShaderSourcePipeline::RAYTRACING,
509             "ahit_shader",
510         },
511         {
512             RayQueryShaderSourceType::CLOSEST_HIT,
513             RayQueryShaderSourcePipeline::RAYTRACING,
514             "chit_shader",
515         },
516         {
517             RayQueryShaderSourceType::MISS,
518             RayQueryShaderSourcePipeline::RAYTRACING,
519             "miss_shader",
520         },
521         {
522             RayQueryShaderSourceType::CALLABLE,
523             RayQueryShaderSourcePipeline::RAYTRACING,
524             "call_shader",
525         },
526     };
527 
528     struct
529     {
530         TestType testType;
531         const char *name;
532     } bottomTestTypes[] = {
533         {TestType::TRIANGLES, "triangles"},
534         {TestType::AABBS, "aabbs"},
535     };
536 
537     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "stress", "Ray query stress tests"));
538 
539     for (size_t shaderSourceNdx = 0; shaderSourceNdx < DE_LENGTH_OF_ARRAY(shaderSourceTypes); ++shaderSourceNdx)
540     {
541         de::MovePtr<tcu::TestCaseGroup> sourceTypeGroup(
542             new tcu::TestCaseGroup(group->getTestContext(), shaderSourceTypes[shaderSourceNdx].name, ""));
543         for (size_t bottomTestNdx = 0; bottomTestNdx < DE_LENGTH_OF_ARRAY(bottomTestTypes); ++bottomTestNdx)
544         {
545             RayQueryTestParams rayQueryTestParams{};
546             rayQueryTestParams.shaderSourceType = shaderSourceTypes[shaderSourceNdx].shaderSourceType;
547             rayQueryTestParams.pipelineType     = shaderSourceTypes[shaderSourceNdx].shaderSourcePipeline;
548             StressTestParams testParams{};
549             testParams.testType = bottomTestTypes[bottomTestNdx].testType;
550             sourceTypeGroup->addChild(new RayQueryStressCase(
551                 group->getTestContext(), bottomTestTypes[bottomTestNdx].name, rayQueryTestParams, testParams));
552         }
553         group->addChild(sourceTypeGroup.release());
554     }
555 
556     return group.release();
557 }
558 
559 } // namespace RayQuery
560 } // namespace vkt
561