diff --git a/src/Tests.cpp b/src/Tests.cpp index 2071fa5..b8bad1f 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -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(); diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 08e2e62..31ca0b9 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -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) {