Skip to content

Commit efa88c4

Browse files
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.
1 parent a900b56 commit efa88c4

2 files changed

Lines changed: 211 additions & 1 deletion

File tree

src/Tests.cpp

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3870,6 +3870,202 @@ static inline bool MemoryRegionsOverlap(char* ptr1, size_t size1, char* ptr2, si
38703870
return true;
38713871
}
38723872

3873+
static void TestMemoryUsage()
3874+
{
3875+
wprintf(L"Testing memory usage:\n");
3876+
3877+
static const VmaMemoryUsage lastUsage = VMA_MEMORY_USAGE_CPU_COPY;
3878+
for(uint32_t usage = 0; usage <= lastUsage; ++usage)
3879+
{
3880+
switch(usage)
3881+
{
3882+
case VMA_MEMORY_USAGE_UNKNOWN: printf(" VMA_MEMORY_USAGE_UNKNOWN:\n"); break;
3883+
case VMA_MEMORY_USAGE_GPU_ONLY: printf(" VMA_MEMORY_USAGE_GPU_ONLY:\n"); break;
3884+
case VMA_MEMORY_USAGE_CPU_ONLY: printf(" VMA_MEMORY_USAGE_CPU_ONLY:\n"); break;
3885+
case VMA_MEMORY_USAGE_CPU_TO_GPU: printf(" VMA_MEMORY_USAGE_CPU_TO_GPU:\n"); break;
3886+
case VMA_MEMORY_USAGE_GPU_TO_CPU: printf(" VMA_MEMORY_USAGE_GPU_TO_CPU:\n"); break;
3887+
case VMA_MEMORY_USAGE_CPU_COPY: printf(" VMA_MEMORY_USAGE_CPU_COPY:\n"); break;
3888+
default: assert(0);
3889+
}
3890+
3891+
auto printResult = [](const char* testName, VkResult res, uint32_t memoryTypeBits, uint32_t memoryTypeIndex)
3892+
{
3893+
if(res == VK_SUCCESS)
3894+
printf(" %s: memoryTypeBits=0x%X, memoryTypeIndex=%u\n", testName, memoryTypeBits, memoryTypeIndex);
3895+
else
3896+
printf(" %s: memoryTypeBits=0x%X, FAILED with res=%d\n", testName, memoryTypeBits, (int32_t)res);
3897+
};
3898+
3899+
// 1: Buffer for copy
3900+
{
3901+
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3902+
bufCreateInfo.size = 65536;
3903+
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
3904+
3905+
VkBuffer buf = VK_NULL_HANDLE;
3906+
VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
3907+
TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
3908+
3909+
VkMemoryRequirements memReq = {};
3910+
vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
3911+
3912+
VmaAllocationCreateInfo allocCreateInfo = {};
3913+
allocCreateInfo.usage = (VmaMemoryUsage)usage;
3914+
VmaAllocation alloc = VK_NULL_HANDLE;
3915+
VmaAllocationInfo allocInfo = {};
3916+
res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
3917+
if(res == VK_SUCCESS)
3918+
{
3919+
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
3920+
res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
3921+
TEST(res == VK_SUCCESS);
3922+
}
3923+
printResult("Buffer TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
3924+
vmaDestroyBuffer(g_hAllocator, buf, alloc);
3925+
}
3926+
3927+
// 2: Vertex buffer
3928+
{
3929+
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
3930+
bufCreateInfo.size = 65536;
3931+
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
3932+
3933+
VkBuffer buf = VK_NULL_HANDLE;
3934+
VkResult res = vkCreateBuffer(g_hDevice, &bufCreateInfo, g_Allocs, &buf);
3935+
TEST(res == VK_SUCCESS && buf != VK_NULL_HANDLE);
3936+
3937+
VkMemoryRequirements memReq = {};
3938+
vkGetBufferMemoryRequirements(g_hDevice, buf, &memReq);
3939+
3940+
VmaAllocationCreateInfo allocCreateInfo = {};
3941+
allocCreateInfo.usage = (VmaMemoryUsage)usage;
3942+
VmaAllocation alloc = VK_NULL_HANDLE;
3943+
VmaAllocationInfo allocInfo = {};
3944+
res = vmaAllocateMemoryForBuffer(g_hAllocator, buf, &allocCreateInfo, &alloc, &allocInfo);
3945+
if(res == VK_SUCCESS)
3946+
{
3947+
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
3948+
res = vkBindBufferMemory(g_hDevice, buf, allocInfo.deviceMemory, allocInfo.offset);
3949+
TEST(res == VK_SUCCESS);
3950+
}
3951+
printResult("Buffer TRANSFER_DST + VERTEX_BUFFER", res, memReq.memoryTypeBits, allocInfo.memoryType);
3952+
vmaDestroyBuffer(g_hAllocator, buf, alloc);
3953+
}
3954+
3955+
// 3: Image for copy, OPTIMAL
3956+
{
3957+
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
3958+
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
3959+
imgCreateInfo.extent.width = 256;
3960+
imgCreateInfo.extent.height = 256;
3961+
imgCreateInfo.extent.depth = 1;
3962+
imgCreateInfo.mipLevels = 1;
3963+
imgCreateInfo.arrayLayers = 1;
3964+
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
3965+
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
3966+
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
3967+
imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
3968+
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
3969+
3970+
VkImage img = VK_NULL_HANDLE;
3971+
VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
3972+
TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
3973+
3974+
VkMemoryRequirements memReq = {};
3975+
vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
3976+
3977+
VmaAllocationCreateInfo allocCreateInfo = {};
3978+
allocCreateInfo.usage = (VmaMemoryUsage)usage;
3979+
VmaAllocation alloc = VK_NULL_HANDLE;
3980+
VmaAllocationInfo allocInfo = {};
3981+
res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
3982+
if(res == VK_SUCCESS)
3983+
{
3984+
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
3985+
res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
3986+
TEST(res == VK_SUCCESS);
3987+
}
3988+
printResult("Image OPTIMAL TRANSFER_DST + TRANSFER_SRC", res, memReq.memoryTypeBits, allocInfo.memoryType);
3989+
3990+
vmaDestroyImage(g_hAllocator, img, alloc);
3991+
}
3992+
3993+
// 4: Image SAMPLED, OPTIMAL
3994+
{
3995+
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
3996+
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
3997+
imgCreateInfo.extent.width = 256;
3998+
imgCreateInfo.extent.height = 256;
3999+
imgCreateInfo.extent.depth = 1;
4000+
imgCreateInfo.mipLevels = 1;
4001+
imgCreateInfo.arrayLayers = 1;
4002+
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4003+
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4004+
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4005+
imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
4006+
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4007+
4008+
VkImage img = VK_NULL_HANDLE;
4009+
VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4010+
TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4011+
4012+
VkMemoryRequirements memReq = {};
4013+
vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4014+
4015+
VmaAllocationCreateInfo allocCreateInfo = {};
4016+
allocCreateInfo.usage = (VmaMemoryUsage)usage;
4017+
VmaAllocation alloc = VK_NULL_HANDLE;
4018+
VmaAllocationInfo allocInfo = {};
4019+
res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4020+
if(res == VK_SUCCESS)
4021+
{
4022+
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4023+
res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4024+
TEST(res == VK_SUCCESS);
4025+
}
4026+
printResult("Image OPTIMAL TRANSFER_DST + SAMPLED", res, memReq.memoryTypeBits, allocInfo.memoryType);
4027+
vmaDestroyImage(g_hAllocator, img, alloc);
4028+
}
4029+
4030+
// 5: Image COLOR_ATTACHMENT, OPTIMAL
4031+
{
4032+
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
4033+
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
4034+
imgCreateInfo.extent.width = 256;
4035+
imgCreateInfo.extent.height = 256;
4036+
imgCreateInfo.extent.depth = 1;
4037+
imgCreateInfo.mipLevels = 1;
4038+
imgCreateInfo.arrayLayers = 1;
4039+
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
4040+
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4041+
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4042+
imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
4043+
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
4044+
4045+
VkImage img = VK_NULL_HANDLE;
4046+
VkResult res = vkCreateImage(g_hDevice, &imgCreateInfo, g_Allocs, &img);
4047+
TEST(res == VK_SUCCESS && img != VK_NULL_HANDLE);
4048+
4049+
VkMemoryRequirements memReq = {};
4050+
vkGetImageMemoryRequirements(g_hDevice, img, &memReq);
4051+
4052+
VmaAllocationCreateInfo allocCreateInfo = {};
4053+
allocCreateInfo.usage = (VmaMemoryUsage)usage;
4054+
VmaAllocation alloc = VK_NULL_HANDLE;
4055+
VmaAllocationInfo allocInfo = {};
4056+
res = vmaAllocateMemoryForImage(g_hAllocator, img, &allocCreateInfo, &alloc, &allocInfo);
4057+
if(res == VK_SUCCESS)
4058+
{
4059+
TEST((memReq.memoryTypeBits & (1u << allocInfo.memoryType)) != 0);
4060+
res = vkBindImageMemory(g_hDevice, img, allocInfo.deviceMemory, allocInfo.offset);
4061+
TEST(res == VK_SUCCESS);
4062+
}
4063+
printResult("Image OPTIMAL SAMPLED + COLOR_ATTACHMENT", res, memReq.memoryTypeBits, allocInfo.memoryType);
4064+
vmaDestroyImage(g_hAllocator, img, alloc);
4065+
}
4066+
}
4067+
}
4068+
38734069
static void TestBudget()
38744070
{
38754071
wprintf(L"Testing budget...\n");
@@ -5200,6 +5396,7 @@ void Test()
52005396
#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
52015397
TestAllocationsInitialization();
52025398
#endif
5399+
TestMemoryUsage();
52035400
TestBudget();
52045401
TestMapping();
52055402
TestDeviceLocalMapped();

src/vk_mem_alloc.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,13 @@ typedef enum VmaMemoryUsage
22782278
- 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.
22792279
*/
22802280
VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2281+
/** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
2282+
2283+
Usage: Staging copy of resources moved from GPU memory to CPU memory as part
2284+
of custom paging/residency mechanism, to be moved back to GPU memory when needed.
2285+
*/
2286+
VMA_MEMORY_USAGE_CPU_COPY = 5,
2287+
22812288
VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
22822289
} VmaMemoryUsage;
22832290

@@ -16491,6 +16498,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1649116498

1649216499
uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
1649316500
uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
16501+
uint32_t notPreferredFlags = 0;
1649416502

1649516503
// Convert usage to requiredFlags and preferredFlags.
1649616504
switch(pAllocationCreateInfo->usage)
@@ -16517,7 +16525,11 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1651716525
requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
1651816526
preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
1651916527
break;
16528+
case VMA_MEMORY_USAGE_CPU_COPY:
16529+
notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
16530+
break;
1652016531
default:
16532+
VMA_ASSERT(0);
1652116533
break;
1652216534
}
1652316535

@@ -16536,7 +16548,8 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1653616548
if((requiredFlags & ~currFlags) == 0)
1653716549
{
1653816550
// Calculate cost as number of bits from preferredFlags not present in this memory type.
16539-
uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
16551+
uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
16552+
VmaCountBitsSet(currFlags & notPreferredFlags);
1654016553
// Remember memory type with lowest cost.
1654116554
if(currCost < minCost)
1654216555
{

0 commit comments

Comments
 (0)