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 "vkBuilderUtil.hpp"
36 #include "vkImageUtil.hpp"
37 #include "vkQueryUtil.hpp"
38 #include "vkTypeUtil.hpp"
39 
40 #include "deUniquePtr.hpp"
41 #include "deStringUtil.hpp"
42 
43 #include <string>
44 #include <vector>
45 
46 using namespace vk;
47 
48 namespace vkt
49 {
50 namespace sparse
51 {
52 namespace
53 {
54 
55 class BufferSparseBindingCase : public TestCase
56 {
57 public:
58 					BufferSparseBindingCase	(tcu::TestContext&	testCtx,
59 											 const std::string&	name,
60 											 const std::string&	description,
61 											 const deUint32		bufferSize);
62 
63 	TestInstance*	createInstance			(Context&			context) const;
64 
65 private:
66 	const deUint32	m_bufferSize;
67 };
68 
BufferSparseBindingCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const deUint32 bufferSize)69 BufferSparseBindingCase::BufferSparseBindingCase (tcu::TestContext&		testCtx,
70 												  const std::string&	name,
71 												  const std::string&	description,
72 												  const deUint32		bufferSize)
73 	: TestCase			(testCtx, name, description)
74 	, m_bufferSize		(bufferSize)
75 {
76 }
77 
78 class BufferSparseBindingInstance : public SparseResourcesBaseInstance
79 {
80 public:
81 					BufferSparseBindingInstance (Context&		context,
82 												 const deUint32	bufferSize);
83 
84 	tcu::TestStatus	iterate						(void);
85 
86 private:
87 	const deUint32	m_bufferSize;
88 };
89 
BufferSparseBindingInstance(Context & context,const deUint32 bufferSize)90 BufferSparseBindingInstance::BufferSparseBindingInstance (Context&			context,
91 														  const deUint32	bufferSize)
92 
93 	: SparseResourcesBaseInstance	(context)
94 	, m_bufferSize					(bufferSize)
95 {
96 }
97 
iterate(void)98 tcu::TestStatus BufferSparseBindingInstance::iterate (void)
99 {
100 	const InstanceInterface&	instance		= m_context.getInstanceInterface();
101 	const DeviceInterface&		deviceInterface	= m_context.getDeviceInterface();
102 	const VkPhysicalDevice		physicalDevice	= m_context.getPhysicalDevice();
103 
104 	VkPhysicalDeviceFeatures deviceFeatures;
105 	instance.getPhysicalDeviceFeatures(physicalDevice, &deviceFeatures);
106 
107 	if (deviceFeatures.sparseBinding == false)
108 	{
109 		return tcu::TestStatus(QP_TEST_RESULT_NOT_SUPPORTED, "Sparse binding not supported");
110 	}
111 
112 	VkPhysicalDeviceProperties deviceProperties;
113 	instance.getPhysicalDeviceProperties(physicalDevice, &deviceProperties);
114 
115 	QueueRequirementsVec queueRequirements;
116 	queueRequirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
117 	queueRequirements.push_back(QueueRequirements(VK_QUEUE_COMPUTE_BIT, 1u));
118 
119 	// Create logical device supporting both sparse and transfer queues
120 	if (!createDeviceSupportingQueues(queueRequirements))
121 	{
122 		return tcu::TestStatus(QP_TEST_RESULT_FAIL, "Could not create device supporting sparse and compute queue");
123 	}
124 
125 	const VkPhysicalDeviceMemoryProperties deviceMemoryProperties = getPhysicalDeviceMemoryProperties(instance, physicalDevice);
126 
127 	// Create memory allocator for logical device
128 	const de::UniquePtr<Allocator> allocator(new SimpleAllocator(deviceInterface, *m_logicalDevice, deviceMemoryProperties));
129 
130 	// Create queue supporting sparse binding operations
131 	const Queue& sparseQueue = getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0);
132 
133 	// Create queue supporting compute and transfer operations
134 	const Queue& computeQueue = getQueue(VK_QUEUE_COMPUTE_BIT, 0);
135 
136 	VkBufferCreateInfo bufferCreateInfo;
137 
138 	bufferCreateInfo.sType					= VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;	// VkStructureType		sType;
139 	bufferCreateInfo.pNext					= DE_NULL;								// const void*			pNext;
140 	bufferCreateInfo.flags					= VK_BUFFER_CREATE_SPARSE_BINDING_BIT;	// VkBufferCreateFlags	flags;
141 	bufferCreateInfo.size					= m_bufferSize;							// VkDeviceSize			size;
142 	bufferCreateInfo.usage					= VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
143 											  VK_BUFFER_USAGE_TRANSFER_DST_BIT;		// VkBufferUsageFlags	usage;
144 	bufferCreateInfo.sharingMode			= VK_SHARING_MODE_EXCLUSIVE;			// VkSharingMode		sharingMode;
145 	bufferCreateInfo.queueFamilyIndexCount	= 0u;									// deUint32				queueFamilyIndexCount;
146 	bufferCreateInfo.pQueueFamilyIndices	= DE_NULL;								// const deUint32*		pQueueFamilyIndices;
147 
148 	const deUint32 queueFamilyIndices[] = { sparseQueue.queueFamilyIndex, computeQueue.queueFamilyIndex };
149 
150 	if (sparseQueue.queueFamilyIndex != computeQueue.queueFamilyIndex)
151 	{
152 		bufferCreateInfo.sharingMode			= VK_SHARING_MODE_CONCURRENT;	// VkSharingMode		sharingMode;
153 		bufferCreateInfo.queueFamilyIndexCount	= 2u;							// deUint32				queueFamilyIndexCount;
154 		bufferCreateInfo.pQueueFamilyIndices	= queueFamilyIndices;			// const deUint32*		pQueueFamilyIndices;
155 	}
156 
157 	// Create sparse buffer
158 	const Unique<VkBuffer> sparseBuffer(createBuffer(deviceInterface, *m_logicalDevice, &bufferCreateInfo));
159 
160 	const VkMemoryRequirements bufferMemRequirement = getBufferMemoryRequirements(deviceInterface, *m_logicalDevice, *sparseBuffer);
161 
162 	if (bufferMemRequirement.size > deviceProperties.limits.sparseAddressSpaceSize)
163 	{
164 		return tcu::TestStatus(QP_TEST_RESULT_NOT_SUPPORTED, "Required memory size for sparse resources exceeds device limits");
165 	}
166 
167 	DE_ASSERT((bufferMemRequirement.size % bufferMemRequirement.alignment) == 0);
168 
169 	const deUint32 numSparseBinds = static_cast<deUint32>(bufferMemRequirement.size / bufferMemRequirement.alignment);
170 
171 	typedef de::SharedPtr< Unique<VkDeviceMemory> > DeviceMemoryUniquePtr;
172 
173 	std::vector<VkSparseMemoryBind>		sparseMemoryBinds;
174 	std::vector<DeviceMemoryUniquePtr>	deviceMemUniquePtrVec;
175 	const deUint32						memoryType = findMatchingMemoryType(deviceMemoryProperties, bufferMemRequirement, MemoryRequirement::Any);
176 
177 	if (memoryType == NO_MATCH_FOUND)
178 	{
179 		return tcu::TestStatus(QP_TEST_RESULT_FAIL, "No matching memory type found");
180 	}
181 
182 	for (deUint32 sparseBindNdx = 0; sparseBindNdx < numSparseBinds; ++sparseBindNdx)
183 	{
184 		const VkMemoryAllocateInfo allocInfo =
185 		{
186 			VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,	//	VkStructureType			sType;
187 			DE_NULL,								//	const void*				pNext;
188 			bufferMemRequirement.alignment,			//	VkDeviceSize			allocationSize;
189 			memoryType,								//	deUint32				memoryTypeIndex;
190 		};
191 
192 		VkDeviceMemory deviceMemory = 0;
193 		VK_CHECK(deviceInterface.allocateMemory(*m_logicalDevice, &allocInfo, DE_NULL, &deviceMemory));
194 
195 		deviceMemUniquePtrVec.push_back(makeVkSharedPtr(Move<VkDeviceMemory>(check<VkDeviceMemory>(deviceMemory), Deleter<VkDeviceMemory>(deviceInterface, *m_logicalDevice, DE_NULL))));
196 
197 		const VkSparseMemoryBind sparseMemoryBind = makeSparseMemoryBind
198 		(
199 			bufferMemRequirement.alignment * sparseBindNdx,	//VkDeviceSize				resourceOffset
200 			bufferMemRequirement.alignment,					//VkDeviceSize				size
201 			deviceMemory,									//VkDeviceMemory			memory
202 			0u,												//VkDeviceSize				memoryOffset
203 			0u												//VkSparseMemoryBindFlags	flags
204 		);
205 
206 		sparseMemoryBinds.push_back(sparseMemoryBind);
207 	}
208 
209 	const VkSparseBufferMemoryBindInfo sparseBufferBindInfo = makeSparseBufferMemoryBindInfo
210 	(
211 		*sparseBuffer,			//VkBuffer					buffer;
212 		numSparseBinds,			//deUint32					bindCount;
213 		&sparseMemoryBinds[0]	//const VkSparseMemoryBind*	Binds;
214 	);
215 
216 	const Unique<VkSemaphore> bufferMemoryBindSemaphore(makeSemaphore(deviceInterface, *m_logicalDevice));
217 
218 	const VkBindSparseInfo bindSparseInfo =
219 	{
220 		VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,			//VkStructureType							sType;
221 		DE_NULL,									//const void*								pNext;
222 		0u,											//deUint32									waitSemaphoreCount;
223 		DE_NULL,									//const VkSemaphore*						pWaitSemaphores;
224 		1u,											//deUint32									bufferBindCount;
225 		&sparseBufferBindInfo,						//const VkSparseBufferMemoryBindInfo*		pBufferBinds;
226 		0u,											//deUint32									imageOpaqueBindCount;
227 		DE_NULL,									//const VkSparseImageOpaqueMemoryBindInfo*	pImageOpaqueBinds;
228 		0u,											//deUint32									imageBindCount;
229 		DE_NULL,									//const VkSparseImageMemoryBindInfo*		pImageBinds;
230 		1u,											//deUint32									signalSemaphoreCount;
231 		&bufferMemoryBindSemaphore.get()			//const VkSemaphore*						pSignalSemaphores;
232 	};
233 
234 	// Submit sparse bind commands for execution
235 	VK_CHECK(deviceInterface.queueBindSparse(sparseQueue.queueHandle, 1u, &bindSparseInfo, DE_NULL));
236 
237 	// Create command buffer for transfer oparations
238 	const Unique<VkCommandPool>		commandPool(makeCommandPool(deviceInterface, *m_logicalDevice, computeQueue.queueFamilyIndex));
239 	const Unique<VkCommandBuffer>	commandBuffer(makeCommandBuffer(deviceInterface, *m_logicalDevice, *commandPool));
240 
241 	// Start recording transfer commands
242 	beginCommandBuffer(deviceInterface, *commandBuffer);
243 
244 	const VkBufferCreateInfo	inputBufferCreateInfo = makeBufferCreateInfo(m_bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
245 	const de::UniquePtr<Buffer>	inputBuffer(new Buffer(deviceInterface, *m_logicalDevice, *allocator, inputBufferCreateInfo, MemoryRequirement::HostVisible));
246 
247 	std::vector<deUint8> referenceData;
248 	referenceData.resize(m_bufferSize);
249 
250 	for (deUint32 valueNdx = 0; valueNdx < m_bufferSize; ++valueNdx)
251 	{
252 		referenceData[valueNdx] = static_cast<deUint8>((valueNdx % bufferMemRequirement.alignment) + 1u);
253 	}
254 
255 	deMemcpy(inputBuffer->getAllocation().getHostPtr(), &referenceData[0], m_bufferSize);
256 
257 	flushMappedMemoryRange(deviceInterface, *m_logicalDevice, inputBuffer->getAllocation().getMemory(), inputBuffer->getAllocation().getOffset(), m_bufferSize);
258 
259 	const VkBufferMemoryBarrier inputBufferBarrier
260 		= makeBufferMemoryBarrier(	VK_ACCESS_HOST_WRITE_BIT,
261 									VK_ACCESS_TRANSFER_READ_BIT,
262 									inputBuffer->get(),
263 									0u,
264 									m_bufferSize);
265 
266 	deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 1u, &inputBufferBarrier, 0u, DE_NULL);
267 
268 	const VkBufferCopy bufferCopy = makeBufferCopy(0u, 0u, m_bufferSize);
269 
270 	deviceInterface.cmdCopyBuffer(*commandBuffer, inputBuffer->get(), *sparseBuffer, 1u, &bufferCopy);
271 
272 	const VkBufferMemoryBarrier sparseBufferBarrier
273 		= makeBufferMemoryBarrier(	VK_ACCESS_TRANSFER_WRITE_BIT,
274 									VK_ACCESS_TRANSFER_READ_BIT,
275 									*sparseBuffer,
276 									0u,
277 									m_bufferSize);
278 
279 	deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 1u, &sparseBufferBarrier, 0u, DE_NULL);
280 
281 	const VkBufferCreateInfo	outputBufferCreateInfo = makeBufferCreateInfo(m_bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
282 	const de::UniquePtr<Buffer>	outputBuffer(new Buffer(deviceInterface, *m_logicalDevice, *allocator, outputBufferCreateInfo, MemoryRequirement::HostVisible));
283 
284 	deviceInterface.cmdCopyBuffer(*commandBuffer, *sparseBuffer, outputBuffer->get(), 1u, &bufferCopy);
285 
286 	const VkBufferMemoryBarrier outputBufferBarrier
287 		= makeBufferMemoryBarrier(	VK_ACCESS_TRANSFER_WRITE_BIT,
288 									VK_ACCESS_HOST_READ_BIT,
289 									outputBuffer->get(),
290 									0u,
291 									m_bufferSize);
292 
293 	deviceInterface.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, 1u, &outputBufferBarrier, 0u, DE_NULL);
294 
295 	// End recording transfer commands
296 	endCommandBuffer(deviceInterface, *commandBuffer);
297 
298 	const VkPipelineStageFlags waitStageBits[] = { VK_PIPELINE_STAGE_TRANSFER_BIT };
299 
300 	// Submit transfer commands for execution and wait for completion
301 	submitCommandsAndWait(deviceInterface, *m_logicalDevice, computeQueue.queueHandle, *commandBuffer, 1u, &bufferMemoryBindSemaphore.get(), waitStageBits);
302 
303 	// Retrieve data from output buffer to host memory
304 	const Allocation& allocation = outputBuffer->getAllocation();
305 
306 	invalidateMappedMemoryRange(deviceInterface, *m_logicalDevice, allocation.getMemory(), allocation.getOffset(), m_bufferSize);
307 
308 	const deUint8*	outputData = static_cast<const deUint8*>(allocation.getHostPtr());
309 	tcu::TestStatus testStatus = tcu::TestStatus::incomplete();
310 
311 	// Compare output data with reference data
312 	if (deMemCmp(&referenceData[0], outputData, m_bufferSize) == 0)
313 		testStatus = tcu::TestStatus::pass("Passed");
314 	else
315 		testStatus = tcu::TestStatus::fail("Failed");
316 
317 	// Wait for sparse queue to become idle
318 	deviceInterface.queueWaitIdle(sparseQueue.queueHandle);
319 
320 	return testStatus;
321 }
322 
createInstance(Context & context) const323 TestInstance* BufferSparseBindingCase::createInstance (Context& context) const
324 {
325 	return new BufferSparseBindingInstance(context, m_bufferSize);
326 }
327 
328 } // anonymous ns
329 
createBufferSparseBindingTests(tcu::TestContext & testCtx)330 tcu::TestCaseGroup* createBufferSparseBindingTests (tcu::TestContext& testCtx)
331 {
332 	de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "buffer_sparse_binding", "Buffer Sparse Binding"));
333 
334 	testGroup->addChild(new BufferSparseBindingCase(testCtx, "buffer_size_2_10", "", 1 << 10));
335 	testGroup->addChild(new BufferSparseBindingCase(testCtx, "buffer_size_2_12", "", 1 << 12));
336 	testGroup->addChild(new BufferSparseBindingCase(testCtx, "buffer_size_2_16", "", 1 << 16));
337 	testGroup->addChild(new BufferSparseBindingCase(testCtx, "buffer_size_2_17", "", 1 << 17));
338 	testGroup->addChild(new BufferSparseBindingCase(testCtx, "buffer_size_2_20", "", 1 << 20));
339 	testGroup->addChild(new BufferSparseBindingCase(testCtx, "buffer_size_2_24", "", 1 << 24));
340 
341 	return testGroup.release();
342 }
343 
344 } // sparse
345 } // vkt
346