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