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 Query Barycentric Coordinates Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktRayQueryBarycentricCoordinatesTests.hpp"
26 #include "vktTestCase.hpp"
27 
28 #include "vkRayTracingUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkCmdUtil.hpp"
31 #include "vkBufferWithMemory.hpp"
32 #include "vkBuilderUtil.hpp"
33 #include "vkTypeUtil.hpp"
34 #include "vkBarrierUtil.hpp"
35 
36 #include "deUniquePtr.hpp"
37 #include "deRandom.hpp"
38 
39 #include <sstream>
40 #include <vector>
41 
42 namespace vkt
43 {
44 namespace RayQuery
45 {
46 
47 namespace
48 {
49 
50 using namespace vk;
51 
52 struct TestParams
53 {
54     uint32_t seed;
55 };
56 
57 constexpr float kZCoord     = 5.0f;
58 constexpr float kXYCoordAbs = 1.0f;
59 
60 constexpr float kThreshold  = 0.001f;            // For the resulting coordinates.
61 constexpr float kTMin       = 1.0f - kThreshold; // Require the same precision in T.
62 constexpr float kTMax       = 1.0f + kThreshold; // Ditto.
63 constexpr uint32_t kNumRays = 20u;
64 
65 class BarycentricCoordinatesCase : public TestCase
66 {
67 public:
68     BarycentricCoordinatesCase(tcu::TestContext &testCtx, const std::string &name, const TestParams &params);
~BarycentricCoordinatesCase(void)69     virtual ~BarycentricCoordinatesCase(void)
70     {
71     }
72 
73     virtual void checkSupport(Context &context) const;
74     virtual void initPrograms(vk::SourceCollections &programCollection) const;
75     virtual TestInstance *createInstance(Context &context) const;
76 
77 protected:
78     TestParams m_params;
79 };
80 
81 class BarycentricCoordinatesInstance : public TestInstance
82 {
83 public:
84     BarycentricCoordinatesInstance(Context &context, const TestParams &params);
~BarycentricCoordinatesInstance(void)85     virtual ~BarycentricCoordinatesInstance(void)
86     {
87     }
88 
89     virtual tcu::TestStatus iterate(void);
90 
91 protected:
92     TestParams m_params;
93 };
94 
BarycentricCoordinatesCase(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)95 BarycentricCoordinatesCase::BarycentricCoordinatesCase(tcu::TestContext &testCtx, const std::string &name,
96                                                        const TestParams &params)
97     : TestCase(testCtx, name)
98     , m_params(params)
99 {
100 }
101 
checkSupport(Context & context) const102 void BarycentricCoordinatesCase::checkSupport(Context &context) const
103 {
104     context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
105     context.requireDeviceFunctionality("VK_KHR_ray_query");
106 }
107 
initPrograms(vk::SourceCollections & programCollection) const108 void BarycentricCoordinatesCase::initPrograms(vk::SourceCollections &programCollection) const
109 {
110     const vk::ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
111 
112     std::ostringstream comp;
113     comp << "#version 460 core\n"
114          << "#extension GL_EXT_ray_query : require\n"
115          << "\n"
116          << "layout(local_size_x=" << kNumRays << ", local_size_y=1, local_size_z=1) in;\n"
117          << "\n"
118          << "layout(set=0, binding=0) uniform accelerationStructureEXT topLevelAS;\n"
119          << "layout(set=0, binding=1) uniform RayDirections {\n"
120          << "  vec4 values[" << kNumRays << "];\n"
121          << "} directions;\n"
122          << "layout(set=0, binding=2, std430) buffer OutputBarycentrics {\n"
123          << "  vec4 values[" << kNumRays << "];\n"
124          << "} coordinates;\n"
125          << "\n"
126          << "void main()\n"
127          << "{\n"
128          << "  const uint  cullMask  = 0xFF;\n"
129          << "  const vec3  origin    = vec3(0.0, 0.0, 0.0);\n"
130          << "  const vec3  direction = directions.values[gl_LocalInvocationID.x].xyz;\n"
131          << "  const float tMin      = " << kTMin << ";\n"
132          << "  const float tMax      = " << kTMax << ";\n"
133          << "  vec4        outputVal = vec4(-1.0, -1.0, -1.0, -1.0);\n"
134          << "  rayQueryEXT rq;\n"
135          << "  rayQueryInitializeEXT(rq, topLevelAS, gl_RayFlagsNoneEXT, cullMask, origin, tMin, direction, tMax);\n"
136          << "  while (rayQueryProceedEXT(rq)) {\n"
137          << "    if (rayQueryGetIntersectionTypeEXT(rq, false) == gl_RayQueryCandidateIntersectionTriangleEXT) {\n"
138          << "      outputVal = vec4(rayQueryGetIntersectionBarycentricsEXT(rq, false), 0.0, 0.0);\n"
139          << "    }\n"
140          << "  }\n"
141          << "  coordinates.values[gl_LocalInvocationID.x] = vec4(outputVal);\n"
142          << "}\n";
143 
144     programCollection.glslSources.add("comp") << glu::ComputeSource(updateRayTracingGLSL(comp.str())) << buildOptions;
145 }
146 
createInstance(Context & context) const147 TestInstance *BarycentricCoordinatesCase::createInstance(Context &context) const
148 {
149     return new BarycentricCoordinatesInstance(context, m_params);
150 }
151 
BarycentricCoordinatesInstance(Context & context,const TestParams & params)152 BarycentricCoordinatesInstance::BarycentricCoordinatesInstance(Context &context, const TestParams &params)
153     : TestInstance(context)
154     , m_params(params)
155 {
156 }
157 
158 // Calculates coordinates in a triangle given barycentric coordinates b and c.
calcCoordinates(const std::vector<tcu::Vec3> & triangle,float b,float c)159 tcu::Vec3 calcCoordinates(const std::vector<tcu::Vec3> &triangle, float b, float c)
160 {
161     DE_ASSERT(triangle.size() == 3u);
162     DE_ASSERT(b > 0.0f);
163     DE_ASSERT(c > 0.0f);
164     DE_ASSERT(b + c < 1.0f);
165 
166     const float a = 1.0f - b - c;
167     DE_ASSERT(a > 0.0f);
168 
169     return triangle[0] * a + triangle[1] * b + triangle[2] * c;
170 }
171 
172 // Return a, b, c with a close to 1.0f and (b, c) close to 0.0f.
getBarycentricVertex(void)173 tcu::Vec3 getBarycentricVertex(void)
174 {
175     const float a   = 0.999f;
176     const float aux = 1.0f - a;
177     const float b   = aux / 2.0f;
178     const float c   = b;
179 
180     return tcu::Vec3(a, b, c);
181 }
182 
extendToV4(const tcu::Vec3 & vec3)183 tcu::Vec4 extendToV4(const tcu::Vec3 &vec3)
184 {
185     return tcu::Vec4(vec3.x(), vec3.y(), vec3.z(), 0.0f);
186 }
187 
iterate(void)188 tcu::TestStatus BarycentricCoordinatesInstance::iterate(void)
189 {
190     const auto &vkd   = m_context.getDeviceInterface();
191     const auto device = m_context.getDevice();
192     auto &alloc       = m_context.getDefaultAllocator();
193     const auto qIndex = m_context.getUniversalQueueFamilyIndex();
194     const auto queue  = m_context.getUniversalQueue();
195     const auto stages = VK_SHADER_STAGE_COMPUTE_BIT;
196 
197     // Command pool and buffer.
198     const auto cmdPool      = makeCommandPool(vkd, device, qIndex);
199     const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
200     const auto cmdBuffer    = cmdBufferPtr.get();
201 
202     beginCommandBuffer(vkd, cmdBuffer);
203 
204     // Build acceleration structures.
205     auto topLevelAS    = makeTopLevelAccelerationStructure();
206     auto bottomLevelAS = makeBottomLevelAccelerationStructure();
207 
208     const std::vector<tcu::Vec3> triangle = {
209         tcu::Vec3(0.0f, -kXYCoordAbs, kZCoord),
210         tcu::Vec3(-kXYCoordAbs, kXYCoordAbs, kZCoord),
211         tcu::Vec3(kXYCoordAbs, kXYCoordAbs, kZCoord),
212     };
213 
214     bottomLevelAS->addGeometry(triangle, true /*is triangles*/, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR);
215     bottomLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
216     de::SharedPtr<BottomLevelAccelerationStructure> blasSharedPtr(bottomLevelAS.release());
217 
218     topLevelAS->setInstanceCount(1);
219     topLevelAS->addInstance(blasSharedPtr, identityMatrix3x4, 0, 0xFFu, 0u,
220                             VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR);
221     topLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
222 
223     // Uniform buffer for directions.
224     const auto directionsBufferSize = static_cast<VkDeviceSize>(sizeof(tcu::Vec4) * kNumRays);
225     const auto directionsBufferInfo = makeBufferCreateInfo(directionsBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
226     BufferWithMemory directionsBuffer(vkd, device, alloc, directionsBufferInfo, MemoryRequirement::HostVisible);
227     auto &directionsBufferAlloc = directionsBuffer.getAllocation();
228     void *directionsBufferData  = directionsBufferAlloc.getHostPtr();
229 
230     // Generate rays towards the 3 triangle coordinates (avoiding exact vertices) and additional coordinates.
231     std::vector<tcu::Vec4> directions;
232     std::vector<tcu::Vec4> expectedOutputCoordinates;
233     directions.reserve(kNumRays);
234     expectedOutputCoordinates.reserve(kNumRays);
235 
236     const auto barycentricABC = getBarycentricVertex();
237 
238     directions.push_back(extendToV4(calcCoordinates(triangle, barycentricABC.x(), barycentricABC.y())));
239     directions.push_back(extendToV4(calcCoordinates(triangle, barycentricABC.y(), barycentricABC.x())));
240     directions.push_back(extendToV4(calcCoordinates(triangle, barycentricABC.y(), barycentricABC.z())));
241 
242     expectedOutputCoordinates.push_back(tcu::Vec4(barycentricABC.x(), barycentricABC.y(), 0.0f, 0.0f));
243     expectedOutputCoordinates.push_back(tcu::Vec4(barycentricABC.y(), barycentricABC.x(), 0.0f, 0.0f));
244     expectedOutputCoordinates.push_back(tcu::Vec4(barycentricABC.y(), barycentricABC.z(), 0.0f, 0.0f));
245 
246     de::Random rnd(m_params.seed);
247     while (directions.size() < kNumRays)
248     {
249         // Avoid 0.0 when choosing b and c.
250         float b;
251         while ((b = rnd.getFloat()) == 0.0f)
252             ;
253         float c;
254         while ((c = rnd.getFloat(0.0f, 1.0f - b)) == 0.0f)
255             ;
256 
257         expectedOutputCoordinates.push_back(tcu::Vec4(b, c, 0.0f, 0.0f));
258         directions.push_back(extendToV4(calcCoordinates(triangle, b, c)));
259     }
260 
261     deMemcpy(directionsBufferData, directions.data(), directionsBufferSize);
262     flushAlloc(vkd, device, directionsBufferAlloc);
263 
264     // Storage buffer for output barycentric coordinates.
265     const auto barycoordsBufferSize = static_cast<VkDeviceSize>(sizeof(tcu::Vec4) * kNumRays);
266     const auto barycoordsBufferInfo = makeBufferCreateInfo(barycoordsBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
267     BufferWithMemory barycoordsBuffer(vkd, device, alloc, barycoordsBufferInfo, MemoryRequirement::HostVisible);
268     auto &barycoordsBufferAlloc = barycoordsBuffer.getAllocation();
269     void *barycoordsBufferData  = barycoordsBufferAlloc.getHostPtr();
270     deMemset(barycoordsBufferData, 0, static_cast<size_t>(barycoordsBufferSize));
271     flushAlloc(vkd, device, barycoordsBufferAlloc);
272 
273     // Descriptor set layout.
274     DescriptorSetLayoutBuilder dsLayoutBuilder;
275     dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, stages);
276     dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stages);
277     dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stages);
278     const auto setLayout = dsLayoutBuilder.build(vkd, device);
279 
280     // Pipeline layout.
281     const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
282 
283     // Descriptor pool and set.
284     DescriptorPoolBuilder poolBuilder;
285     poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
286     poolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
287     poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
288     const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
289     const auto descriptorSet  = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
290 
291     // Update descriptor set.
292     {
293         const VkWriteDescriptorSetAccelerationStructureKHR accelDescInfo = {
294             VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
295             nullptr,
296             1u,
297             topLevelAS.get()->getPtr(),
298         };
299         const auto uniformBufferInfo = makeDescriptorBufferInfo(directionsBuffer.get(), 0ull, VK_WHOLE_SIZE);
300         const auto storageBufferInfo = makeDescriptorBufferInfo(barycoordsBuffer.get(), 0ull, VK_WHOLE_SIZE);
301 
302         DescriptorSetUpdateBuilder updateBuilder;
303         updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u),
304                                   VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelDescInfo);
305         updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u),
306                                   VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &uniformBufferInfo);
307         updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(2u),
308                                   VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &storageBufferInfo);
309         updateBuilder.update(vkd, device);
310     }
311 
312     // Shader module.
313     const auto compModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0);
314 
315     // Pipeline.
316     const VkPipelineShaderStageCreateInfo shaderInfo = {
317         VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
318         nullptr,                                             // const void* pNext;
319         0u,                                                  // VkPipelineShaderStageCreateFlags flags;
320         VK_SHADER_STAGE_COMPUTE_BIT,                         // VkShaderStageFlagBits stage;
321         compModule.get(),                                    // VkShaderModule module;
322         "main",                                              // const char* pName;
323         nullptr,                                             // const VkSpecializationInfo* pSpecializationInfo;
324     };
325     const VkComputePipelineCreateInfo pipelineInfo = {
326         VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
327         nullptr,                                        // const void* pNext;
328         0u,                                             // VkPipelineCreateFlags flags;
329         shaderInfo,                                     // VkPipelineShaderStageCreateInfo stage;
330         pipelineLayout.get(),                           // VkPipelineLayout layout;
331         DE_NULL,                                        // VkPipeline basePipelineHandle;
332         0,                                              // int32_t basePipelineIndex;
333     };
334     const auto pipeline = createComputePipeline(vkd, device, DE_NULL, &pipelineInfo);
335 
336     // Dispatch work with ray queries.
337     vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.get());
338     vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0u, 1u,
339                               &descriptorSet.get(), 0u, nullptr);
340     vkd.cmdDispatch(cmdBuffer, 1u, 1u, 1u);
341 
342     // Barrier for the output buffer.
343     const auto bufferBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
344     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u,
345                            &bufferBarrier, 0u, nullptr, 0u, nullptr);
346 
347     endCommandBuffer(vkd, cmdBuffer);
348     submitCommandsAndWait(vkd, device, queue, cmdBuffer);
349 
350     // Verify results.
351     std::vector<tcu::Vec4> outputData(expectedOutputCoordinates.size());
352     const auto barycoordsBufferSizeSz = static_cast<size_t>(barycoordsBufferSize);
353 
354     invalidateAlloc(vkd, device, barycoordsBufferAlloc);
355     DE_ASSERT(de::dataSize(outputData) == barycoordsBufferSizeSz);
356     deMemcpy(outputData.data(), barycoordsBufferData, barycoordsBufferSizeSz);
357 
358     for (size_t i = 0; i < outputData.size(); ++i)
359     {
360         const auto &outVal      = outputData[i];
361         const auto &expectedVal = expectedOutputCoordinates[i];
362 
363         if (outVal.z() != 0.0f || outVal.w() != 0.0f || de::abs(outVal.x() - expectedVal.x()) > kThreshold ||
364             de::abs(outVal.y() - expectedVal.y()) > kThreshold)
365         {
366             std::ostringstream msg;
367             msg << "Unexpected value found for ray " << i << ": expected " << expectedVal << " and found " << outVal
368                 << ";";
369             TCU_FAIL(msg.str());
370         }
371     }
372 
373     return tcu::TestStatus::pass("Pass");
374 }
375 
376 } // namespace
377 
createBarycentricCoordinatesTests(tcu::TestContext & testCtx)378 tcu::TestCaseGroup *createBarycentricCoordinatesTests(tcu::TestContext &testCtx)
379 {
380     using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
381 
382     // Test barycentric coordinates reported by the ray query
383     GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "barycentric_coordinates"));
384 
385     uint32_t seed = 1614674687u;
386     mainGroup->addChild(new BarycentricCoordinatesCase(testCtx, "compute", TestParams{seed++}));
387 
388     return mainGroup.release();
389 }
390 
391 } // namespace RayQuery
392 } // namespace vkt
393