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 #include "gluVarType.hpp"
27 #include "deStringUtil.hpp"
28 #include "tcuTestLog.hpp"
29 #include "vkPlatform.hpp"
30 #include "vkPrograms.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vkRefUtil.hpp"
33 #include "vktTestCase.hpp"
34 #include "tcuPlatform.hpp"
35
36 #include <algorithm>
37
38 namespace vkt
39 {
40 namespace api
41 {
42 namespace
43 {
44 using namespace vk;
45
46 enum AllocationKind
47 {
48 ALLOCATION_KIND_SUBALLOCATED = 0,
49 ALLOCATION_KIND_DEDICATED,
50
51 ALLOCATION_KIND_LAST,
52 };
53
getPlatformMemoryLimits(Context & context)54 PlatformMemoryLimits getPlatformMemoryLimits (Context& context)
55 {
56 PlatformMemoryLimits memoryLimits;
57
58 context.getTestContext().getPlatform().getVulkanPlatform().getMemoryLimits(memoryLimits);
59
60 return memoryLimits;
61 }
62
getMaxBufferSize(const VkDeviceSize & bufferSize,const VkDeviceSize & alignment,const PlatformMemoryLimits & limits)63 VkDeviceSize getMaxBufferSize(const VkDeviceSize& bufferSize,
64 const VkDeviceSize& alignment,
65 const PlatformMemoryLimits& limits)
66 {
67 VkDeviceSize size = bufferSize;
68
69 if (limits.totalDeviceLocalMemory == 0)
70 {
71 // 'UMA' systems where device memory counts against system memory
72 size = std::min(bufferSize, limits.totalSystemMemory - alignment);
73 }
74 else
75 {
76 // 'LMA' systems where device memory is local to the GPU
77 size = std::min(bufferSize, limits.totalDeviceLocalMemory - alignment);
78 }
79
80 return size;
81 }
82
83 struct BufferCaseParameters
84 {
85 VkBufferUsageFlags usage;
86 VkBufferCreateFlags flags;
87 VkSharingMode sharingMode;
88 };
89
90 class BufferTestInstance : public TestInstance
91 {
92 public:
BufferTestInstance(Context & ctx,BufferCaseParameters testCase)93 BufferTestInstance (Context& ctx,
94 BufferCaseParameters testCase)
95 : TestInstance (ctx)
96 , m_testCase (testCase)
97 , m_sparseContext (createSparseContext())
98 {
99 }
100 virtual tcu::TestStatus iterate (void);
101 virtual tcu::TestStatus bufferCreateAndAllocTest (VkDeviceSize size);
102
103 protected:
104 BufferCaseParameters m_testCase;
105
106 // Wrapper functions around m_context calls to support sparse cases.
getPhysicalDevice(void) const107 VkPhysicalDevice getPhysicalDevice (void) const
108 {
109 // Same in sparse and regular case
110 return m_context.getPhysicalDevice();
111 }
112
getDevice(void) const113 VkDevice getDevice (void) const
114 {
115 if (m_sparseContext)
116 {
117 return *(m_sparseContext->m_device);
118 }
119 return m_context.getDevice();
120 }
121
getInstanceInterface(void) const122 const InstanceInterface& getInstanceInterface (void) const
123 {
124 // Same in sparse and regular case
125 return m_context.getInstanceInterface();
126 }
127
getDeviceInterface(void) const128 const DeviceInterface& getDeviceInterface (void) const
129 {
130 if (m_sparseContext)
131 {
132 return m_sparseContext->m_deviceInterface;
133 }
134 return m_context.getDeviceInterface();
135 }
136
getUniversalQueueFamilyIndex(void) const137 deUint32 getUniversalQueueFamilyIndex (void) const
138 {
139 if (m_sparseContext)
140 {
141 return m_sparseContext->m_queueFamilyIndex;
142 }
143 return m_context.getUniversalQueueFamilyIndex();
144 }
145
146 private:
147 // Custom context for sparse cases
148 struct SparseContext
149 {
SparseContextvkt::api::__anon6299e9640111::BufferTestInstance::SparseContext150 SparseContext (Move<VkDevice>& device,
151 const deUint32 queueFamilyIndex,
152 const PlatformInterface& platformInterface,
153 VkInstance instance)
154 : m_device (device)
155 , m_queueFamilyIndex (queueFamilyIndex)
156 , m_deviceInterface (platformInterface, instance, *m_device)
157 {
158 }
159
160 Unique<VkDevice> m_device;
161 const deUint32 m_queueFamilyIndex;
162 DeviceDriver m_deviceInterface;
163 };
164
165 de::UniquePtr<SparseContext> m_sparseContext;
166
findQueueFamilyIndexWithCaps(const InstanceInterface & vkInstance,VkPhysicalDevice physicalDevice,VkQueueFlags requiredCaps)167 static deUint32 findQueueFamilyIndexWithCaps (const InstanceInterface& vkInstance,
168 VkPhysicalDevice physicalDevice,
169 VkQueueFlags requiredCaps)
170 {
171 const std::vector<vk::VkQueueFamilyProperties>
172 queueProps = getPhysicalDeviceQueueFamilyProperties(vkInstance, physicalDevice);
173
174 for (size_t queueNdx = 0; queueNdx < queueProps.size(); queueNdx++)
175 {
176 if ((queueProps[queueNdx].queueFlags & requiredCaps) == requiredCaps)
177 {
178 return (deUint32)queueNdx;
179 }
180 }
181
182 TCU_THROW(NotSupportedError, "No matching queue found");
183 }
184
185 // Create the sparseContext
createSparseContext(void) const186 SparseContext* createSparseContext (void) const
187 {
188 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)
189 || (m_testCase.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT)
190 || (m_testCase.flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT))
191 {
192 const InstanceInterface& vk = getInstanceInterface();
193 const VkPhysicalDevice physicalDevice = getPhysicalDevice();
194 const vk::VkPhysicalDeviceFeatures
195 deviceFeatures = getPhysicalDeviceFeatures(vk, physicalDevice);
196 const deUint32 queueIndex = findQueueFamilyIndexWithCaps(vk, physicalDevice, VK_QUEUE_GRAPHICS_BIT|VK_QUEUE_SPARSE_BINDING_BIT);
197 const float queuePriority = 1.0f;
198 VkDeviceQueueCreateInfo queueInfo =
199 {
200 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
201 DE_NULL,
202 static_cast<VkDeviceQueueCreateFlags>(0u),
203 queueIndex,
204 1u,
205 &queuePriority
206 };
207 VkDeviceCreateInfo deviceInfo =
208 {
209 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
210 DE_NULL,
211 static_cast<VkDeviceQueueCreateFlags>(0u),
212 1u,
213 &queueInfo,
214 0u,
215 DE_NULL,
216 0u,
217 DE_NULL,
218 &deviceFeatures
219 };
220
221 Move<VkDevice> device = createDevice(m_context.getPlatformInterface(), m_context.getInstance(), vk, physicalDevice, &deviceInfo);
222
223 return new SparseContext(device, queueIndex, m_context.getPlatformInterface(), m_context.getInstance());
224 }
225
226 return DE_NULL;
227 }
228 };
229
230 class DedicatedAllocationBufferTestInstance : public BufferTestInstance
231 {
232 public:
DedicatedAllocationBufferTestInstance(Context & ctx,BufferCaseParameters testCase)233 DedicatedAllocationBufferTestInstance
234 (Context& ctx,
235 BufferCaseParameters testCase)
236 : BufferTestInstance (ctx, testCase)
237 {
238 }
239 virtual tcu::TestStatus bufferCreateAndAllocTest (VkDeviceSize size);
240 };
241
242 class BuffersTestCase : public TestCase
243 {
244 public:
BuffersTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,BufferCaseParameters testCase)245 BuffersTestCase (tcu::TestContext& testCtx,
246 const std::string& name,
247 const std::string& description,
248 BufferCaseParameters testCase)
249 : TestCase (testCtx, name, description)
250 , m_testCase (testCase)
251 {
252 }
253
~BuffersTestCase(void)254 virtual ~BuffersTestCase (void)
255 {
256 }
257
createInstance(Context & ctx) const258 virtual TestInstance* createInstance (Context& ctx) const
259 {
260 tcu::TestLog& log = m_testCtx.getLog();
261 log << tcu::TestLog::Message << getBufferUsageFlagsStr(m_testCase.usage) << tcu::TestLog::EndMessage;
262 return new BufferTestInstance(ctx, m_testCase);
263 }
264
checkSupport(Context & ctx) const265 virtual void checkSupport (Context& ctx) const
266 {
267 const VkPhysicalDeviceFeatures& physicalDeviceFeatures = getPhysicalDeviceFeatures(ctx.getInstanceInterface(), ctx.getPhysicalDevice());
268
269 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) && !physicalDeviceFeatures.sparseBinding)
270 TCU_THROW(NotSupportedError, "Sparse bindings feature is not supported");
271
272 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT) && !physicalDeviceFeatures.sparseResidencyBuffer)
273 TCU_THROW(NotSupportedError, "Sparse buffer residency feature is not supported");
274
275 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT) && !physicalDeviceFeatures.sparseResidencyAliased)
276 TCU_THROW(NotSupportedError, "Sparse aliased residency feature is not supported");
277 }
278
279 private:
280 BufferCaseParameters m_testCase;
281 };
282
283 class DedicatedAllocationBuffersTestCase : public TestCase
284 {
285 public:
DedicatedAllocationBuffersTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,BufferCaseParameters testCase)286 DedicatedAllocationBuffersTestCase
287 (tcu::TestContext& testCtx,
288 const std::string& name,
289 const std::string& description,
290 BufferCaseParameters testCase)
291 : TestCase (testCtx, name, description)
292 , m_testCase (testCase)
293 {
294 }
295
~DedicatedAllocationBuffersTestCase(void)296 virtual ~DedicatedAllocationBuffersTestCase
297 (void)
298 {
299 }
300
createInstance(Context & ctx) const301 virtual TestInstance* createInstance (Context& ctx) const
302 {
303 tcu::TestLog& log = m_testCtx.getLog();
304 log << tcu::TestLog::Message << getBufferUsageFlagsStr(m_testCase.usage) << tcu::TestLog::EndMessage;
305 return new DedicatedAllocationBufferTestInstance(ctx, m_testCase);
306 }
307
checkSupport(Context & ctx) const308 virtual void checkSupport (Context& ctx) const
309 {
310 const std::vector<std::string>& extensions = ctx.getDeviceExtensions();
311 const deBool isSupported = isDeviceExtensionSupported(ctx.getUsedApiVersion(), extensions, "VK_KHR_dedicated_allocation");
312 if (!isSupported)
313 {
314 TCU_THROW(NotSupportedError, "Not supported");
315 }
316 }
317 private:
318 BufferCaseParameters m_testCase;
319 };
320
bufferCreateAndAllocTest(VkDeviceSize size)321 tcu::TestStatus BufferTestInstance::bufferCreateAndAllocTest (VkDeviceSize size)
322 {
323 const VkPhysicalDevice vkPhysicalDevice = getPhysicalDevice();
324 const InstanceInterface& vkInstance = getInstanceInterface();
325 const VkDevice vkDevice = getDevice();
326 const DeviceInterface& vk = getDeviceInterface();
327 const deUint32 queueFamilyIndex = getUniversalQueueFamilyIndex();
328 const VkPhysicalDeviceMemoryProperties
329 memoryProperties = getPhysicalDeviceMemoryProperties(vkInstance, vkPhysicalDevice);
330 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vkInstance, vkPhysicalDevice).limits;
331 Move<VkBuffer> buffer;
332 Move<VkDeviceMemory> memory;
333 VkMemoryRequirements memReqs;
334
335 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0)
336 {
337 size = std::min(size, limits.sparseAddressSpaceSize);
338 }
339
340 // Create the test buffer and a memory allocation for it
341 {
342 // Create a minimal buffer first to get the supported memory types
343 VkBufferCreateInfo bufferParams =
344 {
345 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
346 DE_NULL, // const void* pNext;
347 m_testCase.flags, // VkBufferCreateFlags flags;
348 1u, // VkDeviceSize size;
349 m_testCase.usage, // VkBufferUsageFlags usage;
350 m_testCase.sharingMode, // VkSharingMode sharingMode;
351 1u, // uint32_t queueFamilyIndexCount;
352 &queueFamilyIndex, // const uint32_t* pQueueFamilyIndices;
353 };
354
355 buffer = createBuffer(vk, vkDevice, &bufferParams);
356 vk.getBufferMemoryRequirements(vkDevice, *buffer, &memReqs);
357
358 const deUint32 heapTypeIndex = (deUint32)deCtz32(memReqs.memoryTypeBits);
359 const VkMemoryType memoryType = memoryProperties.memoryTypes[heapTypeIndex];
360 const VkMemoryHeap memoryHeap = memoryProperties.memoryHeaps[memoryType.heapIndex];
361 const deUint32 shrinkBits = 4u; // number of bits to shift when reducing the size with each iteration
362
363 // Buffer size - Choose half of the reported heap size for the maximum buffer size, we
364 // should attempt to test as large a portion as possible.
365 //
366 // However on a system where device memory is shared with the system, the maximum size
367 // should be tested against the platform memory limits as significant portion of the heap
368 // may already be in use by the operating system and other running processes.
369 const VkDeviceSize availableBufferSize = getMaxBufferSize(memoryHeap.size,
370 memReqs.alignment,
371 getPlatformMemoryLimits(m_context));
372
373 // For our test buffer size, halve the maximum available size and align
374 const VkDeviceSize maxBufferSize = deAlign64(availableBufferSize >> 1, memReqs.alignment);
375
376 size = std::min(size, maxBufferSize);
377
378 while (*memory == DE_NULL)
379 {
380 // Create the buffer
381 {
382 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
383 VkBuffer rawBuffer = DE_NULL;
384
385 bufferParams.size = size;
386 buffer = Move<VkBuffer>(); // free the previous buffer, if any
387 result = vk.createBuffer(vkDevice, &bufferParams, (vk::VkAllocationCallbacks*)DE_NULL, &rawBuffer);
388
389 if (result != VK_SUCCESS)
390 {
391 size = deAlign64(size >> shrinkBits, memReqs.alignment);
392
393 if (size == 0 || bufferParams.size == memReqs.alignment)
394 {
395 return tcu::TestStatus::fail("Buffer creation failed! (" + de::toString(getResultName(result)) + ")");
396 }
397
398 continue; // didn't work, try with a smaller buffer
399 }
400
401 buffer = Move<VkBuffer>(check<VkBuffer>(rawBuffer), Deleter<VkBuffer>(vk, vkDevice, DE_NULL));
402 }
403
404 vk.getBufferMemoryRequirements(vkDevice, *buffer, &memReqs); // get the proper size requirement
405
406 if (size > memReqs.size)
407 {
408 std::ostringstream errorMsg;
409 errorMsg << "Requied memory size (" << memReqs.size << " bytes) smaller than the buffer's size (" << size << " bytes)!";
410 return tcu::TestStatus::fail(errorMsg.str());
411 }
412
413 // Allocate the memory
414 {
415 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
416 VkDeviceMemory rawMemory = DE_NULL;
417
418 const VkMemoryAllocateInfo
419 memAlloc =
420 {
421 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType;
422 NULL, // const void* pNext;
423 memReqs.size, // VkDeviceSize allocationSize;
424 heapTypeIndex, // uint32_t memoryTypeIndex;
425 };
426
427 result = vk.allocateMemory(vkDevice, &memAlloc, (VkAllocationCallbacks*)DE_NULL, &rawMemory);
428
429 if (result != VK_SUCCESS)
430 {
431 size = deAlign64(size >> shrinkBits, memReqs.alignment);
432
433 if (size == 0 || memReqs.size == memReqs.alignment)
434 {
435 return tcu::TestStatus::fail("Unable to allocate " + de::toString(memReqs.size) + " bytes of memory");
436 }
437
438 continue; // didn't work, try with a smaller allocation (and a smaller buffer)
439 }
440
441 memory = Move<VkDeviceMemory>(check<VkDeviceMemory>(rawMemory), Deleter<VkDeviceMemory>(vk, vkDevice, DE_NULL));
442 }
443 } // while
444 }
445
446 // Bind the memory
447 if ((m_testCase.flags & (VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT)) != 0)
448 {
449 const VkQueue queue = getDeviceQueue(vk, vkDevice, queueFamilyIndex, 0);
450
451 const VkSparseMemoryBind sparseMemoryBind =
452 {
453 0, // VkDeviceSize resourceOffset;
454 memReqs.size, // VkDeviceSize size;
455 *memory, // VkDeviceMemory memory;
456 0, // VkDeviceSize memoryOffset;
457 0 // VkSparseMemoryBindFlags flags;
458 };
459
460 const VkSparseBufferMemoryBindInfo
461 sparseBufferMemoryBindInfo =
462 {
463 *buffer, // VkBuffer buffer;
464 1u, // deUint32 bindCount;
465 &sparseMemoryBind // const VkSparseMemoryBind* pBinds;
466 };
467
468 const VkBindSparseInfo bindSparseInfo =
469 {
470 VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, // VkStructureType sType;
471 DE_NULL, // const void* pNext;
472 0, // deUint32 waitSemaphoreCount;
473 DE_NULL, // const VkSemaphore* pWaitSemaphores;
474 1u, // deUint32 bufferBindCount;
475 &sparseBufferMemoryBindInfo, // const VkSparseBufferMemoryBindInfo* pBufferBinds;
476 0, // deUint32 imageOpaqueBindCount;
477 DE_NULL, // const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds;
478 0, // deUint32 imageBindCount;
479 DE_NULL, // const VkSparseImageMemoryBindInfo* pImageBinds;
480 0, // deUint32 signalSemaphoreCount;
481 DE_NULL, // const VkSemaphore* pSignalSemaphores;
482 };
483
484 const vk::Unique<vk::VkFence> fence (vk::createFence(vk, vkDevice));
485
486 if (vk.queueBindSparse(queue, 1, &bindSparseInfo, *fence) != VK_SUCCESS)
487 return tcu::TestStatus::fail("Bind sparse buffer memory failed! (requested memory size: " + de::toString(size) + ")");
488
489 VK_CHECK(vk.waitForFences(vkDevice, 1, &fence.get(), VK_TRUE, ~(0ull) /* infinity */));
490 }
491 else if (vk.bindBufferMemory(vkDevice, *buffer, *memory, 0) != VK_SUCCESS)
492 return tcu::TestStatus::fail("Bind buffer memory failed! (requested memory size: " + de::toString(size) + ")");
493
494 return tcu::TestStatus::pass("Pass");
495 }
496
iterate(void)497 tcu::TestStatus BufferTestInstance::iterate (void)
498 {
499 const VkDeviceSize testSizes[] =
500 {
501 1,
502 1181,
503 15991,
504 16384,
505 ~0ull, // try to exercise a very large buffer too (will be clamped to a sensible size later)
506 };
507
508 for (int i = 0; i < DE_LENGTH_OF_ARRAY(testSizes); ++i)
509 {
510 const tcu::TestStatus testStatus = bufferCreateAndAllocTest(testSizes[i]);
511
512 if (testStatus.getCode() != QP_TEST_RESULT_PASS)
513 return testStatus;
514 }
515
516 return tcu::TestStatus::pass("Pass");
517 }
518
bufferCreateAndAllocTest(VkDeviceSize size)519 tcu::TestStatus DedicatedAllocationBufferTestInstance::bufferCreateAndAllocTest
520 (VkDeviceSize size)
521 {
522 const VkPhysicalDevice vkPhysicalDevice = getPhysicalDevice();
523 const InstanceInterface& vkInstance = getInstanceInterface();
524 const VkDevice vkDevice = getDevice();
525 const DeviceInterface& vk = getDeviceInterface();
526 const deUint32 queueFamilyIndex = getUniversalQueueFamilyIndex();
527 const VkPhysicalDeviceMemoryProperties
528 memoryProperties = getPhysicalDeviceMemoryProperties(vkInstance, vkPhysicalDevice);
529 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vkInstance, vkPhysicalDevice).limits;
530
531 VkMemoryDedicatedRequirements dedicatedRequirements =
532 {
533 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, // VkStructureType sType;
534 DE_NULL, // const void* pNext;
535 false, // VkBool32 prefersDedicatedAllocation
536 false // VkBool32 requiresDedicatedAllocation
537 };
538 VkMemoryRequirements2 memReqs =
539 {
540 VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, // VkStructureType sType
541 &dedicatedRequirements, // void* pNext
542 {0, 0, 0} // VkMemoryRequirements memoryRequirements
543 };
544
545 if ((m_testCase.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) != 0)
546 size = std::min(size, limits.sparseAddressSpaceSize);
547
548 // Create a minimal buffer first to get the supported memory types
549 VkBufferCreateInfo bufferParams =
550 {
551 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
552 DE_NULL, // const void* pNext
553 m_testCase.flags, // VkBufferCreateFlags flags
554 1u, // VkDeviceSize size
555 m_testCase.usage, // VkBufferUsageFlags usage
556 m_testCase.sharingMode, // VkSharingMode sharingMode
557 1u, // uint32_t queueFamilyIndexCount
558 &queueFamilyIndex, // const uint32_t* pQueueFamilyIndices
559 };
560
561 Move<VkBuffer> buffer = createBuffer(vk, vkDevice, &bufferParams);
562
563 VkBufferMemoryRequirementsInfo2 info =
564 {
565 VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, // VkStructureType sType
566 DE_NULL, // const void* pNext
567 *buffer // VkBuffer buffer
568 };
569
570 vk.getBufferMemoryRequirements2(vkDevice, &info, &memReqs);
571
572 if (dedicatedRequirements.requiresDedicatedAllocation == VK_TRUE)
573 {
574 std::ostringstream errorMsg;
575 errorMsg << "Nonexternal objects cannot require dedicated allocation.";
576 return tcu::TestStatus::fail(errorMsg.str());
577 }
578
579 const deUint32 heapTypeIndex = static_cast<deUint32>(deCtz32(memReqs.memoryRequirements.memoryTypeBits));
580 const VkMemoryType memoryType = memoryProperties.memoryTypes[heapTypeIndex];
581 const VkMemoryHeap memoryHeap = memoryProperties.memoryHeaps[memoryType.heapIndex];
582 const deUint32 shrinkBits = 4u; // number of bits to shift when reducing the size with each iteration
583
584 // Buffer size - Choose half of the reported heap size for the maximum buffer size, we
585 // should attempt to test as large a portion as possible.
586 //
587 // However on a system where device memory is shared with the system, the maximum size
588 // should be tested against the platform memory limits as a significant portion of the heap
589 // may already be in use by the operating system and other running processes.
590 const VkDeviceSize maxBufferSize = getMaxBufferSize(memoryHeap.size,
591 memReqs.memoryRequirements.alignment,
592 getPlatformMemoryLimits(m_context));
593
594 Move<VkDeviceMemory> memory;
595 size = deAlign64(std::min(size, maxBufferSize >> 1), memReqs.memoryRequirements.alignment);
596 while (*memory == DE_NULL)
597 {
598 // Create the buffer
599 {
600 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
601 VkBuffer rawBuffer = DE_NULL;
602
603 bufferParams.size = size;
604 buffer = Move<VkBuffer>(); // free the previous buffer, if any
605 result = vk.createBuffer(vkDevice, &bufferParams, (VkAllocationCallbacks*)DE_NULL, &rawBuffer);
606
607 if (result != VK_SUCCESS)
608 {
609 size = deAlign64(size >> shrinkBits, memReqs.memoryRequirements.alignment);
610
611 if (size == 0 || bufferParams.size == memReqs.memoryRequirements.alignment)
612 return tcu::TestStatus::fail("Buffer creation failed! (" + de::toString(getResultName(result)) + ")");
613
614 continue; // didn't work, try with a smaller buffer
615 }
616
617 buffer = Move<VkBuffer>(check<VkBuffer>(rawBuffer), Deleter<VkBuffer>(vk, vkDevice, DE_NULL));
618 }
619
620 info.buffer = *buffer;
621 vk.getBufferMemoryRequirements2(vkDevice, &info, &memReqs); // get the proper size requirement
622
623 if (size > memReqs.memoryRequirements.size)
624 {
625 std::ostringstream errorMsg;
626 errorMsg << "Requied memory size (" << memReqs.memoryRequirements.size << " bytes) smaller than the buffer's size (" << size << " bytes)!";
627 return tcu::TestStatus::fail(errorMsg.str());
628 }
629
630 // Allocate the memory
631 {
632 VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
633 VkDeviceMemory rawMemory = DE_NULL;
634
635 vk::VkMemoryDedicatedAllocateInfo
636 dedicatedInfo =
637 {
638 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, // VkStructureType sType
639 DE_NULL, // const void* pNext
640 DE_NULL, // VkImage image
641 *buffer // VkBuffer buffer
642 };
643
644 VkMemoryAllocateInfo memoryAllocateInfo =
645 {
646 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
647 &dedicatedInfo, // const void* pNext
648 memReqs.memoryRequirements.size, // VkDeviceSize allocationSize
649 heapTypeIndex, // deUint32 memoryTypeIndex
650 };
651
652 result = vk.allocateMemory(vkDevice, &memoryAllocateInfo, (VkAllocationCallbacks*)DE_NULL, &rawMemory);
653
654 if (result != VK_SUCCESS)
655 {
656 size = deAlign64(size >> shrinkBits, memReqs.memoryRequirements.alignment);
657
658 if (size == 0 || memReqs.memoryRequirements.size == memReqs.memoryRequirements.alignment)
659 return tcu::TestStatus::fail("Unable to allocate " + de::toString(memReqs.memoryRequirements.size) + " bytes of memory");
660
661 continue; // didn't work, try with a smaller allocation (and a smaller buffer)
662 }
663
664 memory = Move<VkDeviceMemory>(check<VkDeviceMemory>(rawMemory), Deleter<VkDeviceMemory>(vk, vkDevice, DE_NULL));
665 }
666 } // while
667
668 if (vk.bindBufferMemory(vkDevice, *buffer, *memory, 0) != VK_SUCCESS)
669 return tcu::TestStatus::fail("Bind buffer memory failed! (requested memory size: " + de::toString(size) + ")");
670
671 return tcu::TestStatus::pass("Pass");
672 }
673
getBufferUsageFlagsName(const VkBufferUsageFlags flags)674 std::string getBufferUsageFlagsName (const VkBufferUsageFlags flags)
675 {
676 switch (flags)
677 {
678 case VK_BUFFER_USAGE_TRANSFER_SRC_BIT: return "transfer_src";
679 case VK_BUFFER_USAGE_TRANSFER_DST_BIT: return "transfer_dst";
680 case VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT: return "uniform_texel";
681 case VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT: return "storage_texel";
682 case VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT: return "uniform";
683 case VK_BUFFER_USAGE_STORAGE_BUFFER_BIT: return "storage";
684 case VK_BUFFER_USAGE_INDEX_BUFFER_BIT: return "index";
685 case VK_BUFFER_USAGE_VERTEX_BUFFER_BIT: return "vertex";
686 case VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT: return "indirect";
687 default:
688 DE_FATAL("Unknown buffer usage flag");
689 return "";
690 }
691 }
692
getBufferCreateFlagsName(const VkBufferCreateFlags flags)693 std::string getBufferCreateFlagsName (const VkBufferCreateFlags flags)
694 {
695 std::ostringstream name;
696
697 if (flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)
698 name << "_binding";
699 if (flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT)
700 name << "_residency";
701 if (flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT)
702 name << "_aliased";
703 if (flags == 0u)
704 name << "_zero";
705
706 DE_ASSERT(!name.str().empty());
707
708 return name.str().substr(1);
709 }
710
711 // Create all VkBufferUsageFlags combinations recursively
createBufferUsageCases(tcu::TestCaseGroup & testGroup,const deUint32 firstNdx,const deUint32 bufferUsageFlags,const AllocationKind allocationKind)712 void createBufferUsageCases (tcu::TestCaseGroup& testGroup, const deUint32 firstNdx, const deUint32 bufferUsageFlags, const AllocationKind allocationKind)
713 {
714 const VkBufferUsageFlags bufferUsageModes[] =
715 {
716 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
717 VK_BUFFER_USAGE_TRANSFER_DST_BIT,
718 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
719 VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
720 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
721 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
722 VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
723 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
724 VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
725 };
726
727 tcu::TestContext& testCtx = testGroup.getTestContext();
728
729 // Add test groups
730 for (deUint32 currentNdx = firstNdx; currentNdx < DE_LENGTH_OF_ARRAY(bufferUsageModes); currentNdx++)
731 {
732 const deUint32 newBufferUsageFlags = bufferUsageFlags | bufferUsageModes[currentNdx];
733 const std::string newGroupName = getBufferUsageFlagsName(bufferUsageModes[currentNdx]);
734 de::MovePtr<tcu::TestCaseGroup> newTestGroup (new tcu::TestCaseGroup(testCtx, newGroupName.c_str(), ""));
735
736 createBufferUsageCases(*newTestGroup, currentNdx + 1u, newBufferUsageFlags, allocationKind);
737 testGroup.addChild(newTestGroup.release());
738 }
739
740 // Add test cases
741 if (bufferUsageFlags != 0u)
742 {
743 // \note SPARSE_RESIDENCY and SPARSE_ALIASED have to be used together with the SPARSE_BINDING flag.
744 const VkBufferCreateFlags bufferCreateFlags[] =
745 {
746 0,
747 VK_BUFFER_CREATE_SPARSE_BINDING_BIT,
748 VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT,
749 VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
750 VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT,
751 };
752
753 // Dedicated allocation does not support sparse feature
754 const int numBufferCreateFlags = (allocationKind == ALLOCATION_KIND_SUBALLOCATED) ? DE_LENGTH_OF_ARRAY(bufferCreateFlags) : 1;
755
756 de::MovePtr<tcu::TestCaseGroup> newTestGroup (new tcu::TestCaseGroup(testCtx, "create", ""));
757
758 for (int bufferCreateFlagsNdx = 0; bufferCreateFlagsNdx < numBufferCreateFlags; bufferCreateFlagsNdx++)
759 {
760 const BufferCaseParameters testParams =
761 {
762 bufferUsageFlags,
763 bufferCreateFlags[bufferCreateFlagsNdx],
764 VK_SHARING_MODE_EXCLUSIVE
765 };
766
767 const std::string allocStr = (allocationKind == ALLOCATION_KIND_SUBALLOCATED) ? "suballocation of " : "dedicated alloc. of ";
768 const std::string caseName = getBufferCreateFlagsName(bufferCreateFlags[bufferCreateFlagsNdx]);
769 const std::string caseDesc = "vkCreateBuffer test: " + allocStr + de::toString(bufferUsageFlags) + " " + de::toString(testParams.flags);
770
771 switch (allocationKind)
772 {
773 case ALLOCATION_KIND_SUBALLOCATED:
774 newTestGroup->addChild(new BuffersTestCase(testCtx, caseName.c_str(), caseDesc.c_str(), testParams));
775 break;
776 case ALLOCATION_KIND_DEDICATED:
777 newTestGroup->addChild(new DedicatedAllocationBuffersTestCase(testCtx, caseName.c_str(), caseDesc.c_str(), testParams));
778 break;
779 default:
780 DE_FATAL("Unknown test type");
781 }
782 }
783 testGroup.addChild(newTestGroup.release());
784 }
785 }
786
787 } // anonymous
788
createBufferTests(tcu::TestContext & testCtx)789 tcu::TestCaseGroup* createBufferTests (tcu::TestContext& testCtx)
790 {
791 de::MovePtr<tcu::TestCaseGroup> buffersTests (new tcu::TestCaseGroup(testCtx, "buffer", "Buffer Tests"));
792
793 {
794 de::MovePtr<tcu::TestCaseGroup> regularAllocation (new tcu::TestCaseGroup(testCtx, "suballocation", "Regular suballocation of memory."));
795 createBufferUsageCases(*regularAllocation, 0u, 0u, ALLOCATION_KIND_SUBALLOCATED);
796 buffersTests->addChild(regularAllocation.release());
797 }
798
799 {
800 de::MovePtr<tcu::TestCaseGroup> dedicatedAllocation (new tcu::TestCaseGroup(testCtx, "dedicated_alloc", "Dedicated allocation of memory."));
801 createBufferUsageCases(*dedicatedAllocation, 0u, 0u, ALLOCATION_KIND_DEDICATED);
802 buffersTests->addChild(dedicatedAllocation.release());
803 }
804
805 return buffersTests.release();
806 }
807
808 } // api
809 } // vk
810