Add VMA_MEMORY_USAGE_CPU_COPY for memory that is preferably not DEVICE_LOCAL but not guaranteed to be HOST_VISIBLE

Also added test for memory types and usages.
This commit is contained in:
Adam Sawicki 2019-11-18 16:33:56 +01:00
parent a900b56aed
commit efa88c4de0
2 changed files with 211 additions and 1 deletions

View File

@ -3870,6 +3870,202 @@ static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, si
return true;
}
static void TestMemoryUsage()
{
wprintf(L"Testing memory usage:\n");
static const VmaMemoryUsage lastUsage = VMA_MEMORY_USAGE_CPU_COPY;
for(uint32_t usage = 0; usage <= lastUsage; ++usage)
{
switch(usage)
{
case VMA_MEMORY_USAGE_UNKNOWN: printf(" VMA_MEMORY_USAGE_UNKNOWN:\n"); break;
case VMA_MEMORY_USAGE_GPU_ONLY: printf(" VMA_MEMORY_USAGE_GPU_ONLY:\n"); break;
case VMA_MEMORY_USAGE_CPU_ONLY: printf(" VMA_MEMORY_USAGE_CPU_ONLY:\n"); break;
case VMA_MEMORY_USAGE_CPU_TO_GPU: printf(" VMA_MEMORY_USAGE_CPU_TO_GPU:\n"); break;
case VMA_MEMORY_USAGE_GPU_TO_CPU: printf(" VMA_MEMORY_USAGE_GPU_TO_CPU:\n"); break;
case VMA_MEMORY_USAGE_CPU_COPY: printf(" VMA_MEMORY_USAGE_CPU_COPY:\n"); break;
default: assert(0);
}
auto printResult = [](const char* testName, VkResult res, uint32_t memoryTypeBits, uint32_t memoryTypeIndex)
{
if(res == VK_SUCCESS)
printf(" %s: memoryTypeBits=0x%X, memoryTypeIndex=%u\n", testName, memoryTypeBits, memoryTypeIndex);
else
printf(" %s: memoryTypeBits=0x%X, FAILED with res=%d\n", testName, memoryTypeBits, (int32_t)res);
};
// 1: Buffer for copy
{
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = 65536;
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VkBuffer buf = VK_NULL_HANDLE;
VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
VkMemoryRequirements memReq = {};
vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = (VmaMemoryUsage)usage;
VmaAllocation alloc = VK_NULL_HANDLE;
VmaAllocationInfo allocInfo = {};
res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
if(res == VK_SUCCESS)
{
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
TEST(res == VK_SUCCESS);
}
printResult("Buffer TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
vmaDestroyBuffer(g_hAllocator, buf, alloc);
}
// 2: Vertex buffer
{
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = 65536;
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
VkBuffer buf = VK_NULL_HANDLE;
VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
VkMemoryRequirements memReq = {};
vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = (VmaMemoryUsage)usage;
VmaAllocation alloc = VK_NULL_HANDLE;
VmaAllocationInfo allocInfo = {};
res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
if(res == VK_SUCCESS)
{
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
TEST(res == VK_SUCCESS);
}
printResult("Buffer TRANSFER_DST + VERTEX_BUFFER", res, memReq.memoryTypeBits, allocInfo.memoryType);
vmaDestroyBuffer(g_hAllocator, buf, alloc);
}
// 3: Image for copy, OPTIMAL
{
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imgCreateInfo.extent.width = 256;
imgCreateInfo.extent.height = 256;
imgCreateInfo.extent.depth = 1;
imgCreateInfo.mipLevels = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VkImage img = VK_NULL_HANDLE;
VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
VkMemoryRequirements memReq = {};
vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = (VmaMemoryUsage)usage;
VmaAllocation alloc = VK_NULL_HANDLE;
VmaAllocationInfo allocInfo = {};
res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
if(res == VK_SUCCESS)
{
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
TEST(res == VK_SUCCESS);
}
printResult("Image OPTIMAL TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
vmaDestroyImage(g_hAllocator, img, alloc);
}
// 4: Image SAMPLED, OPTIMAL
{
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imgCreateInfo.extent.width = 256;
imgCreateInfo.extent.height = 256;
imgCreateInfo.extent.depth = 1;
imgCreateInfo.mipLevels = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VkImage img = VK_NULL_HANDLE;
VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
VkMemoryRequirements memReq = {};
vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = (VmaMemoryUsage)usage;
VmaAllocation alloc = VK_NULL_HANDLE;
VmaAllocationInfo allocInfo = {};
res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
if(res == VK_SUCCESS)
{
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
TEST(res == VK_SUCCESS);
}
printResult("Image OPTIMAL TRANSFER_DST + SAMPLED", res, memReq.memoryTypeBits, allocInfo.memoryType);
vmaDestroyImage(g_hAllocator, img, alloc);
}
// 5: Image COLOR_ATTACHMENT, OPTIMAL
{
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imgCreateInfo.extent.width = 256;
imgCreateInfo.extent.height = 256;
imgCreateInfo.extent.depth = 1;
imgCreateInfo.mipLevels = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VkImage img = VK_NULL_HANDLE;
VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
VkMemoryRequirements memReq = {};
vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = (VmaMemoryUsage)usage;
VmaAllocation alloc = VK_NULL_HANDLE;
VmaAllocationInfo allocInfo = {};
res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
if(res == VK_SUCCESS)
{
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
TEST(res == VK_SUCCESS);
}
printResult("Image OPTIMAL SAMPLED + COLOR_ATTACHMENT", res, memReq.memoryTypeBits, allocInfo.memoryType);
vmaDestroyImage(g_hAllocator, img, alloc);
}
}
}
static void TestBudget()
{
wprintf(L"Testing budget...\n");
@ -5200,6 +5396,7 @@ void Test()
#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
TestAllocationsInitialization();
#endif
TestMemoryUsage();
TestBudget();
TestMapping();
TestDeviceLocalMapped();

View File

@ -2278,6 +2278,13 @@ typedef enum VmaMemoryUsage
- Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
*/
VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
/** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
Usage: Staging copy of resources moved from GPU memory to CPU memory as part
of custom paging/residency mechanism, to be moved back to GPU memory when needed.
*/
VMA_MEMORY_USAGE_CPU_COPY = 5,
VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
} VmaMemoryUsage;
@ -16491,6 +16498,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
uint32_t notPreferredFlags = 0;
// Convert usage to requiredFlags and preferredFlags.
switch(pAllocationCreateInfo->usage)
@ -16517,7 +16525,11 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
break;
case VMA_MEMORY_USAGE_CPU_COPY:
notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
break;
default:
VMA_ASSERT(0);
break;
}
@ -16536,7 +16548,8 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
if((requiredFlags & ~currFlags) == 0)
{
// Calculate cost as number of bits from preferredFlags not present in this memory type.
uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
VmaCountBitsSet(currFlags & notPreferredFlags);
// Remember memory type with lowest cost.
if(currCost < minCost)
{