1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 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  vktSparseResourcesBufferSparseBinding.cpp
21 * \brief Buffer Sparse Binding tests
22 *//*--------------------------------------------------------------------*/
23
24#include "vktSparseResourcesBufferSparseBinding.hpp"
25#include "vktSparseResourcesTestsUtil.hpp"
26#include "vktSparseResourcesBase.hpp"
27#include "vktTestCaseUtil.hpp"
28
29#include "vkDefs.hpp"
30#include "vkRef.hpp"
31#include "vkRefUtil.hpp"
32#include "vkPlatform.hpp"
33#include "vkPrograms.hpp"
34#include "vkMemUtil.hpp"
35#include "vkBarrierUtil.hpp"
36#include "vkBuilderUtil.hpp"
37#include "vkImageUtil.hpp"
38#include "vkQueryUtil.hpp"
39#include "vkTypeUtil.hpp"
40#include "vkCmdUtil.hpp"
41
42#include "deUniquePtr.hpp"
43#include "deStringUtil.hpp"
44
45#include <string>
46#include <vector>
47
48using namespace vk;
49
50namespace vkt
51{
52namespace sparse
53{
54namespace
55{
56
57class BufferSparseBindingCase : public TestCase
58{
59public:
60					BufferSparseBindingCase	(tcu::TestContext&	testCtx,
61											 const std::string&	name,
62											 const deUint32		bufferSize,
63											 const bool			useDeviceGroups);
64
65	TestInstance*	createInstance			(Context&			context) const;
66	virtual void	checkSupport			(Context&			context) const;
67
68private:
69	const deUint32	m_bufferSize;
70	const bool		m_useDeviceGroups;
71};
72
73BufferSparseBindingCase::BufferSparseBindingCase (tcu::TestContext&		testCtx,
74												  const std::string&	name,
75												  const deUint32		bufferSize,
76												  const bool			useDeviceGroups)
77	: TestCase			(testCtx, name)
78	, m_bufferSize		(bufferSize)
79	, m_useDeviceGroups	(useDeviceGroups)
80{
81}
82
83void BufferSparseBindingCase::checkSupport (Context& context) const
84{
85	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING);
86}
87
88class BufferSparseBindingInstance : public SparseResourcesBaseInstance
89{
90public:
91					BufferSparseBindingInstance (Context&		context,
92												 const deUint32	bufferSize,
93												 const bool		useDeviceGroups);
94
95	tcu::TestStatus	iterate						(void);
96
97private:
98	const deUint32	m_bufferSize;
99	const deUint32	m_useDeviceGroups;
100};
101
102BufferSparseBindingInstance::BufferSparseBindingInstance (Context&			context,
103														  const deUint32	bufferSize,
104														  const bool		useDeviceGroups)
105
106	: SparseResourcesBaseInstance	(context, useDeviceGroups)
107	, m_bufferSize					(bufferSize)
108	, m_useDeviceGroups				(useDeviceGroups)
109{
110}
111
112tcu::TestStatus BufferSparseBindingInstance::iterate (void)
113{
114	const InstanceInterface&	instance		= m_context.getInstanceInterface();
115	{
116		// Create logical device supporting both sparse and compute operations
117		QueueRequirementsVec queueRequirements;
118		queueRequirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
119		queueRequirements.push_back(QueueRequirements(VK_QUEUE_COMPUTE_BIT, 1u));
120
121		createDeviceSupportingQueues(queueRequirements);
122	}
123	const vk::VkPhysicalDevice&	physicalDevice	= getPhysicalDevice();
124
125	const DeviceInterface&	deviceInterface	= getDeviceInterface();
126	const Queue&			sparseQueue		= getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0);
127	const Queue&			computeQueue	= getQueue(VK_QUEUE_COMPUTE_BIT, 0);
128
129	// Go through all physical devices
130	for (deUint32 physDevID = 0; physDevID < m_numPhysicalDevices; physDevID++)
131	{
132		const deUint32	firstDeviceID	= physDevID;
133		const deUint32	secondDeviceID	= (firstDeviceID + 1) % m_numPhysicalDevices;
134
135		VkBufferCreateInfo bufferCreateInfo;
136
137		bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;	// VkStructureType		sType;
138		bufferCreateInfo.pNext = DE_NULL;								// const void*			pNext;
139		bufferCreateInfo.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT;	// VkBufferCreateFlags	flags;
140		bufferCreateInfo.size = m_bufferSize;							// VkDeviceSize			size;
141		bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
142			VK_BUFFER_USAGE_TRANSFER_DST_BIT;							// VkBufferUsageFlags	usage;
143		bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;		// VkSharingMode		sharingMode;
144		bufferCreateInfo.queueFamilyIndexCount = 0u;					// deUint32				queueFamilyIndexCount;
145		bufferCreateInfo.pQueueFamilyIndices = DE_NULL;					// const deUint32*		pQueueFamilyIndices;
146
147		const deUint32 queueFamilyIndices[] = { sparseQueue.queueFamilyIndex, computeQueue.queueFamilyIndex };
148
149		if (sparseQueue.queueFamilyIndex != computeQueue.queueFamilyIndex)
150		{
151			bufferCreateInfo.sharingMode = VK_SHARING_MODE_CONCURRENT;	// VkSharingMode		sharingMode;
152			bufferCreateInfo.queueFamilyIndexCount = 2u;				// deUint32				queueFamilyIndexCount;
153			bufferCreateInfo.pQueueFamilyIndices = queueFamilyIndices;	// const deUint32*		pQueueFamilyIndices;
154		}
155
156		// Create sparse buffer
157		const Unique<VkBuffer> sparseBuffer(createBuffer(deviceInterface, getDevice(), &bufferCreateInfo));
158
159		// Create sparse buffer memory bind semaphore
160		const Unique<VkSemaphore> bufferMemoryBindSemaphore(createSemaphore(deviceInterface, getDevice()));
161
162		const VkMemoryRequirements bufferMemRequirement = getBufferMemoryRequirements(deviceInterface, getDevice(), *sparseBuffer);
163
164		if (bufferMemRequirement.size > getPhysicalDeviceProperties(instance, physicalDevice).limits.sparseAddressSpaceSize)
165			TCU_THROW(NotSupportedError, "Required memory size for sparse resources exceeds device limits");
166
167		DE_ASSERT((bufferMemRequirement.size % bufferMemRequirement.alignment) == 0);
168
169		Move<VkDeviceMemory> sparseMemoryAllocation;
170
171		{
172			std::vector<VkSparseMemoryBind>	sparseMemoryBinds;
173			const deUint32					numSparseBinds = static_cast<deUint32>(bufferMemRequirement.size / bufferMemRequirement.alignment);
174			const deUint32					memoryType	   = findMatchingMemoryType(instance, getPhysicalDevice(secondDeviceID), bufferMemRequirement, MemoryRequirement::Any);
175
176			if (memoryType == NO_MATCH_FOUND)
177				return tcu::TestStatus::fail("No matching memory type found");
178
179			if (firstDeviceID != secondDeviceID)
180			{
181				VkPeerMemoryFeatureFlags	peerMemoryFeatureFlags = (VkPeerMemoryFeatureFlags)0;
182				const deUint32				heapIndex = getHeapIndexForMemoryType(instance, getPhysicalDevice(secondDeviceID), memoryType);
183				deviceInterface.getDeviceGroupPeerMemoryFeatures(getDevice(), heapIndex, firstDeviceID, secondDeviceID, &peerMemoryFeatureFlags);
184
185				if (((peerMemoryFeatureFlags & VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT) == 0) ||
186					((peerMemoryFeatureFlags & VK_PEER_MEMORY_FEATURE_COPY_DST_BIT) == 0))
187				{
188					TCU_THROW(NotSupportedError, "Peer memory does not support COPY_SRC and COPY_DST");
189				}
190			}
191
192			{
193				const VkMemoryAllocateInfo allocateInfo =
194				{
195					VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,			// VkStructureType    sType;
196					DE_NULL,										// const void*        pNext;
197					bufferMemRequirement.size,						// VkDeviceSize       allocationSize;
198					memoryType,										// uint32_t           memoryTypeIndex;
199				};
200
201				sparseMemoryAllocation = allocateMemory(deviceInterface, getDevice(), &allocateInfo);
202			}
203
204			for (deUint32 sparseBindNdx = 0; sparseBindNdx < numSparseBinds; ++sparseBindNdx)
205			{
206				const VkSparseMemoryBind sparseMemoryBind =
207				{
208					bufferMemRequirement.alignment * sparseBindNdx,			// VkDeviceSize               resourceOffset;
209					bufferMemRequirement.alignment,							// VkDeviceSize               size;
210					*sparseMemoryAllocation,								// VkDeviceMemory             memory;
211					bufferMemRequirement.alignment * sparseBindNdx,			// VkDeviceSize               memoryOffset;
212					(VkSparseMemoryBindFlags)0,								// VkSparseMemoryBindFlags    flags;
213				};
214				sparseMemoryBinds.push_back(sparseMemoryBind);
215			}
216
217			const VkSparseBufferMemoryBindInfo sparseBufferBindInfo = makeSparseBufferMemoryBindInfo(*sparseBuffer, numSparseBinds, &sparseMemoryBinds[0]);
218
219			const VkDeviceGroupBindSparseInfo devGroupBindSparseInfo =
220			{
221				VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO,		//VkStructureType							sType;
222				DE_NULL,												//const void*								pNext;
223				firstDeviceID,											//deUint32									resourceDeviceIndex;
224				secondDeviceID,											//deUint32									memoryDeviceIndex;
225			};
226
227			const VkBindSparseInfo bindSparseInfo =
228			{
229				VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,						//VkStructureType							sType;
230				m_useDeviceGroups ? &devGroupBindSparseInfo : DE_NULL,	//const void*								pNext;
231				0u,														//deUint32									waitSemaphoreCount;
232				DE_NULL,												//const VkSemaphore*						pWaitSemaphores;
233				1u,														//deUint32									bufferBindCount;
234				&sparseBufferBindInfo,									//const VkSparseBufferMemoryBindInfo*		pBufferBinds;
235				0u,														//deUint32									imageOpaqueBindCount;
236				DE_NULL,												//const VkSparseImageOpaqueMemoryBindInfo*	pImageOpaqueBinds;
237				0u,														//deUint32									imageBindCount;
238				DE_NULL,												//const VkSparseImageMemoryBindInfo*		pImageBinds;
239				1u,														//deUint32									signalSemaphoreCount;
240				&bufferMemoryBindSemaphore.get()						//const VkSemaphore*						pSignalSemaphores;
241			};
242
243			// Submit sparse bind commands for execution
244			VK_CHECK(deviceInterface.queueBindSparse(sparseQueue.queueHandle, 1u, &bindSparseInfo, DE_NULL));
245		}
246
247		// Create command buffer for transfer operations
248		const Unique<VkCommandPool>		commandPool(makeCommandPool(deviceInterface, getDevice(), computeQueue.queueFamilyIndex));
249		const Unique<VkCommandBuffer>	commandBuffer(allocateCommandBuffer(deviceInterface, getDevice(), *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
250
251		// Start recording transfer commands
252		beginCommandBuffer(deviceInterface, *commandBuffer);
253
254		const VkBufferCreateInfo		inputBufferCreateInfo = makeBufferCreateInfo(m_bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
255		const Unique<VkBuffer>			inputBuffer(createBuffer(deviceInterface, getDevice(), &inputBufferCreateInfo));
256		const de::UniquePtr<Allocation>	inputBufferAlloc(bindBuffer(deviceInterface, getDevice(), getAllocator(), *inputBuffer, MemoryRequirement::HostVisible));
257
258		std::vector<deUint8> referenceData;
259		referenceData.resize(m_bufferSize);
260
261		for (deUint32 valueNdx = 0; valueNdx < m_bufferSize; ++valueNdx)
262		{
263			referenceData[valueNdx] = static_cast<deUint8>((valueNdx % bufferMemRequirement.alignment) + 1u);
264		}
265
266		deMemcpy(inputBufferAlloc->getHostPtr(), &referenceData[0], m_bufferSize);
267
268		flushAlloc(deviceInterface, getDevice(), *inputBufferAlloc);
269
270		{
271			const VkBufferMemoryBarrier inputBufferBarrier
272				= makeBufferMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT,
273					VK_ACCESS_TRANSFER_READ_BIT,
274					*inputBuffer,
275					0u,
276					m_bufferSize);
277
278			deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 1u, &inputBufferBarrier, 0u, DE_NULL);
279		}
280
281		{
282			const VkBufferCopy bufferCopy = makeBufferCopy(0u, 0u, m_bufferSize);
283
284			deviceInterface.cmdCopyBuffer(*commandBuffer, *inputBuffer, *sparseBuffer, 1u, &bufferCopy);
285		}
286
287		{
288			const VkBufferMemoryBarrier sparseBufferBarrier
289				= makeBufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT,
290					VK_ACCESS_TRANSFER_READ_BIT,
291					*sparseBuffer,
292					0u,
293					m_bufferSize);
294
295			deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 1u, &sparseBufferBarrier, 0u, DE_NULL);
296		}
297
298		const VkBufferCreateInfo		outputBufferCreateInfo = makeBufferCreateInfo(m_bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
299		const Unique<VkBuffer>			outputBuffer(createBuffer(deviceInterface, getDevice(), &outputBufferCreateInfo));
300		const de::UniquePtr<Allocation>	outputBufferAlloc(bindBuffer(deviceInterface, getDevice(), getAllocator(), *outputBuffer, MemoryRequirement::HostVisible));
301
302		{
303			const VkBufferCopy bufferCopy = makeBufferCopy(0u, 0u, m_bufferSize);
304
305			deviceInterface.cmdCopyBuffer(*commandBuffer, *sparseBuffer, *outputBuffer, 1u, &bufferCopy);
306		}
307
308		{
309			const VkBufferMemoryBarrier outputBufferBarrier
310				= makeBufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT,
311					VK_ACCESS_HOST_READ_BIT,
312					*outputBuffer,
313					0u,
314					m_bufferSize);
315
316			deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u, &outputBufferBarrier, 0u, DE_NULL);
317		}
318
319		// End recording transfer commands
320		endCommandBuffer(deviceInterface, *commandBuffer);
321
322		const VkPipelineStageFlags waitStageBits[] = { VK_PIPELINE_STAGE_TRANSFER_BIT };
323
324		// Submit transfer commands for execution and wait for completion
325		// In case of device groups, submit on the physical device with the resource
326		submitCommandsAndWait(deviceInterface, getDevice(), computeQueue.queueHandle, *commandBuffer, 1u, &bufferMemoryBindSemaphore.get(),
327			waitStageBits, 0, DE_NULL, m_useDeviceGroups, firstDeviceID);
328
329		// Retrieve data from output buffer to host memory
330		invalidateAlloc(deviceInterface, getDevice(), *outputBufferAlloc);
331
332		const deUint8* outputData = static_cast<const deUint8*>(outputBufferAlloc->getHostPtr());
333
334		// Wait for sparse queue to become idle
335		deviceInterface.queueWaitIdle(sparseQueue.queueHandle);
336
337		// Compare output data with reference data
338		if (deMemCmp(&referenceData[0], outputData, m_bufferSize) != 0)
339			return tcu::TestStatus::fail("Failed");
340	}
341	return tcu::TestStatus::pass("Passed");
342}
343
344TestInstance* BufferSparseBindingCase::createInstance (Context& context) const
345{
346	return new BufferSparseBindingInstance(context, m_bufferSize, m_useDeviceGroups);
347}
348
349} // anonymous ns
350
351void addBufferSparseBindingTests (tcu::TestCaseGroup* group, const bool useDeviceGroups)
352{
353	group->addChild(new BufferSparseBindingCase(group->getTestContext(), "buffer_size_2_10", 1 << 10, useDeviceGroups));
354	group->addChild(new BufferSparseBindingCase(group->getTestContext(), "buffer_size_2_12", 1 << 12, useDeviceGroups));
355	group->addChild(new BufferSparseBindingCase(group->getTestContext(), "buffer_size_2_16", 1 << 16, useDeviceGroups));
356	group->addChild(new BufferSparseBindingCase(group->getTestContext(), "buffer_size_2_17", 1 << 17, useDeviceGroups));
357	group->addChild(new BufferSparseBindingCase(group->getTestContext(), "buffer_size_2_20", 1 << 20, useDeviceGroups));
358	group->addChild(new BufferSparseBindingCase(group->getTestContext(), "buffer_size_2_24", 1 << 24, useDeviceGroups));
359}
360
361} // sparse
362} // vkt
363