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
27 #include "deStringUtil.hpp"
28 #include "gluVarType.hpp"
29 #include "tcuTestLog.hpp"
30 #include "vkPrograms.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vkRefUtil.hpp"
33 #include "vkPlatform.hpp"
34 #include "vktTestCase.hpp"
35
36 namespace vkt
37 {
38 namespace api
39 {
40 namespace
41 {
42 using namespace vk;
43
44 struct BufferCaseParameters
45 {
46 VkBufferUsageFlags usage;
47 VkBufferCreateFlags flags;
48 VkSharingMode sharingMode;
49 };
50
51 class BufferTestInstance : public TestInstance
52 {
53 public:
BufferTestInstance(Context & ctx,BufferCaseParameters testCase)54 BufferTestInstance (Context& ctx,
55 BufferCaseParameters testCase)
56 : TestInstance (ctx)
57 , m_testCase (testCase)
58 , m_sparseContext (createSparseContext())
59 {}
60 virtual tcu::TestStatus iterate (void);
61 tcu::TestStatus bufferCreateAndAllocTest (VkDeviceSize size);
62
63 private:
64 BufferCaseParameters m_testCase;
65
66 private:
67 // Custom context for sparse cases
68 struct SparseContext
69 {
SparseContextvkt::api::__anon95a962c30111::BufferTestInstance::SparseContext70 SparseContext (Move<VkDevice>& device, const deUint32 queueFamilyIndex, const InstanceInterface& interface)
71 : m_device (device)
72 , m_queueFamilyIndex (queueFamilyIndex)
73 , m_deviceInterface (interface, *m_device)
74 {}
75
76 Unique<VkDevice> m_device;
77 const deUint32 m_queueFamilyIndex;
78 DeviceDriver m_deviceInterface;
79 };
80
81 de::UniquePtr<SparseContext> m_sparseContext;
82
83 // Wrapper functions around m_context calls to support sparse cases.
getPhysicalDevice(void) const84 VkPhysicalDevice getPhysicalDevice (void) const
85 {
86 // Same in sparse and regular case
87 return m_context.getPhysicalDevice();
88 }
89
getDevice(void) const90 VkDevice getDevice (void) const
91 {
92 if (m_sparseContext)
93 return *(m_sparseContext->m_device);
94
95 return m_context.getDevice();
96 }
97
getInstanceInterface(void) const98 const InstanceInterface& getInstanceInterface (void) const
99 {
100 // Same in sparse and regular case
101 return m_context.getInstanceInterface();
102 }
103
getDeviceInterface(void) const104 const DeviceInterface& getDeviceInterface (void) const
105 {
106 if (m_sparseContext)
107 return m_sparseContext->m_deviceInterface;
108
109 return m_context.getDeviceInterface();
110 }
111
getUniversalQueueFamilyIndex(void) const112 deUint32 getUniversalQueueFamilyIndex (void) const
113 {
114 if (m_sparseContext)
115 return m_sparseContext->m_queueFamilyIndex;
116
117 return m_context.getUniversalQueueFamilyIndex();
118 }
119
findQueueFamilyIndexWithCaps(const InstanceInterface & vkInstance,VkPhysicalDevice physicalDevice,VkQueueFlags requiredCaps)120 static deUint32 findQueueFamilyIndexWithCaps (const InstanceInterface& vkInstance, VkPhysicalDevice physicalDevice, VkQueueFlags requiredCaps)
121 {
122 const std::vector<VkQueueFamilyProperties> queueProps = getPhysicalDeviceQueueFamilyProperties(vkInstance, physicalDevice);
123
124 for (size_t queueNdx = 0; queueNdx < queueProps.size(); queueNdx++)
125 {
126 if ((queueProps[queueNdx].queueFlags & requiredCaps) == requiredCaps)
127 return (deUint32)queueNdx;
128 }
129
130 TCU_THROW(NotSupportedError, "No matching queue found");
131 }
132
133 // Create the sparseContext
createSparseContext(void) const134 SparseContext* createSparseContext (void) const
135 {
136 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) ||
137 (m_testCase.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT) ||
138 (m_testCase.flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT))
139 {
140 const InstanceInterface& vk = getInstanceInterface();
141 const VkPhysicalDevice physicalDevice = getPhysicalDevice();
142 const VkPhysicalDeviceFeatures deviceFeatures = getPhysicalDeviceFeatures(vk, physicalDevice);
143
144 const deUint32 queueIndex = findQueueFamilyIndexWithCaps(vk, physicalDevice, VK_QUEUE_GRAPHICS_BIT|VK_QUEUE_SPARSE_BINDING_BIT);
145
146 VkDeviceQueueCreateInfo queueInfo;
147 VkDeviceCreateInfo deviceInfo;
148 const float queuePriority = 1.0f;
149
150 deMemset(&queueInfo, 0, sizeof(queueInfo));
151 deMemset(&deviceInfo, 0, sizeof(deviceInfo));
152
153 queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
154 queueInfo.pNext = DE_NULL;
155 queueInfo.flags = (VkDeviceQueueCreateFlags)0u;
156 queueInfo.queueFamilyIndex = queueIndex;
157 queueInfo.queueCount = 1u;
158 queueInfo.pQueuePriorities = &queuePriority;
159
160 deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
161 deviceInfo.pNext = DE_NULL;
162 deviceInfo.queueCreateInfoCount = 1u;
163 deviceInfo.pQueueCreateInfos = &queueInfo;
164 deviceInfo.enabledExtensionCount = 0u;
165 deviceInfo.ppEnabledExtensionNames = DE_NULL;
166 deviceInfo.enabledLayerCount = 0u;
167 deviceInfo.ppEnabledLayerNames = DE_NULL;
168 deviceInfo.pEnabledFeatures = &deviceFeatures;
169
170 Move<VkDevice> device = createDevice(vk, physicalDevice, &deviceInfo);
171
172 return new SparseContext(device, queueIndex, vk);
173 }
174
175 return DE_NULL;
176 }
177 };
178
179 class BuffersTestCase : public TestCase
180 {
181 public:
BuffersTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,BufferCaseParameters testCase)182 BuffersTestCase (tcu::TestContext& testCtx,
183 const std::string& name,
184 const std::string& description,
185 BufferCaseParameters testCase)
186 : TestCase(testCtx, name, description)
187 , m_testCase(testCase)
188 {}
189
~BuffersTestCase(void)190 virtual ~BuffersTestCase (void) {}
createInstance(Context & ctx) const191 virtual TestInstance* createInstance (Context& ctx) const
192 {
193 tcu::TestLog& log = m_testCtx.getLog();
194 log << tcu::TestLog::Message << getBufferUsageFlagsStr(m_testCase.usage) << tcu::TestLog::EndMessage;
195 return new BufferTestInstance(ctx, m_testCase);
196 }
197
198 private:
199 BufferCaseParameters m_testCase;
200 };
201
alignDeviceSize(VkDeviceSize val,VkDeviceSize align)202 inline VkDeviceSize alignDeviceSize (VkDeviceSize val, VkDeviceSize align)
203 {
204 DE_ASSERT(deIsPowerOfTwo64(align));
205 DE_ASSERT(val + align >= val); // crash on overflow
206 return (val + align - 1) & ~(align - 1);
207 }
208
bufferCreateAndAllocTest(VkDeviceSize size)209 tcu::TestStatus BufferTestInstance::bufferCreateAndAllocTest (VkDeviceSize size)
210 {
211 const VkPhysicalDevice vkPhysicalDevice = getPhysicalDevice();
212 const InstanceInterface& vkInstance = getInstanceInterface();
213 const VkDevice vkDevice = getDevice();
214 const DeviceInterface& vk = getDeviceInterface();
215 const deUint32 queueFamilyIndex = getUniversalQueueFamilyIndex();
216 const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vkInstance, vkPhysicalDevice);
217 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vkInstance, vkPhysicalDevice).limits;
218 Move<VkBuffer> buffer;
219 Move<VkDeviceMemory> memory;
220 VkMemoryRequirements memReqs;
221
222 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0)
223 size = std::min(size, limits.sparseAddressSpaceSize);
224
225 // Create the test buffer and a memory allocation for it
226 {
227 // Create a minimal buffer first to get the supported memory types
228 VkBufferCreateInfo bufferParams =
229 {
230 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
231 DE_NULL, // const void* pNext;
232 m_testCase.flags, // VkBufferCreateFlags flags;
233 1u, // VkDeviceSize size;
234 m_testCase.usage, // VkBufferUsageFlags usage;
235 m_testCase.sharingMode, // VkSharingMode sharingMode;
236 1u, // uint32_t queueFamilyIndexCount;
237 &queueFamilyIndex, // const uint32_t* pQueueFamilyIndices;
238 };
239
240 buffer = createBuffer(vk, vkDevice, &bufferParams);
241 vk.getBufferMemoryRequirements(vkDevice, *buffer, &memReqs);
242
243 const deUint32 heapTypeIndex = (deUint32)deCtz32(memReqs.memoryTypeBits);
244 const VkMemoryType memoryType = memoryProperties.memoryTypes[heapTypeIndex];
245 const VkMemoryHeap memoryHeap = memoryProperties.memoryHeaps[memoryType.heapIndex];
246 const VkDeviceSize maxBufferSize = alignDeviceSize(memoryHeap.size >> 1, memReqs.alignment);
247 const deUint32 shrinkBits = 4; // number of bits to shift when reducing the size with each iteration
248
249 size = std::min(size, maxBufferSize);
250
251 while (*memory == DE_NULL)
252 {
253 // Create the buffer
254 {
255 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
256 VkBuffer rawBuffer = DE_NULL;
257
258 bufferParams.size = size;
259 buffer = Move<VkBuffer>(); // free the previous buffer, if any
260 result = vk.createBuffer(vkDevice, &bufferParams, (VkAllocationCallbacks*)DE_NULL, &rawBuffer);
261
262 if (result != VK_SUCCESS)
263 {
264 size = alignDeviceSize(size >> shrinkBits, memReqs.alignment);
265
266 if (size == 0 || bufferParams.size == memReqs.alignment)
267 return tcu::TestStatus::fail("Buffer creation failed! (" + de::toString(getResultName(result)) + ")");
268
269 continue; // didn't work, try with a smaller buffer
270 }
271
272 buffer = Move<VkBuffer>(check<VkBuffer>(rawBuffer), Deleter<VkBuffer>(vk, vkDevice, DE_NULL));
273 }
274
275 vk.getBufferMemoryRequirements(vkDevice, *buffer, &memReqs); // get the proper size requirement
276
277 if (size > memReqs.size)
278 {
279 std::ostringstream errorMsg;
280 errorMsg << "Requied memory size (" << memReqs.size << " bytes) smaller than the buffer's size (" << size << " bytes)!";
281 return tcu::TestStatus::fail(errorMsg.str());
282 }
283
284 // Allocate the memory
285 {
286 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
287 VkDeviceMemory rawMemory = DE_NULL;
288
289 const VkMemoryAllocateInfo memAlloc =
290 {
291 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType;
292 NULL, // const void* pNext;
293 memReqs.size, // VkDeviceSize allocationSize;
294 heapTypeIndex, // uint32_t memoryTypeIndex;
295 };
296
297 result = vk.allocateMemory(vkDevice, &memAlloc, (VkAllocationCallbacks*)DE_NULL, &rawMemory);
298
299 if (result != VK_SUCCESS)
300 {
301 size = alignDeviceSize(size >> shrinkBits, memReqs.alignment);
302
303 if (size == 0 || memReqs.size == memReqs.alignment)
304 return tcu::TestStatus::fail("Unable to allocate " + de::toString(memReqs.size) + " bytes of memory");
305
306 continue; // didn't work, try with a smaller allocation (and a smaller buffer)
307 }
308
309 memory = Move<VkDeviceMemory>(check<VkDeviceMemory>(rawMemory), Deleter<VkDeviceMemory>(vk, vkDevice, DE_NULL));
310 }
311 } // while
312 }
313
314 // Bind the memory
315 if ((m_testCase.flags & (VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT)) != 0)
316 {
317 VkQueue queue = DE_NULL;
318
319 vk.getDeviceQueue(vkDevice, queueFamilyIndex, 0, &queue);
320
321 const VkSparseMemoryBind sparseMemoryBind =
322 {
323 0, // VkDeviceSize resourceOffset;
324 memReqs.size, // VkDeviceSize size;
325 *memory, // VkDeviceMemory memory;
326 0, // VkDeviceSize memoryOffset;
327 0 // VkSparseMemoryBindFlags flags;
328 };
329
330 const VkSparseBufferMemoryBindInfo sparseBufferMemoryBindInfo =
331 {
332 *buffer, // VkBuffer buffer;
333 1u, // deUint32 bindCount;
334 &sparseMemoryBind // const VkSparseMemoryBind* pBinds;
335 };
336
337 const VkBindSparseInfo bindSparseInfo =
338 {
339 VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, // VkStructureType sType;
340 DE_NULL, // const void* pNext;
341 0, // deUint32 waitSemaphoreCount;
342 DE_NULL, // const VkSemaphore* pWaitSemaphores;
343 1u, // deUint32 bufferBindCount;
344 &sparseBufferMemoryBindInfo, // const VkSparseBufferMemoryBindInfo* pBufferBinds;
345 0, // deUint32 imageOpaqueBindCount;
346 DE_NULL, // const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds;
347 0, // deUint32 imageBindCount;
348 DE_NULL, // const VkSparseImageMemoryBindInfo* pImageBinds;
349 0, // deUint32 signalSemaphoreCount;
350 DE_NULL, // const VkSemaphore* pSignalSemaphores;
351 };
352
353 const VkFenceCreateInfo fenceParams =
354 {
355 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
356 DE_NULL, // const void* pNext;
357 0u // VkFenceCreateFlags flags;
358 };
359
360 const vk::Unique<vk::VkFence> fence(vk::createFence(vk, vkDevice, &fenceParams));
361
362 if (vk.queueBindSparse(queue, 1, &bindSparseInfo, *fence) != VK_SUCCESS)
363 return tcu::TestStatus::fail("Bind sparse buffer memory failed! (requested memory size: " + de::toString(size) + ")");
364
365 VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), VK_TRUE, ~(0ull) /* infinity */));
366 }
367 else
368 {
369 if (vk.bindBufferMemory(vkDevice, *buffer, *memory, 0) != VK_SUCCESS)
370 return tcu::TestStatus::fail("Bind buffer memory failed! (requested memory size: " + de::toString(size) + ")");
371 }
372
373 return tcu::TestStatus::pass("Pass");
374 }
375
iterate(void)376 tcu::TestStatus BufferTestInstance::iterate (void)
377 {
378 const VkPhysicalDeviceFeatures& physicalDeviceFeatures = getPhysicalDeviceFeatures(getInstanceInterface(), getPhysicalDevice());
379
380 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT ) && !physicalDeviceFeatures.sparseBinding)
381 TCU_THROW(NotSupportedError, "Sparse bindings feature is not supported");
382
383 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT ) && !physicalDeviceFeatures.sparseResidencyBuffer)
384 TCU_THROW(NotSupportedError, "Sparse buffer residency feature is not supported");
385
386 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT ) && !physicalDeviceFeatures.sparseResidencyAliased)
387 TCU_THROW(NotSupportedError, "Sparse aliased residency feature is not supported");
388
389 const VkDeviceSize testSizes[] =
390 {
391 1,
392 1181,
393 15991,
394 16384,
395 ~0ull, // try to exercise a very large buffer too (will be clamped to a sensible size later)
396 };
397
398 for (int i = 0; i < DE_LENGTH_OF_ARRAY(testSizes); ++i)
399 {
400 const tcu::TestStatus testStatus = bufferCreateAndAllocTest(testSizes[i]);
401
402 if (testStatus.getCode() != QP_TEST_RESULT_PASS)
403 return testStatus;
404 }
405
406 return tcu::TestStatus::pass("Pass");
407 }
408
409 } // anonymous
410
createBufferTests(tcu::TestContext & testCtx)411 tcu::TestCaseGroup* createBufferTests (tcu::TestContext& testCtx)
412 {
413 const VkBufferUsageFlags bufferUsageModes[] =
414 {
415 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
416 VK_BUFFER_USAGE_TRANSFER_DST_BIT,
417 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
418 VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
419 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
420 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
421 VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
422 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
423 VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
424 };
425
426 // \note SPARSE_RESIDENCY and SPARSE_ALIASED have to be used together with the SPARSE_BINDING flag.
427 const VkBufferCreateFlags bufferCreateFlags[] =
428 {
429 0,
430 VK_BUFFER_CREATE_SPARSE_BINDING_BIT,
431 VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT,
432 VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
433 VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
434 };
435
436 de::MovePtr<tcu::TestCaseGroup> buffersTests (new tcu::TestCaseGroup(testCtx, "buffer", "Buffer Tests"));
437
438 const deUint32 maximumValueOfBufferUsageFlags = (1u << (DE_LENGTH_OF_ARRAY(bufferUsageModes) - 1)) - 1u;
439
440 for (deUint32 bufferCreateFlagsNdx = 0u; bufferCreateFlagsNdx < DE_LENGTH_OF_ARRAY(bufferCreateFlags); bufferCreateFlagsNdx++)
441 for (deUint32 combinedBufferUsageFlags = 1u; combinedBufferUsageFlags <= maximumValueOfBufferUsageFlags; combinedBufferUsageFlags++)
442 {
443 const BufferCaseParameters testParams =
444 {
445 combinedBufferUsageFlags,
446 bufferCreateFlags[bufferCreateFlagsNdx],
447 VK_SHARING_MODE_EXCLUSIVE
448 };
449 std::ostringstream testName;
450 std::ostringstream testDescription;
451 testName << "create_buffer_" << combinedBufferUsageFlags << "_" << testParams.flags;
452 testDescription << "vkCreateBuffer test " << combinedBufferUsageFlags << " " << testParams.flags;
453 buffersTests->addChild(new BuffersTestCase(testCtx, testName.str(), testDescription.str(), testParams));
454 }
455
456 return buffersTests.release();
457 }
458
459 } // api
460 } // vk
461