Add VulkanMemoryAllocator (vma) to the libs directory

This commit is contained in:
Stephen Saunders 2023-02-14 01:05:23 -05:00
parent 421160087d
commit 73df924ee2
58 changed files with 36327 additions and 3 deletions

View file

@ -68,7 +68,7 @@ option(USE_DX12
option(USE_VULKAN
"Use Vulkan" ON)
option(USE_AMD_ALLOCATOR
option(USE_VMA
"Use VMA allocator instead of the NVRHI builtin one" ON)
set(NVRHI_INSTALL OFF)
@ -411,9 +411,9 @@ if(USE_VULKAN)
endif()
# SRS - Add support for vma allocator vs. NVRHI builtin allocator
if(USE_AMD_ALLOCATOR)
if(USE_VMA)
add_definitions(-DUSE_AMD_ALLOCATOR)
include_directories("libs/VulkanMemoryAllocator/include")
include_directories("libs/vma/include")
endif()
else()

179
neo/libs/vma/CHANGELOG.md Normal file
View file

@ -0,0 +1,179 @@
# 3.0.1 (2022-05-26)
- Fixes in defragmentation algorithm.
- Fixes in GpuMemDumpVis.py regarding image height calculation.
- Other bug fixes, optimizations, and improvements in the code and documentation.
# 3.0.0 (2022-03-25)
It has been a long time since the previous official release, so hopefully everyone has been using the latest code from "master" branch, which is always maintained in a good state, not the old version. For completeness, here is the list of changes since v2.3.0. The major version number has changed, so there are some compatibility-breaking changes, but the basic API stays the same and is mostly backward-compatible.
Major features added (some compatibility-breaking):
- Added new API for selecting preferred memory type: flags `VMA_MEMORY_USAGE_AUTO`, `VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE`, `VMA_MEMORY_USAGE_AUTO_PREFER_HOST`, `VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT`, `VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT`, `VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT`. Old values like `VMA_MEMORY_USAGE_GPU_ONLY` still work as before, for backward compatibility, but are not recommended.
- Added new defragmentation API and algorithm, replacing the old one. See structure `VmaDefragmentationInfo`, `VmaDefragmentationMove`, `VmaDefragmentationPassMoveInfo`, `VmaDefragmentationStats`, function `vmaBeginDefragmentation`, `vmaEndDefragmentation`, `vmaBeginDefragmentationPass`, `vmaEndDefragmentationPass`.
- Redesigned API for statistics, replacing the old one. See structures: `VmaStatistics`, `VmaDetailedStatistics`, `VmaTotalStatistics`. `VmaBudget`, functions: `vmaGetHeapBudgets`, `vmaCalculateStatistics`, `vmaGetPoolStatistics`, `vmaCalculatePoolStatistics`, `vmaGetVirtualBlockStatistics`, `vmaCalculateVirtualBlockStatistics`.
- Added "Virtual allocator" feature - possibility to use core allocation algorithms for allocation of custom memory, not necessarily Vulkan device memory. See functions like `vmaCreateVirtualBlock`, `vmaDestroyVirtualBlock` and many more.
- `VmaAllocation` now keeps both `void* pUserData` and `char* pName`. Added function `vmaSetAllocationName`, member `VmaAllocationInfo::pName`. Flag `VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT` is now deprecated.
- Clarified and cleaned up various ways of importing Vulkan functions. See macros `VMA_STATIC_VULKAN_FUNCTIONS`, `VMA_DYNAMIC_VULKAN_FUNCTIONS`, structure `VmaVulkanFunctions`. Added members `VmaVulkanFunctions::vkGetInstanceProcAddr`, `vkGetDeviceProcAddr`, which are now required when using `VMA_DYNAMIC_VULKAN_FUNCTIONS`.
Removed (compatibility-breaking):
- Removed whole "lost allocations" feature. Removed from the interface: `VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT`, `VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT`, `vmaCreateLostAllocation`, `vmaMakePoolAllocationsLost`, `vmaTouchAllocation`, `VmaAllocatorCreateInfo::frameInUseCount`, `VmaPoolCreateInfo::frameInUseCount`.
- Removed whole "record & replay" feature. Removed from the API: `VmaAllocatorCreateInfo::pRecordSettings`, `VmaRecordSettings`, `VmaRecordFlagBits`, `VmaRecordFlags`. Removed VmaReplay application.
- Removed "buddy" algorithm - removed flag `VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT`.
Minor but compatibility-breaking changes:
- Changes in `ALLOCATION_CREATE_STRATEGY` flags. Removed flags: `VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT`, `VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT`, which were aliases to other existing flags.
- Added a member `void* pUserData` to `VmaDeviceMemoryCallbacks`. Updated `PFN_vmaAllocateDeviceMemoryFunction`, `PFN_vmaFreeDeviceMemoryFunction` to use the new `pUserData` member.
- Removed function `vmaResizeAllocation` that was already deprecated.
Other major changes:
- Added new features to custom pools: support for dedicated allocations, new member `VmaPoolCreateInfo::pMemoryAllocateNext`, `minAllocationAlignment`.
- Added support for Vulkan 1.2, 1.3.
- Added support for VK_KHR_buffer_device_address extension - flag `VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT`.
- Added support for VK_EXT_memory_priority extension - flag `VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT`, members `VmaAllocationCreateInfo::priority`, `VmaPoolCreateInfo::priority`.
- Added support for VK_AMD_device_coherent_memory extension - flag `VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT`.
- Added member `VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes`.
- Added function `vmaGetAllocatorInfo`, structure `VmaAllocatorInfo`.
- Added functions `vmaFlushAllocations`, `vmaInvalidateAllocations` for multiple allocations at once.
- Added flag `VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT`.
- Added function `vmaCreateBufferWithAlignment`.
- Added convenience function `vmaGetAllocationMemoryProperties`.
- Added convenience functions: `vmaCreateAliasingBuffer`, `vmaCreateAliasingImage`.
Other minor changes:
- Implemented Two-Level Segregated Fit (TLSF) allocation algorithm, replacing previous default one. It is much faster, especially when freeing many allocations at once or when `bufferImageGranularity` is large.
- Renamed debug macro `VMA_DEBUG_ALIGNMENT` to `VMA_MIN_ALIGNMENT`.
- Added CMake support - CMakeLists.txt files. Removed Premake support.
- Changed `vmaInvalidateAllocation` and `vmaFlushAllocation` to return `VkResult`.
- Added nullability annotations for Clang: `VMA_NULLABLE`, `VMA_NOT_NULL`, `VMA_NULLABLE_NON_DISPATCHABLE`, `VMA_NOT_NULL_NON_DISPATCHABLE`, `VMA_LEN_IF_NOT_NULL`.
- JSON dump format has changed.
- Countless fixes and improvements, including performance optimizations, compatibility with various platforms and compilers, documentation.
# 2.3.0 (2019-12-04)
Major release after a year of development in "master" branch and feature branches. Notable new features: supporting Vulkan 1.1, supporting query for memory budget.
Major changes:
- Added support for Vulkan 1.1.
- Added member `VmaAllocatorCreateInfo::vulkanApiVersion`.
- When Vulkan 1.1 is used, there is no need to enable VK_KHR_dedicated_allocation or VK_KHR_bind_memory2 extensions, as they are promoted to Vulkan itself.
- Added support for query for memory budget and staying within the budget.
- Added function `vmaGetBudget`, structure `VmaBudget`. This can also serve as simple statistics, more efficient than `vmaCalculateStats`.
- By default the budget it is estimated based on memory heap sizes. It may be queried from the system using VK_EXT_memory_budget extension if you use `VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT` flag and `VmaAllocatorCreateInfo::instance` member.
- Added flag `VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT` that fails an allocation if it would exceed the budget.
- Added new memory usage options:
- `VMA_MEMORY_USAGE_CPU_COPY` for memory that is preferably not `DEVICE_LOCAL` but not guaranteed to be `HOST_VISIBLE`.
- `VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED` for memory that is `LAZILY_ALLOCATED`.
- Added support for VK_KHR_bind_memory2 extension:
- Added `VMA_ALLOCATION_CREATE_DONT_BIND_BIT` flag that lets you create both buffer/image and allocation, but don't bind them together.
- Added flag `VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT`, functions `vmaBindBufferMemory2`, `vmaBindImageMemory2` that let you specify additional local offset and `pNext` pointer while binding.
- Added functions `vmaSetPoolName`, `vmaGetPoolName` that let you assign string names to custom pools. JSON dump file format and VmaDumpVis tool is updated to show these names.
- Defragmentation is legal only on buffers and images in `VK_IMAGE_TILING_LINEAR`. This is due to the way it is currently implemented in the library and the restrictions of the Vulkan specification. Clarified documentation in this regard. See discussion in #59.
Minor changes:
- Made `vmaResizeAllocation` function deprecated, always returning failure.
- Made changes in the internal algorithm for the choice of memory type. Be careful! You may now get a type that is not `HOST_VISIBLE` or `HOST_COHERENT` if it's not stated as always ensured by some `VMA_MEMORY_USAGE_*` flag.
- Extended VmaReplay application with more detailed statistics printed at the end.
- Added macros `VMA_CALL_PRE`, `VMA_CALL_POST` that let you decorate declarations of all library functions if you want to e.g. export/import them as dynamically linked library.
- Optimized `VmaAllocation` objects to be allocated out of an internal free-list allocator. This makes allocation and deallocation causing 0 dynamic CPU heap allocations on average.
- Updated recording CSV file format version to 1.8, to support new functions.
- Many additions and fixes in documentation. Many compatibility fixes for various compilers and platforms. Other internal bugfixes, optimizations, updates, refactoring...
# 2.2.0 (2018-12-13)
Major release after many months of development in "master" branch and feature branches. Notable new features: defragmentation of GPU memory, buddy algorithm, convenience functions for sparse binding.
Major changes:
- New, more powerful defragmentation:
- Added structure `VmaDefragmentationInfo2`, functions `vmaDefragmentationBegin`, `vmaDefragmentationEnd`.
- Added support for defragmentation of GPU memory.
- Defragmentation of CPU memory now uses `memmove`, so it can move data to overlapping regions.
- Defragmentation of CPU memory is now available for memory types that are `HOST_VISIBLE` but not `HOST_COHERENT`.
- Added structure member `VmaVulkanFunctions::vkCmdCopyBuffer`.
- Major internal changes in defragmentation algorithm.
- VmaReplay: added parameters: `--DefragmentAfterLine`, `--DefragmentationFlags`.
- Old interface (structure `VmaDefragmentationInfo`, function `vmaDefragment`) is now deprecated.
- Added buddy algorithm, available for custom pools - flag `VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT`.
- Added convenience functions for multiple allocations and deallocations at once, intended for sparse binding resources - functions `vmaAllocateMemoryPages`, `vmaFreeMemoryPages`.
- Added function that tries to resize existing allocation in place: `vmaResizeAllocation`.
- Added flags for allocation strategy: `VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT`, and their aliases: `VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT`, `VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT`.
Minor changes:
- Changed behavior of allocation functions to return `VK_ERROR_VALIDATION_FAILED_EXT` when trying to allocate memory of size 0, create buffer with size 0, or image with one of the dimensions 0.
- VmaReplay: Added support for Windows end of lines.
- Updated recording CSV file format version to 1.5, to support new functions.
- Internal optimization: using read-write mutex on some platforms.
- Many additions and fixes in documentation. Many compatibility fixes for various compilers. Other internal bugfixes, optimizations, refactoring, added more internal validation...
# 2.1.0 (2018-09-10)
Minor bugfixes.
# 2.1.0-beta.1 (2018-08-27)
Major release after many months of development in "development" branch and features branches. Many new features added, some bugs fixed. API stays backward-compatible.
Major changes:
- Added linear allocation algorithm, accessible for custom pools, that can be used as free-at-once, stack, double stack, or ring buffer. See "Linear allocation algorithm" documentation chapter.
- Added `VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT`, `VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT`.
- Added feature to record sequence of calls to the library to a file and replay it using dedicated application. See documentation chapter "Record and replay".
- Recording: added `VmaAllocatorCreateInfo::pRecordSettings`.
- Replaying: added VmaReplay project.
- Recording file format: added document "docs/Recording file format.md".
- Improved support for non-coherent memory.
- Added functions: `vmaFlushAllocation`, `vmaInvalidateAllocation`.
- `nonCoherentAtomSize` is now respected automatically.
- Added `VmaVulkanFunctions::vkFlushMappedMemoryRanges`, `vkInvalidateMappedMemoryRanges`.
- Improved debug features related to detecting incorrect mapped memory usage. See documentation chapter "Debugging incorrect memory usage".
- Added debug macro `VMA_DEBUG_DETECT_CORRUPTION`, functions `vmaCheckCorruption`, `vmaCheckPoolCorruption`.
- Added debug macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to initialize contents of allocations with a bit pattern.
- Changed behavior of `VMA_DEBUG_MARGIN` macro - it now adds margin also before first and after last allocation in a block.
- Changed format of JSON dump returned by `vmaBuildStatsString` (not backward compatible!).
- Custom pools and memory blocks now have IDs that don't change after sorting.
- Added properties: "CreationFrameIndex", "LastUseFrameIndex", "Usage".
- Changed VmaDumpVis tool to use these new properties for better coloring.
- Changed behavior of `vmaGetAllocationInfo` and `vmaTouchAllocation` to update `allocation.lastUseFrameIndex` even if allocation cannot become lost.
Minor changes:
- Changes in custom pools:
- Added new structure member `VmaPoolStats::blockCount`.
- Changed behavior of `VmaPoolCreateInfo::blockSize` = 0 (default) - it now means that pool may use variable block sizes, just like default pools do.
- Improved logic of `vmaFindMemoryTypeIndex` for some cases, especially integrated GPUs.
- VulkanSample application: Removed dependency on external library MathFu. Added own vector and matrix structures.
- Changes that improve compatibility with various platforms, including: Visual Studio 2012, 32-bit code, C compilers.
- Changed usage of "VK_KHR_dedicated_allocation" extension in the code to be optional, driven by macro `VMA_DEDICATED_ALLOCATION`, for compatibility with Android.
- Many additions and fixes in documentation, including description of new features, as well as "Validation layer warnings".
- Other bugfixes.
# 2.0.0 (2018-03-19)
A major release with many compatibility-breaking changes.
Notable new features:
- Introduction of `VmaAllocation` handle that you must retrieve from allocation functions and pass to deallocation functions next to normal `VkBuffer` and `VkImage`.
- Introduction of `VmaAllocationInfo` structure that you can retrieve from `VmaAllocation` handle to access parameters of the allocation (like `VkDeviceMemory` and offset) instead of retrieving them directly from allocation functions.
- Support for reference-counted mapping and persistently mapped allocations - see `vmaMapMemory`, `VMA_ALLOCATION_CREATE_MAPPED_BIT`.
- Support for custom memory pools - see `VmaPool` handle, `VmaPoolCreateInfo` structure, `vmaCreatePool` function.
- Support for defragmentation (compaction) of allocations - see function `vmaDefragment` and related structures.
- Support for "lost allocations" - see appropriate chapter on documentation Main Page.
# 1.0.1 (2017-07-04)
- Fixes for Linux GCC compilation.
- Changed "CONFIGURATION SECTION" to contain #ifndef so you can define these macros before including this header, not necessarily change them in the file.
# 1.0.0 (2017-06-16)
First public release.

View file

@ -0,0 +1,55 @@
cmake_minimum_required(VERSION 3.9)
project(VulkanMemoryAllocator LANGUAGES CXX)
# https://cmake.org/cmake/help/latest/variable/PROJECT_IS_TOP_LEVEL.html
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} PROJECT_IS_TOP_LEVEL)
if(PROJECT_IS_TOP_LEVEL)
find_package(Vulkan REQUIRED)
include_directories(${Vulkan_INCLUDE_DIR})
endif()
# VulkanMemoryAllocator contains an sample application which is not built by default
option(VMA_BUILD_SAMPLE "Build VulkanMemoryAllocator sample application" OFF)
option(VMA_BUILD_SAMPLE_SHADERS "Build VulkanMemoryAllocator sample application's shaders" OFF)
message(STATUS "VMA_BUILD_SAMPLE = ${VMA_BUILD_SAMPLE}")
message(STATUS "VMA_BUILD_SAMPLE_SHADERS = ${VMA_BUILD_SAMPLE_SHADERS}")
option(VMA_STATIC_VULKAN_FUNCTIONS "Link statically with Vulkan API" ON)
option(VMA_DYNAMIC_VULKAN_FUNCTIONS "Fetch pointers to Vulkan functions internally (no static linking)" OFF)
option(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY "Every allocation will have its own memory block" OFF)
option(VMA_DEBUG_INITIALIZE_ALLOCATIONS "Automatically fill new allocations and destroyed allocations with some bit pattern" OFF)
option(VMA_DEBUG_GLOBAL_MUTEX "Enable single mutex protecting all entry calls to the library" OFF)
option(VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT "Never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount and return error" OFF)
message(STATUS "VMA_STATIC_VULKAN_FUNCTIONS = ${VMA_STATIC_VULKAN_FUNCTIONS}")
message(STATUS "VMA_DYNAMIC_VULKAN_FUNCTIONS = ${VMA_DYNAMIC_VULKAN_FUNCTIONS}")
message(STATUS "VMA_DEBUG_ALWAYS_DEDICATED_MEMORY = ${VMA_DEBUG_ALWAYS_DEDICATED_MEMORY}")
message(STATUS "VMA_DEBUG_INITIALIZE_ALLOCATIONS = ${VMA_DEBUG_INITIALIZE_ALLOCATIONS}")
message(STATUS "VMA_DEBUG_GLOBAL_MUTEX = ${VMA_DEBUG_GLOBAL_MUTEX}")
message(STATUS "VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT = ${VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT}")
if(VMA_BUILD_SAMPLE)
set(VMA_BUILD_SAMPLE_SHADERS ON)
endif()
option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" OFF)
if(BUILD_DOCUMENTATION)
find_package(Doxygen REQUIRED)
# set input and output files
set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile)
set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
# request to configure the file
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
# note the option ALL which allows to build the docs together with the application
add_custom_target( doc_doxygen ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM )
endif()
add_subdirectory(src)

2739
neo/libs/vma/Doxyfile Normal file

File diff suppressed because it is too large Load diff

19
neo/libs/vma/LICENSE.txt Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

188
neo/libs/vma/README.md Normal file
View file

@ -0,0 +1,188 @@
# Vulkan Memory Allocator
Easy to integrate Vulkan memory allocation library.
**Documentation:** Browse online: [Vulkan Memory Allocator](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/) (generated from Doxygen-style comments in [include/vk_mem_alloc.h](include/vk_mem_alloc.h))
**License:** MIT. See [LICENSE.txt](LICENSE.txt)
**Changelog:** See [CHANGELOG.md](CHANGELOG.md)
**Product page:** [Vulkan Memory Allocator on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
**Build status:**
- Windows: [![Build status](https://ci.appveyor.com/api/projects/status/4vlcrb0emkaio2pn/branch/master?svg=true)](https://ci.appveyor.com/project/adam-sawicki-amd/vulkanmemoryallocator/branch/master)
- Linux: [![Build Status](https://app.travis-ci.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.svg?branch=master)](https://app.travis-ci.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.svg)](http://isitmaintained.com/project/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator "Average time to resolve an issue")
# Problem
Memory allocation and resource (buffer and image) creation in Vulkan is difficult (comparing to older graphics APIs, 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.
- Driver must be queried for supported memory heaps and memory types. Different GPU vendors provide different types of it.
- It is recommended to allocate bigger chunks of memory and assign parts of them to particular resources, as there is a limit on maximum number of memory blocks that can be allocated.
# Features
This library can help game developers to manage memory allocations and resource creation by offering some higher-level functions:
1. Functions that help to choose correct and optimal memory type based on intended usage of the memory.
- Required or preferred traits of the memory are expressed using higher-level description comparing to Vulkan flags.
2. Functions that allocate memory blocks, reserve and return parts of them (`VkDeviceMemory` + offset + size) to the user.
- Library keeps track of allocated memory blocks, used and unused ranges inside them, finds best matching unused ranges for new allocations, respects all the rules of alignment and buffer/image granularity.
3. Functions that can create an image/buffer, allocate memory for it and bind them together - all in one call.
Additional features:
- Well-documented - description of all functions and structures provided, along with chapters that contain general description and example code.
- Thread-safety: Library is designed to be used in multithreaded code. Access to a single device memory block referred by different buffers and textures (binding, mapping) is synchronized internally. Memory mapping is reference-counted.
- Configuration: Fill optional members of `VmaAllocatorCreateInfo` structure to provide custom CPU memory allocator, pointers to Vulkan functions and other parameters.
- Customization and integration with custom engines: Predefine appropriate macros to provide your own implementation of all external facilities used by the library like assert, mutex, atomic.
- Support for memory mapping, reference-counted internally. Support for persistently mapped memory: Just allocate with appropriate flag and access the pointer to already mapped memory.
- Support for non-coherent memory. Functions that flush/invalidate memory. `nonCoherentAtomSize` is respected automatically.
- Support for resource aliasing (overlap).
- Support for sparse binding and sparse residency: Convenience functions that allocate or free multiple memory pages at once.
- 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 Vulkan 1.0, 1.1, 1.2, 1.3.
- Support for extensions (and equivalent functionality included in new Vulkan versions):
- VK_KHR_dedicated_allocation: Just enable it and it will be used automatically by the library.
- VK_KHR_buffer_device_address: Flag `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR` is automatically added to memory allocations where needed.
- VK_EXT_memory_budget: Used internally if available to query for current usage and budget. If not available, it falls back to an estimation based on memory heap sizes.
- VK_EXT_memory_priority: Set `priority` of allocations or custom pools and it will be set automatically using this extension.
- VK_AMD_device_coherent_memory
- Defragmentation of GPU and CPU memory: Let the library move data around to free some memory blocks and make your allocations better compacted.
- Statistics: Obtain brief or detailed statistics about the amount of memory used, unused, number of allocated blocks, number of allocations etc. - globally, per memory heap, and per memory type.
- Debug annotations: Associate custom `void* pUserData` and debug `char* pName` with each allocation.
- JSON dump: Obtain a string in JSON format with detailed map of internal state, including list of allocations, their string names, and gaps between them.
- Convert this JSON dump into a picture to visualize your memory. See [tools/GpuMemDumpVis](tools/GpuMemDumpVis/README.md).
- Debugging incorrect memory usage: Enable initialization of all allocated memory with a bit pattern to detect usage of uninitialized or freed memory. Enable validation of a magic number after every allocation to detect out-of-bounds memory corruption.
- Support for interoperability with OpenGL.
- Virtual allocator: Interface for using core allocation algorithm to allocate any custom data, e.g. pieces of one large buffer.
# Prerequisites
- Self-contained C++ library in single header file. No external dependencies other than standard C and C++ library and of course Vulkan. Some features of C++14 used. STL containers, RTTI, or C++ exceptions are not used.
- 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. Used also on Android, MacOS, and other platforms.
# Example
Basic usage of this library is very simple. Advanced features are optional. After you created global `VmaAllocator` object, a complete code needed to create a buffer may look like this:
```cpp
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufferInfo.size = 65536;
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
VkBuffer buffer;
VmaAllocation allocation;
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
```
With this one function call:
1. `VkBuffer` is created.
2. `VkDeviceMemory` block is allocated if needed.
3. An unused region of the memory block is bound to this buffer.
`VmaAllocation` is an object that represents memory assigned to this buffer. It can be queried for parameters like `VkDeviceMemory` handle and offset.
# How to build
On Windows it is recommended to use [CMake UI](https://cmake.org/runningcmake/). Alternatively you can generate a Visual Studio project map using CMake in command line: `cmake -B./build/ -DCMAKE_BUILD_TYPE=Debug -G "Visual Studio 16 2019" -A x64 ./`
On Linux:
```
mkdir build
cd build
cmake ..
make
```
The following targets are available
| Target | Description | CMake option | Default setting |
| ------------- | ------------- | ------------- | ------------- |
| VmaSample | VMA sample application | `VMA_BUILD_SAMPLE` | `OFF` |
| VmaBuildSampleShaders | Shaders for VmaSample | `VMA_BUILD_SAMPLE_SHADERS` | `OFF` |
Please note that while VulkanMemoryAllocator library is supported on other platforms besides Windows, VmaSample is not.
These CMake options are available
| CMake option | Description | Default setting |
| ------------- | ------------- | ------------- |
| `VMA_RECORDING_ENABLED` | Enable VMA memory recording for debugging | `OFF` |
| `VMA_USE_STL_CONTAINERS` | Use C++ STL containers instead of VMA's containers | `OFF` |
| `VMA_STATIC_VULKAN_FUNCTIONS` | Link statically with Vulkan API | `OFF` |
| `VMA_DYNAMIC_VULKAN_FUNCTIONS` | Fetch pointers to Vulkan functions internally (no static linking) | `ON` |
| `VMA_DEBUG_ALWAYS_DEDICATED_MEMORY` | Every allocation will have its own memory block | `OFF` |
| `VMA_DEBUG_INITIALIZE_ALLOCATIONS` | Automatically fill new allocations and destroyed allocations with some bit pattern | `OFF` |
| `VMA_DEBUG_GLOBAL_MUTEX` | Enable single mutex protecting all entry calls to the library | `OFF` |
| `VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT` | Never exceed [VkPhysicalDeviceLimits::maxMemoryAllocationCount](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#limits-maxMemoryAllocationCount) and return error | `OFF` |
## Building using vcpkg
You can download and install VulkanMemoryAllocator using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install vulkan-memory-allocator
The VulkanMemoryAllocator port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
# Binaries
The release comes with precompiled binary executable for "VulkanSample" application which contains test suite. It is compiled using Visual Studio 2019, so it requires appropriate libraries to work, including "MSVCP140.dll", "VCRUNTIME140.dll", "VCRUNTIME140_1.dll". If the launch fails with error message telling about those files missing, please download and install [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads), "x64" version.
# Read more
See **[Documentation](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/)**.
# Software using this library
- **[X-Plane](https://x-plane.com/)**
- **[Detroit: Become Human](https://gpuopen.com/learn/porting-detroit-3/)**
- **[Vulkan Samples](https://github.com/LunarG/VulkanSamples)** - official Khronos Vulkan samples. License: Apache-style.
- **[Anvil](https://github.com/GPUOpen-LibrariesAndSDKs/Anvil)** - cross-platform framework for Vulkan. License: MIT.
- **[Filament](https://github.com/google/filament)** - physically based rendering engine for Android, Windows, Linux and macOS, from Google. Apache License 2.0.
- **[Atypical Games - proprietary game engine](https://developer.samsung.com/galaxy-gamedev/gamedev-blog/infinitejet.html)**
- **[Flax Engine](https://flaxengine.com/)**
- **[Godot Engine](https://github.com/godotengine/godot/)** - multi-platform 2D and 3D game engine. License: MIT.
- **[Lightweight Java Game Library (LWJGL)](https://www.lwjgl.org/)** - includes binding of the library for Java. License: BSD.
- **[PowerVR SDK](https://github.com/powervr-graphics/Native_SDK)** - C++ cross-platform 3D graphics SDK, from Imagination. License: MIT.
- **[Skia](https://github.com/google/skia)** - complete 2D graphic library for drawing Text, Geometries, and Images, from Google.
- **[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.
- **[vkDOOM3](https://github.com/DustinHLand/vkDOOM3)** - Vulkan port of GPL DOOM 3 BFG Edition. License: GNU GPL.
- **[vkQuake2](https://github.com/kondrak/vkQuake2)** - vanilla Quake 2 with Vulkan support. License: GNU GPL.
- **[Vulkan Best Practice for Mobile Developers](https://github.com/ARM-software/vulkan_best_practice_for_mobile_developers)** from ARM. License: MIT.
- **[RPCS3](https://github.com/RPCS3/rpcs3)** - PlayStation 3 emulator/debugger. License: GNU GPLv2.
- **[PPSSPP](https://github.com/hrydgard/ppsspp)** - Playstation Portable emulator/debugger. License: GNU GPLv2+.
[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
- **[D3D12 Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator)** - equivalent library for Direct3D 12. License: MIT.
- **[Awesome Vulkan](https://github.com/vinjn/awesome-vulkan)** - a curated list of awesome Vulkan libraries, debuggers and resources.
- **[vcpkg](https://github.com/Microsoft/vcpkg)** dependency manager from Microsoft also offers a port of this library.
- **[VulkanMemoryAllocator-Hpp](https://github.com/malte-v/VulkanMemoryAllocator-Hpp)** - C++ binding for this library. License: CC0-1.0.
- **[PyVMA](https://github.com/realitix/pyvma)** - Python wrapper for this library. Author: Jean-Sébastien B. (@realitix). License: Apache 2.0.
- **[vk-mem](https://github.com/gwihlidal/vk-mem-rs)** - Rust binding for this library. Author: Graham Wihlidal. License: Apache 2.0 or MIT.
- **[Haskell bindings](https://hackage.haskell.org/package/VulkanMemoryAllocator)**, **[github](https://github.com/expipiplus1/vulkan/tree/master/VulkanMemoryAllocator)** - Haskell bindings for this library. Author: Ellie Hermaszewska (@expipiplus1). License BSD-3-Clause.
- **[vma_sample_sdl](https://github.com/rextimmy/vma_sample_sdl)** - SDL port of the sample app of this library (with the goal of running it on multiple platforms, including MacOS). Author: @rextimmy. License: MIT.
- **[vulkan-malloc](https://github.com/dylanede/vulkan-malloc)** - Vulkan memory allocation library for Rust. Based on version 1 of this library. Author: Dylan Ede (@dylanede). License: MIT / Apache 2.0.

Binary file not shown.

Binary file not shown.

View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,6 @@
root = true
[**.{cpp,h}]
indent_style = space
indent_size = 4
end_of_line = lf

View file

@ -0,0 +1,107 @@
set(CMAKE_DEBUG_POSTFIX d)
set(CMAKE_RELWITHDEBINFO_POSTFIX rd)
set(CMAKE_MINSIZEREL_POSTFIX s)
add_library(VulkanMemoryAllocator
VmaUsage.cpp
VmaUsage.h
${PROJECT_SOURCE_DIR}/include/vk_mem_alloc.h
)
if (MSVC)
# Provides MSVC users nicer debugging support
target_sources(VulkanMemoryAllocator PRIVATE ${CMAKE_CURRENT_LIST_DIR}/vk_mem_alloc.natvis)
endif()
set_target_properties(
VulkanMemoryAllocator PROPERTIES
CXX_EXTENSIONS OFF
# Use C++14
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
)
target_include_directories(VulkanMemoryAllocator PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
)
# Only link to Vulkan if static linking is used
if(${VMA_STATIC_VULKAN_FUNCTIONS})
target_link_libraries(VulkanMemoryAllocator PUBLIC Vulkan::Vulkan)
endif()
target_compile_definitions(
VulkanMemoryAllocator
PUBLIC
VMA_STATIC_VULKAN_FUNCTIONS=$<BOOL:${VMA_STATIC_VULKAN_FUNCTIONS}>
VMA_DYNAMIC_VULKAN_FUNCTIONS=$<BOOL:${VMA_DYNAMIC_VULKAN_FUNCTIONS}>
VMA_DEBUG_ALWAYS_DEDICATED_MEMORY=$<BOOL:${VMA_DEBUG_ALWAYS_DEDICATED_MEMORY}>
VMA_DEBUG_INITIALIZE_ALLOCATIONS=$<BOOL:${VMA_DEBUG_INITIALIZE_ALLOCATIONS}>
VMA_DEBUG_GLOBAL_MUTEX=$<BOOL:${VMA_DEBUG_GLOBAL_MUTEX}>
VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT=$<BOOL:${VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT}>
VMA_RECORDING_ENABLED=$<BOOL:${VMA_RECORDING_ENABLED}>
)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/install_target.cmake)
if(VMA_BUILD_SAMPLE)
if(WIN32)
set(VMA_SAMPLE_SOURCE_FILES
Common.cpp
Common.h
SparseBindingTest.cpp
SparseBindingTest.h
Tests.cpp
Tests.h
VulkanSample.cpp
)
add_executable(VmaSample ${VMA_SAMPLE_SOURCE_FILES})
add_dependencies(VmaSample VulkanMemoryAllocator VmaSampleShaders)
if(MSVC)
# Use Unicode instead of multibyte set
add_compile_definitions(UNICODE _UNICODE)
# Add C++ warnings and security checks
add_compile_options(/permissive- /sdl /W3)
# Enable multithreaded compiling
target_compile_options(VmaSample PRIVATE "/MP")
# Set VmaSample as startup project
set_property(DIRECTORY "${PROJECT_SOURCE_DIR}" PROPERTY VS_STARTUP_PROJECT "VmaSample")
# Set working directory for Visual Studio debugger
set_target_properties(
VmaSample
PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin"
)
endif()
set_target_properties(
VmaSample PROPERTIES
CXX_EXTENSIONS OFF
# Use C++14
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
)
target_link_libraries(
VmaSample
PRIVATE
VulkanMemoryAllocator
Vulkan::Vulkan
)
else()
message(STATUS "VmaSample application is not supported to Linux")
endif()
endif()
if(VMA_BUILD_SAMPLE_SHADERS)
add_subdirectory(Shaders)
endif()

328
neo/libs/vma/src/Common.cpp Normal file
View file

@ -0,0 +1,328 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Common.h"
#ifdef _WIN32
void ReadFile(std::vector<char>& out, const char* fileName)
{
std::ifstream file(fileName, std::ios::ate | std::ios::binary);
assert(file.is_open());
size_t fileSize = (size_t)file.tellg();
if(fileSize > 0)
{
out.resize(fileSize);
file.seekg(0);
file.read(out.data(), fileSize);
}
else
out.clear();
}
void SetConsoleColor(CONSOLE_COLOR color)
{
WORD attr = 0;
switch(color)
{
case CONSOLE_COLOR::INFO:
attr = FOREGROUND_INTENSITY;
break;
case CONSOLE_COLOR::NORMAL:
attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
case CONSOLE_COLOR::WARNING:
attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case CONSOLE_COLOR::ERROR_:
attr = FOREGROUND_RED | FOREGROUND_INTENSITY;
break;
default:
assert(0);
}
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(out, attr);
}
void PrintMessage(CONSOLE_COLOR color, const char* msg)
{
if(color != CONSOLE_COLOR::NORMAL)
SetConsoleColor(color);
printf("%s\n", msg);
if (color != CONSOLE_COLOR::NORMAL)
SetConsoleColor(CONSOLE_COLOR::NORMAL);
}
void PrintMessage(CONSOLE_COLOR color, const wchar_t* msg)
{
if(color != CONSOLE_COLOR::NORMAL)
SetConsoleColor(color);
wprintf(L"%s\n", msg);
if (color != CONSOLE_COLOR::NORMAL)
SetConsoleColor(CONSOLE_COLOR::NORMAL);
}
static const size_t CONSOLE_SMALL_BUF_SIZE = 256;
void PrintMessageV(CONSOLE_COLOR color, const char* format, va_list argList)
{
size_t dstLen = (size_t)::_vscprintf(format, argList);
if(dstLen)
{
bool useSmallBuf = dstLen < CONSOLE_SMALL_BUF_SIZE;
char smallBuf[CONSOLE_SMALL_BUF_SIZE];
std::vector<char> bigBuf(useSmallBuf ? 0 : dstLen + 1);
char* bufPtr = useSmallBuf ? smallBuf : bigBuf.data();
::vsprintf_s(bufPtr, dstLen + 1, format, argList);
PrintMessage(color, bufPtr);
}
}
void PrintMessageV(CONSOLE_COLOR color, const wchar_t* format, va_list argList)
{
size_t dstLen = (size_t)::_vcwprintf(format, argList);
if(dstLen)
{
bool useSmallBuf = dstLen < CONSOLE_SMALL_BUF_SIZE;
wchar_t smallBuf[CONSOLE_SMALL_BUF_SIZE];
std::vector<wchar_t> bigBuf(useSmallBuf ? 0 : dstLen + 1);
wchar_t* bufPtr = useSmallBuf ? smallBuf : bigBuf.data();
::vswprintf_s(bufPtr, dstLen + 1, format, argList);
PrintMessage(color, bufPtr);
}
}
void PrintMessageF(CONSOLE_COLOR color, const char* format, ...)
{
va_list argList;
va_start(argList, format);
PrintMessageV(color, format, argList);
va_end(argList);
}
void PrintMessageF(CONSOLE_COLOR color, const wchar_t* format, ...)
{
va_list argList;
va_start(argList, format);
PrintMessageV(color, format, argList);
va_end(argList);
}
void PrintWarningF(const char* format, ...)
{
va_list argList;
va_start(argList, format);
PrintMessageV(CONSOLE_COLOR::WARNING, format, argList);
va_end(argList);
}
void PrintWarningF(const wchar_t* format, ...)
{
va_list argList;
va_start(argList, format);
PrintMessageV(CONSOLE_COLOR::WARNING, format, argList);
va_end(argList);
}
void PrintErrorF(const char* format, ...)
{
va_list argList;
va_start(argList, format);
PrintMessageV(CONSOLE_COLOR::WARNING, format, argList);
va_end(argList);
}
void PrintErrorF(const wchar_t* format, ...)
{
va_list argList;
va_start(argList, format);
PrintMessageV(CONSOLE_COLOR::WARNING, format, argList);
va_end(argList);
}
void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize)
{
FILE* f = nullptr;
_wfopen_s(&f, filePath, L"wb");
if(f)
{
fwrite(data, 1, dataSize, f);
fclose(f);
}
else
assert(0);
}
std::wstring SizeToStr(size_t size)
{
if(size == 0)
return L"0";
wchar_t result[32];
double size2 = (double)size;
if (size2 >= 1024.0*1024.0*1024.0*1024.0)
{
swprintf_s(result, L"%.2f TB", size2 / (1024.0*1024.0*1024.0*1024.0));
}
else if (size2 >= 1024.0*1024.0*1024.0)
{
swprintf_s(result, L"%.2f GB", size2 / (1024.0*1024.0*1024.0));
}
else if (size2 >= 1024.0*1024.0)
{
swprintf_s(result, L"%.2f MB", size2 / (1024.0*1024.0));
}
else if (size2 >= 1024.0)
{
swprintf_s(result, L"%.2f KB", size2 / 1024.0);
}
else
swprintf_s(result, L"%llu B", size);
return result;
}
bool ConvertCharsToUnicode(std::wstring *outStr, const std::string &s, unsigned codePage)
{
if (s.empty())
{
outStr->clear();
return true;
}
// Phase 1 - Get buffer size.
const int size = MultiByteToWideChar(codePage, 0, s.data(), (int)s.length(), NULL, 0);
if (size == 0)
{
outStr->clear();
return false;
}
// Phase 2 - Do conversion.
std::unique_ptr<wchar_t[]> buf(new wchar_t[(size_t)size]);
int result = MultiByteToWideChar(codePage, 0, s.data(), (int)s.length(), buf.get(), size);
if (result == 0)
{
outStr->clear();
return false;
}
outStr->assign(buf.get(), (size_t)size);
return true;
}
bool ConvertCharsToUnicode(std::wstring *outStr, const char *s, size_t sCharCount, unsigned codePage)
{
if (sCharCount == 0)
{
outStr->clear();
return true;
}
assert(sCharCount <= (size_t)INT_MAX);
// Phase 1 - Get buffer size.
int size = MultiByteToWideChar(codePage, 0, s, (int)sCharCount, NULL, 0);
if (size == 0)
{
outStr->clear();
return false;
}
// Phase 2 - Do conversion.
std::unique_ptr<wchar_t[]> buf(new wchar_t[(size_t)size]);
int result = MultiByteToWideChar(codePage, 0, s, (int)sCharCount, buf.get(), size);
if (result == 0)
{
outStr->clear();
return false;
}
outStr->assign(buf.get(), (size_t)size);
return true;
}
const wchar_t* PhysicalDeviceTypeToStr(VkPhysicalDeviceType type)
{
// Skipping common prefix VK_PHYSICAL_DEVICE_TYPE_
static const wchar_t* const VALUES[] = {
L"OTHER",
L"INTEGRATED_GPU",
L"DISCRETE_GPU",
L"VIRTUAL_GPU",
L"CPU",
};
return (uint32_t)type < _countof(VALUES) ? VALUES[(uint32_t)type] : L"";
}
const wchar_t* VendorIDToStr(uint32_t vendorID)
{
switch(vendorID)
{
// Skipping common prefix VK_VENDOR_ID_ for these:
case 0x10001: return L"VIV";
case 0x10002: return L"VSI";
case 0x10003: return L"KAZAN";
case 0x10004: return L"CODEPLAY";
case 0x10005: return L"MESA";
case 0x10006: return L"POCL";
// Others...
case VENDOR_ID_AMD: return L"AMD";
case VENDOR_ID_NVIDIA: return L"NVIDIA";
case VENDOR_ID_INTEL: return L"Intel";
case 0x1010: return L"ImgTec";
case 0x13B5: return L"ARM";
case 0x5143: return L"Qualcomm";
}
return L"";
}
#if VMA_VULKAN_VERSION >= 1002000
const wchar_t* DriverIDToStr(VkDriverId driverID)
{
// Skipping common prefix VK_DRIVER_ID_
static const wchar_t* const VALUES[] = {
L"",
L"AMD_PROPRIETARY",
L"AMD_OPEN_SOURCE",
L"MESA_RADV",
L"NVIDIA_PROPRIETARY",
L"INTEL_PROPRIETARY_WINDOWS",
L"INTEL_OPEN_SOURCE_MESA",
L"IMAGINATION_PROPRIETARY",
L"QUALCOMM_PROPRIETARY",
L"ARM_PROPRIETARY",
L"GOOGLE_SWIFTSHADER",
L"GGP_PROPRIETARY",
L"BROADCOM_PROPRIETARY",
L"MESA_LLVMPIPE",
L"MOLTENVK",
};
return (uint32_t)driverID < _countof(VALUES) ? VALUES[(uint32_t)driverID] : L"";
}
#endif // #if VMA_VULKAN_VERSION >= 1002000
#endif // #ifdef _WIN32

339
neo/libs/vma/src/Common.h Normal file
View file

@ -0,0 +1,339 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#ifndef COMMON_H_
#define COMMON_H_
#include "VmaUsage.h"
#ifdef _WIN32
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <algorithm>
#include <numeric>
#include <array>
#include <type_traits>
#include <utility>
#include <chrono>
#include <string>
#include <exception>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>
typedef std::chrono::high_resolution_clock::time_point time_point;
typedef std::chrono::high_resolution_clock::duration duration;
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define LINE_STRING STRINGIZE(__LINE__)
#define TEST(expr) do { if(!(expr)) { \
assert(0 && #expr); \
throw std::runtime_error(__FILE__ "(" LINE_STRING "): ( " #expr " ) == false"); \
} } while(false)
#define ERR_GUARD_VULKAN(expr) do { if((expr) < 0) { \
assert(0 && #expr); \
throw std::runtime_error(__FILE__ "(" LINE_STRING "): VkResult( " #expr " ) < 0"); \
} } while(false)
static const uint32_t VENDOR_ID_AMD = 0x1002;
static const uint32_t VENDOR_ID_NVIDIA = 0x10DE;
static const uint32_t VENDOR_ID_INTEL = 0x8086;
extern VkInstance g_hVulkanInstance;
extern VkPhysicalDevice g_hPhysicalDevice;
extern VkDevice g_hDevice;
extern VkInstance g_hVulkanInstance;
extern VmaAllocator g_hAllocator;
extern bool VK_AMD_device_coherent_memory_enabled;
void SetAllocatorCreateInfo(VmaAllocatorCreateInfo& outInfo);
inline float ToFloatSeconds(duration d)
{
return std::chrono::duration_cast<std::chrono::duration<float>>(d).count();
}
template <typename T>
inline T ceil_div(T x, T y)
{
return (x+y-1) / y;
}
template <typename T>
inline T round_div(T x, T y)
{
return (x+y/(T)2) / y;
}
template <typename T>
static inline T align_up(T val, T align)
{
return (val + align - 1) / align * align;
}
static const float PI = 3.14159265358979323846264338327950288419716939937510582f;
template<typename MainT, typename NewT>
inline void PnextChainPushFront(MainT* mainStruct, NewT* newStruct)
{
newStruct->pNext = mainStruct->pNext;
mainStruct->pNext = newStruct;
}
template<typename MainT, typename NewT>
inline void PnextChainPushBack(MainT* mainStruct, NewT* newStruct)
{
struct VkAnyStruct
{
VkStructureType sType;
void* pNext;
};
VkAnyStruct* lastStruct = (VkAnyStruct*)mainStruct;
while(lastStruct->pNext != nullptr)
{
lastStruct = (VkAnyStruct*)lastStruct->pNext;
}
newStruct->pNext = nullptr;
lastStruct->pNext = newStruct;
}
struct vec3
{
float x, y, z;
vec3() { }
vec3(float x, float y, float z) : x(x), y(y), z(z) { }
float& operator[](uint32_t index) { return *(&x + index); }
const float& operator[](uint32_t index) const { return *(&x + index); }
vec3 operator+(const vec3& rhs) const { return vec3(x + rhs.x, y + rhs.y, z + rhs.z); }
vec3 operator-(const vec3& rhs) const { return vec3(x - rhs.x, y - rhs.y, z - rhs.z); }
vec3 operator*(float s) const { return vec3(x * s, y * s, z * s); }
vec3 Normalized() const
{
return (*this) * (1.f / sqrt(x * x + y * y + z * z));
}
};
inline float Dot(const vec3& lhs, const vec3& rhs)
{
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
}
inline vec3 Cross(const vec3& lhs, const vec3& rhs)
{
return vec3(
lhs.y * rhs.z - lhs.z * rhs.y,
lhs.z * rhs.x - lhs.x * rhs.z,
lhs.x * rhs.y - lhs.y * rhs.x);
}
struct vec4
{
float x, y, z, w;
vec4() { }
vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) { }
vec4(const vec3& v, float w) : x(v.x), y(v.y), z(v.z), w(w) { }
float& operator[](uint32_t index) { return *(&x + index); }
const float& operator[](uint32_t index) const { return *(&x + index); }
};
struct mat4
{
union
{
struct
{
float _11, _12, _13, _14;
float _21, _22, _23, _24;
float _31, _32, _33, _34;
float _41, _42, _43, _44;
};
float m[4][4]; // [row][column]
};
mat4() { }
mat4(
float _11, float _12, float _13, float _14,
float _21, float _22, float _23, float _24,
float _31, float _32, float _33, float _34,
float _41, float _42, float _43, float _44) :
_11(_11), _12(_12), _13(_13), _14(_14),
_21(_21), _22(_22), _23(_23), _24(_24),
_31(_31), _32(_32), _33(_33), _34(_34),
_41(_41), _42(_42), _43(_43), _44(_44)
{
}
mat4(
const vec4& row1,
const vec4& row2,
const vec4& row3,
const vec4& row4) :
_11(row1.x), _12(row1.y), _13(row1.z), _14(row1.w),
_21(row2.x), _22(row2.y), _23(row2.z), _24(row2.w),
_31(row3.x), _32(row3.y), _33(row3.z), _34(row3.w),
_41(row4.x), _42(row4.y), _43(row4.z), _44(row4.w)
{
}
mat4 operator*(const mat4 &rhs) const
{
return mat4(
_11 * rhs._11 + _12 * rhs._21 + _13 * rhs._31 + _14 * rhs._41,
_11 * rhs._12 + _12 * rhs._22 + _13 * rhs._32 + _14 * rhs._42,
_11 * rhs._13 + _12 * rhs._23 + _13 * rhs._33 + _14 * rhs._43,
_11 * rhs._14 + _12 * rhs._24 + _13 * rhs._34 + _14 * rhs._44,
_21 * rhs._11 + _22 * rhs._21 + _23 * rhs._31 + _24 * rhs._41,
_21 * rhs._12 + _22 * rhs._22 + _23 * rhs._32 + _24 * rhs._42,
_21 * rhs._13 + _22 * rhs._23 + _23 * rhs._33 + _24 * rhs._43,
_21 * rhs._14 + _22 * rhs._24 + _23 * rhs._34 + _24 * rhs._44,
_31 * rhs._11 + _32 * rhs._21 + _33 * rhs._31 + _34 * rhs._41,
_31 * rhs._12 + _32 * rhs._22 + _33 * rhs._32 + _34 * rhs._42,
_31 * rhs._13 + _32 * rhs._23 + _33 * rhs._33 + _34 * rhs._43,
_31 * rhs._14 + _32 * rhs._24 + _33 * rhs._34 + _34 * rhs._44,
_41 * rhs._11 + _42 * rhs._21 + _43 * rhs._31 + _44 * rhs._41,
_41 * rhs._12 + _42 * rhs._22 + _43 * rhs._32 + _44 * rhs._42,
_41 * rhs._13 + _42 * rhs._23 + _43 * rhs._33 + _44 * rhs._43,
_41 * rhs._14 + _42 * rhs._24 + _43 * rhs._34 + _44 * rhs._44);
}
static mat4 RotationY(float angle)
{
const float s = sin(angle), c = cos(angle);
return mat4(
c, 0.f, -s, 0.f,
0.f, 1.f, 0.f, 0.f,
s, 0.f, c, 0.f,
0.f, 0.f, 0.f, 1.f);
}
static mat4 Perspective(float fovY, float aspectRatio, float zNear, float zFar)
{
float yScale = 1.0f / tan(fovY * 0.5f);
float xScale = yScale / aspectRatio;
return mat4(
xScale, 0.0f, 0.0f, 0.0f,
0.0f, yScale, 0.0f, 0.0f,
0.0f, 0.0f, zFar / (zFar - zNear), 1.0f,
0.0f, 0.0f, -zNear * zFar / (zFar - zNear), 0.0f);
}
static mat4 LookAt(vec3 at, vec3 eye, vec3 up)
{
vec3 zAxis = (at - eye).Normalized();
vec3 xAxis = Cross(up, zAxis).Normalized();
vec3 yAxis = Cross(zAxis, xAxis);
return mat4(
xAxis.x, yAxis.x, zAxis.x, 0.0f,
xAxis.y, yAxis.y, zAxis.y, 0.0f,
xAxis.z, yAxis.z, zAxis.z, 0.0f,
-Dot(xAxis, eye), -Dot(yAxis, eye), -Dot(zAxis, eye), 1.0f);
}
};
class RandomNumberGenerator
{
public:
RandomNumberGenerator() : m_Value{GetTickCount()} {}
RandomNumberGenerator(uint32_t seed) : m_Value{seed} { }
void Seed(uint32_t seed) { m_Value = seed; }
uint32_t Generate() { return GenerateFast() ^ (GenerateFast() >> 7); }
private:
uint32_t m_Value;
uint32_t GenerateFast() { return m_Value = (m_Value * 196314165 + 907633515); }
};
// Wrapper for RandomNumberGenerator compatible with STL "UniformRandomNumberGenerator" idea.
struct MyUniformRandomNumberGenerator
{
typedef uint32_t result_type;
MyUniformRandomNumberGenerator(RandomNumberGenerator& gen) : m_Gen(gen) { }
static uint32_t min() { return 0; }
static uint32_t max() { return UINT32_MAX; }
uint32_t operator()() { return m_Gen.Generate(); }
private:
RandomNumberGenerator& m_Gen;
};
void ReadFile(std::vector<char>& out, const char* fileName);
enum class CONSOLE_COLOR
{
INFO,
NORMAL,
WARNING,
ERROR_,
COUNT
};
void SetConsoleColor(CONSOLE_COLOR color);
void PrintMessage(CONSOLE_COLOR color, const char* msg);
void PrintMessage(CONSOLE_COLOR color, const wchar_t* msg);
inline void Print(const char* msg) { PrintMessage(CONSOLE_COLOR::NORMAL, msg); }
inline void Print(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::NORMAL, msg); }
inline void PrintWarning(const char* msg) { PrintMessage(CONSOLE_COLOR::WARNING, msg); }
inline void PrintWarning(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::WARNING, msg); }
inline void PrintError(const char* msg) { PrintMessage(CONSOLE_COLOR::ERROR_, msg); }
inline void PrintError(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::ERROR_, msg); }
void PrintMessageV(CONSOLE_COLOR color, const char* format, va_list argList);
void PrintMessageV(CONSOLE_COLOR color, const wchar_t* format, va_list argList);
void PrintMessageF(CONSOLE_COLOR color, const char* format, ...);
void PrintMessageF(CONSOLE_COLOR color, const wchar_t* format, ...);
void PrintWarningF(const char* format, ...);
void PrintWarningF(const wchar_t* format, ...);
void PrintErrorF(const char* format, ...);
void PrintErrorF(const wchar_t* format, ...);
void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize);
std::wstring SizeToStr(size_t size);
// As codePage use e.g. CP_ACP for native Windows 1-byte codepage or CP_UTF8.
bool ConvertCharsToUnicode(std::wstring *outStr, const std::string &s, unsigned codePage);
bool ConvertCharsToUnicode(std::wstring *outStr, const char *s, size_t sCharCount, unsigned codePage);
const wchar_t* PhysicalDeviceTypeToStr(VkPhysicalDeviceType type);
const wchar_t* VendorIDToStr(uint32_t vendorID);
#if VMA_VULKAN_VERSION >= 1002000
const wchar_t* DriverIDToStr(VkDriverId driverID);
#endif
#endif // #ifdef _WIN32
#endif

View file

@ -0,0 +1,32 @@
# This file will only be executed if VMA_BUILD_SAMPLE_SHADERS is set to ON
find_program(GLSL_VALIDATOR glslangValidator REQUIRED)
if(NOT GLSL_VALIDATOR)
message(FATAL_ERROR "glslangValidator not found!")
endif()
set(SHADERS
Shader.vert
Shader.frag
SparseBindingTest.comp
)
# Compile each shader using glslangValidator
foreach(SHADER ${SHADERS})
get_filename_component(FILE_NAME ${SHADER} NAME)
# Put the .spv files into the bin folder
set(SPIRV ${PROJECT_SOURCE_DIR}/bin/${FILE_NAME}.spv)
add_custom_command(
OUTPUT ${SPIRV}
# Use the same file name and append .spv to the compiled shader
COMMAND ${GLSL_VALIDATOR} -V ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER} -o ${SPIRV}
DEPENDS ${SHADER}
)
list(APPEND SPIRV_FILES ${SPIRV})
endforeach()
add_custom_target(VmaSampleShaders ALL DEPENDS ${SPIRV_FILES})

View file

@ -0,0 +1,4 @@
%VULKAN_SDK%/Bin32/glslangValidator.exe -V -o ../../bin/Shader.vert.spv Shader.vert
%VULKAN_SDK%/Bin32/glslangValidator.exe -V -o ../../bin/Shader.frag.spv Shader.frag
%VULKAN_SDK%/Bin32/glslangValidator.exe -V -o ../../bin/SparseBindingTest.comp.spv SparseBindingTest.comp
pause

View file

@ -0,0 +1,37 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 inColor;
layout(location = 1) in vec2 inTexCoord;
layout(location = 0) out vec4 outColor;
layout(binding = 1) uniform sampler2D texSampler;
void main()
{
outColor = texture(texSampler, inTexCoord);
outColor.rgb *= inColor;
}

View file

@ -0,0 +1,42 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(push_constant) uniform UniformBufferObject
{
mat4 ModelViewProj;
} ubo;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 outColor;
layout(location = 1) out vec2 outTexCoord;
void main() {
gl_Position = ubo.ModelViewProj * vec4(inPosition, 1.0);
outColor = inColor;
outTexCoord = inTexCoord;
}

View file

@ -0,0 +1,44 @@
//
// Copyright (c) 2018-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding=0) uniform sampler2D img;
layout(binding=1) buffer buf
{
uint bufValues[];
};
void main()
{
ivec2 xy = ivec2(bufValues[gl_GlobalInvocationID.x * 3],
bufValues[gl_GlobalInvocationID.x * 3 + 1]);
vec4 color = texture(img, xy);
bufValues[gl_GlobalInvocationID.x * 3 + 2] =
uint(color.r * 255.0) << 24 |
uint(color.g * 255.0) << 16 |
uint(color.b * 255.0) << 8 |
uint(color.a * 255.0);
}

View file

@ -0,0 +1,597 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Common.h"
#include "SparseBindingTest.h"
#ifdef _WIN32
////////////////////////////////////////////////////////////////////////////////
// External imports
extern VkDevice g_hDevice;
extern VmaAllocator g_hAllocator;
extern uint32_t g_FrameIndex;
extern bool g_SparseBindingEnabled;
extern VkQueue g_hSparseBindingQueue;
extern VkFence g_ImmediateFence;
extern VkCommandBuffer g_hTemporaryCommandBuffer;
void BeginSingleTimeCommands();
void EndSingleTimeCommands();
void SaveAllocatorStatsToFile(const wchar_t* filePath, bool detailed = true);
void LoadShader(std::vector<char>& out, const char* fileName);
////////////////////////////////////////////////////////////////////////////////
// Class definitions
static uint32_t CalculateMipMapCount(uint32_t width, uint32_t height, uint32_t depth)
{
uint32_t mipMapCount = 1;
while(width > 1 || height > 1 || depth > 1)
{
++mipMapCount;
width /= 2;
height /= 2;
depth /= 2;
}
return mipMapCount;
}
class BaseImage
{
public:
virtual void Init(RandomNumberGenerator& rand) = 0;
virtual ~BaseImage();
const VkImageCreateInfo& GetCreateInfo() const { return m_CreateInfo; }
void TestContent(RandomNumberGenerator& rand);
protected:
VkImageCreateInfo m_CreateInfo = {};
VkImage m_Image = VK_NULL_HANDLE;
void FillImageCreateInfo(RandomNumberGenerator& rand);
void UploadContent();
void ValidateContent(RandomNumberGenerator& rand);
};
class TraditionalImage : public BaseImage
{
public:
virtual void Init(RandomNumberGenerator& rand);
virtual ~TraditionalImage();
private:
VmaAllocation m_Allocation = VK_NULL_HANDLE;
};
class SparseBindingImage : public BaseImage
{
public:
virtual void Init(RandomNumberGenerator& rand);
virtual ~SparseBindingImage();
private:
std::vector<VmaAllocation> m_Allocations;
};
////////////////////////////////////////////////////////////////////////////////
// class BaseImage
BaseImage::~BaseImage()
{
if(m_Image)
{
vkDestroyImage(g_hDevice, m_Image, nullptr);
}
}
void BaseImage::TestContent(RandomNumberGenerator& rand)
{
printf("Validating content of %u x %u texture...\n",
m_CreateInfo.extent.width, m_CreateInfo.extent.height);
UploadContent();
ValidateContent(rand);
}
void BaseImage::FillImageCreateInfo(RandomNumberGenerator& rand)
{
constexpr uint32_t imageSizeMin = 8;
constexpr uint32_t imageSizeMax = 2048;
const bool useMipMaps = rand.Generate() % 2 != 0;
ZeroMemory(&m_CreateInfo, sizeof(m_CreateInfo));
m_CreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
m_CreateInfo.imageType = VK_IMAGE_TYPE_2D;
m_CreateInfo.extent.width = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
m_CreateInfo.extent.height = rand.Generate() % (imageSizeMax - imageSizeMin) + imageSizeMin;
m_CreateInfo.extent.depth = 1;
m_CreateInfo.mipLevels = useMipMaps ?
CalculateMipMapCount(m_CreateInfo.extent.width, m_CreateInfo.extent.height, m_CreateInfo.extent.depth) : 1;
m_CreateInfo.arrayLayers = 1;
m_CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
m_CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
m_CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
m_CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
m_CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
m_CreateInfo.flags = 0;
}
void BaseImage::UploadContent()
{
VkBufferCreateInfo srcBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
srcBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
srcBufCreateInfo.size = 4 * m_CreateInfo.extent.width * m_CreateInfo.extent.height;
VmaAllocationCreateInfo srcBufAllocCreateInfo = {};
srcBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
srcBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
VkBuffer srcBuf = nullptr;
VmaAllocation srcBufAlloc = nullptr;
VmaAllocationInfo srcAllocInfo = {};
TEST( vmaCreateBuffer(g_hAllocator, &srcBufCreateInfo, &srcBufAllocCreateInfo, &srcBuf, &srcBufAlloc, &srcAllocInfo) == VK_SUCCESS );
// Fill texels with: r = x % 255, g = u % 255, b = 13, a = 25
uint32_t* srcBufPtr = (uint32_t*)srcAllocInfo.pMappedData;
for(uint32_t y = 0, sizeY = m_CreateInfo.extent.height; y < sizeY; ++y)
{
for(uint32_t x = 0, sizeX = m_CreateInfo.extent.width; x < sizeX; ++x, ++srcBufPtr)
{
const uint8_t r = (uint8_t)x;
const uint8_t g = (uint8_t)y;
const uint8_t b = 13;
const uint8_t a = 25;
*srcBufPtr = (uint32_t)r << 24 | (uint32_t)g << 16 |
(uint32_t)b << 8 | (uint32_t)a;
}
}
BeginSingleTimeCommands();
// Barrier undefined to transfer dst.
{
VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_Image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
VK_PIPELINE_STAGE_TRANSFER_BIT, // dstStageMask
0, // dependencyFlags
0, nullptr, // memoryBarriers
0, nullptr, // bufferMemoryBarriers
1, &barrier); // imageMemoryBarriers
}
// CopyBufferToImage
{
VkBufferImageCopy region = {};
region.bufferOffset = 0;
region.bufferRowLength = 0; // Zeros mean tightly packed.
region.bufferImageHeight = 0; // Zeros mean tightly packed.
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = m_CreateInfo.extent;
vkCmdCopyBufferToImage(g_hTemporaryCommandBuffer, srcBuf, m_Image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
}
// Barrier transfer dst to fragment shader read only.
{
VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_Image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(g_hTemporaryCommandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask
0, // dependencyFlags
0, nullptr, // memoryBarriers
0, nullptr, // bufferMemoryBarriers
1, &barrier); // imageMemoryBarriers
}
EndSingleTimeCommands();
vmaDestroyBuffer(g_hAllocator, srcBuf, srcBufAlloc);
}
void BaseImage::ValidateContent(RandomNumberGenerator& rand)
{
/*
dstBuf has following layout:
For each of texels to be sampled, [0..valueCount):
struct {
in uint32_t pixelX;
in uint32_t pixelY;
out uint32_t pixelColor;
}
*/
const uint32_t valueCount = 128;
VkBufferCreateInfo dstBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
dstBufCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
dstBufCreateInfo.size = valueCount * sizeof(uint32_t) * 3;
VmaAllocationCreateInfo dstBufAllocCreateInfo = {};
dstBufAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
dstBufAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
VkBuffer dstBuf = nullptr;
VmaAllocation dstBufAlloc = nullptr;
VmaAllocationInfo dstBufAllocInfo = {};
TEST( vmaCreateBuffer(g_hAllocator, &dstBufCreateInfo, &dstBufAllocCreateInfo, &dstBuf, &dstBufAlloc, &dstBufAllocInfo) == VK_SUCCESS );
// Fill dstBuf input data.
{
uint32_t* dstBufContent = (uint32_t*)dstBufAllocInfo.pMappedData;
for(uint32_t i = 0; i < valueCount; ++i)
{
const uint32_t x = rand.Generate() % m_CreateInfo.extent.width;
const uint32_t y = rand.Generate() % m_CreateInfo.extent.height;
dstBufContent[i * 3 ] = x;
dstBufContent[i * 3 + 1] = y;
dstBufContent[i * 3 + 2] = 0;
}
}
VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerCreateInfo.unnormalizedCoordinates = VK_TRUE;
VkSampler sampler = nullptr;
TEST( vkCreateSampler( g_hDevice, &samplerCreateInfo, nullptr, &sampler) == VK_SUCCESS );
VkDescriptorSetLayoutBinding bindings[2] = {};
bindings[0].binding = 0;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].descriptorCount = 1;
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings[0].pImmutableSamplers = &sampler;
bindings[1].binding = 1;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[1].descriptorCount = 1;
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
descSetLayoutCreateInfo.bindingCount = 2;
descSetLayoutCreateInfo.pBindings = bindings;
VkDescriptorSetLayout descSetLayout = nullptr;
TEST( vkCreateDescriptorSetLayout(g_hDevice, &descSetLayoutCreateInfo, nullptr, &descSetLayout) == VK_SUCCESS );
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pipelineLayoutCreateInfo.setLayoutCount = 1;
pipelineLayoutCreateInfo.pSetLayouts = &descSetLayout;
VkPipelineLayout pipelineLayout = nullptr;
TEST( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout) == VK_SUCCESS );
std::vector<char> shaderCode;
LoadShader(shaderCode, "SparseBindingTest.comp.spv");
VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
shaderModuleCreateInfo.codeSize = shaderCode.size();
shaderModuleCreateInfo.pCode = (const uint32_t*)shaderCode.data();
VkShaderModule shaderModule = nullptr;
TEST( vkCreateShaderModule(g_hDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) == VK_SUCCESS );
VkComputePipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
pipelineCreateInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipelineCreateInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
pipelineCreateInfo.stage.module = shaderModule;
pipelineCreateInfo.stage.pName = "main";
pipelineCreateInfo.layout = pipelineLayout;
VkPipeline pipeline = nullptr;
TEST( vkCreateComputePipelines(g_hDevice, nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline) == VK_SUCCESS );
VkDescriptorPoolSize poolSizes[2] = {};
poolSizes[0].type = bindings[0].descriptorType;
poolSizes[0].descriptorCount = bindings[0].descriptorCount;
poolSizes[1].type = bindings[1].descriptorType;
poolSizes[1].descriptorCount = bindings[1].descriptorCount;
VkDescriptorPoolCreateInfo descPoolCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
descPoolCreateInfo.maxSets = 1;
descPoolCreateInfo.poolSizeCount = 2;
descPoolCreateInfo.pPoolSizes = poolSizes;
VkDescriptorPool descPool = nullptr;
TEST( vkCreateDescriptorPool(g_hDevice, &descPoolCreateInfo, nullptr, &descPool) == VK_SUCCESS );
VkDescriptorSetAllocateInfo descSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
descSetAllocInfo.descriptorPool = descPool;
descSetAllocInfo.descriptorSetCount = 1;
descSetAllocInfo.pSetLayouts = &descSetLayout;
VkDescriptorSet descSet = nullptr;
TEST( vkAllocateDescriptorSets(g_hDevice, &descSetAllocInfo, &descSet) == VK_SUCCESS );
VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
imageViewCreateInfo.image = m_Image;
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
imageViewCreateInfo.format = m_CreateInfo.format;
imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageViewCreateInfo.subresourceRange.layerCount = 1;
imageViewCreateInfo.subresourceRange.levelCount = 1;
VkImageView imageView = nullptr;
TEST( vkCreateImageView(g_hDevice, &imageViewCreateInfo, nullptr, &imageView) == VK_SUCCESS );
VkDescriptorImageInfo descImageInfo = {};
descImageInfo.imageView = imageView;
descImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkDescriptorBufferInfo descBufferInfo = {};
descBufferInfo.buffer = dstBuf;
descBufferInfo.offset = 0;
descBufferInfo.range = VK_WHOLE_SIZE;
VkWriteDescriptorSet descWrites[2] = {};
descWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrites[0].dstSet = descSet;
descWrites[0].dstBinding = bindings[0].binding;
descWrites[0].dstArrayElement = 0;
descWrites[0].descriptorCount = 1;
descWrites[0].descriptorType = bindings[0].descriptorType;
descWrites[0].pImageInfo = &descImageInfo;
descWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrites[1].dstSet = descSet;
descWrites[1].dstBinding = bindings[1].binding;
descWrites[1].dstArrayElement = 0;
descWrites[1].descriptorCount = 1;
descWrites[1].descriptorType = bindings[1].descriptorType;
descWrites[1].pBufferInfo = &descBufferInfo;
vkUpdateDescriptorSets(g_hDevice, 2, descWrites, 0, nullptr);
BeginSingleTimeCommands();
vkCmdBindPipeline(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
vkCmdBindDescriptorSets(g_hTemporaryCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descSet, 0, nullptr);
vkCmdDispatch(g_hTemporaryCommandBuffer, valueCount, 1, 1);
EndSingleTimeCommands();
// Validate dstBuf output data.
{
const uint32_t* dstBufContent = (const uint32_t*)dstBufAllocInfo.pMappedData;
for(uint32_t i = 0; i < valueCount; ++i)
{
const uint32_t x = dstBufContent[i * 3 ];
const uint32_t y = dstBufContent[i * 3 + 1];
const uint32_t color = dstBufContent[i * 3 + 2];
const uint8_t a = (uint8_t)(color >> 24);
const uint8_t b = (uint8_t)(color >> 16);
const uint8_t g = (uint8_t)(color >> 8);
const uint8_t r = (uint8_t)color;
TEST(r == (uint8_t)x && g == (uint8_t)y && b == 13 && a == 25);
}
}
vkDestroyImageView(g_hDevice, imageView, nullptr);
vkDestroyDescriptorPool(g_hDevice, descPool, nullptr);
vmaDestroyBuffer(g_hAllocator, dstBuf, dstBufAlloc);
vkDestroyPipeline(g_hDevice, pipeline, nullptr);
vkDestroyShaderModule(g_hDevice, shaderModule, nullptr);
vkDestroyPipelineLayout(g_hDevice, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(g_hDevice, descSetLayout, nullptr);
vkDestroySampler(g_hDevice, sampler, nullptr);
}
////////////////////////////////////////////////////////////////////////////////
// class TraditionalImage
void TraditionalImage::Init(RandomNumberGenerator& rand)
{
FillImageCreateInfo(rand);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// Default BEST_FIT is clearly better.
//allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT;
ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &m_CreateInfo, &allocCreateInfo,
&m_Image, &m_Allocation, nullptr) );
}
TraditionalImage::~TraditionalImage()
{
if(m_Allocation)
{
vmaFreeMemory(g_hAllocator, m_Allocation);
}
}
////////////////////////////////////////////////////////////////////////////////
// class SparseBindingImage
void SparseBindingImage::Init(RandomNumberGenerator& rand)
{
assert(g_SparseBindingEnabled && g_hSparseBindingQueue);
// Create image.
FillImageCreateInfo(rand);
m_CreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
ERR_GUARD_VULKAN( vkCreateImage(g_hDevice, &m_CreateInfo, nullptr, &m_Image) );
// Get memory requirements.
VkMemoryRequirements imageMemReq;
vkGetImageMemoryRequirements(g_hDevice, m_Image, &imageMemReq);
// This is just to silence validation layer warning.
// But it doesn't help. Looks like a bug in Vulkan validation layers.
// See: https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/364
uint32_t sparseMemReqCount = 0;
vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, nullptr);
TEST(sparseMemReqCount <= 8);
VkSparseImageMemoryRequirements sparseMemReq[8];
vkGetImageSparseMemoryRequirements(g_hDevice, m_Image, &sparseMemReqCount, sparseMemReq);
// According to Vulkan specification, for sparse resources memReq.alignment is also page size.
const VkDeviceSize pageSize = imageMemReq.alignment;
const uint32_t pageCount = (uint32_t)ceil_div<VkDeviceSize>(imageMemReq.size, pageSize);
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkMemoryRequirements pageMemReq = imageMemReq;
pageMemReq.size = pageSize;
// Allocate and bind memory pages.
m_Allocations.resize(pageCount);
std::fill(m_Allocations.begin(), m_Allocations.end(), nullptr);
std::vector<VkSparseMemoryBind> binds{pageCount};
std::vector<VmaAllocationInfo> allocInfo{pageCount};
ERR_GUARD_VULKAN( vmaAllocateMemoryPages(g_hAllocator, &pageMemReq, &allocCreateInfo, pageCount, m_Allocations.data(), allocInfo.data()) );
for(uint32_t i = 0; i < pageCount; ++i)
{
binds[i] = {};
binds[i].resourceOffset = pageSize * i;
binds[i].size = pageSize;
binds[i].memory = allocInfo[i].deviceMemory;
binds[i].memoryOffset = allocInfo[i].offset;
}
VkSparseImageOpaqueMemoryBindInfo imageBindInfo;
imageBindInfo.image = m_Image;
imageBindInfo.bindCount = pageCount;
imageBindInfo.pBinds = binds.data();
VkBindSparseInfo bindSparseInfo = { VK_STRUCTURE_TYPE_BIND_SPARSE_INFO };
bindSparseInfo.pImageOpaqueBinds = &imageBindInfo;
bindSparseInfo.imageOpaqueBindCount = 1;
ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &g_ImmediateFence) );
ERR_GUARD_VULKAN( vkQueueBindSparse(g_hSparseBindingQueue, 1, &bindSparseInfo, g_ImmediateFence) );
ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &g_ImmediateFence, VK_TRUE, UINT64_MAX) );
}
SparseBindingImage::~SparseBindingImage()
{
vmaFreeMemoryPages(g_hAllocator, m_Allocations.size(), m_Allocations.data());
}
////////////////////////////////////////////////////////////////////////////////
// Private functions
////////////////////////////////////////////////////////////////////////////////
// Public functions
void TestSparseBinding()
{
wprintf(L"TESTING SPARSE BINDING:\n");
struct ImageInfo
{
std::unique_ptr<BaseImage> image;
uint32_t endFrame;
};
std::vector<ImageInfo> images;
constexpr uint32_t frameCount = 1000;
constexpr uint32_t imageLifeFramesMin = 1;
constexpr uint32_t imageLifeFramesMax = 400;
RandomNumberGenerator rand(4652467);
for(uint32_t frameIndex = 0; frameIndex < frameCount; ++frameIndex)
{
// Bump frame index.
++g_FrameIndex;
vmaSetCurrentFrameIndex(g_hAllocator, g_FrameIndex);
// Create one new, random image.
ImageInfo imageInfo;
//imageInfo.image = std::make_unique<TraditionalImage>();
imageInfo.image = std::make_unique<SparseBindingImage>();
imageInfo.image->Init(rand);
imageInfo.endFrame = g_FrameIndex + rand.Generate() % (imageLifeFramesMax - imageLifeFramesMin) + imageLifeFramesMin;
images.push_back(std::move(imageInfo));
// Delete all images that expired.
for(size_t imageIndex = images.size(); imageIndex--; )
{
if(g_FrameIndex >= images[imageIndex].endFrame)
{
images.erase(images.begin() + imageIndex);
}
}
}
SaveAllocatorStatsToFile(L"SparseBindingTest.json");
// Choose biggest image. Test uploading and sampling.
BaseImage* biggestImage = nullptr;
for(size_t i = 0, count = images.size(); i < count; ++i)
{
if(!biggestImage ||
images[i].image->GetCreateInfo().extent.width * images[i].image->GetCreateInfo().extent.height >
biggestImage->GetCreateInfo().extent.width * biggestImage->GetCreateInfo().extent.height)
{
biggestImage = images[i].image.get();
}
}
assert(biggestImage);
biggestImage->TestContent(rand);
// Free remaining images.
images.clear();
wprintf(L"Done.\n");
}
#endif // #ifdef _WIN32

View file

@ -0,0 +1,29 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#pragma once
#ifdef _WIN32
void TestSparseBinding();
#endif // #ifdef _WIN32

8069
neo/libs/vma/src/Tests.cpp Normal file

File diff suppressed because it is too large Load diff

32
neo/libs/vma/src/Tests.h Normal file
View file

@ -0,0 +1,32 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#ifndef TESTS_H_
#define TESTS_H_
#ifdef _WIN32
void Test();
#endif // #ifdef _WIN32
#endif

View file

@ -0,0 +1,30 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/*
In exactly one CPP file define macro VMA_IMPLEMENTATION and then include
vk_mem_alloc.h to include definitions of its internal implementation
*/
#define VMA_IMPLEMENTATION
#include "VmaUsage.h"

107
neo/libs/vma/src/VmaUsage.h Normal file
View file

@ -0,0 +1,107 @@
//
// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#ifndef VMA_USAGE_H_
#define VMA_USAGE_H_
#ifdef _WIN32
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#if !defined(VK_USE_PLATFORM_WIN32_KHR)
#define VK_USE_PLATFORM_WIN32_KHR
#endif // #if !defined(VK_USE_PLATFORM_WIN32_KHR)
#else // #ifdef _WIN32
#include <vulkan/vulkan.h>
#endif // #ifdef _WIN32
#ifdef _MSVC_LANG
// Uncomment to test including `vulkan.h` on your own before including VMA.
//#include <vulkan/vulkan.h>
/*
In every place where you want to use Vulkan Memory Allocator, define appropriate
macros if you want to configure the library and then include its header to
include all public interface declarations. Example:
*/
//#define VMA_HEAVY_ASSERT(expr) assert(expr)
//#define VMA_DEDICATED_ALLOCATION 0
//#define VMA_DEBUG_MARGIN 16
//#define VMA_DEBUG_DETECT_CORRUPTION 1
//#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY 256
//#define VMA_USE_STL_SHARED_MUTEX 0
//#define VMA_MEMORY_BUDGET 0
//#define VMA_STATS_STRING_ENABLED 0
//#define VMA_MAPPING_HYSTERESIS_ENABLED 0
//#define VMA_VULKAN_VERSION 1003000 // Vulkan 1.3
//#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2
//#define VMA_VULKAN_VERSION 1001000 // Vulkan 1.1
//#define VMA_VULKAN_VERSION 1000000 // Vulkan 1.0
/*
#define VMA_DEBUG_LOG(format, ...) do { \
printf(format, __VA_ARGS__); \
printf("\n"); \
} while(false)
*/
#pragma warning(push, 4)
#pragma warning(disable: 4127) // conditional expression is constant
#pragma warning(disable: 4100) // unreferenced formal parameter
#pragma warning(disable: 4189) // local variable is initialized but not referenced
#pragma warning(disable: 4324) // structure was padded due to alignment specifier
#endif // #ifdef _MSVC_LANG
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare" // comparison of unsigned expression < 0 is always false
#pragma clang diagnostic ignored "-Wunused-private-field"
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#pragma clang diagnostic ignored "-Wnullability-completeness"
#endif
#include "../include/vk_mem_alloc.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#ifdef _MSVC_LANG
#pragma warning(pop)
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_package(Vulkan REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/VulkanMemoryAllocatorTargets.cmake")
check_required_components("@PROJECT_NAME@")

View file

@ -0,0 +1,32 @@
include(GNUInstallDirs)
target_include_directories(VulkanMemoryAllocator PUBLIC
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDE_DIRS}>
)
install(TARGETS VulkanMemoryAllocator
EXPORT VulkanMemoryAllocatorTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# install(FILES "${PROJECT_SOURCE_DIR}/include/vk_mem_alloc.h" DESTINATION "include")
install(FILES "${PROJECT_SOURCE_DIR}/include/vk_mem_alloc.h"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT VulkanMemoryAllocatorTargets
FILE VulkanMemoryAllocatorTargets.cmake
NAMESPACE VulkanMemoryAllocator::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanMemoryAllocator
)
include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/VulkanMemoryAllocatorConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanMemoryAllocator
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/VulkanMemoryAllocatorConfig.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VulkanMemoryAllocator
)

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="VmaRawList&lt;*&gt;">
<DisplayString>{{ Count={m_Count} }}</DisplayString>
<Expand>
<Item Name="[Count]">m_Count</Item>
<LinkedListItems>
<Size>m_Count</Size>
<HeadPointer>m_pFront</HeadPointer>
<NextPointer>pNext</NextPointer>
<ValueNode>Value</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="VmaList&lt;*&gt;">
<DisplayString>{{ Count={m_RawList.m_Count} }}</DisplayString>
<Expand>
<Item Name="[Count]">m_RawList.m_Count</Item>
<LinkedListItems>
<Size>m_RawList.m_Count</Size>
<HeadPointer>m_RawList.m_pFront</HeadPointer>
<NextPointer>pNext</NextPointer>
<ValueNode>Value</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="VmaVector&lt;*&gt;">
<DisplayString>{{ Count={m_Count} }}</DisplayString>
<Expand>
<Item Name="[Count]">m_Count</Item>
<Item Name="[Capacity]">m_Capacity</Item>
<ArrayItems>
<Size>m_Count</Size>
<ValuePointer>m_pArray</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!--
Due to custom way of accesing next items in
VmaIntrusiveLinkedList via methods in provided type traits,
every specialization must be manually added with
custom <NextPointer> field describing proper way of iterating the list.
-->
<Type Name="VmaIntrusiveLinkedList&lt;VmaDedicatedAllocationListItemTraits&gt;">
<DisplayString>{{ Count={m_Count} }}</DisplayString>
<Expand>
<Item Name="[Count]">m_Count</Item>
<LinkedListItems>
<Size>m_Count</Size>
<HeadPointer>m_Front</HeadPointer>
<NextPointer>m_DedicatedAllocation.m_Next</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="VmaIntrusiveLinkedList&lt;VmaPoolListItemTraits&gt;">
<DisplayString>{{ Count={m_Count} }}</DisplayString>
<Expand>
<Item Name="[Count]">m_Count</Item>
<LinkedListItems>
<Size>m_Count</Size>
<HeadPointer>m_Front</HeadPointer>
<NextPointer>m_NextPool</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
</AutoVisualizer>

View file

@ -0,0 +1,163 @@
{
"$id": "https://gpuopen.com/vulkan-memory-allocator/schemas/GpuMemDump",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"General": {
"type": "object",
"properties": {
"API": {"type": "string", "enum": ["Vulkan", "Direct3D 12"]},
"GPU": {"type": "string"}
},
"required": ["API", "GPU"]
},
"Total": {"$ref": "#/$defs/Stats"},
"MemoryInfo": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"Flags": {
"type": "array",
"items": {"type": "string"}
},
"Size": {"type": "integer"},
"Budget": {
"type": "object",
"properties": {
"BudgetBytes": {"type": "integer"},
"UsageBytes": {"type": "integer"}
},
"additionalProperties": false
},
"Stats": {"$ref": "#/$defs/Stats"},
"MemoryPools": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"Flags": {
"type": "array",
"items": {"type": "string"}
},
"Stats": {"$ref": "#/$defs/Stats"}
},
"additionalProperties": false
}
}
},
"required": ["Budget", "Stats"],
"additionalProperties": false
}
},
"DefaultPools": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"PreferredBlockSize": {"type": "integer"},
"Blocks": {
"type": "object",
"propertyNames": {"pattern": "[0-9]+"},
"additionalProperties": {"$ref": "#/$defs/Block"}
},
"DedicatedAllocations": {
"type": "array",
"items": {"$ref": "#/$defs/DedicatedAllocation"}
}
}
}
},
"CustomPools": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "object",
"properties": {
"Name": {"type": "string"},
"Flags": {"type": "array"},
"PreferredBlockSize": {"type": "integer"},
"Blocks": {
"type": "object",
"additionalProperties": {"$ref": "#/$defs/Block"}
},
"DedicatedAllocations": {
"type": "array",
"items": {"$ref": "#/$defs/DedicatedAllocation"}
}
},
"required": ["PreferredBlockSize"],
"additionalProperties": false
}
}
}
},
"required": ["General", "Total", "MemoryInfo"],
"additionalProperties": false,
"$defs": {
"CustomData": {
"type": "string",
"pattern": "^[0-9a-zA-Z]+$"
},
"Stats": {
"type": "object",
"properties": {
"BlockCount": {"type": "integer"},
"BlockBytes": {"type": "integer"},
"AllocationCount": {"type": "integer"},
"AllocationBytes": {"type": "integer"},
"UnusedRangeCount": {"type": "integer"},
"AllocationSizeMin": {"type": "integer"},
"AllocationSizeMax": {"type": "integer"},
"UnusedRangeSizeMin": {"type": "integer"},
"UnusedRangeSizeMax": {"type": "integer"}
},
"required": [
"BlockCount", "BlockBytes",
"AllocationCount", "AllocationBytes",
"UnusedRangeCount"
],
"additionalProperties": false
},
"Block": {
"type": "object",
"properties": {
"MapRefCount": {"type": "integer"},
"TotalBytes": {"type": "integer"},
"UnusedBytes": {"type": "integer"},
"Allocations": {"type": "integer"},
"UnusedRanges": {"type": "integer"},
"Suballocations": {"type": "array", "items": {"$ref": "#/$defs/Suballocation"}}
},
"required": ["TotalBytes", "UnusedBytes", "Allocations", "UnusedRanges"]
},
"DedicatedAllocation": {
"type": "object",
"properties": {
"Type": {"type": "string"},
"Size": {"type": "integer"},
"Usage": {"type": "integer"},
"CustomData": {"$ref": "#/$defs/CustomData"},
"Name": {"type": "string"},
"Layout": {"type": "integer"}
},
"required": ["Type", "Size"],
"additionalProperties": false
},
"Suballocation": {
"type": "object",
"properties": {
"Offset": {"type": "integer"},
"Type": {"type": "string"},
"Size": {"type": "integer"},
"Usage": {"type": "integer"},
"CustomData": {"$ref": "#/$defs/CustomData"},
"Name": {"type": "string"},
"Layout": {"type": "integer"}
},
"required": ["Offset", "Type", "Size"],
"additionalProperties": false
}
}
}

View file

@ -0,0 +1,333 @@
#
# Copyright (c) 2018-2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import argparse
import json
from PIL import Image, ImageDraw, ImageFont
PROGRAM_VERSION = 'Vulkan/D3D12 Memory Allocator Dump Visualization 3.0.1'
IMG_WIDTH = 1200
IMG_MARGIN = 8
TEXT_MARGIN = 4
FONT_SIZE = 10
MAP_SIZE = 24
COLOR_TEXT_H1 = (0, 0, 0, 255)
COLOR_TEXT_H2 = (150, 150, 150, 255)
COLOR_OUTLINE = (155, 155, 155, 255)
COLOR_OUTLINE_HARD = (0, 0, 0, 255)
COLOR_GRID_LINE = (224, 224, 224, 255)
currentApi = ""
data = {}
def ParseArgs():
argParser = argparse.ArgumentParser(description='Visualization of Vulkan/D3D12 Memory Allocator JSON dump.')
argParser.add_argument('DumpFile', help='Path to source JSON file with memory dump created by Vulkan/D3D12 Memory Allocator library')
argParser.add_argument('-v', '--version', action='version', version=PROGRAM_VERSION)
argParser.add_argument('-o', '--output', required=True, help='Path to destination image file (e.g. PNG)')
return argParser.parse_args()
def GetDataForMemoryPool(poolTypeName):
global data
if poolTypeName in data:
return data[poolTypeName]
else:
newPoolData = {'DedicatedAllocations':[], 'Blocks':[], 'CustomPools':{}}
data[poolTypeName] = newPoolData
return newPoolData
def ProcessBlock(poolData, block):
blockInfo = {'ID': block[0], 'Size': int(block[1]['TotalBytes']), 'Suballocations':[]}
for alloc in block[1]['Suballocations']:
allocData = {'Type': alloc['Type'], 'Size': int(alloc['Size']), 'Usage': int(alloc['Usage']) if 'Usage' in alloc else 0 }
blockInfo['Suballocations'].append(allocData)
poolData['Blocks'].append(blockInfo)
def IsDataEmpty():
global data
for poolData in data.values():
if len(poolData['DedicatedAllocations']) > 0:
return False
if len(poolData['Blocks']) > 0:
return False
for customPool in poolData['CustomPools'].values():
if len(customPool['Blocks']) > 0:
return False
if len(customPool['DedicatedAllocations']) > 0:
return False
return True
def RemoveEmptyType():
global data
for poolType in list(data.keys()):
if len(data[poolType]['DedicatedAllocations']) > 0:
continue
if len(data[poolType]['Blocks']) > 0:
continue
empty = True
for customPool in data[poolType]['CustomPools'].values():
if len(data[poolType]['Blocks']) > 0:
empty = False
break
if len(data[poolType]['DedicatedAllocations']) > 0:
empty = False
break
if empty:
del data[poolType]
# Returns tuple:
# [0] image height : integer
# [1] pixels per byte : float
def CalcParams():
global data
height = IMG_MARGIN
height += FONT_SIZE + IMG_MARGIN # Grid lines legend - sizes
maxBlockSize = 0
# Get height occupied by every memory pool
for poolData in data.values():
height += FONT_SIZE + IMG_MARGIN # Memory pool title
height += len(poolData['Blocks']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
height += len(poolData['DedicatedAllocations']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
# Get longest block size
for dedicatedAlloc in poolData['DedicatedAllocations']:
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
for block in poolData['Blocks']:
maxBlockSize = max(maxBlockSize, block['Size'])
# Same for custom pools
for customPoolData in poolData['CustomPools'].values():
height += len(customPoolData['Blocks']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
height += len(customPoolData['DedicatedAllocations']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
# Get longest block size
for dedicatedAlloc in customPoolData['DedicatedAllocations']:
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
for block in customPoolData['Blocks']:
maxBlockSize = max(maxBlockSize, block['Size'])
return height, (IMG_WIDTH - IMG_MARGIN * 2) / float(maxBlockSize)
def BytesToStr(bytes):
if bytes < 1024:
return "%d B" % bytes
bytes /= 1024
if bytes < 1024:
return "%d KiB" % bytes
bytes /= 1024
if bytes < 1024:
return "%d MiB" % bytes
bytes /= 1024
return "%d GiB" % bytes
def TypeToColor(type, usage):
global currentApi
if type == 'FREE':
return 220, 220, 220, 255
elif type == 'UNKNOWN':
return 175, 175, 175, 255 # Gray
if currentApi == 'Vulkan':
if type == 'BUFFER':
if (usage & 0x1C0) != 0: # INDIRECT_BUFFER | VERTEX_BUFFER | INDEX_BUFFER
return 255, 148, 148, 255 # Red
elif (usage & 0x28) != 0: # STORAGE_BUFFER | STORAGE_TEXEL_BUFFER
return 255, 187, 121, 255 # Orange
elif (usage & 0x14) != 0: # UNIFORM_BUFFER | UNIFORM_TEXEL_BUFFER
return 255, 255, 0, 255 # Yellow
else:
return 255, 255, 165, 255 # Light yellow
elif type == 'IMAGE_OPTIMAL':
if (usage & 0x20) != 0: # DEPTH_STENCIL_ATTACHMENT
return 246, 128, 255, 255 # Pink
elif (usage & 0xD0) != 0: # INPUT_ATTACHMENT | TRANSIENT_ATTACHMENT | COLOR_ATTACHMENT
return 179, 179, 255, 255 # Blue
elif (usage & 0x4) != 0: # SAMPLED
return 0, 255, 255, 255 # Aqua
else:
return 183, 255, 255, 255 # Light aqua
elif type == 'IMAGE_LINEAR' :
return 0, 255, 0, 255 # Green
elif type == 'IMAGE_UNKNOWN':
return 0, 255, 164, 255 # Green/aqua
elif currentApi == 'Direct3D 12':
if type == 'BUFFER':
return 255, 255, 165, 255 # Light yellow
elif type == 'TEXTURE1D' or type == 'TEXTURE2D' or type == 'TEXTURE3D':
if (usage & 0x2) != 0: # D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL
return 246, 128, 255, 255 # Pink
elif (usage & 0x5) != 0: # D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
return 179, 179, 255, 255 # Blue
elif (usage & 0x8) == 0: # Not having D3D12_RESOURCE_FLAG_DENY_SHARED_RESOURCE
return 0, 255, 255, 255 # Aqua
else:
return 183, 255, 255, 255 # Light aqua
else:
print("Unknown graphics API!")
exit(1)
assert False
return 0, 0, 0, 255
def DrawBlock(draw, y, block, pixelsPerByte):
sizePixels = int(block['Size'] * pixelsPerByte)
draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + sizePixels, y + MAP_SIZE], fill=TypeToColor('FREE', 0), outline=None)
byte = 0
x = 0
lastHardLineX = -1
for alloc in block['Suballocations']:
byteEnd = byte + alloc['Size']
xEnd = int(byteEnd * pixelsPerByte)
if alloc['Type'] != 'FREE':
if xEnd > x + 1:
draw.rectangle([IMG_MARGIN + x, y, IMG_MARGIN + xEnd, y + MAP_SIZE], fill=TypeToColor(alloc['Type'], alloc['Usage']), outline=COLOR_OUTLINE)
# Hard line was been overwritten by rectangle outline: redraw it.
if lastHardLineX == x:
draw.line([IMG_MARGIN + x, y, IMG_MARGIN + x, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
else:
draw.line([IMG_MARGIN + x, y, IMG_MARGIN + x, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
lastHardLineX = x
byte = byteEnd
x = xEnd
def DrawDedicatedAllocationBlock(draw, y, dedicatedAlloc, pixelsPerByte):
sizePixels = int(dedicatedAlloc['Size'] * pixelsPerByte)
draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + sizePixels, y + MAP_SIZE], fill=TypeToColor(dedicatedAlloc['Type'], dedicatedAlloc['Usage']), outline=COLOR_OUTLINE)
if __name__ == '__main__':
args = ParseArgs()
jsonSrc = json.load(open(args.DumpFile, 'rb'))
if 'General' in jsonSrc:
currentApi = jsonSrc['General']['API']
else:
print("Wrong JSON format, cannot determine graphics API!")
exit(1)
# Process default pools
if 'DefaultPools' in jsonSrc:
for memoryPool in jsonSrc['DefaultPools'].items():
poolData = GetDataForMemoryPool(memoryPool[0])
# Get dedicated allocations
for dedicatedAlloc in memoryPool[1]['DedicatedAllocations']:
allocData = {'Type': dedicatedAlloc['Type'], 'Size': int(dedicatedAlloc['Size']), 'Usage': int(dedicatedAlloc['Usage'])}
poolData['DedicatedAllocations'].append(allocData)
# Get allocations in block vectors
for block in memoryPool[1]['Blocks'].items():
ProcessBlock(poolData, block)
# Process custom pools
if 'CustomPools' in jsonSrc:
for memoryPool in jsonSrc['CustomPools'].items():
poolData = GetDataForMemoryPool(memoryPool[0])
for pool in memoryPool[1]:
poolName = pool['Name']
poolData['CustomPools'][poolName] = {'DedicatedAllocations':[], 'Blocks':[]}
# Get dedicated allocations
for dedicatedAlloc in pool['DedicatedAllocations']:
allocData = {'Type': dedicatedAlloc['Type'], 'Size': int(dedicatedAlloc['Size']), 'Usage': int(dedicatedAlloc['Usage'])}
poolData['CustomPools'][poolName]['DedicatedAllocations'].append(allocData)
# Get allocations in block vectors
for block in pool['Blocks'].items():
ProcessBlock(poolData['CustomPools'][poolName], block)
if IsDataEmpty():
print("There is nothing to put on the image. Please make sure you generated the stats string with detailed map enabled.")
exit(1)
RemoveEmptyType()
# Calculate dimmensions and create data image
imgHeight, pixelsPerByte = CalcParams()
img = Image.new('RGB', (IMG_WIDTH, imgHeight), 'white')
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype('segoeuib.ttf')
except:
font = ImageFont.load_default()
# Draw grid lines
bytesBetweenGridLines = 32
while bytesBetweenGridLines * pixelsPerByte < 64:
bytesBetweenGridLines *= 2
byte = 0
y = IMG_MARGIN
while True:
x = int(byte * pixelsPerByte)
if x > IMG_WIDTH - 2 * IMG_MARGIN:
break
draw.line([x + IMG_MARGIN, 0, x + IMG_MARGIN, imgHeight], fill=COLOR_GRID_LINE)
if byte == 0:
draw.text((x + IMG_MARGIN + TEXT_MARGIN, y), "0", fill=COLOR_TEXT_H2, font=font)
else:
text = BytesToStr(byte)
textSize = draw.textsize(text, font=font)
draw.text((x + IMG_MARGIN - textSize[0] - TEXT_MARGIN, y), text, fill=COLOR_TEXT_H2, font=font)
byte += bytesBetweenGridLines
y += FONT_SIZE + IMG_MARGIN
# Draw main content
for memType in sorted(data.keys()):
memPoolData = data[memType]
draw.text((IMG_MARGIN, y), "Memory pool %s" % memType, fill=COLOR_TEXT_H1, font=font)
y += FONT_SIZE + IMG_MARGIN
# Draw block vectors
for block in memPoolData['Blocks']:
draw.text((IMG_MARGIN, y), "Default pool block %s" % block['ID'], fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawBlock(draw, y, block, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index = 0
# Draw dedicated allocations
for dedicatedAlloc in memPoolData['DedicatedAllocations']:
draw.text((IMG_MARGIN, y), "Dedicated allocation %d" % index, fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawDedicatedAllocationBlock(draw, y, dedicatedAlloc, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index += 1
for poolName, pool in memPoolData['CustomPools'].items():
for block in pool['Blocks']:
draw.text((IMG_MARGIN, y), "Custom pool %s block %s" % (poolName, block['ID']), fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawBlock(draw, y, block, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index = 0
for dedicatedAlloc in pool['DedicatedAllocations']:
draw.text((IMG_MARGIN, y), "Custom pool %s dedicated allocation %d" % (poolName, index), fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawDedicatedAllocationBlock(draw, y, dedicatedAlloc, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index += 1
del draw
img.save(args.output)
"""
Main data structure - variable `data` - is a dictionary. Key is string - memory type name. Value is dictionary of:
- Fixed key 'DedicatedAllocations'. Value is list of objects, each containing dictionary with:
- Fixed key 'Type'. Value is string.
- Fixed key 'Size'. Value is int.
- Fixed key 'Usage'. Value is int.
- Fixed key 'Blocks'. Value is list of objects, each containing dictionary with:
- Fixed key 'ID'. Value is int.
- Fixed key 'Size'. Value is int.
- Fixed key 'Suballocations'. Value is list of objects as above.
- Fixed key 'CustomPools'. Value is dictionary.
- Key is string with pool ID/name. Value is a dictionary with:
- Fixed key 'DedicatedAllocations'. Value is list of objects as above.
- Fixed key 'Blocks'. Value is a list of objects representing memory blocks as above.
"""

View file

@ -0,0 +1,45 @@
# GpuMemDumpVis
Vulkan/D3D12 Memory Allocator Dump Visualization.
It is an auxiliary tool that can visualize internal state of [Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) and
[D3D12 Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator) libraries on a picture.
It is a Python script that must be launched from command line with appropriate parameters.
## Requirements
- Python 3 installed
- [Pillow](http://python-pillow.org/) - Python Imaging Library (Fork) installed
## Usage
```
python GpuMemDumpVis.py -o OUTPUT_FILE INPUT_FILE
```
* `INPUT_FILE` - path to source file to be read, containing dump of internal state of the VMA/D3D12MA library in JSON format (encoding: UTF-8/UTF-16), generated using `vmaBuildStatsString()` and `D3D12MA::Allocator::BuildStatsString()` functions.
* `OUTPUT_FILE` - path to destination file to be written that will contain generated image. Image format is automatically recognized based on file extension. List of supported formats can be found [here](http://pillow.readthedocs.io/en/latest/handbook/image-file-formats.html) and includes: BMP, GIF, JPEG, PNG, TGA.
You can also use typical options:
* `-h` - to see help on command line syntax
* `-v` - to see program version number
## Example output
![Example output](README_files/ExampleOutput.png "Example output")
## Legend
* ![Free space](README_files/Legend_Bkg.png "Free space") Light gray without border - a space in Vulkan device memory block unused by any allocation.
* ![Buffer 1](README_files/Legend_Buffer_1.png "Buffer 1") Buffer with usage containing INDIRECT_BUFFER, VERTEX_BUFFER, or INDEX_BUFFER (Vulkan).
* ![Buffer 2](README_files/Legend_Buffer_2.png "Buffer 2") Buffer with usage containing STORAGE_BUFFER or STORAGE_TEXEL_BUFFER (Vulkan).
* ![Buffer 3](README_files/Legend_Buffer_3.png "Buffer 3") Buffer with usage containing UNIFORM_BUFFER or UNIFORM_TEXEL_BUFFER (Vulkan).
* ![Buffer 4](README_files/Legend_Buffer_4.png "Buffer 4") Other buffer.
* ![Image 1](README_files/Legend_Image_1.png "Image 1") Image with OPTIMAL tiling and usage containing DEPTH_STENCIL_ATTACHMENT (Vulkan) or a texture with usage containing D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL (D3D12).
* ![Image 2](README_files/Legend_Image_2.png "Image 2") Image with OPTIMAL tiling and usage containing INPUT_ATTACHMENT, TRANSIENT_ATTACHMENT or COLOR_ATTACHMENT (Vulkan), or a texture with usage containing D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS (D3D12).
* ![Image 3](README_files/Legend_Image_3.png "Image 3") Image with OPTIMAL tiling and usage containing SAMPLED (Vulkan) or a texture with usage not containing D3D12_RESOURCE_FLAG_DENY_SHARED_RESOURCE (D3D12).
* ![Image 4](README_files/Legend_Image_4.png "Image 4") Other image with OPTIMAL tiling (Vulkan) or a texture (D3D12).
* ![Image Linear](README_files/Legend_Image_Linear.png "Image Linear") Image with LINEAR tiling (Vulkan).
* ![Image Unknown](README_files/Legend_Image_Unknown.png "Image Unknown") Image with tiling unknown to the allocator (Vulkan).
* ![Unknown](README_files/Legend_Unknown.png "Unknown") Allocation of unknown type.
* ![Details](README_files/Legend_Details.png "Details") Black bar - one or more allocations of any kind too small to be visualized as filled rectangles.

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,426 @@
{
"General": {
"API": "Vulkan",
"apiVersion": "1.3.203",
"GPU": "AMD Radeon RX 6600 XT",
"deviceType": 2,
"maxMemoryAllocationCount": 4096,
"bufferImageGranularity": 1,
"nonCoherentAtomSize": 128,
"memoryHeapCount": 2,
"memoryTypeCount": 8
},
"Total": {
"BlockCount": 69,
"BlockBytes": 201392128,
"AllocationCount": 132,
"AllocationBytes": 73401500,
"UnusedRangeCount": 11,
"AllocationSizeMin": 60,
"AllocationSizeMax": 6095200,
"UnusedRangeSizeMin": 196,
"UnusedRangeSizeMax": 33550336
},
"MemoryInfo": {
"Heap 0": {
"Flags": [],
"Size": 16862150656,
"Budget": {
"BudgetBytes": 16325847040,
"UsageBytes": 122392576
},
"Stats": {
"BlockCount": 35,
"BlockBytes": 117473280,
"AllocationCount": 64,
"AllocationBytes": 33619968,
"UnusedRangeCount": 5,
"AllocationSizeMin": 1024,
"AllocationSizeMax": 2097152,
"UnusedRangeSizeMin": 49152,
"UnusedRangeSizeMax": 33550336
},
"MemoryPools": {
"Type 1": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT"],
"Stats": {
"BlockCount": 5,
"BlockBytes": 33558528,
"AllocationCount": 8,
"AllocationBytes": 8192,
"UnusedRangeCount": 1,
"AllocationSizeMin": 1024,
"AllocationSizeMax": 1024
}
},
"Type 3": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "HOST_CACHED"],
"Stats": {
"BlockCount": 30,
"BlockBytes": 83914752,
"AllocationCount": 56,
"AllocationBytes": 33611776,
"UnusedRangeCount": 4,
"AllocationSizeMin": 1024,
"AllocationSizeMax": 2097152,
"UnusedRangeSizeMin": 49152,
"UnusedRangeSizeMax": 25100288
}
},
"Type 5": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
},
"Type 7": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "HOST_CACHED", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
}
}
},
"Heap 1": {
"Flags": ["DEVICE_LOCAL", "MULTI_INSTANCE"],
"Size": 8573157376,
"Budget": {
"BudgetBytes": 7737008128,
"UsageBytes": 155025408
},
"Stats": {
"BlockCount": 34,
"BlockBytes": 83918848,
"AllocationCount": 68,
"AllocationBytes": 39781532,
"UnusedRangeCount": 6,
"AllocationSizeMin": 60,
"AllocationSizeMax": 6095200,
"UnusedRangeSizeMin": 196,
"UnusedRangeSizeMax": 25100288
},
"MemoryPools": {
"Type 0": {
"Flags": ["DEVICE_LOCAL"],
"Stats": {
"BlockCount": 34,
"BlockBytes": 83918848,
"AllocationCount": 68,
"AllocationBytes": 39781532,
"UnusedRangeCount": 6,
"AllocationSizeMin": 60,
"AllocationSizeMax": 6095200,
"UnusedRangeSizeMin": 196,
"UnusedRangeSizeMax": 25100288
}
},
"Type 2": {
"Flags": ["DEVICE_LOCAL", "HOST_VISIBLE", "HOST_COHERENT"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
},
"Type 4": {
"Flags": ["DEVICE_LOCAL", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
},
"Type 6": {
"Flags": ["DEVICE_LOCAL", "HOST_VISIBLE", "HOST_COHERENT", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
}
}
}
},
"DefaultPools": {
"Type 0": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 18987876,
"Allocations": 20,
"UnusedRanges": 4,
"Suballocations": [
{"Offset": 0, "Type": "IMAGE_OPTIMAL", "Size": 65536, "Usage": 6},
{"Offset": 65536, "Type": "BUFFER", "Size": 768, "Usage": 130},
{"Offset": 66304, "Type": "BUFFER", "Size": 60, "Usage": 66},
{"Offset": 66364, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 67388, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 68412, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 69436, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 70460, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 71484, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 72508, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 73532, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 74556, "Type": "FREE", "Size": 196},
{"Offset": 74752, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 76800, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 78848, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 80896, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 82944, "Type": "FREE", "Size": 48128},
{"Offset": 131072, "Type": "IMAGE_OPTIMAL", "Size": 6095200, "Usage": 32},
{"Offset": 6226272, "Type": "FREE", "Size": 65184},
{"Offset": 6291456, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 8388608, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 10485760, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 12582912, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 14680064, "Type": "FREE", "Size": 18874368}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
},
"Type 1": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 33550336,
"Allocations": 4,
"UnusedRanges": 1,
"Suballocations": [
{"Offset": 0, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 1024, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "FREE", "Size": 33550336}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
},
"Type 2": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 3": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 25153536,
"Allocations": 12,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 1024, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 6144, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 8192, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 10240, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 12288, "Type": "FREE", "Size": 53248},
{"Offset": 65536, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 2162688, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 4259840, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 6356992, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8454144, "Type": "FREE", "Size": 25100288}
]
}
},
"DedicatedAllocations": [
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
},
"Type 4": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 5": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 6": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 7": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
}
},
"CustomPools": {
"Type 0": [
{
"Name": "0",
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 25149440,
"Allocations": 16,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 1024, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 5120, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 6144, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 7168, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8192, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 10240, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 12288, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 14336, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 16384, "Type": "FREE", "Size": 49152},
{"Offset": 65536, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 2162688, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 4259840, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 6356992, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8454144, "Type": "FREE", "Size": 25100288}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
}
],
"Type 3": [
{
"Name": "0",
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 25149440,
"Allocations": 16,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 1024, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 5120, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 6144, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 7168, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8192, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 10240, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 12288, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 14336, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 16384, "Type": "FREE", "Size": 49152},
{"Offset": 65536, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 2162688, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 4259840, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 6356992, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8454144, "Type": "FREE", "Size": 25100288}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
}
]
}
}