1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2021 The Khronos Group Inc.
6  * Copyright (c) 2021 Valve Corporation.
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 Tracing Direction Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktRayTracingDirectionTests.hpp"
26 #include "vktTestCase.hpp"
27 
28 #include "vkObjUtil.hpp"
29 #include "vkCmdUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vkBuilderUtil.hpp"
32 #include "vkRayTracingUtil.hpp"
33 #include "vkBufferWithMemory.hpp"
34 #include "vkBarrierUtil.hpp"
35 
36 #include "tcuVector.hpp"
37 #include "tcuMatrix.hpp"
38 
39 #include "deUniquePtr.hpp"
40 #include "deRandom.hpp"
41 #include "deStringUtil.hpp"
42 #include "deDefs.hpp"
43 
44 #include <vector>
45 #include <cmath>
46 #include <sstream>
47 #include <utility>
48 #include <algorithm>
49 #include <limits>
50 
51 namespace vkt
52 {
53 namespace RayTracing
54 {
55 
56 namespace
57 {
58 
59 using namespace vk;
60 
61 using GeometryData = std::vector<tcu::Vec3>;
62 
63 // Should rays be shot from inside the geometry or not?
64 enum class RayOriginType
65 {
66     OUTSIDE = 0, // Works with AABBs and triangles.
67     INSIDE,      // Works with AABBs only.
68 };
69 
70 // When rays are shot from the outside, they are expected to cross the geometry.
71 // When shot from the inside, they can end inside, at the edge or outside the geometry.
72 enum class RayEndType
73 {
74     CROSS = 0, // For RayOriginType::OUTSIDE.
75     ZERO,      // For RayOriginType::INSIDE.
76     INSIDE,    // For RayOriginType::INSIDE.
77     EDGE,      // For RayOriginType::INSIDE.
78     OUTSIDE,   // For RayOriginType::INSIDE.
79 };
80 
81 struct SpaceObjects
82 {
83     tcu::Vec3 origin;
84     tcu::Vec3 direction;
85     GeometryData geometry;
86 
SpaceObjectsvkt::RayTracing::__anonad5c83140111::SpaceObjects87     SpaceObjects(RayOriginType rayOriginType, VkGeometryTypeKHR geometryType)
88         : origin(0.0f, 0.0f, 1.0f)    // Origin of the ray at (0, 0, 1).
89         , direction(0.0f, 0.0f, 1.0f) // Shooting towards (0, 0, 1).
90         , geometry()
91     {
92         DE_ASSERT(geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR || geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
93         DE_ASSERT(rayOriginType == RayOriginType::OUTSIDE || geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
94 
95         if (geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR)
96         {
97             // Triangle around (0, 0, 5).
98             geometry.reserve(3u);
99             geometry.push_back(tcu::Vec3(0.0f, 0.5f, 5.0f));
100             geometry.push_back(tcu::Vec3(-0.5f, -0.5f, 5.0f));
101             geometry.push_back(tcu::Vec3(0.5f, -0.5f, 5.0f));
102         }
103         else
104         {
105             // AABB around (0, 0, 5) or with its back side at that distance when shot from the inside.
106             geometry.reserve(2u);
107             geometry.push_back(tcu::Vec3(-0.5f, -0.5f, ((rayOriginType == RayOriginType::INSIDE) ? 0.0f : 5.0f)));
108             geometry.push_back(tcu::Vec3(0.5f, 0.5f, 5.0f));
109         }
110     }
111 
getDefaultDistancevkt::RayTracing::__anonad5c83140111::SpaceObjects112     static float getDefaultDistance(void)
113     {
114         // Consistent with the Z coordinates of the origin, direction and points in constructors.
115         return 4.0f;
116     }
117 
118     // Calculates distance to geometry edge given the direction scaling factor.
getDistanceToEdgevkt::RayTracing::__anonad5c83140111::SpaceObjects119     static float getDistanceToEdge(float directionScale)
120     {
121         return getDefaultDistance() / directionScale;
122     }
123 };
124 
125 // Default test tolerance for distance values.
126 constexpr float kDefaultTolerance = 0.001f;
127 
128 // Calculates appropriate values for Tmin/Tmax given the distance to the geometry edge.
calcTminTmax(RayOriginType rayOriginType,RayEndType rayEndType,float distanceToEdge)129 std::pair<float, float> calcTminTmax(RayOriginType rayOriginType, RayEndType rayEndType, float distanceToEdge)
130 {
131     std::pair<float, float> result;
132 
133     if (rayOriginType == RayOriginType::OUTSIDE)
134     {
135         DE_ASSERT(rayEndType == RayEndType::CROSS);
136         const auto margin = kDefaultTolerance / 2.0f;
137         result            = std::make_pair(de::max(distanceToEdge - margin, 0.0f), distanceToEdge + margin);
138     }
139     else
140     {
141         result.first = 0.0f;
142         switch (rayEndType)
143         {
144         case RayEndType::ZERO:
145             result.second = 0.0f;
146             break;
147         case RayEndType::INSIDE:
148             result.second = distanceToEdge / 2.0f;
149             break;
150         case RayEndType::EDGE:
151             result.second = distanceToEdge;
152             break;
153         case RayEndType::OUTSIDE:
154             result.second = distanceToEdge + 1.0f;
155             break;
156         default:
157             DE_ASSERT(false);
158             break;
159         }
160     }
161 
162     return result;
163 }
164 
165 // Get matrix to scale a point with the given scale factor.
getScaleMatrix(float scaleFactor)166 tcu::Mat3 getScaleMatrix(float scaleFactor)
167 {
168     const float scaleDirectionMatrixData[] = {
169         scaleFactor, 0.f, 0.f, 0.f, scaleFactor, 0.f, 0.f, 0.f, scaleFactor,
170     };
171     return tcu::Mat3(scaleDirectionMatrixData);
172 }
173 
174 // Get a matrix to rotate a point around the X and Y axis by the given angles in radians.
getRotationMatrix(float rotationX,float rotationY)175 tcu::Mat3 getRotationMatrix(float rotationX, float rotationY)
176 {
177     const float cosA = std::cos(rotationX);
178     const float sinA = std::sin(rotationX);
179 
180     const float cosB = std::cos(rotationY);
181     const float sinB = std::sin(rotationY);
182 
183     const float rotationMatrixDataX[] = {
184         1.0f, 0.0f, 0.0f, 0.0f, cosA, -sinA, 0.0f, sinA, cosA,
185     };
186     const tcu::Mat3 rotationMatrixX(rotationMatrixDataX);
187 
188     const float rotationMatrixDataY[] = {
189         cosB, 0.0f, -sinB, 0.0f, 1.0f, 0.0f, sinB, 0.0f, cosB,
190     };
191     const tcu::Mat3 rotationMatrixY(rotationMatrixDataY);
192 
193     return rotationMatrixX * rotationMatrixY;
194 }
195 
196 // Converts transformation matrix to the expected KHR format.
toTransformMatrixKHR(const tcu::Mat3 & mat3)197 VkTransformMatrixKHR toTransformMatrixKHR(const tcu::Mat3 &mat3)
198 {
199     VkTransformMatrixKHR result;
200 
201     deMemset(result.matrix, 0, sizeof(result.matrix));
202     for (int y = 0; y < 3; ++y)
203         for (int x = 0; x < 3; ++x)
204             result.matrix[x][y] = mat3[x][y];
205 
206     return result;
207 }
208 
209 struct TestParams
210 {
211     SpaceObjects spaceObjects;
212     float directionScale;
213     float rotationX;
214     float rotationY;
215     VkShaderStageFlagBits testStage;
216     VkGeometryTypeKHR geometryType;
217     bool useArraysOfPointers;
218     bool updateMatrixAfterBuild;
219     RayOriginType rayOriginType;
220     RayEndType rayEndtype;
221 
usedStagesvkt::RayTracing::__anonad5c83140111::TestParams222     VkShaderStageFlags usedStages(void) const
223     {
224         VkShaderStageFlags flags = (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR | testStage);
225 
226         if (geometryType == VK_GEOMETRY_TYPE_AABBS_KHR)
227             flags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
228 
229         return flags;
230     }
231 
232     // True if we are testing the intersection shader.
isecMainvkt::RayTracing::__anonad5c83140111::TestParams233     bool isecMain(void) const
234     {
235         return (testStage == VK_SHADER_STAGE_INTERSECTION_BIT_KHR);
236     }
237 
238     // True if the intersection shader is needed as an auxiliar shader.
isecAuxvkt::RayTracing::__anonad5c83140111::TestParams239     bool isecAux(void) const
240     {
241         return (!isecMain() && geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
242     }
243 
244     // True if the intersection shader is used in some capacity.
isecUsedvkt::RayTracing::__anonad5c83140111::TestParams245     bool isecUsed(void) const
246     {
247         return (isecMain() || isecAux());
248     }
249 };
250 
251 class DirectionTestCase : public vkt::TestCase
252 {
253 public:
254     DirectionTestCase(tcu::TestContext &testCtx, const std::string &name, const TestParams &params);
~DirectionTestCase(void)255     virtual ~DirectionTestCase(void)
256     {
257     }
258 
259     virtual void checkSupport(Context &context) const;
260     virtual void initPrograms(vk::SourceCollections &programCollection) const;
261     virtual TestInstance *createInstance(Context &context) const;
262 
263 protected:
264     TestParams m_params;
265 };
266 
267 class DirectionTestInstance : public vkt::TestInstance
268 {
269 public:
270     DirectionTestInstance(Context &context, const TestParams &params);
~DirectionTestInstance(void)271     virtual ~DirectionTestInstance(void)
272     {
273     }
274 
275     virtual tcu::TestStatus iterate(void);
276 
277 protected:
278     TestParams m_params;
279 };
280 
DirectionTestCase(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)281 DirectionTestCase::DirectionTestCase(tcu::TestContext &testCtx, const std::string &name, const TestParams &params)
282     : vkt::TestCase(testCtx, name)
283     , m_params(params)
284 {
285 }
286 
checkSupport(Context & context) const287 void DirectionTestCase::checkSupport(Context &context) const
288 {
289     context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
290     context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
291 }
292 
293 // Push constants. They need to match the shaders.
294 // Note: origin and direction will be used as a Vec3. Declaring them as Vec4 eases matching alignments.
295 struct PushConstants
296 {
297     tcu::Vec4 origin;
298     tcu::Vec4 direction;
299     float tmix;
300     float tmax;
301 };
302 
toVec4(const tcu::Vec3 & vec3)303 tcu::Vec4 toVec4(const tcu::Vec3 &vec3)
304 {
305     return tcu::Vec4(vec3.x(), vec3.y(), vec3.z(), 0.0f);
306 }
307 
initPrograms(vk::SourceCollections & programCollection) const308 void DirectionTestCase::initPrograms(vk::SourceCollections &programCollection) const
309 {
310     const vk::ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
311 
312     std::ostringstream rgen;
313     rgen << "#version 460 core\n"
314          << "#extension GL_EXT_ray_tracing : require\n"
315          << "layout(location=0) rayPayloadEXT vec3 hitValue;\n"
316          << "layout(set=0, binding=0) uniform accelerationStructureEXT topLevelAS;\n"
317          // Needs to match the PushConstants struct above.
318          << "layout(push_constant, std430) uniform PushConstants {\n"
319          << "  vec4 origin;\n"
320          << "  vec4 direction;\n"
321          << "  float tmin;\n"
322          << "  float tmax;\n"
323          << "} pc;\n"
324          << "\n"
325          << "void main()\n"
326          << "{\n"
327          << "  const uint cullMask = 0xFF;\n"
328          << "  traceRayEXT(topLevelAS, gl_RayFlagsNoneEXT, cullMask, 0, 0, 0, pc.origin.xyz, pc.tmin, "
329             "pc.direction.xyz, pc.tmax, 0);\n"
330          << "}\n";
331 
332     programCollection.glslSources.add("rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
333 
334     const bool isecTest          = m_params.isecMain();
335     const std::string bufferDecl = "layout(set=0, binding=1, std430) buffer OutBuffer { float val; } outBuffer;\n";
336 
337     std::ostringstream isec;
338     isec << "#version 460 core\n"
339          << "#extension GL_EXT_ray_tracing : require\n"
340          << "hitAttributeEXT vec3 hitAttribute;\n"
341          << (isecTest ? bufferDecl : "") << "void main()\n"
342          << "{\n"
343          << "  hitAttribute = vec3(0.0f, 0.0f, 0.0f);\n"
344          << (isecTest ? "  outBuffer.val = gl_RayTminEXT;\n" : "") << "  reportIntersectionEXT(gl_RayTminEXT, 0);\n"
345          << "}\n";
346 
347     std::ostringstream hits;
348     hits << "#version 460 core\n"
349          << "#extension GL_EXT_ray_tracing : require\n"
350          << "layout(location=0) rayPayloadInEXT vec3 hitValue;\n"
351          << "hitAttributeEXT vec3 attribs;\n"
352          << bufferDecl << "\n"
353          << "void main()\n"
354          << "{\n"
355          << "  outBuffer.val = gl_HitTEXT;\n"
356          << "}\n";
357 
358     switch (m_params.testStage)
359     {
360     case VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR:
361         programCollection.glslSources.add("hits")
362             << glu::ClosestHitSource(updateRayTracingGLSL(hits.str())) << buildOptions;
363         break;
364     case VK_SHADER_STAGE_ANY_HIT_BIT_KHR:
365         programCollection.glslSources.add("hits")
366             << glu::AnyHitSource(updateRayTracingGLSL(hits.str())) << buildOptions;
367         break;
368     case VK_SHADER_STAGE_INTERSECTION_BIT_KHR:
369         programCollection.glslSources.add("isec")
370             << glu::IntersectionSource(updateRayTracingGLSL(isec.str())) << buildOptions;
371         break;
372     default:
373         DE_ASSERT(false);
374         break;
375     }
376 
377     // Also add the intersection shader if needed for AABBs.
378     if (m_params.isecAux())
379         programCollection.glslSources.add("isec")
380             << glu::IntersectionSource(updateRayTracingGLSL(isec.str())) << buildOptions;
381 
382     std::ostringstream miss;
383     miss << "#version 460 core\n"
384          << "#extension GL_EXT_ray_tracing : require\n"
385          << "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
386          << bufferDecl << "\n"
387          << "void main()\n"
388          << "{\n"
389          << "  outBuffer.val = -10000.0f;\n"
390          << "}\n";
391 
392     programCollection.glslSources.add("miss") << glu::MissSource(updateRayTracingGLSL(miss.str())) << buildOptions;
393 }
394 
createInstance(Context & context) const395 TestInstance *DirectionTestCase::createInstance(Context &context) const
396 {
397     return new DirectionTestInstance(context, m_params);
398 }
399 
DirectionTestInstance(Context & context,const TestParams & params)400 DirectionTestInstance::DirectionTestInstance(Context &context, const TestParams &params)
401     : vkt::TestInstance(context)
402     , m_params(params)
403 {
404 }
405 
iterate(void)406 tcu::TestStatus DirectionTestInstance::iterate(void)
407 {
408     const auto &vki    = m_context.getInstanceInterface();
409     const auto physDev = m_context.getPhysicalDevice();
410     const auto &vkd    = m_context.getDeviceInterface();
411     const auto device  = m_context.getDevice();
412     auto &alloc        = m_context.getDefaultAllocator();
413     const auto qIndex  = m_context.getUniversalQueueFamilyIndex();
414     const auto queue   = m_context.getUniversalQueue();
415     const auto stages  = m_params.usedStages();
416     const auto pcSize  = static_cast<uint32_t>(sizeof(PushConstants));
417 
418     const auto scaleMatrix     = getScaleMatrix(m_params.directionScale);
419     const auto rotationMatrix  = getRotationMatrix(m_params.rotationX, m_params.rotationY);
420     const auto transformMatrix = toTransformMatrixKHR(rotationMatrix);
421 
422     // Command pool and buffer.
423     const auto cmdPool      = makeCommandPool(vkd, device, qIndex);
424     const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
425     const auto cmdBuffer    = cmdBufferPtr.get();
426 
427     beginCommandBuffer(vkd, cmdBuffer);
428 
429     // Build acceleration structures.
430     auto topLevelAS    = makeTopLevelAccelerationStructure();
431     auto bottomLevelAS = makeBottomLevelAccelerationStructure();
432 
433     const bool isTriangles = (m_params.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR);
434     const VkGeometryInstanceFlagsKHR instanceFlags =
435         (isTriangles ? VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR : 0);
436 
437     bottomLevelAS->addGeometry(m_params.spaceObjects.geometry, isTriangles,
438                                VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR);
439     bottomLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
440 
441     de::SharedPtr<BottomLevelAccelerationStructure> blasSharedPtr(bottomLevelAS.release());
442     topLevelAS->setUseArrayOfPointers(m_params.useArraysOfPointers);
443     topLevelAS->setUsePPGeometries(m_params.useArraysOfPointers);
444     topLevelAS->setInstanceCount(1);
445     {
446         const auto &initialMatrix = (m_params.updateMatrixAfterBuild ? identityMatrix3x4 : transformMatrix);
447         topLevelAS->addInstance(blasSharedPtr, initialMatrix, 0, 0xFFu, 0u, instanceFlags);
448     }
449     topLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
450     if (m_params.updateMatrixAfterBuild)
451         topLevelAS->updateInstanceMatrix(vkd, device, 0u, transformMatrix);
452 
453     // Create output buffer.
454     const auto bufferSize       = static_cast<VkDeviceSize>(sizeof(float));
455     const auto bufferCreateInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
456     BufferWithMemory buffer(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible);
457     auto &bufferAlloc = buffer.getAllocation();
458 
459     // Fill output buffer with an initial value.
460     deMemset(bufferAlloc.getHostPtr(), 0, sizeof(float));
461     flushAlloc(vkd, device, bufferAlloc);
462 
463     // Descriptor set layout and pipeline layout.
464     DescriptorSetLayoutBuilder setLayoutBuilder;
465     setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, stages);
466     setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stages);
467     const auto setLayout = setLayoutBuilder.build(vkd, device);
468 
469     const VkPushConstantRange pcRange = {
470         stages, // VkShaderStageFlags stageFlags;
471         0u,     // uint32_t offset;
472         pcSize, // uint32_t size;
473     };
474 
475     const VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
476         VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
477         nullptr,                                       // const void* pNext;
478         0u,                                            // VkPipelineLayoutCreateFlags flags;
479         1u,                                            // uint32_t setLayoutCount;
480         &setLayout.get(),                              // const VkDescriptorSetLayout* pSetLayouts;
481         1u,                                            // uint32_t pushConstantRangeCount;
482         &pcRange,                                      // const VkPushConstantRange* pPushConstantRanges;
483     };
484     const auto pipelineLayout = createPipelineLayout(vkd, device, &pipelineLayoutInfo);
485 
486     // Descriptor pool and set.
487     DescriptorPoolBuilder poolBuilder;
488     poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
489     poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u);
490     const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
491     const auto descriptorSet  = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
492 
493     // Update descriptor set.
494     {
495         const VkWriteDescriptorSetAccelerationStructureKHR accelDescInfo = {
496             VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
497             nullptr,
498             1u,
499             topLevelAS.get()->getPtr(),
500         };
501 
502         const auto bufferDescInfo = makeDescriptorBufferInfo(buffer.get(), 0ull, VK_WHOLE_SIZE);
503 
504         DescriptorSetUpdateBuilder updateBuilder;
505         updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u),
506                                   VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelDescInfo);
507         updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u),
508                                   VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufferDescInfo);
509         updateBuilder.update(vkd, device);
510     }
511 
512     // Shader modules.
513     Move<VkShaderModule> rgenModule;
514     Move<VkShaderModule> missModule;
515     Move<VkShaderModule> hitsModule;
516     Move<VkShaderModule> isecModule;
517 
518     rgenModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("rgen"), 0);
519     missModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("miss"), 0);
520 
521     if (!m_params.isecMain())
522         hitsModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("hits"), 0);
523 
524     if (m_params.isecUsed())
525         isecModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("isec"), 0);
526 
527     // Get some ray tracing properties.
528     uint32_t shaderGroupHandleSize    = 0u;
529     uint32_t shaderGroupBaseAlignment = 1u;
530     {
531         const auto rayTracingPropertiesKHR = makeRayTracingProperties(vki, physDev);
532         shaderGroupHandleSize              = rayTracingPropertiesKHR->getShaderGroupHandleSize();
533         shaderGroupBaseAlignment           = rayTracingPropertiesKHR->getShaderGroupBaseAlignment();
534     }
535 
536     // Create raytracing pipeline and shader binding tables.
537     Move<VkPipeline> pipeline;
538 
539     de::MovePtr<BufferWithMemory> raygenSBT;
540     de::MovePtr<BufferWithMemory> missSBT;
541     de::MovePtr<BufferWithMemory> hitSBT;
542     de::MovePtr<BufferWithMemory> callableSBT;
543 
544     VkStridedDeviceAddressRegionKHR raygenSBTRegion   = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
545     VkStridedDeviceAddressRegionKHR missSBTRegion     = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
546     VkStridedDeviceAddressRegionKHR hitSBTRegion      = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
547     VkStridedDeviceAddressRegionKHR callableSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
548 
549     {
550         const auto hitModuleCount     = (m_params.isecAux() ? 2 : 1);
551         const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
552 
553         rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, 0);
554         rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, missModule, 1);
555 
556         if (!m_params.isecMain())
557             rayTracingPipeline->addShader(m_params.testStage, hitsModule, 2);
558 
559         if (m_params.isecUsed())
560             rayTracingPipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, isecModule, 2);
561 
562         pipeline = rayTracingPipeline->createPipeline(vkd, device, pipelineLayout.get());
563 
564         raygenSBT       = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc,
565                                                                        shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
566         raygenSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, raygenSBT->get(), 0),
567                                                             shaderGroupHandleSize, shaderGroupHandleSize);
568 
569         missSBT       = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc,
570                                                                      shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
571         missSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, missSBT->get(), 0),
572                                                           shaderGroupHandleSize, shaderGroupHandleSize);
573 
574         hitSBT = rayTracingPipeline->createShaderBindingTable(
575             vkd, device, pipeline.get(), alloc, shaderGroupHandleSize * hitModuleCount, shaderGroupBaseAlignment, 2, 1);
576         hitSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, hitSBT->get(), 0),
577                                                          shaderGroupHandleSize, shaderGroupHandleSize * hitModuleCount);
578     }
579 
580     // Push constants.
581     const auto rotatedOrigin   = m_params.spaceObjects.origin * rotationMatrix;
582     const auto finalDirection  = m_params.spaceObjects.direction * scaleMatrix * rotationMatrix;
583     const auto distanceToEdge  = SpaceObjects::getDistanceToEdge(m_params.directionScale);
584     const auto tMinMax         = calcTminTmax(m_params.rayOriginType, m_params.rayEndtype, distanceToEdge);
585     const PushConstants pcData = {
586         toVec4(rotatedOrigin),  // tcu::Vec4 origin;
587         toVec4(finalDirection), // tcu::Vec4 direction;
588         tMinMax.first,          // float tmix;
589         tMinMax.second,         // float tmax;
590     };
591 
592     // Trace rays.
593     vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.get());
594     vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout.get(), 0u, 1u,
595                               &descriptorSet.get(), 0u, nullptr);
596     vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), stages, 0u, pcSize, &pcData);
597     vkd.cmdTraceRaysKHR(cmdBuffer, &raygenSBTRegion, &missSBTRegion, &hitSBTRegion, &callableSBTRegion, 1u, 1u, 1u);
598 
599     // Barrier for the output buffer.
600     const auto bufferBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
601     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u,
602                            &bufferBarrier, 0u, nullptr, 0u, nullptr);
603 
604     endCommandBuffer(vkd, cmdBuffer);
605     submitCommandsAndWait(vkd, device, queue, cmdBuffer);
606 
607     // Read value back from the buffer.
608     float bufferValue = 0.0f;
609     invalidateAlloc(vkd, device, bufferAlloc);
610     deMemcpy(&bufferValue, bufferAlloc.getHostPtr(), sizeof(bufferValue));
611 
612     if (m_params.rayEndtype == RayEndType::CROSS)
613     {
614         // Shooting from the ouside.
615         if (de::abs(bufferValue - distanceToEdge) > kDefaultTolerance)
616         {
617             std::ostringstream msg;
618             msg << "Result distance (" << bufferValue << ") differs from expected distance (" << distanceToEdge
619                 << ", tolerance " << kDefaultTolerance << ")";
620             TCU_FAIL(msg.str());
621         }
622     }
623     else
624     {
625         // Rays are shot from inside AABBs, rayTMin should be zero and the reported hit distance.
626         if (bufferValue != 0.0f)
627         {
628             std::ostringstream msg;
629             msg << "Result distance nonzero (" << bufferValue << ")";
630             TCU_FAIL(msg.str());
631         }
632     }
633 
634     return tcu::TestStatus::pass("Pass");
635 }
636 
637 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
638 
639 // Generate a list of scaling factors suitable for the tests.
generateScalingFactors(de::Random & rnd)640 std::vector<float> generateScalingFactors(de::Random &rnd)
641 {
642     const float kMinScalingFactor      = 0.5f;
643     const float kMaxScalingFactor      = 10.0f;
644     const int kNumRandomScalingFactors = 5;
645 
646     // Scaling factors: 1.0 and some randomly-generated ones.
647     std::vector<float> scalingFactors;
648 
649     scalingFactors.reserve(kNumRandomScalingFactors + 1);
650     scalingFactors.push_back(1.0f);
651 
652     for (int i = 0; i < kNumRandomScalingFactors; ++i)
653         scalingFactors.push_back(rnd.getFloat() * (kMaxScalingFactor - kMinScalingFactor) + kMinScalingFactor);
654 
655     return scalingFactors;
656 }
657 
658 // Generate a list of rotation angles suitable for the tests.
generateRotationAngles(de::Random & rnd)659 std::vector<std::pair<float, float>> generateRotationAngles(de::Random &rnd)
660 {
661     const float kPi2              = DE_PI * 2.0f;
662     const int kNumRandomRotations = 4;
663 
664     // Rotations: 0.0 on both axis and some randomly-generated ones.
665     std::vector<std::pair<float, float>> rotationAngles;
666 
667     rotationAngles.reserve(kNumRandomRotations + 1);
668     rotationAngles.push_back(std::make_pair(0.0f, 0.0f));
669 
670     for (int i = 0; i < kNumRandomRotations; ++i)
671         rotationAngles.push_back(std::make_pair(rnd.getFloat() * kPi2, rnd.getFloat() * kPi2));
672 
673     return rotationAngles;
674 }
675 
676 } // namespace
677 
createDirectionLengthTests(tcu::TestContext & testCtx)678 tcu::TestCaseGroup *createDirectionLengthTests(tcu::TestContext &testCtx)
679 {
680     // Test direction vector length when tracing rays
681     GroupPtr directionGroup(new tcu::TestCaseGroup(testCtx, "direction_length"));
682 
683     struct
684     {
685         VkShaderStageFlagBits hitStage;
686         const char *name;
687     } stages[] = {
688         {VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, "chit"},
689         {VK_SHADER_STAGE_ANY_HIT_BIT_KHR, "ahit"},
690         {VK_SHADER_STAGE_INTERSECTION_BIT_KHR, "isec"},
691     };
692 
693     struct
694     {
695         VkGeometryTypeKHR geometryType;
696         const char *name;
697     } geometryTypes[] = {
698         {VK_GEOMETRY_TYPE_TRIANGLES_KHR, "triangles"},
699         {VK_GEOMETRY_TYPE_AABBS_KHR, "aabbs"},
700     };
701 
702     de::Random rnd(1613648516u);
703     uint32_t caseCounter = 0u;
704 
705     // Scaling factors and rotation angles.
706     const auto scalingFactors = generateScalingFactors(rnd);
707     const auto rotationAngles = generateRotationAngles(rnd);
708 
709     for (int stageIdx = 0; stageIdx < DE_LENGTH_OF_ARRAY(stages); ++stageIdx)
710     {
711         const auto &stageData = stages[stageIdx];
712         GroupPtr stageGroup(new tcu::TestCaseGroup(testCtx, stageData.name));
713 
714         for (int geometryTypeIdx = 0; geometryTypeIdx < DE_LENGTH_OF_ARRAY(geometryTypes); ++geometryTypeIdx)
715         {
716             const auto &gType = geometryTypes[geometryTypeIdx];
717 
718             // We cannot test triangles with the ray intersection stage.
719             if (gType.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR &&
720                 stageData.hitStage == VK_SHADER_STAGE_INTERSECTION_BIT_KHR)
721                 continue;
722 
723             GroupPtr geomGroup(new tcu::TestCaseGroup(testCtx, gType.name));
724 
725             for (size_t scalingIdx = 0; scalingIdx < scalingFactors.size(); ++scalingIdx)
726             {
727                 const auto scale     = scalingFactors[scalingIdx];
728                 const auto scaleName = "scaling_factor_" + de::toString(scalingIdx);
729                 GroupPtr factorGroup(new tcu::TestCaseGroup(testCtx, scaleName.c_str()));
730 
731                 for (size_t rotationIdx = 0; rotationIdx < rotationAngles.size(); ++rotationIdx)
732                 {
733                     const auto angles       = rotationAngles[rotationIdx];
734                     const auto angleName    = "rotation_" + de::toString(rotationIdx);
735                     const auto geometryType = gType.geometryType;
736                     const auto rayOrigType  = RayOriginType::OUTSIDE;
737                     const auto rayEndType   = RayEndType::CROSS;
738 
739                     SpaceObjects spaceObjects(rayOrigType, geometryType);
740 
741                     TestParams params = {
742                         spaceObjects,       // SpaceObjects spaceObjects;
743                         scale,              // float directionScale;
744                         angles.first,       // float rotationX;
745                         angles.second,      // float rotationY;
746                         stageData.hitStage, // VkShaderStageFlagBits hitStage;
747                         geometryType,       // VkGeometryTypeKHR geometryType;
748                         // Use arrays of pointers when building the TLAS in every other test.
749                         (caseCounter % 2u == 0u), // bool useArraysOfPointers;
750                         // Sometimes, update matrix after building the lop level AS and before submitting the command buffer.
751                         (caseCounter % 3u == 0u), // bool updateMatrixAfterBuild;
752                         rayOrigType,              // RayOriginType rayOriginType;
753                         rayEndType,               // RayEndType rayEndType;
754                     };
755                     ++caseCounter;
756 
757                     factorGroup->addChild(new DirectionTestCase(testCtx, angleName, params));
758                 }
759 
760                 geomGroup->addChild(factorGroup.release());
761             }
762 
763             stageGroup->addChild(geomGroup.release());
764         }
765 
766         directionGroup->addChild(stageGroup.release());
767     }
768 
769     return directionGroup.release();
770 }
771 
createInsideAABBsTests(tcu::TestContext & testCtx)772 tcu::TestCaseGroup *createInsideAABBsTests(tcu::TestContext &testCtx)
773 {
774     // Test shooting rays that start inside AABBs
775     GroupPtr insideAABBsGroup(new tcu::TestCaseGroup(testCtx, "inside_aabbs"));
776 
777     struct
778     {
779         VkShaderStageFlagBits hitStage;
780         const char *name;
781     } stages[] = {
782         {VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, "chit"},
783         {VK_SHADER_STAGE_ANY_HIT_BIT_KHR, "ahit"},
784         {VK_SHADER_STAGE_INTERSECTION_BIT_KHR, "isec"},
785     };
786 
787     struct
788     {
789         RayEndType rayEndType;
790         const char *name;
791     } rayEndCases[] = {
792         {RayEndType::ZERO, "tmax_zero"},
793         {RayEndType::INSIDE, "inside"},
794         {RayEndType::EDGE, "edge"},
795         {RayEndType::OUTSIDE, "outside"},
796     };
797 
798     de::Random rnd(1621936010u);
799 
800     // Scaling factors and rotation angles.
801     const auto scalingFactors = generateScalingFactors(rnd);
802     const auto rotationAngles = generateRotationAngles(rnd);
803 
804     for (int stageIdx = 0; stageIdx < DE_LENGTH_OF_ARRAY(stages); ++stageIdx)
805     {
806         const auto &stageData = stages[stageIdx];
807         GroupPtr stageGroup(new tcu::TestCaseGroup(testCtx, stageData.name));
808 
809         for (int rayEndCaseIdx = 0; rayEndCaseIdx < DE_LENGTH_OF_ARRAY(rayEndCases); ++rayEndCaseIdx)
810         {
811             const auto &rayEndCase       = rayEndCases[rayEndCaseIdx];
812             const std::string rayEndName = std::string("ray_end_") + rayEndCase.name;
813             GroupPtr rayEndGroup(new tcu::TestCaseGroup(testCtx, rayEndName.c_str()));
814 
815             for (size_t scalingIdx = 0; scalingIdx < scalingFactors.size(); ++scalingIdx)
816             {
817                 const auto scale     = scalingFactors[scalingIdx];
818                 const auto scaleName = "scaling_factor_" + de::toString(scalingIdx);
819                 GroupPtr factorGroup(new tcu::TestCaseGroup(testCtx, scaleName.c_str()));
820 
821                 for (size_t rotationIdx = 0; rotationIdx < rotationAngles.size(); ++rotationIdx)
822                 {
823                     const auto angles       = rotationAngles[rotationIdx];
824                     const auto angleName    = "rotation_" + de::toString(rotationIdx);
825                     const auto geometryType = VK_GEOMETRY_TYPE_AABBS_KHR;
826                     const auto rayOrigType  = RayOriginType::INSIDE;
827 
828                     SpaceObjects spaceObjects(rayOrigType, geometryType);
829 
830                     TestParams params = {
831                         spaceObjects,          // SpaceObjects spaceObjects;
832                         scale,                 // float directionScale;
833                         angles.first,          // float rotationX;
834                         angles.second,         // float rotationY;
835                         stageData.hitStage,    // VkShaderStageFlagBits hitStage;
836                         geometryType,          // VkGeometryTypeKHR geometryType;
837                         false,                 // bool useArraysOfPointers;
838                         false,                 // bool updateMatrixAfterBuild;
839                         rayOrigType,           // RayOriginType rayOriginType;
840                         rayEndCase.rayEndType, // RayEndType rayEndType;
841                     };
842 
843                     factorGroup->addChild(new DirectionTestCase(testCtx, angleName, params));
844                 }
845 
846                 rayEndGroup->addChild(factorGroup.release());
847             }
848 
849             stageGroup->addChild(rayEndGroup.release());
850         }
851 
852         insideAABBsGroup->addChild(stageGroup.release());
853     }
854 
855     return insideAABBsGroup.release();
856 }
857 
858 } // namespace RayTracing
859 } // namespace vkt
860