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 15521bb..5e3aa2d 100644 --- a/docs/html/globals.html +++ b/docs/html/globals.html @@ -366,6 +366,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 58f7f40..923fba5 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 c0e99e6..f670fb3 100644 --- a/docs/html/search/all_10.js +++ b/docs/html/search/all_10.js @@ -118,6 +118,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 748cee6..4746a90 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/vk__mem__alloc_8h.html b/docs/html/vk__mem__alloc_8h.html index 71d3e4e..c801a1a 100644 --- a/docs/html/vk__mem__alloc_8h.html +++ b/docs/html/vk__mem__alloc_8h.html @@ -332,6 +332,9 @@ Functions void vmaFreeMemory (VmaAllocator allocator, VmaAllocation allocation)  Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). More...
      +VkResult vmaResizeAllocation (VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize newSize) + Tries to resize an allocation in place, if there is enough free memory after it. More...
    +  void vmaGetAllocationInfo (VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)  Returns current information about specified allocation and atomically marks it as used in current frame. More...
      @@ -898,7 +901,8 @@ Functions VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT 

    Allocation strategy that chooses biggest possible free range for the allocation.

    -VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT 

    Allocation strategy that chooses first suitable free range for the allocation.

    +VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT 

    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_MIN_MEMORY_BIT 

    Allocation strategy that tries to minimize memory usage.

    @@ -2559,6 +2563,50 @@ Functions

    This function fails when used on allocation made in memory type that is not HOST_VISIBLE.

    This function always fails when called for allocation that was created with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be mapped.

    + + + +

    ◆ vmaResizeAllocation()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    VkResult vmaResizeAllocation (VmaAllocator allocator,
    VmaAllocation allocation,
    VkDeviceSize newSize 
    )
    +
    + +

    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.

    + +
    diff --git a/src/Tests.cpp b/src/Tests.cpp index d34c34f..ff6aa3a 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -1629,6 +1629,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; @@ -1733,6 +1783,8 @@ static void TestBasics() } TestUserData(); + + TestInvalidAllocations(); } void TestHeapSizeLimit() @@ -3093,6 +3145,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; @@ -4707,6 +4912,7 @@ void Test() #else TestPool_SameSize(); TestHeapSizeLimit(); + TestResize(); #endif #if VMA_DEBUG_INITIALIZE_ALLOCATIONS TestAllocationsInitialization(); diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 5d9e6e4..e81aa1d 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -83,6 +83,7 @@ enum class VMA_FUNCTION TouchAllocation, GetAllocationInfo, MakePoolAllocationsLost, + ResizeAllocation, Count }; static const char* VMA_FUNCTION_NAMES[] = { @@ -105,6 +106,7 @@ static const char* VMA_FUNCTION_NAMES[] = { "vmaTouchAllocation", "vmaGetAllocationInfo", "vmaMakePoolAllocationsLost", + "vmaResizeAllocation", }; static_assert( _countof(VMA_FUNCTION_NAMES) == (size_t)VMA_FUNCTION::Count, @@ -146,7 +148,7 @@ static size_t g_DefragmentAfterLineNextIndex = 0; static bool ValidateFileVersion() { if(GetVersionMajor(g_FileVersion) == 1 && - GetVersionMinor(g_FileVersion) <= 3) + GetVersionMinor(g_FileVersion) <= 4) { return true; } @@ -1024,6 +1026,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); @@ -1168,6 +1171,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()) @@ -2849,6 +2854,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 fda803b..8d3b05c 100644 --- a/src/VulkanSample.cpp +++ b/src/VulkanSample.cpp @@ -1306,6 +1306,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 queue (doesn't need to be destroyed) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index dfac886..a59134f 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 @@ -1445,7 +1451,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 `VkCommandBuffer` or `VkQueue` and related synchronization is responsibility of the user. @@ -1453,6 +1460,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. @@ -1934,6 +1953,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, @@ -2359,6 +2381,31 @@ void vmaFreeMemory( VmaAllocator allocator, VmaAllocation allocation); +/** \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`. @@ -2927,7 +2974,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) { @@ -4679,7 +4738,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( @@ -4941,6 +5002,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; } @@ -5020,6 +5084,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; @@ -5887,6 +5953,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); @@ -6053,6 +6123,10 @@ public: // Main deallocation function. void FreeMemory(const VmaAllocation allocation); + VkResult ResizeAllocation( + const VmaAllocation alloc, + VkDeviceSize newSize); + void CalculateStats(VmaStats* pStats); #if VMA_STATS_STRING_ENABLED @@ -6586,6 +6660,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) @@ -7512,6 +7592,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; @@ -12041,7 +12248,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; } @@ -12197,6 +12404,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) @@ -12998,6 +13219,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) { @@ -13159,6 +13384,40 @@ void VmaAllocator_T::FreeMemory(const VmaAllocation allocation) vma_delete(this, allocation); } +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. @@ -14477,6 +14736,30 @@ void vmaFreeMemory( allocator->FreeMemory(allocation); } +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, @@ -14761,6 +15044,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"); @@ -14900,6 +15188,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 = '';