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 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
54 DE_NULL, // const void* pNext;
55 0u, // VkDeviceQueueCreateFlags flags;
56 context.getUniversalQueueFamilyIndex(), // uint32_t queueFamilyIndex;
57 1u, // uint32_t queueCount;
58 &queuePriority // const float* pQueuePriorities;
59 };
60
61 std::vector<const char *> extensionPtrs = {"VK_KHR_maintenance5"};
62 VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeatures = initVulkanStructure();
63 VkPhysicalDeviceFeatures2 features2 = initVulkanStructure();
64 const auto addFeatures = makeStructChainAdder(&features2);
65
66 deMemset(&features2.features, 0, sizeof(VkPhysicalDeviceFeatures));
67 if (context.getDeviceFeatures().pipelineStatisticsQuery)
68 features2.features.pipelineStatisticsQuery = 1;
69
70 if (context.isDeviceFunctionalitySupported("VK_KHR_timeline_semaphore"))
71 {
72 extensionPtrs.push_back("VK_KHR_timeline_semaphore");
73 timelineSemaphoreFeatures.timelineSemaphore = 1;
74 addFeatures(&timelineSemaphoreFeatures);
75 }
76
77 const VkDeviceCreateInfo deviceParams{
78 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType;
79 &features2, // const void* pNext;
80 0u, // VkDeviceCreateFlags flags;
81 1u, // uint32_t queueCreateInfoCount;
82 &queueParams, // const VkDeviceQueueCreateInfo* pQueueCreateInfos;
83 0u, // uint32_t enabledLayerCount;
84 DE_NULL, // const char* const* ppEnabledLayerNames;
85 (uint32_t)extensionPtrs.size(), // uint32_t enabledExtensionCount;
86 extensionPtrs.data(), // const char* const* ppEnabledExtensionNames;
87 DE_NULL // const VkPhysicalDeviceFeatures* pEnabledFeatures;
88 };
89
90 return createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(),
91 context.getPlatformInterface(), context.getInstance(), context.getInstanceInterface(),
92 context.getPhysicalDevice(), &deviceParams);
93 }
94
95 class DeviceLossInstance : public TestInstance
96 {
97 public:
DeviceLossInstance(Context & context)98 DeviceLossInstance(Context &context) : TestInstance(context)
99 {
100 }
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,
110 m_context.getUsedApiVersion(), m_context.getTestContext().getCommandLine());
111 uint32_t queueFamilyIndex(0);
112 vk::VkQueue queue(getDeviceQueue(deviceDriver, *logicalDevice, queueFamilyIndex, 0));
113 vk::SimpleAllocator allocator(
114 deviceDriver, *logicalDevice,
115 getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
116
117 // create query pool
118 const VkQueryPoolCreateInfo queryPoolInfo{
119 VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, // VkStructureType sType
120 DE_NULL, // const void* pNext
121 (VkQueryPoolCreateFlags)0, // VkQueryPoolCreateFlags flags
122 VK_QUERY_TYPE_PIPELINE_STATISTICS, // VkQueryType queryType
123 1u, // uint32_t entryCount
124 VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT, // VkQueryPipelineStatisticFlags pipelineStatistics
125 };
126 Move<VkQueryPool> queryPool;
127 const bool usePipelineStatisticsQuery = m_context.getDeviceFeatures().pipelineStatisticsQuery;
128 if (usePipelineStatisticsQuery)
129 queryPool = createQueryPool(deviceDriver, *logicalDevice, &queryPoolInfo);
130
131 // create output buffer
132 const auto outBufferInfo =
133 makeBufferCreateInfo(sizeof(uint32_t), (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT));
134 de::MovePtr<BufferWithMemory> outBuffer = de::MovePtr<BufferWithMemory>(
135 new BufferWithMemory(deviceDriver, *logicalDevice, allocator, outBufferInfo, MemoryRequirement::HostVisible));
136
137 // create descriptor set layout
138 auto descriptorSetLayout = DescriptorSetLayoutBuilder()
139 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)
140 .build(deviceDriver, *logicalDevice);
141
142 // create descriptor pool
143 auto descriptorPool =
144 DescriptorPoolBuilder()
145 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
146 .build(deviceDriver, *logicalDevice, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
147
148 // create and update descriptor set
149 const VkDescriptorSetAllocateInfo allocInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, DE_NULL,
150 *descriptorPool, 1u, &(*descriptorSetLayout)};
151 auto descriptorSet = allocateDescriptorSet(deviceDriver, *logicalDevice, &allocInfo);
152 const VkDescriptorBufferInfo descriptorInfo =
153 makeDescriptorBufferInfo(**outBuffer, (VkDeviceSize)0u, sizeof(uint32_t));
154 DescriptorSetUpdateBuilder()
155 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
156 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo)
157 .update(deviceDriver, *logicalDevice);
158
159 // create compute pipeline
160 const Unique<VkShaderModule> shaderModule(
161 createShaderModule(deviceDriver, *logicalDevice, m_context.getBinaryCollection().get("comp"), 0u));
162 const VkPushConstantRange pushConstantRange{VK_SHADER_STAGE_COMPUTE_BIT, 0u, 2 * sizeof(uint32_t)};
163 const Unique<VkPipelineLayout> pipelineLayout(
164 makePipelineLayout(deviceDriver, *logicalDevice, 1u, &(*descriptorSetLayout), 1, &pushConstantRange));
165 const Unique<VkPipeline> pipeline(
166 makeComputePipeline(deviceDriver, *logicalDevice, *pipelineLayout, *shaderModule));
167
168 // create command buffer
169 const Unique<VkCommandPool> cmdPool(makeCommandPool(deviceDriver, *logicalDevice, queueFamilyIndex));
170 const Unique<VkCommandBuffer> cmdBuffer(
171 allocateCommandBuffer(deviceDriver, *logicalDevice, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
172
173 const uint32_t pushConstant[]{4u, 0u};
174
175 beginCommandBuffer(deviceDriver, *cmdBuffer, VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT);
176 if (usePipelineStatisticsQuery)
177 deviceDriver.cmdResetQueryPool(*cmdBuffer, *queryPool, 0u, 1u);
178 deviceDriver.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
179 if (usePipelineStatisticsQuery)
180 deviceDriver.cmdBeginQuery(*cmdBuffer, *queryPool, 0u, (VkQueryControlFlags)0u);
181 deviceDriver.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1,
182 &descriptorSet.get(), 0, 0);
183 deviceDriver.cmdPushConstants(*cmdBuffer, *pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pushConstant),
184 &pushConstant);
185 deviceDriver.cmdDispatch(*cmdBuffer, 1, 1, 1);
186 if (usePipelineStatisticsQuery)
187 deviceDriver.cmdEndQuery(*cmdBuffer, *queryPool, 0u);
188 endCommandBuffer(deviceDriver, *cmdBuffer);
189
190 const uint64_t waitValue(0);
191 uint64_t waitTimeout(5000000000ull);
192 uint64_t queryResult(0);
193 const Move<VkFence> fence[2]{createFence(deviceDriver, *logicalDevice), createFence(deviceDriver, *logicalDevice)};
194 const Move<VkEvent> event[2]{createEvent(deviceDriver, *logicalDevice), createEvent(deviceDriver, *logicalDevice)};
195
196 Move<VkSemaphore> semaphore[2];
197 if (m_context.isDeviceFunctionalitySupported("VK_KHR_timeline_semaphore"))
198 {
199 semaphore[0] = createSemaphoreType(deviceDriver, *logicalDevice, VK_SEMAPHORE_TYPE_TIMELINE);
200 semaphore[1] = createSemaphoreType(deviceDriver, *logicalDevice, VK_SEMAPHORE_TYPE_TIMELINE);
201 }
202
203 VkSemaphoreWaitInfo waitInfo{
204 VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, // VkStructureType sType
205 DE_NULL, // const void* pNext
206 VK_SEMAPHORE_WAIT_ANY_BIT, // VkSemaphoreWaitFlags flags;
207 1u, // uint32_t semaphoreCount;
208 &*semaphore[0], // const VkSemaphore* pSemaphores;
209 &waitValue // const uint64_t* pValues;
210 };
211
212 const VkSubmitInfo submitInfo{
213 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
214 DE_NULL, // const void* pNext;
215 0u, // uint32_t waitSemaphoreCount;
216 DE_NULL, // const VkSemaphore* pWaitSemaphores;
217 DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
218 1u, // uint32_t commandBufferCount;
219 &*cmdBuffer, // const VkCommandBuffer* pCommandBuffers;
220 0u, // uint32_t signalSemaphoreCount;
221 DE_NULL, // const VkSemaphore* pSignalSemaphores;
222 };
223
224 // create vector containing lambdas with all functions that we need to check;
225 // this will simplify testing code by allowing us to check those functions within a loop;
226 // note that order of functions is important; we cant break any VUIDs
227 uint32_t handleIndex = 0;
228 std::vector<std::pair<std::string, std::function<VkResult()>>> functionsToCheck{
229 {"queueSubmit", [&]() { return deviceDriver.queueSubmit(queue, 1u, &submitInfo, *fence[handleIndex]); }},
230 {"waitSemaphores",
231 [&]()
232 {
233 if (*semaphore[handleIndex] == 0)
234 return VK_RESULT_MAX_ENUM;
235 waitInfo.pSemaphores = &*semaphore[handleIndex];
236 return deviceDriver.waitSemaphores(*logicalDevice, &waitInfo, waitTimeout);
237 }},
238 {"getEventStatus", [&]() { return deviceDriver.getEventStatus(*logicalDevice, *event[handleIndex]); }},
239 {"waitForFences",
240 [&]() { return deviceDriver.waitForFences(*logicalDevice, 1u, &(*fence[handleIndex]), true, waitTimeout); }},
241 {"getFenceStatus", [&]() { return deviceDriver.getFenceStatus(*logicalDevice, *fence[handleIndex]); }},
242 {"deviceWaitIdle", [&]() { return deviceDriver.deviceWaitIdle(*logicalDevice); }},
243 {"getQueryPoolResults", [&]()
244 {
245 if (usePipelineStatisticsQuery)
246 return deviceDriver.getQueryPoolResults(*logicalDevice, *queryPool, 0u, 1u, sizeof(queryResult),
247 &queryResult, 0u, 0u);
248 return VK_RESULT_MAX_ENUM;
249 }}};
250
251 // call all functions untill one returns VK_ERROR_DEVICE_LOST
252 bool deviceWasLost = 0;
253 for (const auto &funPair : functionsToCheck)
254 {
255 VkResult result = funPair.second();
256
257 deviceWasLost = (result == VK_ERROR_DEVICE_LOST);
258 if (deviceWasLost)
259 break;
260
261 if (result == VK_TIMEOUT)
262 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "Timeout exceeded");
263 }
264
265 // never returning DEVICE_LOST is fine
266 if (!deviceWasLost)
267 return TestStatus::pass("DEVICE_LOST was never returned");
268
269 // call all functions once egain and expect all to return VK_ERROR_DEVICE_LOST
270 handleIndex = 1;
271 for (const auto &funPair : functionsToCheck)
272 {
273 VkResult result = funPair.second();
274 if (result == VK_ERROR_DEVICE_LOST)
275 continue;
276
277 // skip waitSemaphores / getQueryPoolResults
278 if (result == VK_RESULT_MAX_ENUM)
279 continue;
280
281 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, std::string("Wrong VkResult for ") + funPair.first);
282 }
283
284 return TestStatus::pass("DEVICE_LOST returned by all functions");
285 }
286
287 class DeviceLossCase : public TestCase
288 {
289 public:
290 DeviceLossCase(TestContext &testCtx, const std::string &name);
291 virtual ~DeviceLossCase() = default;
292 virtual void checkSupport(Context &context) const override;
293 virtual void initPrograms(SourceCollections &programCollection) const override;
294 virtual TestInstance *createInstance(Context &context) const override;
295 };
296
DeviceLossCase(TestContext & testCtx,const std::string & name)297 DeviceLossCase::DeviceLossCase(TestContext &testCtx, const std::string &name) : TestCase(testCtx, name)
298 {
299 }
300
checkSupport(Context & context) const301 void DeviceLossCase::checkSupport(Context &context) const
302 {
303 context.requireDeviceFunctionality("VK_KHR_maintenance5");
304 }
305
initPrograms(vk::SourceCollections & programCollection) const306 void DeviceLossCase::initPrograms(vk::SourceCollections &programCollection) const
307 {
308 // create shader with infinite loop to trigger DEVICE_LOST
309 programCollection.glslSources.add("comp")
310 << glu::ComputeSource("#version 320 es\n"
311 "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1)\n"
312 "layout(push_constant) uniform Constants { uvec2 inp; } pc; \n"
313 "layout(std430, set = 0, binding = 0) writeonly buffer Data { uint outp[]; } data;\n"
314 "void main()\n"
315 "{\n"
316 " uint i = pc.inp.x;\n"
317 " while (i > pc.inp.y)\n"
318 " {\n"
319 " i = i + uint(1);\n"
320 " if (i == uint(0))\n"
321 " i = pc.inp.x;\n"
322 " }\n"
323 " data.outp[0] = i;\n"
324 "}\n");
325 }
326
createInstance(Context & context) const327 TestInstance *DeviceLossCase::createInstance(Context &context) const
328 {
329 return new DeviceLossInstance(context);
330 }
331
332 } // namespace
333
createDeviceLossTests(tcu::TestContext & testCtx)334 tcu::TestCaseGroup *createDeviceLossTests(tcu::TestContext &testCtx)
335 {
336 auto rootGroup = new TestCaseGroup(testCtx, "device_loss");
337
338 rootGroup->addChild(new DeviceLossCase(testCtx, "maintenance5"));
339
340 return rootGroup;
341 }
342
343 } // namespace postmortem
344 } // namespace vkt
345