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 Barycentric Coordinates Tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktRayTracingBarycentricCoordinatesTests.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 RayTracing
45 {
46
47 namespace
48 {
49
50 using namespace vk;
51
52 struct TestParams
53 {
54 VkShaderStageFlagBits stage;
55 deUint32 seed;
56 };
57
getUsedStages(const TestParams & params)58 VkShaderStageFlags getUsedStages (const TestParams& params)
59 {
60 return (VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR | params.stage);
61 }
62
63 constexpr float kZCoord = 5.0f;
64 constexpr float kXYCoordAbs = 1.0f;
65
66 constexpr float kThreshold = 0.001f; // For the resulting coordinates.
67 constexpr float kTMin = 1.0f - kThreshold; // Require the same precision in T.
68 constexpr float kTMax = 1.0f + kThreshold; // Ditto.
69 constexpr deUint32 kNumRays = 20u;
70
71 class BarycentricCoordinatesCase : public TestCase
72 {
73 public:
74 BarycentricCoordinatesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params);
~BarycentricCoordinatesCase(void)75 virtual ~BarycentricCoordinatesCase (void) {}
76
77 virtual void checkSupport (Context& context) const;
78 virtual void initPrograms (vk::SourceCollections& programCollection) const;
79 virtual TestInstance* createInstance (Context& context) const;
80
81 protected:
82 TestParams m_params;
83 };
84
85 class BarycentricCoordinatesInstance : public TestInstance
86 {
87 public:
88 BarycentricCoordinatesInstance (Context& context, const TestParams& params);
~BarycentricCoordinatesInstance(void)89 virtual ~BarycentricCoordinatesInstance (void) {}
90
91 virtual tcu::TestStatus iterate (void);
92
93 protected:
94 TestParams m_params;
95 };
96
BarycentricCoordinatesCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)97 BarycentricCoordinatesCase::BarycentricCoordinatesCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
98 : TestCase (testCtx, name, description)
99 , m_params (params)
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_tracing_pipeline");
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 layoutDecls;
113 layoutDecls
114 << "layout(set=0, binding=0) uniform accelerationStructureEXT topLevelAS;\n"
115 << "layout(set=0, binding=1) uniform RayDirections {\n"
116 << " vec4 values[" << kNumRays << "];\n"
117 << "} directions;\n"
118 << "layout(set=0, binding=2, std430) buffer OutputBarycentrics {\n"
119 << " vec4 values[" << kNumRays << "];\n"
120 << "} coordinates;\n"
121 ;
122 const auto layoutDeclsStr = layoutDecls.str();
123
124 std::ostringstream rgen;
125 rgen
126 << "#version 460 core\n"
127 << "#extension GL_EXT_ray_tracing : require\n"
128 << "\n"
129 << "layout(location=0) rayPayloadEXT vec3 hitValue;\n"
130 << "\n"
131 << layoutDeclsStr
132 << "\n"
133 << "void main()\n"
134 << "{\n"
135 << " const uint cullMask = 0xFF;\n"
136 << " const vec3 origin = vec3(0.0, 0.0, 0.0);\n"
137 << " const vec3 direction = directions.values[gl_LaunchIDEXT.x].xyz;\n"
138 << " const float tMin = " << kTMin << ";\n"
139 << " const float tMax = " << kTMax << ";\n"
140 << " traceRayEXT(topLevelAS, gl_RayFlagsNoneEXT, cullMask, 0, 0, 0, origin, tMin, direction, tMax, 0);\n"
141 << "}\n"
142 ;
143
144 std::ostringstream hits;
145 hits
146 << "#version 460 core\n"
147 << "#extension GL_EXT_ray_tracing : require\n"
148 << "\n"
149 << "hitAttributeEXT vec2 baryCoord;\n"
150 << "\n"
151 << layoutDeclsStr
152 << "\n"
153 << "void main()\n"
154 << "{\n"
155 << " coordinates.values[gl_LaunchIDEXT.x] = vec4(baryCoord, 0.0, 0.0);\n"
156 << "}\n"
157 ;
158
159 std::ostringstream miss;
160 miss
161 << "#version 460 core\n"
162 << "#extension GL_EXT_ray_tracing : require\n"
163 << "layout(location = 0) rayPayloadInEXT vec3 hitValue;\n"
164 << layoutDeclsStr
165 << "\n"
166 << "void main()\n"
167 << "{\n"
168 << " coordinates.values[gl_LaunchIDEXT.x] = vec4(-1.0, -1.0, -1.0, -1.0);\n"
169 << "}\n";
170
171 programCollection.glslSources.add("rgen") << glu::RaygenSource(updateRayTracingGLSL(rgen.str())) << buildOptions;
172 programCollection.glslSources.add("miss") << glu::MissSource(updateRayTracingGLSL(miss.str())) << buildOptions;
173
174 if (m_params.stage == VK_SHADER_STAGE_ANY_HIT_BIT_KHR)
175 programCollection.glslSources.add("hits") << glu::AnyHitSource(updateRayTracingGLSL(hits.str())) << buildOptions;
176 else if (m_params.stage == VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR)
177 programCollection.glslSources.add("hits") << glu::ClosestHitSource(updateRayTracingGLSL(hits.str())) << buildOptions;
178 else
179 DE_ASSERT(false);
180 }
181
createInstance(Context & context) const182 TestInstance* BarycentricCoordinatesCase::createInstance (Context& context) const
183 {
184 return new BarycentricCoordinatesInstance(context, m_params);
185 }
186
BarycentricCoordinatesInstance(Context & context,const TestParams & params)187 BarycentricCoordinatesInstance::BarycentricCoordinatesInstance (Context& context, const TestParams& params)
188 : TestInstance (context)
189 , m_params (params)
190 {}
191
192 // Calculates coordinates in a triangle given barycentric coordinates b and c.
calcCoordinates(const std::vector<tcu::Vec3> & triangle,float b,float c)193 tcu::Vec3 calcCoordinates (const std::vector<tcu::Vec3>& triangle, float b, float c)
194 {
195 DE_ASSERT(triangle.size() == 3u);
196 DE_ASSERT(b > 0.0f);
197 DE_ASSERT(c > 0.0f);
198 DE_ASSERT(b + c < 1.0f);
199
200 const float a = 1.0f - b - c;
201 DE_ASSERT(a > 0.0f);
202
203 return triangle[0] * a + triangle[1] * b + triangle[2] * c;
204 }
205
206 // Return a, b, c with a close to 1.0f and (b, c) close to 0.0f.
getBarycentricVertex(void)207 tcu::Vec3 getBarycentricVertex (void)
208 {
209 const float a = 0.999f;
210 const float aux = 1.0f - a;
211 const float b = aux / 2.0f;
212 const float c = b;
213
214 return tcu::Vec3(a, b, c);
215 }
216
extendToV4(const tcu::Vec3 & vec3)217 tcu::Vec4 extendToV4 (const tcu::Vec3& vec3)
218 {
219 return tcu::Vec4(vec3.x(), vec3.y(), vec3.z(), 0.0f);
220 }
221
iterate(void)222 tcu::TestStatus BarycentricCoordinatesInstance::iterate (void)
223 {
224 const auto& vki = m_context.getInstanceInterface();
225 const auto physDev = m_context.getPhysicalDevice();
226 const auto& vkd = m_context.getDeviceInterface();
227 const auto device = m_context.getDevice();
228 auto& alloc = m_context.getDefaultAllocator();
229 const auto qIndex = m_context.getUniversalQueueFamilyIndex();
230 const auto queue = m_context.getUniversalQueue();
231 const auto stages = getUsedStages(m_params);
232
233 // Command pool and buffer.
234 const auto cmdPool = makeCommandPool(vkd, device, qIndex);
235 const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
236 const auto cmdBuffer = cmdBufferPtr.get();
237
238 beginCommandBuffer(vkd, cmdBuffer);
239
240 // Build acceleration structures.
241 auto topLevelAS = makeTopLevelAccelerationStructure();
242 auto bottomLevelAS = makeBottomLevelAccelerationStructure();
243
244 const std::vector<tcu::Vec3> triangle =
245 {
246 tcu::Vec3( 0.0f, -kXYCoordAbs, kZCoord),
247 tcu::Vec3(-kXYCoordAbs, kXYCoordAbs, kZCoord),
248 tcu::Vec3( kXYCoordAbs, kXYCoordAbs, kZCoord),
249 };
250
251 bottomLevelAS->addGeometry(triangle, true/*is triangles*/, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR);
252 bottomLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
253 de::SharedPtr<BottomLevelAccelerationStructure> blasSharedPtr (bottomLevelAS.release());
254
255 topLevelAS->setInstanceCount(1);
256 topLevelAS->addInstance(blasSharedPtr, identityMatrix3x4, 0, 0xFFu, 0u, VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR);
257 topLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
258
259 // Uniform buffer for directions.
260 const auto directionsBufferSize = static_cast<VkDeviceSize>(sizeof(tcu::Vec4) * kNumRays);
261 const auto directionsBufferInfo = makeBufferCreateInfo(directionsBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
262 BufferWithMemory directionsBuffer (vkd, device, alloc, directionsBufferInfo, MemoryRequirement::HostVisible);
263 auto& directionsBufferAlloc = directionsBuffer.getAllocation();
264 void* directionsBufferData = directionsBufferAlloc.getHostPtr();
265
266 // Generate rays towards the 3 triangle coordinates (avoiding exact vertices) and additional coordinates.
267 std::vector<tcu::Vec4> directions;
268 std::vector<tcu::Vec4> expectedOutputCoordinates;
269 directions.reserve(kNumRays);
270 expectedOutputCoordinates.reserve(kNumRays);
271
272 const auto barycentricABC = getBarycentricVertex();
273
274 directions.push_back(extendToV4(calcCoordinates(triangle, barycentricABC.x(), barycentricABC.y())));
275 directions.push_back(extendToV4(calcCoordinates(triangle, barycentricABC.y(), barycentricABC.x())));
276 directions.push_back(extendToV4(calcCoordinates(triangle, barycentricABC.y(), barycentricABC.z())));
277
278 expectedOutputCoordinates.push_back(tcu::Vec4(barycentricABC.x(), barycentricABC.y(), 0.0f, 0.0f));
279 expectedOutputCoordinates.push_back(tcu::Vec4(barycentricABC.y(), barycentricABC.x(), 0.0f, 0.0f));
280 expectedOutputCoordinates.push_back(tcu::Vec4(barycentricABC.y(), barycentricABC.z(), 0.0f, 0.0f));
281
282 de::Random rnd(m_params.seed);
283 while (directions.size() < kNumRays)
284 {
285 // Avoid 0.0 when choosing b and c.
286 float b;
287 while ((b = rnd.getFloat()) == 0.0f)
288 ;
289 float c;
290 while ((c = rnd.getFloat(0.0f, 1.0f - b)) == 0.0f)
291 ;
292
293 expectedOutputCoordinates.push_back(tcu::Vec4(b, c, 0.0f, 0.0f));
294 directions.push_back(extendToV4(calcCoordinates(triangle, b, c)));
295 }
296
297 deMemcpy(directionsBufferData, directions.data(), directionsBufferSize);
298 flushAlloc(vkd, device, directionsBufferAlloc);
299
300 // Storage buffer for output barycentric coordinates.
301 const auto barycoordsBufferSize = static_cast<VkDeviceSize>(sizeof(tcu::Vec4) * kNumRays);
302 const auto barycoordsBufferInfo = makeBufferCreateInfo(barycoordsBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
303 BufferWithMemory barycoordsBuffer (vkd, device, alloc, barycoordsBufferInfo, MemoryRequirement::HostVisible);
304 auto& barycoordsBufferAlloc = barycoordsBuffer.getAllocation();
305 void* barycoordsBufferData = barycoordsBufferAlloc.getHostPtr();
306 deMemset(barycoordsBufferData, 0, static_cast<size_t>(barycoordsBufferSize));
307 flushAlloc(vkd, device, barycoordsBufferAlloc);
308
309 // Descriptor set layout.
310 DescriptorSetLayoutBuilder dsLayoutBuilder;
311 dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, stages);
312 dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stages);
313 dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stages);
314 const auto setLayout = dsLayoutBuilder.build(vkd, device);
315
316 // Pipeline layout.
317 const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
318
319 // Descriptor pool and set.
320 DescriptorPoolBuilder poolBuilder;
321 poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
322 poolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
323 poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
324 const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
325 const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
326
327 // Update descriptor set.
328 {
329 const VkWriteDescriptorSetAccelerationStructureKHR accelDescInfo =
330 {
331 VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
332 nullptr,
333 1u,
334 topLevelAS.get()->getPtr(),
335 };
336 const auto uniformBufferInfo = makeDescriptorBufferInfo(directionsBuffer.get(), 0ull, VK_WHOLE_SIZE);
337 const auto storageBufferInfo = makeDescriptorBufferInfo(barycoordsBuffer.get(), 0ull, VK_WHOLE_SIZE);
338
339 DescriptorSetUpdateBuilder updateBuilder;
340 updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelDescInfo);
341 updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &uniformBufferInfo);
342 updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(2u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &storageBufferInfo);
343 updateBuilder.update(vkd, device);
344 }
345
346 // Shader modules.
347 auto rgenModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("rgen"), 0);
348 auto missModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("miss"), 0);
349 auto hitsModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("hits"), 0);
350
351 // Get some ray tracing properties.
352 deUint32 shaderGroupHandleSize = 0u;
353 deUint32 shaderGroupBaseAlignment = 1u;
354 {
355 const auto rayTracingPropertiesKHR = makeRayTracingProperties(vki, physDev);
356 shaderGroupHandleSize = rayTracingPropertiesKHR->getShaderGroupHandleSize();
357 shaderGroupBaseAlignment = rayTracingPropertiesKHR->getShaderGroupBaseAlignment();
358 }
359
360 // Create raytracing pipeline and shader binding tables.
361 Move<VkPipeline> pipeline;
362 de::MovePtr<BufferWithMemory> raygenSBT;
363 de::MovePtr<BufferWithMemory> missSBT;
364 de::MovePtr<BufferWithMemory> hitSBT;
365 de::MovePtr<BufferWithMemory> callableSBT;
366
367 auto raygenSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
368 auto missSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
369 auto hitSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
370 auto callableSBTRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
371
372 {
373 const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>();
374 rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, 0);
375 rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, missModule, 1);
376 rayTracingPipeline->addShader(m_params.stage, hitsModule, 2);
377
378 pipeline = rayTracingPipeline->createPipeline(vkd, device, pipelineLayout.get());
379
380 raygenSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 0, 1);
381 raygenSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, raygenSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
382
383 missSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 1, 1);
384 missSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, missSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
385
386 hitSBT = rayTracingPipeline->createShaderBindingTable(vkd, device, pipeline.get(), alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, 2, 1);
387 hitSBTRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, hitSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize);
388 }
389
390 // Trace rays.
391 vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline.get());
392 vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
393 vkd.cmdTraceRaysKHR(cmdBuffer, &raygenSBTRegion, &missSBTRegion, &hitSBTRegion, &callableSBTRegion, kNumRays, 1u, 1u);
394
395 // Barrier for the output buffer.
396 const auto bufferBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
397 vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &bufferBarrier, 0u, nullptr, 0u, nullptr);
398
399 endCommandBuffer(vkd, cmdBuffer);
400 submitCommandsAndWait(vkd, device, queue, cmdBuffer);
401
402 // Verify results.
403 std::vector<tcu::Vec4> outputData (expectedOutputCoordinates.size());
404 const auto barycoordsBufferSizeSz = static_cast<size_t>(barycoordsBufferSize);
405
406 invalidateAlloc(vkd, device, barycoordsBufferAlloc);
407 DE_ASSERT(de::dataSize(outputData) == barycoordsBufferSizeSz);
408 deMemcpy(outputData.data(), barycoordsBufferData, barycoordsBufferSizeSz);
409
410 for (size_t i = 0; i < outputData.size(); ++i)
411 {
412 const auto& outVal = outputData[i];
413 const auto& expectedVal = expectedOutputCoordinates[i];
414
415 if (outVal.z() != 0.0f || outVal.w() != 0.0f || de::abs(outVal.x() - expectedVal.x()) > kThreshold || de::abs(outVal.y() - expectedVal.y()) > kThreshold)
416 {
417 std::ostringstream msg;
418 msg << "Unexpected value found for ray " << i << ": expected " << expectedVal << " and found " << outVal << ";";
419 TCU_FAIL(msg.str());
420 }
421 }
422
423 return tcu::TestStatus::pass("Pass");
424 }
425
426 } // anonymous
427
createBarycentricCoordinatesTests(tcu::TestContext & testCtx)428 tcu::TestCaseGroup* createBarycentricCoordinatesTests (tcu::TestContext& testCtx)
429 {
430 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
431
432 GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "barycentric_coordinates", "Test barycentric coordinates reported in hit attributes"));
433
434 deUint32 seed = 1614343620u;
435 mainGroup->addChild(new BarycentricCoordinatesCase(testCtx, "ahit", "", TestParams{VK_SHADER_STAGE_ANY_HIT_BIT_KHR, seed++}));
436 mainGroup->addChild(new BarycentricCoordinatesCase(testCtx, "chit", "", TestParams{VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, seed++}));
437
438 return mainGroup.release();
439 }
440
441 } // RayTracing
442 } // vkt
443
444