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