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