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