1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 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  Vulkan SC VkCommandPoolMemoryReservationCreateInfo tests
22*//*--------------------------------------------------------------------*/
23
24#include "vktCommandPoolMemoryReservationTests.hpp"
25
26#include <set>
27#include <vector>
28#include <string>
29
30#include "vkRefUtil.hpp"
31#include "vktTestCaseUtil.hpp"
32#include "vkSafetyCriticalUtil.hpp"
33#include "tcuTestLog.hpp"
34
35namespace vkt
36{
37namespace sc
38{
39
40using namespace vk;
41
42namespace
43{
44
45typedef de::SharedPtr<vk::Unique<vk::VkEvent> >	VkEventSp;
46
47enum CommandPoolReservedSize
48{
49	CPS_UNUSED = 0,
50	CPS_SMALL,
51	CPS_BIG
52};
53
54struct TestParams
55{
56	CommandPoolReservedSize	commandPoolReservedSize;
57	deUint32				commandBufferCount;
58	deUint32				iterations;
59	bool					multipleRecording;
60};
61
62void beginCommandBuffer (const DeviceInterface& vk, const VkCommandBuffer commandBuffer, VkCommandBufferUsageFlags flags)
63{
64	const VkCommandBufferBeginInfo commandBufBeginParams =
65	{
66		VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,	// VkStructureType                  sType;
67		DE_NULL,										// const void*                      pNext;
68		flags,											// VkCommandBufferUsageFlags        flags;
69		(const VkCommandBufferInheritanceInfo*)DE_NULL,
70	};
71	VK_CHECK(vk.beginCommandBuffer(commandBuffer, &commandBufBeginParams));
72}
73
74void endCommandBuffer (const DeviceInterface& vk, const VkCommandBuffer commandBuffer)
75{
76	VK_CHECK(vk.endCommandBuffer(commandBuffer));
77}
78
79
80// verify that VkCommandPoolMemoryReservationCreateInfo::commandPoolReservedSize == VkCommandPoolMemoryConsumption::commandPoolReservedSize
81tcu::TestStatus verifyCommandPoolReservedSize (Context& context, TestParams testParams)
82{
83	const VkDevice							device					= context.getDevice();
84	const DeviceInterface&					vk						= context.getDeviceInterface();
85	const deUint32							queueFamilyIndex		= context.getUniversalQueueFamilyIndex();
86
87	if ( testParams.commandBufferCount >  context.getDeviceVulkanSC10Properties().maxCommandPoolCommandBuffers )
88		TCU_THROW(NotSupportedError, "commandBufferCount is greater than maxCommandPoolCommandBuffers");
89
90	VkDeviceSize							commandPoolReservedSize	= 0u;
91	switch (testParams.commandPoolReservedSize)
92	{
93		case CPS_SMALL:
94			commandPoolReservedSize = de::max(VkDeviceSize(64u * context.getTestContext().getCommandLine().getCommandDefaultSize()), VkDeviceSize(context.getTestContext().getCommandLine().getCommandPoolMinSize()) );
95			break;
96		case CPS_BIG:
97			commandPoolReservedSize = de::max(VkDeviceSize(8192u * context.getTestContext().getCommandLine().getCommandDefaultSize()), VkDeviceSize(context.getTestContext().getCommandLine().getCommandPoolMinSize()));
98			break;
99		default:
100			TCU_THROW(InternalError, "Unsupported commandPoolReservedSize value");
101	}
102	commandPoolReservedSize = de::max(commandPoolReservedSize, VkDeviceSize(testParams.commandBufferCount * context.getTestContext().getCommandLine().getCommandBufferMinSize()));
103
104	// Create command pool with declared size
105	// By connecting our own VkCommandPoolMemoryReservationCreateInfo we avoid getting unknown data from DeviceDriverSC::createCommandPoolHandlerNorm()
106	VkCommandPoolMemoryReservationCreateInfo cpMemReservationCI		=
107	{
108		VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_RESERVATION_CREATE_INFO,	// VkStructureType		sType
109		DE_NULL,														// const void*			pNext
110		commandPoolReservedSize,										// VkDeviceSize			commandPoolReservedSize
111		testParams.commandBufferCount									// uint32_t				commandPoolMaxCommandBuffers
112	};
113
114	const VkCommandPoolCreateInfo			cmdPoolParams			=
115	{
116		VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,					//	VkStructureType				sType;
117		(const void*)&cpMemReservationCI,							//	const void*					pNext;
118		0u,															//	VkCommandPoolCreateFlags	flags;
119		queueFamilyIndex,											//	deUint32					queueFamilyIndex;
120	};
121	const Unique<VkCommandPool>				cmdPool					(createCommandPool(vk, device, &cmdPoolParams));
122
123	// check if size collected by vkGetCommandPoolMemoryConsumption matches size from VkCommandPoolMemoryReservationCreateInfo
124
125	VkCommandPoolMemoryConsumption			memConsumption			=
126	{
127		VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_CONSUMPTION,	// VkStructureType	sType
128		DE_NULL,											// void*			pNext
129		0,													// VkDeviceSize		commandPoolAllocated
130		0,													// VkDeviceSize		commandPoolReservedSize
131		0,													// VkDeviceSize		commandBufferAllocated
132	};
133
134	vk.getCommandPoolMemoryConsumption(device, *cmdPool, DE_NULL, &memConsumption);
135
136	if (commandPoolReservedSize != memConsumption.commandPoolReservedSize)
137		return tcu::TestStatus::fail("Failed");
138	return tcu::TestStatus::pass("Pass");
139}
140
141// verify that VkCommandPoolMemoryReservationCreateInfo::commandPoolAllocated == sum of VkCommandPoolMemoryConsumption::commandBufferAllocated
142tcu::TestStatus verifyCommandPoolAllocEqualsCommandBufferAlloc (Context& context, TestParams testParams)
143{
144	const VkDevice							device					= context.getDevice();
145	const DeviceInterface&					vk						= context.getDeviceInterface();
146	const deUint32							queueFamilyIndex		= context.getUniversalQueueFamilyIndex();
147
148	// fill command buffers
149	deUint32								eventCount				= 0u;
150	switch (testParams.commandPoolReservedSize)
151	{
152		case CPS_SMALL:
153			eventCount				= 1u;
154			break;
155		case CPS_BIG:
156			eventCount				= 32u;
157			break;
158		default:
159			TCU_THROW(InternalError, "Unsupported commandPoolReservedSize value");
160	}
161	VkDeviceSize							commandPoolReservedSize	= de::max(VkDeviceSize(eventCount * context.getTestContext().getCommandLine().getCommandDefaultSize()), VkDeviceSize(context.getTestContext().getCommandLine().getCommandPoolMinSize()));
162	commandPoolReservedSize											= de::max(commandPoolReservedSize, VkDeviceSize(testParams.commandBufferCount * context.getTestContext().getCommandLine().getCommandBufferMinSize()));
163
164	// Create command pool with declared size
165	// By connecting our own VkCommandPoolMemoryReservationCreateInfo we avoid getting unknown data from DeviceDriverSC::createCommandPoolHandlerNorm()
166	VkCommandPoolMemoryReservationCreateInfo cpMemReservationCI		=
167	{
168		VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_RESERVATION_CREATE_INFO,	// VkStructureType		sType
169		DE_NULL,														// const void*			pNext
170		commandPoolReservedSize,										// VkDeviceSize			commandPoolReservedSize
171		testParams.commandBufferCount									// uint32_t				commandPoolMaxCommandBuffers
172	};
173
174	const VkCommandPoolCreateInfo			cmdPoolParams			=
175	{
176		VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,					//	VkStructureType				sType;
177		(const void*)&cpMemReservationCI,							//	const void*					pNext;
178		0u,															//	VkCommandPoolCreateFlags	flags;
179		queueFamilyIndex,											//	deUint32					queueFamilyIndex;
180	};
181	const Unique<VkCommandPool>				cmdPool					(createCommandPool(vk, device, &cmdPoolParams));
182
183	// Allocate command buffers
184	std::vector<Move<VkCommandBuffer>>			commandBuffers			(testParams.commandBufferCount);
185	const VkCommandBufferAllocateInfo		cmdBufferAllocateInfo	=
186	{
187		VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,				// VkStructureType             sType;
188		DE_NULL,													// const void*                 pNext;
189		*cmdPool,													// VkCommandPool               commandPool;
190		VK_COMMAND_BUFFER_LEVEL_PRIMARY,							// VkCommandBufferLevel        level;
191		testParams.commandBufferCount								// deUint32                    commandBufferCount;
192	};
193    allocateCommandBuffers(vk, device, &cmdBufferAllocateInfo, commandBuffers.data());
194
195	std::vector<VkEventSp>					events;
196	for (deUint32 ndx = 0; ndx < eventCount; ++ndx)
197		events.push_back(VkEventSp(new vk::Unique<VkEvent>(createEvent(vk, device))));
198
199	bool isOK = true;
200	for (deUint32 iter = 0; iter < 2 * testParams.iterations; ++iter)
201	{
202		// Build command buffers on even iteration
203		if (0 == iter % 2)
204		{
205			if (testParams.multipleRecording)
206			{
207				for (deUint32 i = 0; i < testParams.commandBufferCount; ++i)
208					beginCommandBuffer(vk, commandBuffers[i].get(), 0u);
209
210				for (deUint32 i = 0; i < testParams.commandBufferCount; ++i)
211					for (deUint32 j = 0; j < eventCount; ++j)
212						vk.cmdSetEvent(commandBuffers[i].get(), events[j]->get(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
213
214				for (deUint32 i = 0; i < testParams.commandBufferCount; ++i)
215					endCommandBuffer(vk, commandBuffers[i].get());
216			}
217			else
218			{
219				for (deUint32 i = 0; i < testParams.commandBufferCount; ++i)
220				{
221					beginCommandBuffer(vk, commandBuffers[i].get(), 0u);
222
223					for (deUint32 j = 0; j < eventCount; ++j)
224						vk.cmdSetEvent(commandBuffers[i].get(), events[j]->get(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
225
226					endCommandBuffer(vk, commandBuffers[i].get());
227				}
228			}
229		}
230		else // Reset command buffers on odd iteration
231		{
232			// leave loop when implementation is not able to perform vkResetCommandPool()
233			if (context.getDeviceVulkanSC10Properties().commandPoolResetCommandBuffer == VK_FALSE)
234				break;
235			vk.resetCommandPool(device, *cmdPool, VkCommandPoolResetFlags(0u));
236		}
237
238		// check if size collected by sum of command buffer allocs is equal to command pool alloc
239		VkDeviceSize							cbAllocSum				= 0u;
240		VkDeviceSize							commandPoolAlloc		= 0u;
241		for (deUint32 i = 0; i < testParams.commandBufferCount; ++i)
242		{
243			VkCommandPoolMemoryConsumption			memConsumption =
244			{
245				VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_CONSUMPTION,	// VkStructureType	sType
246				DE_NULL,											// void*			pNext
247				0,													// VkDeviceSize		commandPoolAllocated
248				0,													// VkDeviceSize		commandPoolReservedSize
249				0,													// VkDeviceSize		commandBufferAllocated
250			};
251			vk.getCommandPoolMemoryConsumption(device, *cmdPool, commandBuffers[i].get(), &memConsumption);
252			cbAllocSum			+=	memConsumption.commandBufferAllocated;
253			commandPoolAlloc	=	memConsumption.commandPoolAllocated;
254		}
255		if (cbAllocSum != commandPoolAlloc)
256			isOK = false;
257		// if we just performed a vkResetCommandPool() then allocated commandPool memory should be equal to 0
258		if ( (1 == iter % 2 ) && commandPoolAlloc != 0u )
259			isOK = false;
260	}
261	return isOK ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Failed");
262}
263
264void checkSupport (Context& context, TestParams testParams)
265{
266	if (testParams.iterations > 1 && context.getDeviceVulkanSC10Properties().commandPoolResetCommandBuffer == VK_FALSE)
267		TCU_THROW(NotSupportedError, "commandPoolResetCommandBuffer is not supported");
268	if (testParams.multipleRecording && context.getDeviceVulkanSC10Properties().commandPoolMultipleCommandBuffersRecording == VK_FALSE)
269		TCU_THROW(NotSupportedError, "commandPoolMultipleCommandBuffersRecording is not supported");
270	if (testParams.commandBufferCount > context.getDeviceVulkanSC10Properties().maxCommandPoolCommandBuffers)
271		TCU_THROW(NotSupportedError, "commandBufferCount is greater than maxCommandPoolCommandBuffers");
272
273}
274
275} // anonymous
276
277tcu::TestCaseGroup*	createCommandPoolMemoryReservationTests (tcu::TestContext& testCtx)
278{
279	// Tests verifying memory reservation for command pools in Vulkan SC
280	de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "command_pool_memory_reservation"));
281
282	// add vkGetCommandPoolMemoryConsumption tests
283	const struct
284	{
285		deUint32					commandPoolMaxCommandBuffers;
286		const char*					name;
287	} maxCommandBuffers[] =
288	{
289		{ 1,						"cb_single"				},
290		{ 4,						"cb_few"				},
291		{ 21,						"cb_many"				},
292		{ 256,						"cb_min_limit"			},
293		{ 1024,						"cb_above_min_limit"	},
294	};
295
296	const struct
297	{
298		CommandPoolReservedSize		commandPoolReservedSize;
299		const char*					name;
300	} reservedSizes[] =
301	{
302		{ CPS_SMALL,				"size_small"	},
303		{ CPS_BIG,					"size_big"		},
304	};
305
306	const struct
307	{
308		bool						multipleRecording;
309		const char*					name;
310	} recording[] =
311	{
312		{ false,					"single_recording"		},
313		{ true,						"multiple_recording"	},
314	};
315
316	const struct
317	{
318		deUint32					count;
319		const char*					name;
320	} iterations[] =
321	{
322		{ 1u,						"1"				},
323		{ 2u,						"2"				},
324		{ 4u,						"8"				},
325		{ 8u,						"16"			},
326	};
327
328	{
329		de::MovePtr<tcu::TestCaseGroup>	memConGroup(new tcu::TestCaseGroup(testCtx, "memory_consumption", "Testing vkGetCommandPoolMemoryConsumption"));
330
331		for (int cbIdx = 0; cbIdx < DE_LENGTH_OF_ARRAY(maxCommandBuffers); ++cbIdx)
332		{
333			de::MovePtr<tcu::TestCaseGroup> cbGroup(new tcu::TestCaseGroup(testCtx, maxCommandBuffers[cbIdx].name));
334
335			for (int sizeIdx = 0; sizeIdx < DE_LENGTH_OF_ARRAY(reservedSizes); ++sizeIdx)
336			{
337				de::MovePtr<tcu::TestCaseGroup> sizeGroup(new tcu::TestCaseGroup(testCtx, reservedSizes[sizeIdx].name));
338
339				for (int simIdx = 0; simIdx < DE_LENGTH_OF_ARRAY(recording); ++simIdx)
340				{
341					de::MovePtr<tcu::TestCaseGroup> simGroup(new tcu::TestCaseGroup(testCtx, recording[simIdx].name));
342
343					if(!recording[simIdx].multipleRecording)
344					{
345						TestParams	testParams =
346						{
347							reservedSizes[sizeIdx].commandPoolReservedSize,
348							maxCommandBuffers[cbIdx].commandPoolMaxCommandBuffers,
349							1u,
350							false
351						};
352						addFunctionCase(simGroup.get(), "reserved_size", verifyCommandPoolReservedSize, testParams);
353					}
354
355					for (int iterIdx = 0; iterIdx < DE_LENGTH_OF_ARRAY(iterations); ++iterIdx)
356					{
357						TestParams	testParams =
358						{
359							reservedSizes[sizeIdx].commandPoolReservedSize,
360							maxCommandBuffers[cbIdx].commandPoolMaxCommandBuffers,
361							iterations[iterIdx].count,
362							recording[simIdx].multipleRecording
363						};
364						std::ostringstream testName;
365						testName << "allocated_size_" << iterations[iterIdx].name;
366						addFunctionCase(simGroup.get(), testName.str(), checkSupport, verifyCommandPoolAllocEqualsCommandBufferAlloc, testParams);
367					}
368
369					sizeGroup->addChild(simGroup.release());
370				}
371				cbGroup->addChild(sizeGroup.release());
372			}
373			memConGroup->addChild(cbGroup.release());
374		}
375		group->addChild(memConGroup.release());
376	}
377
378	return group.release();
379}
380
381}	// sc
382
383}	// vkt
384