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 ¶ms);
~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 ¶ms);
~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 ¶ms)
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 ¶ms)
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