• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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