1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2023 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Device loss tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktPostmortemDeviceLossTests.hpp"
25 #include "vktCustomInstancesDevices.hpp"
26 #include "tcuCommandLine.hpp"
27 #include "vkBufferWithMemory.hpp"
28 #include "vkBuilderUtil.hpp"
29 #include "vktTestCase.hpp"
30 #include "vkQueryUtil.hpp"
31 #include "vkTypeUtil.hpp"
32 #include "vkCmdUtil.hpp"
33 #include "vkObjUtil.hpp"
34 #include "vkMemUtil.hpp"
35 #include <functional>
36 #include <vector>
37
38 namespace vkt
39 {
40 namespace postmortem
41 {
42 namespace
43 {
44 using namespace vk;
45 using namespace tcu;
46
createPostmortemDevice(Context & context)47 Move<VkDevice> createPostmortemDevice(Context& context)
48 {
49 const float queuePriority = 1.0f;
50
51 // Create a universal queue that supports graphics and compute
52 const VkDeviceQueueCreateInfo queueParams
53 {
54 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
55 DE_NULL, // const void* pNext;
56 0u, // VkDeviceQueueCreateFlags flags;
57 context.getUniversalQueueFamilyIndex(), // deUint32 queueFamilyIndex;
58 1u, // deUint32 queueCount;
59 &queuePriority // const float* pQueuePriorities;
60 };
61
62 std::vector<const char*> extensionPtrs = { "VK_KHR_maintenance5" };
63 VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeatures = initVulkanStructure();
64 VkPhysicalDeviceFeatures2 features2 = initVulkanStructure();
65 const auto addFeatures = makeStructChainAdder(&features2);
66
67 deMemset(&features2.features, 0, sizeof(VkPhysicalDeviceFeatures));
68 if (context.getDeviceFeatures().pipelineStatisticsQuery)
69 features2.features.pipelineStatisticsQuery = 1;
70
71 if (context.isDeviceFunctionalitySupported("VK_KHR_timeline_semaphore"))
72 {
73 extensionPtrs.push_back("VK_KHR_timeline_semaphore");
74 timelineSemaphoreFeatures.timelineSemaphore = 1;
75 addFeatures(&timelineSemaphoreFeatures);
76 }
77
78 const VkDeviceCreateInfo deviceParams
79 {
80 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType;
81 &features2, // const void* pNext;
82 0u, // VkDeviceCreateFlags flags;
83 1u, // deUint32 queueCreateInfoCount;
84 &queueParams, // const VkDeviceQueueCreateInfo* pQueueCreateInfos;
85 0u, // deUint32 enabledLayerCount;
86 DE_NULL, // const char* const* ppEnabledLayerNames;
87 (deUint32)extensionPtrs.size(), // deUint32 enabledExtensionCount;
88 extensionPtrs.data(), // const char* const* ppEnabledExtensionNames;
89 DE_NULL // const VkPhysicalDeviceFeatures* pEnabledFeatures;
90 };
91
92 return createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), context.getPlatformInterface(),
93 context.getInstance(), context.getInstanceInterface(), context.getPhysicalDevice(), &deviceParams);
94 }
95
96 class DeviceLossInstance : public TestInstance
97 {
98 public:
DeviceLossInstance(Context & context)99 DeviceLossInstance (Context& context)
100 : TestInstance (context) {}
101 virtual ~DeviceLossInstance () = default;
102
103 virtual TestStatus iterate (void) override;
104 };
105
iterate(void)106 TestStatus DeviceLossInstance::iterate (void)
107 {
108 vk::Unique<vk::VkDevice> logicalDevice (createPostmortemDevice(m_context));
109 vk::DeviceDriver deviceDriver (m_context.getPlatformInterface(), m_context.getInstance(), *logicalDevice, m_context.getUsedApiVersion());
110 deUint32 queueFamilyIndex (0);
111 vk::VkQueue queue (getDeviceQueue(deviceDriver, *logicalDevice, queueFamilyIndex, 0));
112 vk::SimpleAllocator allocator (deviceDriver, *logicalDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
113
114 // create query pool
115 const VkQueryPoolCreateInfo queryPoolInfo
116 {
117 VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, // VkStructureType sType
118 DE_NULL, // const void* pNext
119 (VkQueryPoolCreateFlags)0, // VkQueryPoolCreateFlags flags
120 VK_QUERY_TYPE_PIPELINE_STATISTICS , // VkQueryType queryType
121 1u, // deUint32 entryCount
122 VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT, // VkQueryPipelineStatisticFlags pipelineStatistics
123 };
124 Move<VkQueryPool> queryPool;
125 const bool usePipelineStatisticsQuery = m_context.getDeviceFeatures().pipelineStatisticsQuery;
126 if (usePipelineStatisticsQuery)
127 queryPool = createQueryPool(deviceDriver, *logicalDevice, &queryPoolInfo);
128
129 // create output buffer
130 const auto outBufferInfo = makeBufferCreateInfo(sizeof(deUint32), (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT));
131 de::MovePtr<BufferWithMemory> outBuffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(deviceDriver, *logicalDevice, allocator, outBufferInfo, MemoryRequirement::HostVisible));
132
133 // create descriptor set layout
134 auto descriptorSetLayout = DescriptorSetLayoutBuilder()
135 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)
136 .build(deviceDriver, *logicalDevice);
137
138 // create descriptor pool
139 auto descriptorPool = DescriptorPoolBuilder()
140 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
141 .build(deviceDriver, *logicalDevice, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
142
143 // create and update descriptor set
144 const VkDescriptorSetAllocateInfo allocInfo
145 {
146 VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
147 DE_NULL,
148 *descriptorPool,
149 1u,
150 &(*descriptorSetLayout)
151 };
152 auto descriptorSet = allocateDescriptorSet(deviceDriver, *logicalDevice, &allocInfo);
153 const VkDescriptorBufferInfo descriptorInfo = makeDescriptorBufferInfo(**outBuffer, (VkDeviceSize)0u, sizeof(deUint32));
154 DescriptorSetUpdateBuilder()
155 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo)
156 .update(deviceDriver, *logicalDevice);
157
158 // create compute pipeline
159 const Unique<VkShaderModule> shaderModule (createShaderModule(deviceDriver, *logicalDevice, m_context.getBinaryCollection().get("comp"), 0u));
160 const VkPushConstantRange pushConstantRange { VK_SHADER_STAGE_COMPUTE_BIT, 0u, 2 * sizeof(deUint32) };
161 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(deviceDriver, *logicalDevice, 1u, &(*descriptorSetLayout), 1, &pushConstantRange));
162 const Unique<VkPipeline> pipeline (makeComputePipeline(deviceDriver, *logicalDevice, *pipelineLayout, *shaderModule));
163
164 // create command buffer
165 const Unique<VkCommandPool> cmdPool (makeCommandPool(deviceDriver, *logicalDevice, queueFamilyIndex));
166 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(deviceDriver, *logicalDevice, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
167
168 const deUint32 pushConstant[] { 4u, 0u };
169
170 beginCommandBuffer(deviceDriver, *cmdBuffer, VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT);
171 if (usePipelineStatisticsQuery)
172 deviceDriver.cmdResetQueryPool(*cmdBuffer, *queryPool, 0u, 1u);
173 deviceDriver.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
174 if (usePipelineStatisticsQuery)
175 deviceDriver.cmdBeginQuery(*cmdBuffer, *queryPool, 0u, (VkQueryControlFlags)0u);
176 deviceDriver.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, 0);
177 deviceDriver.cmdPushConstants(*cmdBuffer, *pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pushConstant), &pushConstant);
178 deviceDriver.cmdDispatch(*cmdBuffer, 1, 1, 1);
179 if (usePipelineStatisticsQuery)
180 deviceDriver.cmdEndQuery(*cmdBuffer, *queryPool, 0u);
181 endCommandBuffer(deviceDriver, *cmdBuffer);
182
183 const deUint64 waitValue (0);
184 deUint64 waitTimeout (5000000000ull);
185 deUint64 queryResult (0);
186 const Move<VkFence> fence[2]
187 {
188 createFence(deviceDriver, *logicalDevice),
189 createFence(deviceDriver, *logicalDevice)
190 };
191 const Move<VkEvent> event[2]
192 {
193 createEvent(deviceDriver, *logicalDevice),
194 createEvent(deviceDriver, *logicalDevice)
195 };
196
197 Move<VkSemaphore> semaphore[2];
198 if (m_context.isDeviceFunctionalitySupported("VK_KHR_timeline_semaphore"))
199 {
200 semaphore[0] = createSemaphoreType(deviceDriver, *logicalDevice, VK_SEMAPHORE_TYPE_TIMELINE);
201 semaphore[1] = createSemaphoreType(deviceDriver, *logicalDevice, VK_SEMAPHORE_TYPE_TIMELINE);
202 }
203
204 VkSemaphoreWaitInfo waitInfo
205 {
206 VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, // VkStructureType sType
207 DE_NULL, // const void* pNext
208 VK_SEMAPHORE_WAIT_ANY_BIT, // VkSemaphoreWaitFlags flags;
209 1u, // deUint32 semaphoreCount;
210 &*semaphore[0], // const VkSemaphore* pSemaphores;
211 &waitValue // const deUint64* pValues;
212 };
213
214 const VkSubmitInfo submitInfo
215 {
216 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
217 DE_NULL, // const void* pNext;
218 0u, // deUint32 waitSemaphoreCount;
219 DE_NULL, // const VkSemaphore* pWaitSemaphores;
220 DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
221 1u, // deUint32 commandBufferCount;
222 &*cmdBuffer, // const VkCommandBuffer* pCommandBuffers;
223 0u, // deUint32 signalSemaphoreCount;
224 DE_NULL, // const VkSemaphore* pSignalSemaphores;
225 };
226
227 // create vector containing lambdas with all functions that we need to check;
228 // this will simplify testing code by allowing us to check those functions within a loop;
229 // note that order of functions is important; we cant break any VUIDs
230 deUint32 handleIndex = 0;
231 std::vector< std::pair<std::string, std::function<VkResult () > > > functionsToCheck
232 {
233 {
234 "queueSubmit",
235 [&]() {
236 return deviceDriver.queueSubmit(queue, 1u, &submitInfo, *fence[handleIndex]);
237 }
238 },
239 {
240 "waitSemaphores",
241 [&]() {
242 if (*semaphore[handleIndex] == 0)
243 return VK_RESULT_MAX_ENUM;
244 waitInfo.pSemaphores = &*semaphore[handleIndex];
245 return deviceDriver.waitSemaphores(*logicalDevice, &waitInfo, waitTimeout);
246 }
247 },
248 {
249 "getEventStatus",
250 [&]() {
251 return deviceDriver.getEventStatus(*logicalDevice, *event[handleIndex]);
252 }
253 },
254 {
255 "waitForFences",
256 [&]() {
257 return deviceDriver.waitForFences(*logicalDevice, 1u, &(*fence[handleIndex]), DE_TRUE, waitTimeout);
258 }
259 },
260 {
261 "getFenceStatus",
262 [&]() {
263 return deviceDriver.getFenceStatus(*logicalDevice, *fence[handleIndex]);
264 }
265 },
266 {
267 "deviceWaitIdle",
268 [&]() {
269 return deviceDriver.deviceWaitIdle(*logicalDevice);
270 }
271 },
272 {
273 "getQueryPoolResults",
274 [&]() {
275 if (usePipelineStatisticsQuery)
276 return deviceDriver.getQueryPoolResults(*logicalDevice, *queryPool, 0u, 1u, sizeof(queryResult), &queryResult, 0u, 0u);
277 return VK_RESULT_MAX_ENUM;
278 }
279 }
280 };
281
282 // call all functions untill one returns VK_ERROR_DEVICE_LOST
283 bool deviceWasLost = 0;
284 for (const auto& funPair : functionsToCheck)
285 {
286 VkResult result = funPair.second();
287
288 deviceWasLost = (result == VK_ERROR_DEVICE_LOST);
289 if (deviceWasLost)
290 break;
291
292 if (result == VK_TIMEOUT)
293 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Timeout exceeded");
294 }
295
296 // never returning DEVICE_LOST is fine
297 if (!deviceWasLost)
298 return TestStatus::pass("DEVICE_LOST was never returned");
299
300 // call all functions once egain and expect all to return VK_ERROR_DEVICE_LOST
301 handleIndex = 1;
302 for (const auto& funPair : functionsToCheck)
303 {
304 VkResult result = funPair.second();
305 if (result == VK_ERROR_DEVICE_LOST)
306 continue;
307
308 // skip waitSemaphores / getQueryPoolResults
309 if (result == VK_RESULT_MAX_ENUM)
310 continue;
311
312 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, std::string("Wrong VkResult for ") + funPair.first);
313 }
314
315 return TestStatus::pass("DEVICE_LOST returned by all functions");
316 }
317
318 class DeviceLossCase : public TestCase
319 {
320 public:
321 DeviceLossCase (TestContext& testCtx,
322 const std::string& name);
323 virtual ~DeviceLossCase () = default;
324 virtual void checkSupport (Context& context) const override;
325 virtual void initPrograms (SourceCollections& programCollection) const override;
326 virtual TestInstance* createInstance (Context& context) const override;
327 };
328
DeviceLossCase(TestContext & testCtx,const std::string & name)329 DeviceLossCase::DeviceLossCase (TestContext& testCtx, const std::string& name)
330 : TestCase(testCtx, name)
331 {
332 }
333
checkSupport(Context & context) const334 void DeviceLossCase::checkSupport(Context& context) const
335 {
336 context.requireDeviceFunctionality("VK_KHR_maintenance5");
337 }
338
initPrograms(vk::SourceCollections & programCollection) const339 void DeviceLossCase::initPrograms(vk::SourceCollections& programCollection) const
340 {
341 // create shader with infinite loop to trigger DEVICE_LOST
342 programCollection.glslSources.add("comp") << glu::ComputeSource(
343 "#version 320 es\n"
344 "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1)\n"
345 "layout(push_constant) uniform Constants { uvec2 inp; } pc; \n"
346 "layout(std430, set = 0, binding = 0) writeonly buffer Data { uint outp[]; } data;\n"
347 "void main()\n"
348 "{\n"
349 " uint i = pc.inp.x;\n"
350 " while (i > pc.inp.y)\n"
351 " {\n"
352 " i = i + uint(1);\n"
353 " if (i == uint(0))\n"
354 " i = pc.inp.x;\n"
355 " }\n"
356 " data.outp[0] = i;\n"
357 "}\n");
358 }
359
createInstance(Context & context) const360 TestInstance* DeviceLossCase::createInstance(Context& context) const
361 {
362 return new DeviceLossInstance(context);
363 }
364
365 } // unnamed
366
createDeviceLossTests(tcu::TestContext & testCtx)367 tcu::TestCaseGroup* createDeviceLossTests(tcu::TestContext& testCtx)
368 {
369 auto rootGroup = new TestCaseGroup(testCtx, "device_loss", "");
370
371 rootGroup->addChild(new DeviceLossCase(testCtx, "maintenance5"));
372
373 return rootGroup;
374 }
375
376 } // postmortem
377 } // vkt
378