1 /*------------------------------------------------------------------------
2  *  Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2015 The Khronos Group Inc.
6  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Vulkan Buffers Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktApiBufferTests.hpp"
26 #include "gluVarType.hpp"
27 #include "deStringUtil.hpp"
28 #include "tcuTestLog.hpp"
29 #include "vkPlatform.hpp"
30 #include "vkPrograms.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vkRefUtil.hpp"
33 #include "vktTestCase.hpp"
34 #include "tcuPlatform.hpp"
35 
36 #include <algorithm>
37 
38 namespace vkt
39 {
40 namespace api
41 {
42 namespace
43 {
44 using namespace vk;
45 
46 enum AllocationKind
47 {
48 	ALLOCATION_KIND_SUBALLOCATED = 0,
49 	ALLOCATION_KIND_DEDICATED,
50 
51 	ALLOCATION_KIND_LAST,
52 };
53 
getPlatformMemoryLimits(Context & context)54 PlatformMemoryLimits getPlatformMemoryLimits (Context& context)
55 {
56 	PlatformMemoryLimits	memoryLimits;
57 
58 	context.getTestContext().getPlatform().getVulkanPlatform().getMemoryLimits(memoryLimits);
59 
60 	return memoryLimits;
61 }
62 
getMaxBufferSize(const VkDeviceSize & bufferSize,const VkDeviceSize & alignment,const PlatformMemoryLimits & limits)63 VkDeviceSize getMaxBufferSize(const VkDeviceSize& bufferSize,
64 							  const VkDeviceSize& alignment,
65 							  const PlatformMemoryLimits& limits)
66 {
67 	VkDeviceSize size = bufferSize;
68 
69 	if (limits.totalDeviceLocalMemory == 0)
70 	{
71 		// 'UMA' systems where device memory counts against system memory
72 		size = std::min(bufferSize, limits.totalSystemMemory - alignment);
73 	}
74 	else
75 	{
76 		// 'LMA' systems where device memory is local to the GPU
77 		size = std::min(bufferSize, limits.totalDeviceLocalMemory - alignment);
78 	}
79 
80 	return size;
81 }
82 
83 struct BufferCaseParameters
84 {
85 	VkBufferUsageFlags	usage;
86 	VkBufferCreateFlags	flags;
87 	VkSharingMode		sharingMode;
88 };
89 
90 class BufferTestInstance : public TestInstance
91 {
92 public:
BufferTestInstance(Context & ctx,BufferCaseParameters testCase)93 										BufferTestInstance				(Context&					ctx,
94 																		 BufferCaseParameters		testCase)
95 										: TestInstance					(ctx)
96 										, m_testCase					(testCase)
97 										, m_sparseContext				(createSparseContext())
98 	{
99 	}
100 	virtual tcu::TestStatus				iterate							(void);
101 	virtual tcu::TestStatus				bufferCreateAndAllocTest		(VkDeviceSize				size);
102 
103 protected:
104 	BufferCaseParameters				m_testCase;
105 
106 	// Wrapper functions around m_context calls to support sparse cases.
getPhysicalDevice(void) const107 	VkPhysicalDevice					getPhysicalDevice				(void) const
108 	{
109 		// Same in sparse and regular case
110 		return m_context.getPhysicalDevice();
111 	}
112 
getDevice(void) const113 	VkDevice							getDevice						(void) const
114 	{
115 		if (m_sparseContext)
116 		{
117 			return *(m_sparseContext->m_device);
118 		}
119 		return m_context.getDevice();
120 	}
121 
getInstanceInterface(void) const122 	const InstanceInterface&			getInstanceInterface			(void) const
123 	{
124 		// Same in sparse and regular case
125 		return m_context.getInstanceInterface();
126 	}
127 
getDeviceInterface(void) const128 	const DeviceInterface&				getDeviceInterface				(void) const
129 	{
130 		if (m_sparseContext)
131 		{
132 			return m_sparseContext->m_deviceInterface;
133 		}
134 		return m_context.getDeviceInterface();
135 	}
136 
getUniversalQueueFamilyIndex(void) const137 	deUint32							getUniversalQueueFamilyIndex	(void) const
138 	{
139 		if (m_sparseContext)
140 		{
141 			return m_sparseContext->m_queueFamilyIndex;
142 		}
143 		return m_context.getUniversalQueueFamilyIndex();
144 	}
145 
146 private:
147 	// Custom context for sparse cases
148 	struct SparseContext
149 	{
SparseContextvkt::api::__anon6299e9640111::BufferTestInstance::SparseContext150 										SparseContext					(Move<VkDevice>&			device,
151 																		 const deUint32				queueFamilyIndex,
152 																		 const PlatformInterface&	platformInterface,
153 																		 VkInstance					instance)
154 										: m_device						(device)
155 										, m_queueFamilyIndex			(queueFamilyIndex)
156 										, m_deviceInterface				(platformInterface, instance, *m_device)
157 		{
158 		}
159 
160 		Unique<VkDevice>				m_device;
161 		const deUint32					m_queueFamilyIndex;
162 		DeviceDriver					m_deviceInterface;
163 	};
164 
165 	de::UniquePtr<SparseContext>		m_sparseContext;
166 
findQueueFamilyIndexWithCaps(const InstanceInterface & vkInstance,VkPhysicalDevice physicalDevice,VkQueueFlags requiredCaps)167 	static deUint32						findQueueFamilyIndexWithCaps	(const InstanceInterface&	vkInstance,
168 																		 VkPhysicalDevice			physicalDevice,
169 																		 VkQueueFlags				requiredCaps)
170 	{
171 		const std::vector<vk::VkQueueFamilyProperties>
172 										queueProps						= getPhysicalDeviceQueueFamilyProperties(vkInstance, physicalDevice);
173 
174 		for (size_t queueNdx = 0; queueNdx < queueProps.size(); queueNdx++)
175 		{
176 			if ((queueProps[queueNdx].queueFlags & requiredCaps) == requiredCaps)
177 			{
178 				return (deUint32)queueNdx;
179 			}
180 		}
181 
182 		TCU_THROW(NotSupportedError, "No matching queue found");
183 	}
184 
185 	// Create the sparseContext
createSparseContext(void) const186 	SparseContext*						createSparseContext				(void) const
187 	{
188 		if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)
189 		||	(m_testCase.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT)
190 		||	(m_testCase.flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT))
191 		{
192 			const InstanceInterface&	vk								= getInstanceInterface();
193 			const VkPhysicalDevice		physicalDevice					= getPhysicalDevice();
194 			const vk::VkPhysicalDeviceFeatures
195 										deviceFeatures					= getPhysicalDeviceFeatures(vk, physicalDevice);
196 			const deUint32				queueIndex						= findQueueFamilyIndexWithCaps(vk, physicalDevice, VK_QUEUE_GRAPHICS_BIT|VK_QUEUE_SPARSE_BINDING_BIT);
197 			const float					queuePriority					= 1.0f;
198 			VkDeviceQueueCreateInfo		queueInfo						=
199 			{
200 				VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
201 				DE_NULL,
202 				static_cast<VkDeviceQueueCreateFlags>(0u),
203 				queueIndex,
204 				1u,
205 				&queuePriority
206 			};
207 			VkDeviceCreateInfo			deviceInfo						=
208 			{
209 				VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
210 				DE_NULL,
211 				static_cast<VkDeviceQueueCreateFlags>(0u),
212 				1u,
213 				&queueInfo,
214 				0u,
215 				DE_NULL,
216 				0u,
217 				DE_NULL,
218 				&deviceFeatures
219 			};
220 
221 			Move<VkDevice>				device							= createDevice(m_context.getPlatformInterface(), m_context.getInstance(), vk, physicalDevice, &deviceInfo);
222 
223 			return new SparseContext(device, queueIndex, m_context.getPlatformInterface(), m_context.getInstance());
224 		}
225 
226 		return DE_NULL;
227 	}
228 };
229 
230 class DedicatedAllocationBufferTestInstance : public BufferTestInstance
231 {
232 public:
DedicatedAllocationBufferTestInstance(Context & ctx,BufferCaseParameters testCase)233 										DedicatedAllocationBufferTestInstance
234 																		(Context&					ctx,
235 																		 BufferCaseParameters		testCase)
236 										: BufferTestInstance			(ctx, testCase)
237 	{
238 	}
239 	virtual tcu::TestStatus				bufferCreateAndAllocTest		(VkDeviceSize				size);
240 };
241 
242 class BuffersTestCase : public TestCase
243 {
244 public:
BuffersTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,BufferCaseParameters testCase)245 										BuffersTestCase					(tcu::TestContext&			testCtx,
246 																		 const std::string&			name,
247 																		 const std::string&			description,
248 																		 BufferCaseParameters		testCase)
249 										: TestCase						(testCtx, name, description)
250 										, m_testCase					(testCase)
251 	{
252 	}
253 
~BuffersTestCase(void)254 	virtual								~BuffersTestCase				(void)
255 	{
256 	}
257 
createInstance(Context & ctx) const258 	virtual TestInstance*				createInstance					(Context&					ctx) const
259 	{
260 		tcu::TestLog&					log								= m_testCtx.getLog();
261 		log << tcu::TestLog::Message << getBufferUsageFlagsStr(m_testCase.usage) << tcu::TestLog::EndMessage;
262 		return new BufferTestInstance(ctx, m_testCase);
263 	}
264 
checkSupport(Context & ctx) const265 	virtual void						checkSupport					(Context&					ctx) const
266 	{
267 		const VkPhysicalDeviceFeatures&		physicalDeviceFeatures = getPhysicalDeviceFeatures(ctx.getInstanceInterface(), ctx.getPhysicalDevice());
268 
269 		if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) && !physicalDeviceFeatures.sparseBinding)
270 			TCU_THROW(NotSupportedError, "Sparse bindings feature is not supported");
271 
272 		if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT) && !physicalDeviceFeatures.sparseResidencyBuffer)
273 			TCU_THROW(NotSupportedError, "Sparse buffer residency feature is not supported");
274 
275 		if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT) && !physicalDeviceFeatures.sparseResidencyAliased)
276 			TCU_THROW(NotSupportedError, "Sparse aliased residency feature is not supported");
277 	}
278 
279 private:
280 	BufferCaseParameters				m_testCase;
281 };
282 
283 class DedicatedAllocationBuffersTestCase : public TestCase
284 {
285 	public:
DedicatedAllocationBuffersTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,BufferCaseParameters testCase)286 										DedicatedAllocationBuffersTestCase
287 																		(tcu::TestContext&			testCtx,
288 																		 const std::string&			name,
289 																		 const std::string&			description,
290 																		 BufferCaseParameters		testCase)
291 										: TestCase						(testCtx, name, description)
292 										, m_testCase					(testCase)
293 	{
294 	}
295 
~DedicatedAllocationBuffersTestCase(void)296 	virtual								~DedicatedAllocationBuffersTestCase
297 																		(void)
298 	{
299 	}
300 
createInstance(Context & ctx) const301 	virtual TestInstance*				createInstance					(Context&					ctx) const
302 	{
303 		tcu::TestLog&					log								= m_testCtx.getLog();
304 		log << tcu::TestLog::Message << getBufferUsageFlagsStr(m_testCase.usage) << tcu::TestLog::EndMessage;
305 		return new DedicatedAllocationBufferTestInstance(ctx, m_testCase);
306 	}
307 
checkSupport(Context & ctx) const308 	virtual void						checkSupport					(Context&					ctx) const
309 	{
310 		const std::vector<std::string>&	extensions		= ctx.getDeviceExtensions();
311 		const deBool					isSupported		= isDeviceExtensionSupported(ctx.getUsedApiVersion(), extensions, "VK_KHR_dedicated_allocation");
312 		if (!isSupported)
313 		{
314 			TCU_THROW(NotSupportedError, "Not supported");
315 		}
316 	}
317 private:
318 	BufferCaseParameters				m_testCase;
319 };
320 
bufferCreateAndAllocTest(VkDeviceSize size)321 tcu::TestStatus BufferTestInstance::bufferCreateAndAllocTest			(VkDeviceSize				size)
322 {
323 	const VkPhysicalDevice				vkPhysicalDevice				= getPhysicalDevice();
324 	const InstanceInterface&			vkInstance						= getInstanceInterface();
325 	const VkDevice						vkDevice						= getDevice();
326 	const DeviceInterface&				vk								= getDeviceInterface();
327 	const deUint32						queueFamilyIndex				= getUniversalQueueFamilyIndex();
328 	const VkPhysicalDeviceMemoryProperties
329 										memoryProperties				= getPhysicalDeviceMemoryProperties(vkInstance, vkPhysicalDevice);
330 	const VkPhysicalDeviceLimits		limits							= getPhysicalDeviceProperties(vkInstance, vkPhysicalDevice).limits;
331 	Move<VkBuffer>						buffer;
332 	Move<VkDeviceMemory>				memory;
333 	VkMemoryRequirements				memReqs;
334 
335 	if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0)
336 	{
337 		size = std::min(size, limits.sparseAddressSpaceSize);
338 	}
339 
340 	// Create the test buffer and a memory allocation for it
341 	{
342 		// Create a minimal buffer first to get the supported memory types
343 		VkBufferCreateInfo				bufferParams					=
344 		{
345 			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,						// VkStructureType			sType;
346 			DE_NULL,													// const void*				pNext;
347 			m_testCase.flags,											// VkBufferCreateFlags		flags;
348 			1u,															// VkDeviceSize				size;
349 			m_testCase.usage,											// VkBufferUsageFlags		usage;
350 			m_testCase.sharingMode,										// VkSharingMode			sharingMode;
351 			1u,															// uint32_t					queueFamilyIndexCount;
352 			&queueFamilyIndex,											// const uint32_t*			pQueueFamilyIndices;
353 		};
354 
355 		buffer = createBuffer(vk, vkDevice, &bufferParams);
356 		vk.getBufferMemoryRequirements(vkDevice, *buffer, &memReqs);
357 
358 		const deUint32					heapTypeIndex					= (deUint32)deCtz32(memReqs.memoryTypeBits);
359 		const VkMemoryType				memoryType						= memoryProperties.memoryTypes[heapTypeIndex];
360 		const VkMemoryHeap				memoryHeap						= memoryProperties.memoryHeaps[memoryType.heapIndex];
361 		const deUint32					shrinkBits						= 4u;	// number of bits to shift when reducing the size with each iteration
362 
363 		// Buffer size - Choose half of the reported heap size for the maximum buffer size, we
364 		// should attempt to test as large a portion as possible.
365 		//
366 		// However on a system where device memory is shared with the system, the maximum size
367 		// should be tested against the platform memory limits as significant portion of the heap
368 		// may already be in use by the operating system and other running processes.
369 		const VkDeviceSize  availableBufferSize	= getMaxBufferSize(memoryHeap.size,
370 																   memReqs.alignment,
371 																   getPlatformMemoryLimits(m_context));
372 
373 		// For our test buffer size, halve the maximum available size and align
374 		const VkDeviceSize maxBufferSize = deAlign64(availableBufferSize >> 1, memReqs.alignment);
375 
376 		size = std::min(size, maxBufferSize);
377 
378 		while (*memory == DE_NULL)
379 		{
380 			// Create the buffer
381 			{
382 				VkResult				result							= VK_ERROR_OUT_OF_HOST_MEMORY;
383 				VkBuffer				rawBuffer						= DE_NULL;
384 
385 				bufferParams.size = size;
386 				buffer = Move<VkBuffer>();		// free the previous buffer, if any
387 				result = vk.createBuffer(vkDevice, &bufferParams, (vk::VkAllocationCallbacks*)DE_NULL, &rawBuffer);
388 
389 				if (result != VK_SUCCESS)
390 				{
391 					size = deAlign64(size >> shrinkBits, memReqs.alignment);
392 
393 					if (size == 0 || bufferParams.size == memReqs.alignment)
394 					{
395 						return tcu::TestStatus::fail("Buffer creation failed! (" + de::toString(getResultName(result)) + ")");
396 					}
397 
398 					continue;	// didn't work, try with a smaller buffer
399 				}
400 
401 				buffer = Move<VkBuffer>(check<VkBuffer>(rawBuffer), Deleter<VkBuffer>(vk, vkDevice, DE_NULL));
402 			}
403 
404 			vk.getBufferMemoryRequirements(vkDevice, *buffer, &memReqs);	// get the proper size requirement
405 
406 			if (size > memReqs.size)
407 			{
408 				std::ostringstream		errorMsg;
409 				errorMsg << "Requied memory size (" << memReqs.size << " bytes) smaller than the buffer's size (" << size << " bytes)!";
410 				return tcu::TestStatus::fail(errorMsg.str());
411 			}
412 
413 			// Allocate the memory
414 			{
415 				VkResult				result							= VK_ERROR_OUT_OF_HOST_MEMORY;
416 				VkDeviceMemory			rawMemory						= DE_NULL;
417 
418 				const VkMemoryAllocateInfo
419 										memAlloc						=
420 				{
421 					VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,				// VkStructureType			sType;
422 					NULL,												// const void*				pNext;
423 					memReqs.size,										// VkDeviceSize				allocationSize;
424 					heapTypeIndex,										// uint32_t					memoryTypeIndex;
425 				};
426 
427 				result = vk.allocateMemory(vkDevice, &memAlloc, (VkAllocationCallbacks*)DE_NULL, &rawMemory);
428 
429 				if (result != VK_SUCCESS)
430 				{
431 					size = deAlign64(size >> shrinkBits, memReqs.alignment);
432 
433 					if (size == 0 || memReqs.size == memReqs.alignment)
434 					{
435 						return tcu::TestStatus::fail("Unable to allocate " + de::toString(memReqs.size) + " bytes of memory");
436 					}
437 
438 					continue;	// didn't work, try with a smaller allocation (and a smaller buffer)
439 				}
440 
441 				memory = Move<VkDeviceMemory>(check<VkDeviceMemory>(rawMemory), Deleter<VkDeviceMemory>(vk, vkDevice, DE_NULL));
442 			}
443 		} // while
444 	}
445 
446 	// Bind the memory
447 	if ((m_testCase.flags & (VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT)) != 0)
448 	{
449 		const VkQueue					queue							= getDeviceQueue(vk, vkDevice, queueFamilyIndex, 0);
450 
451 		const VkSparseMemoryBind		sparseMemoryBind				=
452 		{
453 			0,															// VkDeviceSize				resourceOffset;
454 			memReqs.size,												// VkDeviceSize				size;
455 			*memory,													// VkDeviceMemory			memory;
456 			0,															// VkDeviceSize				memoryOffset;
457 			0															// VkSparseMemoryBindFlags	flags;
458 		};
459 
460 		const VkSparseBufferMemoryBindInfo
461 										sparseBufferMemoryBindInfo		=
462 		{
463 			*buffer,													// VkBuffer					buffer;
464 			1u,															// deUint32					bindCount;
465 			&sparseMemoryBind											// const VkSparseMemoryBind* pBinds;
466 		};
467 
468 		const VkBindSparseInfo			bindSparseInfo					=
469 		{
470 			VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,							// VkStructureType			sType;
471 			DE_NULL,													// const void*				pNext;
472 			0,															// deUint32					waitSemaphoreCount;
473 			DE_NULL,													// const VkSemaphore*		pWaitSemaphores;
474 			1u,															// deUint32					bufferBindCount;
475 			&sparseBufferMemoryBindInfo,								// const VkSparseBufferMemoryBindInfo* pBufferBinds;
476 			0,															// deUint32					imageOpaqueBindCount;
477 			DE_NULL,													// const VkSparseImageOpaqueMemoryBindInfo*	pImageOpaqueBinds;
478 			0,															// deUint32					imageBindCount;
479 			DE_NULL,													// const VkSparseImageMemoryBindInfo* pImageBinds;
480 			0,															// deUint32					signalSemaphoreCount;
481 			DE_NULL,													// const VkSemaphore*		pSignalSemaphores;
482 		};
483 
484 		const vk::Unique<vk::VkFence>	fence							(vk::createFence(vk, vkDevice));
485 
486 		if (vk.queueBindSparse(queue, 1, &bindSparseInfo, *fence) != VK_SUCCESS)
487 			return tcu::TestStatus::fail("Bind sparse buffer memory failed! (requested memory size: " + de::toString(size) + ")");
488 
489 		VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), VK_TRUE, ~(0ull) /* infinity */));
490 	}
491 	else if (vk.bindBufferMemory(vkDevice, *buffer, *memory, 0) != VK_SUCCESS)
492 		return tcu::TestStatus::fail("Bind buffer memory failed! (requested memory size: " + de::toString(size) + ")");
493 
494 	return tcu::TestStatus::pass("Pass");
495 }
496 
iterate(void)497 tcu::TestStatus							BufferTestInstance::iterate		(void)
498 {
499 	const VkDeviceSize					testSizes[]						=
500 	{
501 		1,
502 		1181,
503 		15991,
504 		16384,
505 		~0ull,		// try to exercise a very large buffer too (will be clamped to a sensible size later)
506 	};
507 
508 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(testSizes); ++i)
509 	{
510 		const tcu::TestStatus			testStatus						= bufferCreateAndAllocTest(testSizes[i]);
511 
512 		if (testStatus.getCode() != QP_TEST_RESULT_PASS)
513 			return testStatus;
514 	}
515 
516 	return tcu::TestStatus::pass("Pass");
517 }
518 
bufferCreateAndAllocTest(VkDeviceSize size)519 tcu::TestStatus							DedicatedAllocationBufferTestInstance::bufferCreateAndAllocTest
520 																		(VkDeviceSize				size)
521 {
522 	const VkPhysicalDevice				vkPhysicalDevice				= getPhysicalDevice();
523 	const InstanceInterface&			vkInstance						= getInstanceInterface();
524 	const VkDevice						vkDevice						= getDevice();
525 	const DeviceInterface&				vk								= getDeviceInterface();
526 	const deUint32						queueFamilyIndex				= getUniversalQueueFamilyIndex();
527 	const VkPhysicalDeviceMemoryProperties
528 										memoryProperties				= getPhysicalDeviceMemoryProperties(vkInstance, vkPhysicalDevice);
529 	const VkPhysicalDeviceLimits		limits							= getPhysicalDeviceProperties(vkInstance, vkPhysicalDevice).limits;
530 
531 	VkMemoryDedicatedRequirements	dedicatedRequirements			=
532 	{
533 		VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS,				// VkStructureType			sType;
534 		DE_NULL,														// const void*				pNext;
535 		false,															// VkBool32					prefersDedicatedAllocation
536 		false															// VkBool32					requiresDedicatedAllocation
537 	};
538 	VkMemoryRequirements2			memReqs							=
539 	{
540 		VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,						// VkStructureType			sType
541 		&dedicatedRequirements,											// void*					pNext
542 		{0, 0, 0}														// VkMemoryRequirements		memoryRequirements
543 	};
544 
545 	if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0)
546 		size = std::min(size, limits.sparseAddressSpaceSize);
547 
548 	// Create a minimal buffer first to get the supported memory types
549 	VkBufferCreateInfo					bufferParams					=
550 	{
551 		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,							// VkStructureType			sType
552 		DE_NULL,														// const void*				pNext
553 		m_testCase.flags,												// VkBufferCreateFlags		flags
554 		1u,																// VkDeviceSize				size
555 		m_testCase.usage,												// VkBufferUsageFlags		usage
556 		m_testCase.sharingMode,											// VkSharingMode			sharingMode
557 		1u,																// uint32_t					queueFamilyIndexCount
558 		&queueFamilyIndex,												// const uint32_t*			pQueueFamilyIndices
559 	};
560 
561 	Move<VkBuffer>						buffer							= createBuffer(vk, vkDevice, &bufferParams);
562 
563 	VkBufferMemoryRequirementsInfo2	info							=
564 	{
565 		VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2,			// VkStructureType			sType
566 		DE_NULL,														// const void*				pNext
567 		*buffer															// VkBuffer					buffer
568 	};
569 
570 	vk.getBufferMemoryRequirements2(vkDevice, &info, &memReqs);
571 
572 	if (dedicatedRequirements.requiresDedicatedAllocation == VK_TRUE)
573 	{
574 		std::ostringstream				errorMsg;
575 		errorMsg << "Nonexternal objects cannot require dedicated allocation.";
576 		return tcu::TestStatus::fail(errorMsg.str());
577 	}
578 
579 	const deUint32						heapTypeIndex					= static_cast<deUint32>(deCtz32(memReqs.memoryRequirements.memoryTypeBits));
580 	const VkMemoryType					memoryType						= memoryProperties.memoryTypes[heapTypeIndex];
581 	const VkMemoryHeap					memoryHeap						= memoryProperties.memoryHeaps[memoryType.heapIndex];
582 	const deUint32						shrinkBits						= 4u;	// number of bits to shift when reducing the size with each iteration
583 
584 	// Buffer size - Choose half of the reported heap size for the maximum buffer size, we
585 	// should attempt to test as large a portion as possible.
586 	//
587 	// However on a system where device memory is shared with the system, the maximum size
588 	// should be tested against the platform memory limits as a significant portion of the heap
589 	// may already be in use by the operating system and other running processes.
590 	const VkDeviceSize maxBufferSize = getMaxBufferSize(memoryHeap.size,
591 													   memReqs.memoryRequirements.alignment,
592 													   getPlatformMemoryLimits(m_context));
593 
594 	Move<VkDeviceMemory>				memory;
595 	size = deAlign64(std::min(size, maxBufferSize >> 1), memReqs.memoryRequirements.alignment);
596 	while (*memory == DE_NULL)
597 	{
598 		// Create the buffer
599 		{
600 			VkResult					result							= VK_ERROR_OUT_OF_HOST_MEMORY;
601 			VkBuffer					rawBuffer						= DE_NULL;
602 
603 			bufferParams.size = size;
604 			buffer = Move<VkBuffer>(); // free the previous buffer, if any
605 			result = vk.createBuffer(vkDevice, &bufferParams, (VkAllocationCallbacks*)DE_NULL, &rawBuffer);
606 
607 			if (result != VK_SUCCESS)
608 			{
609 				size = deAlign64(size >> shrinkBits, memReqs.memoryRequirements.alignment);
610 
611 				if (size == 0 || bufferParams.size == memReqs.memoryRequirements.alignment)
612 					return tcu::TestStatus::fail("Buffer creation failed! (" + de::toString(getResultName(result)) + ")");
613 
614 				continue; // didn't work, try with a smaller buffer
615 			}
616 
617 			buffer = Move<VkBuffer>(check<VkBuffer>(rawBuffer), Deleter<VkBuffer>(vk, vkDevice, DE_NULL));
618 		}
619 
620 		info.buffer = *buffer;
621 		vk.getBufferMemoryRequirements2(vkDevice, &info, &memReqs);		// get the proper size requirement
622 
623 		if (size > memReqs.memoryRequirements.size)
624 		{
625 			std::ostringstream			errorMsg;
626 			errorMsg << "Requied memory size (" << memReqs.memoryRequirements.size << " bytes) smaller than the buffer's size (" << size << " bytes)!";
627 			return tcu::TestStatus::fail(errorMsg.str());
628 		}
629 
630 		// Allocate the memory
631 		{
632 			VkResult					result							= VK_ERROR_OUT_OF_HOST_MEMORY;
633 			VkDeviceMemory				rawMemory						= DE_NULL;
634 
635 			vk::VkMemoryDedicatedAllocateInfo
636 										dedicatedInfo					=
637 			{
638 				VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,		// VkStructureType			sType
639 				DE_NULL,												// const void*				pNext
640 				DE_NULL,												// VkImage					image
641 				*buffer													// VkBuffer					buffer
642 			};
643 
644 			VkMemoryAllocateInfo		memoryAllocateInfo				=
645 			{
646 				VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,					// VkStructureType			sType
647 				&dedicatedInfo,											// const void*				pNext
648 				memReqs.memoryRequirements.size,						// VkDeviceSize				allocationSize
649 				heapTypeIndex,											// deUint32					memoryTypeIndex
650 			};
651 
652 			result = vk.allocateMemory(vkDevice, &memoryAllocateInfo, (VkAllocationCallbacks*)DE_NULL, &rawMemory);
653 
654 			if (result != VK_SUCCESS)
655 			{
656 				size = deAlign64(size >> shrinkBits, memReqs.memoryRequirements.alignment);
657 
658 				if (size == 0 || memReqs.memoryRequirements.size == memReqs.memoryRequirements.alignment)
659 					return tcu::TestStatus::fail("Unable to allocate " + de::toString(memReqs.memoryRequirements.size) + " bytes of memory");
660 
661 				continue; // didn't work, try with a smaller allocation (and a smaller buffer)
662 			}
663 
664 			memory = Move<VkDeviceMemory>(check<VkDeviceMemory>(rawMemory), Deleter<VkDeviceMemory>(vk, vkDevice, DE_NULL));
665 		}
666 	} // while
667 
668 	if (vk.bindBufferMemory(vkDevice, *buffer, *memory, 0) != VK_SUCCESS)
669 		return tcu::TestStatus::fail("Bind buffer memory failed! (requested memory size: " + de::toString(size) + ")");
670 
671 	return tcu::TestStatus::pass("Pass");
672 }
673 
getBufferUsageFlagsName(const VkBufferUsageFlags flags)674 std::string getBufferUsageFlagsName (const VkBufferUsageFlags flags)
675 {
676 	switch (flags)
677 	{
678 		case VK_BUFFER_USAGE_TRANSFER_SRC_BIT:			return "transfer_src";
679 		case VK_BUFFER_USAGE_TRANSFER_DST_BIT:			return "transfer_dst";
680 		case VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT:	return "uniform_texel";
681 		case VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT:	return "storage_texel";
682 		case VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT:		return "uniform";
683 		case VK_BUFFER_USAGE_STORAGE_BUFFER_BIT:		return "storage";
684 		case VK_BUFFER_USAGE_INDEX_BUFFER_BIT:			return "index";
685 		case VK_BUFFER_USAGE_VERTEX_BUFFER_BIT:			return "vertex";
686 		case VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT:		return "indirect";
687 		default:
688 			DE_FATAL("Unknown buffer usage flag");
689 			return "";
690 	}
691 }
692 
getBufferCreateFlagsName(const VkBufferCreateFlags flags)693 std::string getBufferCreateFlagsName (const VkBufferCreateFlags flags)
694 {
695 	std::ostringstream name;
696 
697 	if (flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)
698 		name << "_binding";
699 	if (flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT)
700 		name << "_residency";
701 	if (flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT)
702 		name << "_aliased";
703 	if (flags == 0u)
704 		name << "_zero";
705 
706 	DE_ASSERT(!name.str().empty());
707 
708 	return name.str().substr(1);
709 }
710 
711 // Create all VkBufferUsageFlags combinations recursively
createBufferUsageCases(tcu::TestCaseGroup & testGroup,const deUint32 firstNdx,const deUint32 bufferUsageFlags,const AllocationKind allocationKind)712 void createBufferUsageCases (tcu::TestCaseGroup& testGroup, const deUint32 firstNdx, const deUint32 bufferUsageFlags, const AllocationKind allocationKind)
713 {
714 	const VkBufferUsageFlags			bufferUsageModes[]	=
715 	{
716 		VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
717 		VK_BUFFER_USAGE_TRANSFER_DST_BIT,
718 		VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
719 		VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
720 		VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
721 		VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
722 		VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
723 		VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
724 		VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
725 	};
726 
727 	tcu::TestContext&					testCtx				= testGroup.getTestContext();
728 
729 	// Add test groups
730 	for (deUint32 currentNdx = firstNdx; currentNdx < DE_LENGTH_OF_ARRAY(bufferUsageModes); currentNdx++)
731 	{
732 		const deUint32					newBufferUsageFlags	= bufferUsageFlags | bufferUsageModes[currentNdx];
733 		const std::string				newGroupName		= getBufferUsageFlagsName(bufferUsageModes[currentNdx]);
734 		de::MovePtr<tcu::TestCaseGroup>	newTestGroup		(new tcu::TestCaseGroup(testCtx, newGroupName.c_str(), ""));
735 
736 		createBufferUsageCases(*newTestGroup, currentNdx + 1u, newBufferUsageFlags, allocationKind);
737 		testGroup.addChild(newTestGroup.release());
738 	}
739 
740 	// Add test cases
741 	if (bufferUsageFlags != 0u)
742 	{
743 		// \note SPARSE_RESIDENCY and SPARSE_ALIASED have to be used together with the SPARSE_BINDING flag.
744 		const VkBufferCreateFlags		bufferCreateFlags[]		=
745 		{
746 			0,
747 			VK_BUFFER_CREATE_SPARSE_BINDING_BIT,
748 			VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT,
749 			VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
750 			VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
751 		};
752 
753 		// Dedicated allocation does not support sparse feature
754 		const int						numBufferCreateFlags	= (allocationKind == ALLOCATION_KIND_SUBALLOCATED) ? DE_LENGTH_OF_ARRAY(bufferCreateFlags) : 1;
755 
756 		de::MovePtr<tcu::TestCaseGroup>	newTestGroup			(new tcu::TestCaseGroup(testCtx, "create", ""));
757 
758 		for (int bufferCreateFlagsNdx = 0; bufferCreateFlagsNdx < numBufferCreateFlags; bufferCreateFlagsNdx++)
759 		{
760 			const BufferCaseParameters	testParams	=
761 			{
762 				bufferUsageFlags,
763 				bufferCreateFlags[bufferCreateFlagsNdx],
764 				VK_SHARING_MODE_EXCLUSIVE
765 			};
766 
767 			const std::string			allocStr	= (allocationKind == ALLOCATION_KIND_SUBALLOCATED) ? "suballocation of " : "dedicated alloc. of ";
768 			const std::string			caseName	= getBufferCreateFlagsName(bufferCreateFlags[bufferCreateFlagsNdx]);
769 			const std::string			caseDesc	= "vkCreateBuffer test: " + allocStr + de::toString(bufferUsageFlags) + " " + de::toString(testParams.flags);
770 
771 			switch (allocationKind)
772 			{
773 				case ALLOCATION_KIND_SUBALLOCATED:
774 					newTestGroup->addChild(new BuffersTestCase(testCtx, caseName.c_str(), caseDesc.c_str(), testParams));
775 					break;
776 				case ALLOCATION_KIND_DEDICATED:
777 					newTestGroup->addChild(new DedicatedAllocationBuffersTestCase(testCtx, caseName.c_str(), caseDesc.c_str(), testParams));
778 					break;
779 				default:
780 					DE_FATAL("Unknown test type");
781 			}
782 		}
783 		testGroup.addChild(newTestGroup.release());
784 	}
785 }
786 
787 } // anonymous
788 
createBufferTests(tcu::TestContext & testCtx)789  tcu::TestCaseGroup* createBufferTests (tcu::TestContext& testCtx)
790 {
791 	de::MovePtr<tcu::TestCaseGroup> buffersTests (new tcu::TestCaseGroup(testCtx, "buffer", "Buffer Tests"));
792 
793 	{
794 		de::MovePtr<tcu::TestCaseGroup>	regularAllocation	(new tcu::TestCaseGroup(testCtx, "suballocation", "Regular suballocation of memory."));
795 		createBufferUsageCases(*regularAllocation, 0u, 0u, ALLOCATION_KIND_SUBALLOCATED);
796 		buffersTests->addChild(regularAllocation.release());
797 	}
798 
799 	{
800 		de::MovePtr<tcu::TestCaseGroup>	dedicatedAllocation	(new tcu::TestCaseGroup(testCtx, "dedicated_alloc", "Dedicated allocation of memory."));
801 		createBufferUsageCases(*dedicatedAllocation, 0u, 0u, ALLOCATION_KIND_DEDICATED);
802 		buffersTests->addChild(dedicatedAllocation.release());
803 	}
804 
805 	return buffersTests.release();
806 }
807 
808 } // api
809 } // vk
810