diff --git a/README.md b/README.md index f82caa9..ebc9a23 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Easy to integrate Vulkan memory allocation library. Memory allocation and resource (buffer and image) creation in Vulkan is difficult (comparing to older graphics API-s, like D3D11 or OpenGL) for several reasons: - It requires a lot of boilerplate code, just like everything else in Vulkan, because it is a low-level and high-performance API. -- There is additional level of indirection: `VkDeviceMemory` is allocated separately from creating `VkBuffer`/`VkImage` and they must be bound together. The binding cannot be changed later - resource must be recreated. +- There is additional level of indirection: `VkDeviceMemory` is allocated separately from creating `VkBuffer`/`VkImage` and they must be bound together. - Driver must be queried for supported memory heaps and memory types. Different IHVs provide different types of it. - It is recommended practice to allocate bigger chunks of memory and assign parts of them to particular resources. @@ -41,7 +41,7 @@ Additional features: - Configuration: Fill optional members of CreateInfo structure to provide custom CPU memory allocator, pointers to Vulkan functions and other parameters. - Customization: Predefine appropriate macros to provide your own implementation of all external facilities used by the library, from assert, mutex, and atomic, to vector and linked list. - Support for memory mapping, reference-counted internally. Support for persistently mapped memory: Just allocate with appropriate flag and you get access to mapped pointer. -- Support for non-coherent memory. Functions that flush/invalidate memory. nonCoherentAtomSize is respected automatically. +- Support for non-coherent memory. Functions that flush/invalidate memory. `nonCoherentAtomSize` is respected automatically. - Custom memory pools: Create a pool with desired parameters (e.g. fixed or limited maximum size) and allocate memory out of it. - Linear allocator: Create a pool with linear algorithm and use it for much faster allocations and deallocations in free-at-once, stack, double stack, or ring buffer fashion. - Support for VK_KHR_dedicated_allocation extension: Just enable it and it will be used automatically by the library. @@ -60,7 +60,7 @@ Additional features: - Public interface in C, in same convention as Vulkan API. Implementation in C++. - Error handling implemented by returning `VkResult` error codes - same way as in Vulkan. - Interface documented using Doxygen-style comments. -- Platform-independent, but developed and tested on Windows using Visual Studio. Continuous integration setup for Windows and Linux. Tested also on Android and MacOS. +- Platform-independent, but developed and tested on Windows using Visual Studio. Continuous integration setup for Windows and Linux. Tested also on Android, MacOS, and other platforms. # Example @@ -94,12 +94,16 @@ See **[Documentation](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAll # Software using this library - **[Filament](https://github.com/google/filament)** - physically based rendering engine for Android, Windows, Linux and macOS, from Google. Apache License 2.0. +- **[Skia](https://github.com/google/skia)** - complete 2D graphic library for drawing Text, Geometries, and Images, from Google. +- **[PowerVR SDK](https://github.com/powervr-graphics/Native_SDK)** - C++ cross-platform 3D graphics SDK, from Imagination. License: MIT. - **[Anvil](https://github.com/GPUOpen-LibrariesAndSDKs/Anvil)** - cross-platform framework for Vulkan. License: MIT. - **[vkDOOM3](https://github.com/DustinHLand/vkDOOM3)** - Vulkan port of GPL DOOM 3 BFG Edition. License: GNU GPL. - **[Lightweight Java Game Library (LWJGL)](https://www.lwjgl.org/)** - includes binding of the library for Java. License: BSD. - **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0. - **[VK9](https://github.com/disks86/VK9)** - Direct3D 9 compatibility layer using Vulkan. Zlib lincese. +[Many other projects on GitHub](https://github.com/search?q=AMD_VULKAN_MEMORY_ALLOCATOR_H&type=Code) and some game development studios that use Vulkan in their games. + # See also - **[Awesome Vulkan](https://github.com/vinjn/awesome-vulkan)** - a curated list of awesome Vulkan libraries, debuggers and resources. diff --git a/docs/Recording file format.md b/docs/Recording file format.md index b01eaf4..875c638 100644 --- a/docs/Recording file format.md +++ b/docs/Recording file format.md @@ -23,7 +23,7 @@ Formats with only minor version incremented are backward compatible. VmaReplay application supports all older versions. Current version is: - 1,3 + 1,4 # Configuration @@ -204,6 +204,11 @@ No parameters. - pool : pointer +**vmaResizeAllocation** (min format version: 1.4) + +- allocation : pointer +- newSize : uint64 + # Data types **bool** @@ -228,7 +233,7 @@ It should not contain end-of-line characters - results are then undefined. # Example file Vulkan Memory Allocator,Calls recording - 1,3 + 1,4 Config,Begin PhysicalDevice,apiVersion,4198477 PhysicalDevice,driverVersion,8388653 @@ -284,4 +289,4 @@ It should not contain end-of-line characters - results are then undefined. 12552,0.695,0,vmaDestroyImage,000001D85B8B1620 12552,0.695,0,vmaDestroyBuffer,000001D85B8B16C0 12552,0.695,0,vmaDestroyBuffer,000001D85B8B1A80 - 12552,0.695,0,vmaDestroyAllocator \ No newline at end of file + 12552,0.695,0,vmaDestroyAllocator diff --git a/docs/html/general_considerations.html b/docs/html/general_considerations.html index b3e7dea..3dc7017 100644 --- a/docs/html/general_considerations.html +++ b/docs/html/general_considerations.html @@ -110,9 +110,12 @@ Allocation algorithm Features not supported

Features deliberately excluded from the scope of this library:

diff --git a/docs/html/globals.html b/docs/html/globals.html index 5b2d293..ac78f54 100644 --- a/docs/html/globals.html +++ b/docs/html/globals.html @@ -322,7 +322,7 @@ $(function() { : vk_mem_alloc.h
  • VmaPoolCreateFlagBits -: vk_mem_alloc.h +: vk_mem_alloc.h
  • VmaPoolCreateFlags : vk_mem_alloc.h @@ -342,6 +342,9 @@ $(function() {
  • VmaRecordSettings : vk_mem_alloc.h
  • +
  • vmaResizeAllocation() +: vk_mem_alloc.h +
  • vmaSetAllocationUserData() : vk_mem_alloc.h
  • diff --git a/docs/html/globals_func.html b/docs/html/globals_func.html index 36c7782..203a2c4 100644 --- a/docs/html/globals_func.html +++ b/docs/html/globals_func.html @@ -169,6 +169,9 @@ $(function() {
  • vmaMapMemory() : vk_mem_alloc.h
  • +
  • vmaResizeAllocation() +: vk_mem_alloc.h +
  • vmaSetAllocationUserData() : vk_mem_alloc.h
  • diff --git a/docs/html/memory_mapping.html b/docs/html/memory_mapping.html index e464c14..c584e46 100644 --- a/docs/html/memory_mapping.html +++ b/docs/html/memory_mapping.html @@ -82,6 +82,7 @@ Persistently mapped memory
    VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
    bufCreateInfo.size = sizeof(ConstantBuffer);
    bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
    VmaAllocationCreateInfo allocCreateInfo = {};
    allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
    allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
    VkBuffer buf;
    VmaAllocation alloc;
    VmaAllocationInfo allocInfo;
    vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
    // Buffer is already mapped. You can access its memory.
    memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));

    There are some exceptions though, when you should consider mapping memory only for a short period of time:

    diff --git a/docs/html/quick_start.html b/docs/html/quick_start.html index bc21235..628fd09 100644 --- a/docs/html/quick_start.html +++ b/docs/html/quick_start.html @@ -79,6 +79,7 @@ Project setup

  • In exacly one CPP file define following macro before this include. It enables also internal definitions.
  • #define VMA_IMPLEMENTATION
    #include "vk_mem_alloc.h"

    It may be a good idea to create dedicated CPP file just for this purpose.

    +

    Note on language: This library is written in C++, but has C-compatible interface. Thus you can include and use vk_mem_alloc.h in C or C++ code, but full implementation with VMA_IMPLEMENTATION macro must be compiled as C++, NOT as C.

    Please note that this library includes header <vulkan/vulkan.h>, which in turn includes <windows.h> on Windows. If you need some specific macros defined before including these headers (like WIN32_LEAN_AND_MEAN or WINVER for Windows, VK_USE_PLATFORM_WIN32_KHR for Vulkan), you must define them before every #include of this library.

    Initialization

    diff --git a/docs/html/search/all_10.js b/docs/html/search/all_10.js index fea5d80..4a2fb02 100644 --- a/docs/html/search/all_10.js +++ b/docs/html/search/all_10.js @@ -109,6 +109,7 @@ var searchData= ['vmarecordflagbits',['VmaRecordFlagBits',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2',1,'VmaRecordFlagBits(): vk_mem_alloc.h'],['../vk__mem__alloc_8h.html#ade20b626a6635ce1bf30ea53dea774e4',1,'VmaRecordFlagBits(): vk_mem_alloc.h']]], ['vmarecordflags',['VmaRecordFlags',['../vk__mem__alloc_8h.html#af3929a1a4547c592fc0b0e55ef452828',1,'vk_mem_alloc.h']]], ['vmarecordsettings',['VmaRecordSettings',['../struct_vma_record_settings.html',1,'VmaRecordSettings'],['../vk__mem__alloc_8h.html#a0ab61e87ff6365f1d59915eadc37a9f0',1,'VmaRecordSettings(): vk_mem_alloc.h']]], + ['vmaresizeallocation',['vmaResizeAllocation',['../vk__mem__alloc_8h.html#a0ff488958ca72b28e545880463cb8696',1,'vk_mem_alloc.h']]], ['vmasetallocationuserdata',['vmaSetAllocationUserData',['../vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f',1,'vk_mem_alloc.h']]], ['vmasetcurrentframeindex',['vmaSetCurrentFrameIndex',['../vk__mem__alloc_8h.html#ade56bf8dc9f5a5eaddf5f119ed525236',1,'vk_mem_alloc.h']]], ['vmastatinfo',['VmaStatInfo',['../struct_vma_stat_info.html',1,'VmaStatInfo'],['../vk__mem__alloc_8h.html#a810b009a788ee8aac72a25b42ffbe31c',1,'VmaStatInfo(): vk_mem_alloc.h']]], diff --git a/docs/html/search/functions_0.js b/docs/html/search/functions_0.js index 53a1d34..e812f3d 100644 --- a/docs/html/search/functions_0.js +++ b/docs/html/search/functions_0.js @@ -35,6 +35,7 @@ var searchData= ['vmainvalidateallocation',['vmaInvalidateAllocation',['../vk__mem__alloc_8h.html#a0d0eb0c1102268fa9a476d12ecbe4006',1,'vk_mem_alloc.h']]], ['vmamakepoolallocationslost',['vmaMakePoolAllocationsLost',['../vk__mem__alloc_8h.html#a736bd6cbda886f36c891727e73bd4024',1,'vk_mem_alloc.h']]], ['vmamapmemory',['vmaMapMemory',['../vk__mem__alloc_8h.html#ad5bd1243512d099706de88168992f069',1,'vk_mem_alloc.h']]], + ['vmaresizeallocation',['vmaResizeAllocation',['../vk__mem__alloc_8h.html#a0ff488958ca72b28e545880463cb8696',1,'vk_mem_alloc.h']]], ['vmasetallocationuserdata',['vmaSetAllocationUserData',['../vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f',1,'vk_mem_alloc.h']]], ['vmasetcurrentframeindex',['vmaSetCurrentFrameIndex',['../vk__mem__alloc_8h.html#ade56bf8dc9f5a5eaddf5f119ed525236',1,'vk_mem_alloc.h']]], ['vmatouchallocation',['vmaTouchAllocation',['../vk__mem__alloc_8h.html#a43d8ba9673c846f049089a5029d5c73a',1,'vk_mem_alloc.h']]], diff --git a/docs/html/search/variables_c.html b/docs/html/search/variables_c.html new file mode 100644 index 0000000..75709df --- /dev/null +++ b/docs/html/search/variables_c.html @@ -0,0 +1,30 @@ + + + + + + + + + +
    +
    Loading...
    +
    + +
    Searching...
    +
    No Matches
    + +
    + + diff --git a/docs/html/search/variables_c.js b/docs/html/search/variables_c.js new file mode 100644 index 0000000..e7b9e7f --- /dev/null +++ b/docs/html/search/variables_c.js @@ -0,0 +1,20 @@ +var searchData= +[ + ['vkallocatememory',['vkAllocateMemory',['../struct_vma_vulkan_functions.html#a2943bf99dfd784a0e8f599d987e22e6c',1,'VmaVulkanFunctions']]], + ['vkbindbuffermemory',['vkBindBufferMemory',['../struct_vma_vulkan_functions.html#a94fc4f3a605d9880bb3c0ba2c2fc80b2',1,'VmaVulkanFunctions']]], + ['vkbindimagememory',['vkBindImageMemory',['../struct_vma_vulkan_functions.html#a1338d96a128a5ade648b8d934907c637',1,'VmaVulkanFunctions']]], + ['vkcmdcopybuffer',['vkCmdCopyBuffer',['../struct_vma_vulkan_functions.html#ae5c0db8c89a3b82593dc16aa6a49fa3a',1,'VmaVulkanFunctions']]], + ['vkcreatebuffer',['vkCreateBuffer',['../struct_vma_vulkan_functions.html#ae8084315a25006271a2edfc3a447519f',1,'VmaVulkanFunctions']]], + ['vkcreateimage',['vkCreateImage',['../struct_vma_vulkan_functions.html#a23ebe70be515b9b5010a1d691200e325',1,'VmaVulkanFunctions']]], + ['vkdestroybuffer',['vkDestroyBuffer',['../struct_vma_vulkan_functions.html#a7e054606faddb07f0e8556f3ed317d45',1,'VmaVulkanFunctions']]], + ['vkdestroyimage',['vkDestroyImage',['../struct_vma_vulkan_functions.html#a90b898227039b1dcb3520f6e91f09ffa',1,'VmaVulkanFunctions']]], + ['vkflushmappedmemoryranges',['vkFlushMappedMemoryRanges',['../struct_vma_vulkan_functions.html#a33c322f4c4ad2810f8a9c97a277572f9',1,'VmaVulkanFunctions']]], + ['vkfreememory',['vkFreeMemory',['../struct_vma_vulkan_functions.html#a4c658701778564d62034255b5dda91b4',1,'VmaVulkanFunctions']]], + ['vkgetbuffermemoryrequirements',['vkGetBufferMemoryRequirements',['../struct_vma_vulkan_functions.html#a5b92901df89a4194b0d12f6071d4d143',1,'VmaVulkanFunctions']]], + ['vkgetimagememoryrequirements',['vkGetImageMemoryRequirements',['../struct_vma_vulkan_functions.html#a475f6f49f8debe4d10800592606d53f4',1,'VmaVulkanFunctions']]], + ['vkgetphysicaldevicememoryproperties',['vkGetPhysicalDeviceMemoryProperties',['../struct_vma_vulkan_functions.html#a60d25c33bba06bb8592e6875cbaa9830',1,'VmaVulkanFunctions']]], + ['vkgetphysicaldeviceproperties',['vkGetPhysicalDeviceProperties',['../struct_vma_vulkan_functions.html#a77b7a74082823e865dd6546623468f96',1,'VmaVulkanFunctions']]], + ['vkinvalidatemappedmemoryranges',['vkInvalidateMappedMemoryRanges',['../struct_vma_vulkan_functions.html#a5c1093bc32386a8060c37c9f282078a1',1,'VmaVulkanFunctions']]], + ['vkmapmemory',['vkMapMemory',['../struct_vma_vulkan_functions.html#ab5c1f38dea3a2cf00dc9eb4f57218c49',1,'VmaVulkanFunctions']]], + ['vkunmapmemory',['vkUnmapMemory',['../struct_vma_vulkan_functions.html#acc798589736f0becb317fc2196c1d8b9',1,'VmaVulkanFunctions']]] +]; diff --git a/docs/html/struct_vma_allocator_create_info.html b/docs/html/struct_vma_allocator_create_info.html index 25fd89f..564e562 100644 --- a/docs/html/struct_vma_allocator_create_info.html +++ b/docs/html/struct_vma_allocator_create_info.html @@ -216,7 +216,7 @@ Public Attributes
  • If user tries to allocate more memory from that heap using this allocator, the allocation fails with VK_ERROR_OUT_OF_DEVICE_MEMORY.
  • If the limit is smaller than heap size reported in VkMemoryHeap::size, the value of this limit will be reported instead when using vmaGetMemoryProperties().
  • -

    Warning! Using this feature may not be equivalent to installing a GPU with smaller amount of memory, because graphics driver doesn't necessary fail new allocations with VK_ERROR_OUT_OF_DEVICE_MEMORY result when memory capacity is exceeded. It may return success and just silently migrate some device memory blocks to system RAM.

    +

    Warning! Using this feature may not be equivalent to installing a GPU with smaller amount of memory, because graphics driver doesn't necessary fail new allocations with VK_ERROR_OUT_OF_DEVICE_MEMORY result when memory capacity is exceeded. It may return success and just silently migrate some device memory blocks to system RAM. This driver behavior can also be controlled using VK_AMD_memory_overallocation_behavior extension.

    diff --git a/src/Tests.cpp b/src/Tests.cpp index c8445f9..3a5c78b 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -1195,6 +1195,56 @@ static void TestUserData() } } +static void TestInvalidAllocations() +{ + VkResult res; + + VmaAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + + // Try to allocate 0 bytes. + { + VkMemoryRequirements memReq = {}; + memReq.size = 0; // !!! + memReq.alignment = 4; + memReq.memoryTypeBits = UINT32_MAX; + VmaAllocation alloc = VK_NULL_HANDLE; + res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr); + TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE); + } + + // Try to create buffer with size = 0. + { + VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufCreateInfo.size = 0; // !!! + VkBuffer buf = VK_NULL_HANDLE; + VmaAllocation alloc = VK_NULL_HANDLE; + res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); + TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE); + } + + // Try to create image with one dimension = 0. + { + VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM; + imageCreateInfo.extent.width = 128; + imageCreateInfo.extent.height = 0; // !!! + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; + imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + VkImage image = VK_NULL_HANDLE; + VmaAllocation alloc = VK_NULL_HANDLE; + res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr); + TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE); + } +} + static void TestMemoryRequirements() { VkResult res; @@ -1299,6 +1349,8 @@ static void TestBasics() } TestUserData(); + + TestInvalidAllocations(); } void TestHeapSizeLimit() @@ -2659,6 +2711,159 @@ static void TestPool_SameSize() vmaDestroyPool(g_hAllocator, pool); } +static void TestResize() +{ + wprintf(L"Testing vmaResizeAllocation...\n"); + + const VkDeviceSize KILOBYTE = 1024ull; + const VkDeviceSize MEGABYTE = KILOBYTE * 1024; + + VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufCreateInfo.size = 2 * MEGABYTE; + bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + + uint32_t memTypeIndex = UINT32_MAX; + TEST( vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &memTypeIndex) == VK_SUCCESS ); + + VmaPoolCreateInfo poolCreateInfo = {}; + poolCreateInfo.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT; + poolCreateInfo.blockSize = 8 * MEGABYTE; + poolCreateInfo.minBlockCount = 1; + poolCreateInfo.maxBlockCount = 1; + poolCreateInfo.memoryTypeIndex = memTypeIndex; + + VmaPool pool; + TEST( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) == VK_SUCCESS ); + + allocCreateInfo.pool = pool; + + // Fill 8 MB pool with 4 * 2 MB allocations. + VmaAllocation allocs[4] = {}; + + VkMemoryRequirements memReq = {}; + memReq.memoryTypeBits = UINT32_MAX; + memReq.alignment = 4; + memReq.size = bufCreateInfo.size; + + VmaAllocationInfo allocInfo = {}; + + for(uint32_t i = 0; i < 4; ++i) + { + TEST( vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &allocs[i], nullptr) == VK_SUCCESS ); + } + + // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 2MB + + // Case: Resize to the same size always succeeds. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 2 * MEGABYTE) == VK_SUCCESS); + vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo); + TEST(allocInfo.size == 2ull * 1024 * 1024); + } + + // Case: Shrink allocation at the end. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 1 * MEGABYTE) == VK_SUCCESS ); + vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo); + TEST(allocInfo.size == 1ull * 1024 * 1024); + } + + // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 1MB, free 1MB + + // Case: Shrink allocation before free space. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 512 * KILOBYTE) == VK_SUCCESS ); + vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo); + TEST(allocInfo.size == 512 * KILOBYTE); + } + + // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 0.5MB, free 1.5MB + + // Case: Shrink allocation before next allocation. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 1 * MEGABYTE) == VK_SUCCESS ); + vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo); + TEST(allocInfo.size == 1 * MEGABYTE); + } + + // Now it's: a0 1MB, free 1 MB, a1 2MB, a2 2MB, a3 0.5MB, free 1.5MB + + // Case: Grow allocation while there is even more space available. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 1 * MEGABYTE) == VK_SUCCESS ); + vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo); + TEST(allocInfo.size == 1 * MEGABYTE); + } + + // Now it's: a0 1MB, free 1 MB, a1 2MB, a2 2MB, a3 1MB, free 1MB + + // Case: Grow allocation while there is exact amount of free space available. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 2 * MEGABYTE) == VK_SUCCESS ); + vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo); + TEST(allocInfo.size == 2 * MEGABYTE); + } + + // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 1MB, free 1MB + + // Case: Fail to grow when there is not enough free space due to next allocation. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 3 * MEGABYTE) == VK_ERROR_OUT_OF_POOL_MEMORY ); + vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo); + TEST(allocInfo.size == 2 * MEGABYTE); + } + + // Case: Fail to grow when there is not enough free space due to end of memory block. + { + TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 3 * MEGABYTE) == VK_ERROR_OUT_OF_POOL_MEMORY ); + vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo); + TEST(allocInfo.size == 1 * MEGABYTE); + } + + for(uint32_t i = 4; i--; ) + { + vmaFreeMemory(g_hAllocator, allocs[i]); + } + + vmaDestroyPool(g_hAllocator, pool); + + // Test dedicated allocation + { + VmaAllocationCreateInfo dedicatedAllocCreateInfo = {}; + dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + + VmaAllocation dedicatedAlloc = VK_NULL_HANDLE; + TEST( vmaAllocateMemory(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, &dedicatedAlloc, nullptr) == VK_SUCCESS ); + + // Case: Resize to the same size always succeeds. + { + TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 2 * MEGABYTE) == VK_SUCCESS); + vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo); + TEST(allocInfo.size == 2ull * 1024 * 1024); + } + + // Case: Shrinking fails. + { + TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 1 * MEGABYTE) < VK_SUCCESS); + vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo); + TEST(allocInfo.size == 2ull * 1024 * 1024); + } + + // Case: Growing fails. + { + TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 3 * MEGABYTE) < VK_SUCCESS); + vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo); + TEST(allocInfo.size == 2ull * 1024 * 1024); + } + + vmaFreeMemory(g_hAllocator, dedicatedAlloc); + } +} + static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern) { const uint8_t* pBytes = (const uint8_t*)pMemory; @@ -4320,7 +4525,7 @@ void Test() // ######################################## // ######################################## - BasicTestBuddyAllocator(); + TestResize(); return; } @@ -4332,6 +4537,7 @@ void Test() #else TestPool_SameSize(); TestHeapSizeLimit(); + TestResize(); #endif #if VMA_DEBUG_INITIALIZE_ALLOCATIONS TestAllocationsInitialization(); diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp index c324245..515c1ce 100644 --- a/src/VmaReplay/Common.cpp +++ b/src/VmaReplay/Common.cpp @@ -12,6 +12,11 @@ bool LineSplit::GetNextLine(StrRange& out) while(currLineEnd < m_NumBytes && m_Data[currLineEnd] != '\n') ++currLineEnd; out.end = m_Data + currLineEnd; + // Ignore trailing '\r' to support Windows end of line. + if(out.end > out.beg && *(out.end - 1) == '\r') + { + --out.end; + } m_NextLineBeg = currLineEnd + 1; // Past '\n' ++m_NextLineIndex; return true; diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 9635b03..a9acfa5 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -82,6 +82,7 @@ enum class VMA_FUNCTION TouchAllocation, GetAllocationInfo, MakePoolAllocationsLost, + ResizeAllocation, Count }; static const char* VMA_FUNCTION_NAMES[] = { @@ -104,6 +105,7 @@ static const char* VMA_FUNCTION_NAMES[] = { "vmaTouchAllocation", "vmaGetAllocationInfo", "vmaMakePoolAllocationsLost", + "vmaResizeAllocation", }; static_assert( _countof(VMA_FUNCTION_NAMES) == (size_t)VMA_FUNCTION::Count, @@ -143,7 +145,7 @@ static size_t g_DumpStatsAfterLineNextIndex = 0; static bool ValidateFileVersion() { if(GetVersionMajor(g_FileVersion) == 1 && - GetVersionMinor(g_FileVersion) <= 3) + GetVersionMinor(g_FileVersion) <= 4) { return true; } @@ -1015,6 +1017,7 @@ private: void ExecuteTouchAllocation(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit); void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit); }; @@ -1156,6 +1159,8 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) ExecuteGetAllocationInfo(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaMakePoolAllocationsLost")) ExecuteMakePoolAllocationsLost(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaResizeAllocation")) + ExecuteResizeAllocation(lineNumber, csvSplit); else { if(IssueWarning()) @@ -2599,6 +2604,45 @@ void Player::ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& c } } +void Player::ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit) +{ + m_Stats.RegisterFunctionCall(VMA_FUNCTION::ResizeAllocation); + + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, false)) + { + uint64_t origPtr = 0; + uint64_t newSize = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), newSize)) + { + if(origPtr != 0) + { + const auto it = m_Allocations.find(origPtr); + if(it != m_Allocations.end()) + { + vmaResizeAllocation(m_Allocator, it->second.allocation, newSize); + UpdateMemStats(); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaResizeAllocation.\n", lineNumber); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Main functions diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp index 7c78469..0da5be2 100644 --- a/src/VulkanSample.cpp +++ b/src/VulkanSample.cpp @@ -1345,6 +1345,15 @@ static void InitializeApplication() allocatorInfo.pAllocationCallbacks = &cpuAllocationCallbacks; } + // Uncomment to enable recording to CSV file. + /* + { + VmaRecordSettings recordSettings = {}; + recordSettings.pFilePath = "VulkanSample.csv"; + allocatorInfo.pRecordSettings = &recordSettings; + } + */ + ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &g_hAllocator) ); // Retrieve queues (don't need to be destroyed). diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 119a63e..eebeb0f 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -124,6 +124,10 @@ To do it properly: It may be a good idea to create dedicated CPP file just for this purpose. +Note on language: This library is written in C++, but has C-compatible interface. +Thus you can include and use vk_mem_alloc.h in C or C++ code, but full +implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. + Please note that this library includes header ``, which in turn includes `` on Windows. If you need some specific macros defined before including these headers (like `WIN32_LEAN_AND_MEAN` or @@ -387,7 +391,9 @@ There are some exceptions though, when you should consider mapping memory only f for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this block is migrated by WDDM to system RAM, which degrades performance. It doesn't matter if that particular memory block is actually used by the command buffer - being submitted. + being submitted. +- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175) + which requires unmapping before GPU can see updated texture. - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools. \section memory_mapping_cache_control Cache control @@ -1444,7 +1450,8 @@ Features deliberately excluded from the scope of this library: - Support for sparse binding and sparse residency. You can still use these features (when supported by the device) with VMA. You just need to do it - yourself. Any explicit support for sparse binding/residency would rather + yourself. Allocate memory pages with vmaAllocateMemory(). + Any explicit support for sparse binding/residency would rather require another, higher-level library on top of VMA. - Data transfer - issuing commands that transfer data between buffers or images, any usage of `VkCommandList` or `VkQueue` and related synchronization is responsibility of the user. @@ -1452,6 +1459,18 @@ Features deliberately excluded from the scope of this library: explicit memory type index and dedicated allocation anyway, so they don't interact with main features of this library. Such special purpose allocations should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`. +- Recreation of buffers and images. Although the library has functions for + buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to + recreate these objects yourself after defragmentation. That's because the big + structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in + #VmaAllocation object. +- Handling CPU memory allocation failures. When dynamically creating small C++ + objects in CPU memory (not Vulkan memory), allocation failures are not checked + and handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. +- Code free of any compiler warnings. Maintaining the library to compile and + work correctly on so many different platforms is hard enough. Being free of + any warnings, on any version of any compiler, is simply not feasible. - Support for any programming languages other than C/C++. Bindings to other languages are welcomed as external projects. @@ -1669,7 +1688,8 @@ typedef struct VmaAllocatorCreateInfo smaller amount of memory, because graphics driver doesn't necessary fail new allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is exceeded. It may return success and just silently migrate some device memory - blocks to system RAM. + blocks to system RAM. This driver behavior can also be controlled using + VK_AMD_memory_overallocation_behavior extension. */ const VkDeviceSize* pHeapSizeLimit; /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`. @@ -1932,6 +1952,9 @@ typedef enum VmaAllocationCreateFlagBits { VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000, /** Allocation strategy that chooses first suitable free range for the allocation. + + "First" doesn't necessarily means the one with smallest offset in memory, + but rather the one that is easiest and fastest to find. */ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000, @@ -2402,6 +2425,31 @@ void vmaFreeMemoryPages( size_t allocationCount, VmaAllocation* pAllocations); +/** \brief Tries to resize an allocation in place, if there is enough free memory after it. + +Tries to change allocation's size without moving or reallocating it. +You can both shrink and grow allocation size. +When growing, it succeeds only when the allocation belongs to a memory block with enough +free space after it. + +Returns `VK_SUCCESS` if allocation's size has been successfully changed. +Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed. + +After successful call to this function, VmaAllocationInfo::size of this allocation changes. +All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer. + +- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`. +- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`. +- Resizing dedicated allocations, as well as allocations created in pools that use linear + or buddy algorithm, is not supported. + The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases. + Support may be added in the future. +*/ +VkResult vmaResizeAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize newSize); + /** \brief Returns current information about specified allocation and atomically marks it as used in current frame. Current paramters of given allocation are returned in `pAllocationInfo`. @@ -2826,7 +2874,19 @@ remove them if not needed. #define VMA_NULL nullptr #endif -#if defined(__APPLE__) || defined(__ANDROID__) +#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) +#include +void *aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + return memalign(alignment, size); +} +#elif defined(__APPLE__) || defined(__ANDROID__) #include void *aligned_alloc(size_t alignment, size_t size) { @@ -4520,7 +4580,9 @@ public: void ChangeBlockAllocation( VmaAllocator hAllocator, VmaDeviceMemoryBlock* block, - VkDeviceSize offset); + VkDeviceSize offset); + + void ChangeSize(VkDeviceSize newSize); // pMappedData not null means allocation is created with MAPPED flag. void InitDedicatedAllocation( @@ -4782,6 +4844,9 @@ public: virtual void Free(const VmaAllocation allocation) = 0; virtual void FreeAtOffset(VkDeviceSize offset) = 0; + // Tries to resize (grow or shrink) space for given allocation, in place. + virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; } + protected: const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } @@ -4861,6 +4926,8 @@ public: virtual void Free(const VmaAllocation allocation); virtual void FreeAtOffset(VkDeviceSize offset); + virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize); + private: uint32_t m_FreeCount; VkDeviceSize m_SumFreeSize; @@ -5623,6 +5690,10 @@ public: VmaAllocation allocation); void RecordFreeMemory(uint32_t frameIndex, VmaAllocation allocation); + void RecordResizeAllocation( + uint32_t frameIndex, + VmaAllocation allocation, + VkDeviceSize newSize); void RecordSetAllocationUserData(uint32_t frameIndex, VmaAllocation allocation, const void* pUserData); @@ -5792,6 +5863,10 @@ public: size_t allocationCount, const VmaAllocation* pAllocations); + VkResult ResizeAllocation( + const VmaAllocation alloc, + VkDeviceSize newSize); + void CalculateStats(VmaStats* pStats); #if VMA_STATS_STRING_ENABLED @@ -6338,6 +6413,12 @@ void VmaAllocation_T::ChangeBlockAllocation( m_BlockAllocation.m_Offset = offset; } +void VmaAllocation_T::ChangeSize(VkDeviceSize newSize) +{ + VMA_ASSERT(newSize > 0); + m_Size = newSize; +} + VkDeviceSize VmaAllocation_T::GetOffset() const { switch(m_Type) @@ -7264,6 +7345,133 @@ void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset) VMA_ASSERT(0 && "Not found!"); } +bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) +{ + typedef VmaSuballocationList::iterator iter_type; + for(iter_type suballocItem = m_Suballocations.begin(); + suballocItem != m_Suballocations.end(); + ++suballocItem) + { + VmaSuballocation& suballoc = *suballocItem; + if(suballoc.hAllocation == alloc) + { + iter_type nextItem = suballocItem; + ++nextItem; + + // Should have been ensured on higher level. + VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0); + + // Shrinking. + if(newSize < alloc->GetSize()) + { + const VkDeviceSize sizeDiff = suballoc.size - newSize; + + // There is next item. + if(nextItem != m_Suballocations.end()) + { + // Next item is free. + if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + // Grow this next item backward. + UnregisterFreeSuballocation(nextItem); + nextItem->offset -= sizeDiff; + nextItem->size += sizeDiff; + RegisterFreeSuballocation(nextItem); + } + // Next item is not free. + else + { + // Create free item after current one. + VmaSuballocation newFreeSuballoc; + newFreeSuballoc.hAllocation = VK_NULL_HANDLE; + newFreeSuballoc.offset = suballoc.offset + newSize; + newFreeSuballoc.size = sizeDiff; + newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc); + RegisterFreeSuballocation(newFreeSuballocIt); + + ++m_FreeCount; + } + } + // This is the last item. + else + { + // Create free item at the end. + VmaSuballocation newFreeSuballoc; + newFreeSuballoc.hAllocation = VK_NULL_HANDLE; + newFreeSuballoc.offset = suballoc.offset + newSize; + newFreeSuballoc.size = sizeDiff; + newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + m_Suballocations.push_back(newFreeSuballoc); + + iter_type newFreeSuballocIt = m_Suballocations.end(); + RegisterFreeSuballocation(--newFreeSuballocIt); + + ++m_FreeCount; + } + + suballoc.size = newSize; + m_SumFreeSize += sizeDiff; + } + // Growing. + else + { + const VkDeviceSize sizeDiff = newSize - suballoc.size; + + // There is next item. + if(nextItem != m_Suballocations.end()) + { + // Next item is free. + if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + // There is not enough free space, including margin. + if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN) + { + return false; + } + + // There is more free space than required. + if(nextItem->size > sizeDiff) + { + // Move and shrink this next item. + UnregisterFreeSuballocation(nextItem); + nextItem->offset += sizeDiff; + nextItem->size -= sizeDiff; + RegisterFreeSuballocation(nextItem); + } + // There is exactly the amount of free space required. + else + { + // Remove this next free item. + UnregisterFreeSuballocation(nextItem); + m_Suballocations.erase(nextItem); + --m_FreeCount; + } + } + // Next item is not free - there is no space to grow. + else + { + return false; + } + } + // This is the last item - there is no space to grow. + else + { + return false; + } + + suballoc.size = newSize; + m_SumFreeSize -= sizeDiff; + } + + // We cannot call Validate() here because alloc object is updated to new size outside of this call. + return true; + } + } + VMA_ASSERT(0 && "Not found!"); + return false; +} + bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const { VkDeviceSize lastSize = 0; @@ -11453,7 +11661,7 @@ VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex) // Write header. fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); - fprintf(m_File, "%s\n", "1,3"); + fprintf(m_File, "%s\n", "1,4"); return VK_SUCCESS; } @@ -11609,6 +11817,20 @@ void VmaRecorder::RecordFreeMemory(uint32_t frameIndex, Flush(); } +void VmaRecorder::RecordResizeAllocation( + uint32_t frameIndex, + VmaAllocation allocation, + VkDeviceSize newSize) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex, + allocation, newSize); + Flush(); +} + void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex, VmaAllocation allocation, const void* pUserData) @@ -12478,6 +12700,10 @@ VkResult VmaAllocator_T::AllocateMemory( VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); + if(vkMemReq.size == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) { @@ -12652,6 +12878,40 @@ void VmaAllocator_T::FreeMemory( } } +VkResult VmaAllocator_T::ResizeAllocation( + const VmaAllocation alloc, + VkDeviceSize newSize) +{ + if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + if(newSize == alloc->GetSize()) + { + return VK_SUCCESS; + } + + switch(alloc->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + return VK_ERROR_FEATURE_NOT_PRESENT; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize)) + { + alloc->ChangeSize(newSize); + VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate()); + return VK_SUCCESS; + } + else + { + return VK_ERROR_OUT_OF_POOL_MEMORY; + } + default: + VMA_ASSERT(0); + return VK_ERROR_VALIDATION_FAILED_EXT; + } +} + void VmaAllocator_T::CalculateStats(VmaStats* pStats) { // Initialize. @@ -12714,7 +12974,7 @@ VkResult VmaAllocator_T::Defragment( { if(pAllocationsChanged != VMA_NULL) { - memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged)); + memset(pAllocationsChanged, 0, allocationCount * sizeof(VkBool32)); } if(pDefragmentationStats != VMA_NULL) { @@ -14145,6 +14405,30 @@ void vmaFreeMemoryPages( allocator->FreeMemory(allocationCount, pAllocations); } +VkResult vmaResizeAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize newSize) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaResizeAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordResizeAllocation( + allocator->GetCurrentFrameIndex(), + allocation, + newSize); + } +#endif + + return allocator->ResizeAllocation(allocation, newSize); +} + void vmaGetAllocationInfo( VmaAllocator allocator, VmaAllocation allocation, @@ -14376,6 +14660,11 @@ VkResult vmaCreateBuffer( VmaAllocationInfo* pAllocationInfo) { VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } VMA_DEBUG_LOG("vmaCreateBuffer"); @@ -14520,6 +14809,15 @@ VkResult vmaCreateImage( { VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); + if(pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_VALIDATION_FAILED_EXT; + } + VMA_DEBUG_LOG("vmaCreateImage"); VMA_DEBUG_GLOBAL_MUTEX_LOCK diff --git a/tools/VmaDumpVis/VmaDumpVis.py b/tools/VmaDumpVis/VmaDumpVis.py index a0d42ca..c1477c3 100644 --- a/tools/VmaDumpVis/VmaDumpVis.py +++ b/tools/VmaDumpVis/VmaDumpVis.py @@ -25,7 +25,7 @@ import json from PIL import Image, ImageDraw, ImageFont -PROGRAM_VERSION = 'VMA Dump Visualization 1.0.0' +PROGRAM_VERSION = 'VMA Dump Visualization 2.0.0' IMG_SIZE_X = 800 IMG_MARGIN = 8 FONT_SIZE = 10 @@ -246,7 +246,7 @@ for iMemTypeIndex in sorted(data.keys()): index = 0 for iPoolId, listPool in dictMemType['CustomPools'].items(): for objBlock in listPool: - if 'Algorithm' in objBlock: + if 'Algorithm' in objBlock and objBlock['Algorithm']: sAlgorithm = ' (Algorithm: %s)' % (objBlock['Algorithm']); else: sAlgorithm = '';