├── .gitignore ├── .gitmodules ├── .travis.yml ├── .vscode └── launch.json ├── CHANGES.md ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── examples └── demo.rs ├── src ├── definitions.rs ├── defragmentation.rs ├── ffi.rs ├── lib.rs ├── pool.rs └── virtual_block.rs ├── tests └── mod.rs └── wrapper.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/Vulkan-Headers"] 2 | path = vendor/Vulkan-Headers 3 | url = https://github.com/KhronosGroup/Vulkan-Headers 4 | [submodule "vendor/VulkanMemoryAllocator"] 5 | path = vendor/VulkanMemoryAllocator 6 | url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | cache: cargo 4 | 5 | matrix: 6 | allow_failures: 7 | - rust: nightly 8 | fast_finish: true 9 | 10 | include: 11 | - rust: stable 12 | 13 | - name: "Clippy" 14 | rust: nightly 15 | before_script: rustup component add clippy-preview 16 | script: cargo clippy --all 17 | 18 | - name: "rustfmt" 19 | rust: stable 20 | before_script: rustup component add rustfmt-preview 21 | script: cargo fmt --all -- --check 22 | 23 | - name: "warnings" 24 | rust: stable 25 | script: cargo check --all 26 | 27 | - name: "release" 28 | rust: stable 29 | script: 30 | - cargo build --verbose --all --release 31 | #- cargo test --verbose --all --release 32 | - cargo doc --verbose --all --release 33 | 34 | - os: osx 35 | osx_image: xcode10 36 | - os: windows 37 | 38 | script: 39 | - cargo build --all --verbose 40 | - cargo build --all --verbose --no-default-features 41 | #- cargo test --all --verbose 42 | - cargo doc --all --verbose -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/target/debug/examples/demo.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## 0.2.3 (Unreleased) 4 | 5 | * Removed `Result` return values from functions that always returned `Ok(())` 6 | 7 | ## 0.2.2 (2020-03-28) 8 | 9 | * Fixed Windows bindgen regression. 10 | 11 | ## 0.2.1 (2020-03-25) 12 | 13 | * Added `Eq`, `Hash` and further derives to enum `MemoryUsage`. 14 | * Added `Copy` to `Allocation`. 15 | * Updated VMA vendoring to commit hash `e73e988dafba80633cd9d8d1abb1ae1af0439bae`. 16 | * Updated dependencies and bindings. 17 | 18 | ## 0.2.0 (2019-11-16) 19 | 20 | * Updated VMA vendoring to commit hash `a020fb81cb67b376fb33228475f22d0d9c29f9fd`. 21 | * Implemented `vk_mem::Allocation::null()` for symmetry with vk::Image::null(). 22 | 23 | ## 0.1.9 (2019-10-29) 24 | 25 | * Removed unnecessary mut specifiers. 26 | * Implemented `std::error::Error` for `vk_mem::Error`. 27 | * Disabled usage of `failure` by default. 28 | * Updated VMA vendoring to commit hash `6ac1d3a4b732f50aef3a884ef7020cce53007065`. 29 | * Bumped all dependencies to latest versions. 30 | * Removed clone from `Allocator`, as it was unsafe and invalid. 31 | 32 | ## 0.1.8 (2019-07-14) 33 | 34 | * Allow the failure crate to be disabled through a feature toggle. 35 | * Removed the parallel feature for cc. 36 | * Removed max ash version (only require >= minimum). 37 | * Added a way to cleanup Allocator without dropping it. 38 | * Added a note to `create_image` describing VMA panic behavior in some circumstances. 39 | * Updated VMA vendoring to commit hash `195016b0348038537dbd73d45d6ccaf795bfb367`. 40 | * Regenerated bindings and added function pointer wrapping for `bind_buffer_memory2` and `bind_image_memory2`. 41 | 42 | ## 0.1.7 (2019-04-29) 43 | 44 | * Removed max ash version from dependencies. 45 | 46 | ## 0.1.6 (2019-03-25) 47 | 48 | * Fixed AllocationCreateInfo Default implementation. 49 | * Added windows-gnu to build.rs, and add cross-compilation information to README.md. 50 | 51 | ## 0.1.5 (2019-03-12) 52 | 53 | * Support both ash 0.27.1 and 0 0.28.0. 54 | * Updated vendor to latest version of VMA (fixes, optimizations). 55 | * Added CREATE_DONT_BIND allocation create flag. 56 | 57 | ## 0.1.4 (2019-03-05) 58 | 59 | * Added Sync+Send to Allocation and AllocationInfo. 60 | * Bumped ash and failure deps to latest, updated tests to comply with latest ash. 61 | * Removed unnecessary heap allocation. 62 | 63 | ## 0.1.3 (2018-12-17) 64 | 65 | **Updated to AMD VMA 2.2 release!** 66 | 67 | Notable new features: defragmentation of GPU memory, buddy algorithm, convenience functions for sparse binding. 68 | 69 | Major changes: 70 | 71 | * New, more powerful defragmentation: 72 | * `DefragmentationInfo2` 73 | * `Allocator::defragmentation_begin` 74 | * `Allocator::defragmentation_end` 75 | * Added support for defragmentation of GPU memory. 76 | * Defragmentation of CPU memory now uses `memmove` internally, so it can move data to overlapping regions. 77 | * Defragmentation of CPU memory is now available for memory types that are `ash::vk::MemoryPropertyFlags::HOST_VISIBLE` but not `ash::vk::MemoryPropertyFlags::HOST_COHERENT`. 78 | * Major internal changes in defragmentation algorithm. 79 | * Old interface (structure `DefragmentationInfo`, function `Allocator::defragment`) is now deprecated. 80 | * Added buddy algorithm, available for custom pools: 81 | * `AllocatorPoolCreateFlags::BUDDY_ALGORITHM` 82 | * Added convenience functions for multiple allocations and deallocations at once, intended for sparse binding resources: 83 | * `Allocator::allocate_memory_pages` 84 | * `Allocator::free_memory_pages` 85 | * Added function that tries to resize existing allocation in place: 86 | * `Allocator::resize_allocation` 87 | * Added flags for allocation strategy 88 | * New flags: 89 | * `AllocationCreateFlags::STRATEGY_BEST_FIT` 90 | * `AllocationCreateFlags::STRATEGY_WORST_FIT` 91 | * `AllocationCreateFlags::STRATEGY_FIRST_FIT` 92 | * Their aliases: 93 | * `AllocationCreateFlags::STRATEGY_MIN_MEMORY` 94 | * `AllocationCreateFlags::STRATEGY_MIN_TIME` 95 | * `AllocationCreateFlags::STRATEGY_MIN_FRAGMENTATION` 96 | 97 | Minor changes: 98 | 99 | * Changed behavior of allocation functions to return `ash::vk::Result::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. 100 | * Internal optimization: using read-write mutex on some platforms. 101 | * Many additions and fixes in documentation. Many compatibility fixes for various compilers. Other internal bugfixes, optimizations, refactoring, added more internal validation... 102 | 103 | ## 0.1.2 (2018-12-11) 104 | 105 | * Minor documentation tweak. 106 | 107 | ## 0.1.1 (2018-12-11) 108 | 109 | * Major refactors. 110 | * Full documentation pass. 111 | 112 | ## 0.1.0 (2018-12-11) 113 | 114 | * First release. 115 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at graham@wihlidal.ca. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vk-mem" 3 | version = "0.4.0" 4 | authors = ["Graham Wihlidal ", "Zhixing Zhang "] 5 | description = "Rust ffi bindings and idiomatic wrapper for AMD Vulkan Memory Allocator (VMA)" 6 | homepage = "https://github.com/gwihlidal/vk-mem-rs" 7 | repository = "https://github.com/gwihlidal/vk-mem-rs" 8 | documentation = "https://docs.rs/vk-mem" 9 | readme = "README.md" 10 | keywords = ["vulkan", "vk", "ash", "memory", "allocator"] 11 | categories = ["api-bindings", "rendering", "rendering::engine", "rendering::graphics-api", ] 12 | license = "MIT/Apache-2.0" 13 | build = "build.rs" 14 | include = [ 15 | "src/*.rs", 16 | "build.rs", 17 | "Cargo.toml", 18 | "vendor/VulkanMemoryAllocator/include/vk_mem_alloc.h", 19 | "vendor/Vulkan-Headers/include", 20 | "wrapper.cpp", 21 | ] 22 | edition = "2021" 23 | 24 | [badges] 25 | travis-ci = { repository = "gwihlidal/vk-mem-rs" } 26 | maintenance = { status = "actively-developed" } 27 | 28 | [dependencies] 29 | ash = { version = "0.38", default-features = false } 30 | bitflags = "2.5" 31 | 32 | [build-dependencies] 33 | cc = "1.0" 34 | 35 | [build-dependencies.bindgen] 36 | version = "0.69" 37 | optional = true 38 | 39 | [profile.release] 40 | lto = true 41 | opt-level = 3 42 | codegen-units = 1 43 | 44 | [features] 45 | default = ["loaded"] 46 | generate_bindings=["bindgen"] 47 | linked=["ash/linked"] 48 | loaded=["ash/loaded"] 49 | recording=[] 50 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Graham Wihlidal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vk-mem 2 | ======== 3 | 4 | [![vk-mem on travis-ci.com](https://travis-ci.com/gwihlidal/vk-mem-rs.svg?branch=master)](https://travis-ci.com/gwihlidal/vk-mem-rs) 5 | [![Latest version](https://img.shields.io/crates/v/vk-mem.svg)](https://crates.io/crates/vk-mem) 6 | [![Documentation](https://docs.rs/vk-mem/badge.svg)](https://docs.rs/vk-mem) 7 | [![Lines of Code](https://tokei.rs/b1/github/gwihlidal/vk-mem-rs)](https://github.com/gwihlidal/vk-mem-rs) 8 | ![MIT](https://img.shields.io/badge/license-MIT-blue.svg) 9 | ![APACHE2](https://img.shields.io/badge/license-APACHE2-blue.svg) 10 | 11 | This crate provides an FFI layer and idiomatic rust wrappers for the excellent AMD Vulkan Memory Allocator (VMA) C/C++ library. 12 | 13 | - [Documentation](https://docs.rs/vk-mem) 14 | - [Release Notes](https://github.com/gwihlidal/vk-mem-rs/releases) 15 | - [VMA GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) 16 | - [VMA Documentation](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/) 17 | - [GPU Open Announce](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) 18 | - [GPU Open Update](https://gpuopen.com/vulkan-memory-allocator-2-3-0/) 19 | 20 | ## Problem 21 | 22 | Memory allocation and resource (buffer and image) creation in Vulkan is difficult (comparing to older graphics API-s, like D3D11 or OpenGL) for several reasons: 23 | 24 | - It requires a lot of boilerplate code, just like everything else in Vulkan, because it is a low-level and high-performance API. 25 | - There is additional level of indirection: VkDeviceMemory is allocated separately from creating VkBuffer/VkImage and they must be bound together. The binding cannot be changed later - resource must be recreated. 26 | - Driver must be queried for supported memory heaps and memory types. Different IHVs provide different types of it. 27 | - It is recommended practice to allocate bigger chunks of memory and assign parts of them to particular resources. 28 | 29 | ## Features 30 | 31 | This crate can help game developers to manage memory allocations and resource creation by offering some higher-level functions: 32 | 33 | - Functions that help to choose correct and optimal memory type based on intended usage of the memory. 34 | - Required or preferred traits of the memory are expressed using higher-level description comparing to Vulkan flags. 35 | - Functions that allocate memory blocks, reserve and return parts of them (VkDeviceMemory + offset + size) to the user. 36 | - 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. 37 | - Functions that can create an image/buffer, allocate memory for it and bind them together - all in one call. 38 | 39 | Additional features: 40 | 41 | - Cross-platform 42 | - Windows 43 | - Linux 44 | - macOS (MoltenVK) 45 | - Well tested and documented API 46 | - Underlying library ships in a number of commerical game titles. 47 | - Extensive documentation (including full algorithm descriptions in the VMA repository) 48 | - Support for custom memory pools: 49 | - Create a pool with desired parameters (e.g. fixed or limited maximum size) 50 | - Allocate memory out of it. 51 | - Support for a linear or buddy allocation strategy 52 | - 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. 53 | - Detailed statistics: 54 | - Globally, per memory heap, and per memory type. 55 | - Amount of memory used 56 | - Amount of memory unused 57 | - Number of allocated blocks 58 | - Number of allocations 59 | - etc. 60 | - Debug annotations: 61 | - Associate string with name or opaque pointer to your own data with every allocation. 62 | - JSON dump: 63 | - Obtain a string in JSON format with detailed map of internal state, including list of allocations and gaps between them. 64 | - Convert this JSON dump into a picture to visualize your memory. See [tools/VmaDumpVis](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/blob/master/tools/VmaDumpVis/README.md). 65 | - Support for memory mapping: 66 | - Reference-counted internally. 67 | - Support for persistently mapped memory; just allocate with appropriate flag and you get access to mapped pointer. 68 | - Support for defragmenting allocations: 69 | - Call one function and let the library move data around to free some memory blocks and make your allocations better compacted. 70 | - Support for lost allocations: 71 | - Allocate memory with appropriate flags and let the library remove allocations that are not used for many frames to make room for new ones. 72 | - Support for non-coherent memory and flushing allocations: 73 | - `nonCoherentAtomSize` is respected automatically. 74 | - Supporting for attempting to detect incorrect mapped memory usage: 75 | - Enable initialization of all allocated memory with a bit pattern to detect usage of uninitialized or freed memory. 76 | - Enable validation of a magic number before and after every allocation to detect out-of-bounds memory corruption. 77 | 78 | ## Planned Features 79 | 80 | - Extensive unit tests and examples. 81 | - Some unit tests already, but not full coverage 82 | - Example isn't written - likely will port the VMA sample to `ash` and `vk_mem` 83 | - Record and replay allocations, for in-depth analysis of memory usage, resource transitions, etc 84 | - Check for correctness, measure performance, and gather statistics. 85 | 86 | ## Example 87 | 88 | Basic usage of this crate is very simple; advanced features are optional. 89 | 90 | After you create a `vk_mem::Allocator` instance, very little code is needed to create a buffer: 91 | 92 | ```rust 93 | // Create the buffer (GPU only, 16KiB in this example) 94 | let create_info = vk_mem::AllocationCreateInfo { 95 | usage: vk_mem::MemoryUsage::GpuOnly, 96 | ..Default::default() 97 | }; 98 | 99 | let (buffer, allocation, allocation_info) = allocator 100 | .create_buffer( 101 | &ash::vk::BufferCreateInfo::builder() 102 | .size(16 * 1024) 103 | .usage(ash::vk::BufferUsageFlags::VERTEX_BUFFER | ash::vk::BufferUsageFlags::TRANSFER_DST) 104 | .build(), 105 | &create_info, 106 | ) 107 | .unwrap(); 108 | 109 | // Do stuff with buffer! (type is ash::vk::Buffer) 110 | 111 | // Destroy the buffer 112 | allocator.destroy_buffer(buffer, &allocation).unwrap(); 113 | ``` 114 | 115 | With this one function call (`vk_mem::Allocator::create_buffer`): 116 | 117 | - `ash::vk::Buffer` (`VkBuffer`) is created. 118 | - `ash::vk::DeviceMemory` (`VkDeviceMemory`) block is allocated if needed. 119 | - An unused region of the memory block is bound to this buffer. 120 | - `vk_mem::Allocation` is created that represents memory assigned to this buffer. It can be queried for parameters like Vulkan memory handle and offset. 121 | 122 | ## MoltenVK 123 | 124 | For MoltenVK on macOS, you need to have the proper environment variables set. Something like: 125 | 126 | ```bash 127 | export SDK_PATH=/path/to/vulkansdk-macos-1.1.106.0 128 | export DYLD_LIBRARY_PATH=$SDK_PATH/macOS/lib 129 | export VK_ICD_FILENAMES=$SDK_PATH/macOS/etc/vulkan/icd.d/MoltenVK_icd.json 130 | export VK_LAYER_PATH=$SDK_PATH/macOS/etc/vulkan/explicit_layer.d 131 | cargo test 132 | ``` 133 | 134 | ## Usage 135 | 136 | Add this to your `Cargo.toml`: 137 | 138 | ```toml 139 | [dependencies] 140 | vk-mem = "0.3.0" 141 | ``` 142 | 143 | and add this to your crate root: 144 | 145 | ```rust 146 | extern crate vk_mem; 147 | ``` 148 | 149 | ## Compiling using MinGW W64 150 | 151 | Vulkan Memory Allocator requires C++11 threads. 152 | MinGW W64 does not support these by default, so you need to switch to the posix build. 153 | For example, on debian you need to run the following: 154 | 155 | ```bash 156 | update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix 157 | update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix 158 | update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix 159 | update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix 160 | ``` 161 | 162 | ## License 163 | 164 | Licensed under either of 165 | 166 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 167 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 168 | 169 | at your option. 170 | 171 | ## Credits and Special Thanks 172 | 173 | - [Adam Sawicki - AMD](https://github.com/adam-sawicki-amd) (Author of C/C++ library) 174 | - [Maik Klein](https://github.com/MaikKlein) (Author of ash - Vulkan rust bindings) 175 | - [Johan Andersson](https://github.com/repi) (Contributions) 176 | - [Patrick Minogue](https://github.com/afpatmin) (Contributions) 177 | - [Layl Conway](https://github.com/LaylConway) (Contributions) 178 | - [aloucks](https://github.com/aloucks) (Contributions) 179 | - [Henrik Rydgård](https://github.com/hrydgard) (Contributions) 180 | - [msiglreith](https://github.com/msiglreith) (Contributions) 181 | - [Maksym Pavlenko](https://github.com/mxpv) (Contributions) 182 | - [Brandon Pollack](https://github.com/brandonpollack23) (Contributions) 183 | 184 | ## Contribution 185 | 186 | Unless you explicitly state otherwise, any contribution intentionally submitted 187 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 188 | be dual licensed as above, without any additional terms or conditions. 189 | 190 | Contributions are always welcome; please look at the [issue tracker](https://github.com/gwihlidal/vk-mem-rs/issues) to see what known improvements are documented. 191 | 192 | ## Code of Conduct 193 | 194 | Contribution to the vk-mem crate is organized under the terms of the 195 | Contributor Covenant, the maintainer of vk-mem, @Neo-Zhixing, promises to 196 | intervene to uphold that code of conduct. 197 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "generate_bindings")] 2 | extern crate bindgen; 3 | extern crate cc; 4 | 5 | use std::env; 6 | 7 | fn main() { 8 | let mut build = cc::Build::new(); 9 | 10 | build.include("vendor/VulkanMemoryAllocator/include"); 11 | build.include("vendor/Vulkan-Headers/include"); 12 | 13 | // Disable VMA_ASSERT when rust assertions are disabled 14 | #[cfg(not(debug_assertions))] 15 | build.define("NDEBUG", ""); 16 | 17 | // We want to use the loader in ash, instead of requiring us to link 18 | // in vulkan.dll/.dylib in addition to ash. This is especially important 19 | // for MoltenVK, where there is no default installation path, unlike 20 | // Linux (pkconfig) and Windows (VULKAN_SDK environment variable). 21 | build.define("VMA_STATIC_VULKAN_FUNCTIONS", "0"); 22 | 23 | // This prevents VMA from trying to fetch any remaining pointers 24 | // that are still null after using the loader in ash, which can 25 | // cause linker errors. 26 | build.define("VMA_DYNAMIC_VULKAN_FUNCTIONS", "0"); 27 | 28 | // TODO: Add some configuration options under crate features 29 | //#define VMA_HEAVY_ASSERT(expr) assert(expr) 30 | //#define VMA_USE_STL_CONTAINERS 1 31 | //#define VMA_DEDICATED_ALLOCATION 0 32 | //#define VMA_DEBUG_MARGIN 16 33 | //#define VMA_DEBUG_DETECT_CORRUPTION 1 34 | //#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 35 | //#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY 256 36 | 37 | #[cfg(feature = "recording")] 38 | build.define("VMA_RECORDING_ENABLED", "1"); 39 | 40 | // Add the files we build 41 | build.file("wrapper.cpp"); 42 | 43 | let target = env::var("TARGET").unwrap(); 44 | if target.contains("darwin") { 45 | build 46 | .flag("-std=c++17") 47 | .flag("-Wno-missing-field-initializers") 48 | .flag("-Wno-unused-variable") 49 | .flag("-Wno-unused-parameter") 50 | .flag("-Wno-unused-private-field") 51 | .flag("-Wno-reorder") 52 | .flag("-Wno-nullability-completeness") 53 | .cpp_link_stdlib("c++") 54 | .cpp_set_stdlib("c++") 55 | .cpp(true); 56 | } else if target.contains("ios") { 57 | build 58 | .flag("-std=c++17") 59 | .flag("-Wno-missing-field-initializers") 60 | .flag("-Wno-unused-variable") 61 | .flag("-Wno-unused-parameter") 62 | .flag("-Wno-unused-private-field") 63 | .flag("-Wno-reorder") 64 | .cpp_link_stdlib("c++") 65 | .cpp_set_stdlib("c++") 66 | .cpp(true); 67 | } else if target.contains("android") { 68 | build 69 | .flag("-std=c++17") 70 | .flag("-Wno-missing-field-initializers") 71 | .flag("-Wno-unused-variable") 72 | .flag("-Wno-unused-parameter") 73 | .flag("-Wno-unused-private-field") 74 | .flag("-Wno-reorder") 75 | .cpp_link_stdlib("c++") 76 | .cpp(true); 77 | } else if target.contains("linux") { 78 | build 79 | .flag("-std=c++17") 80 | .flag("-Wno-missing-field-initializers") 81 | .flag("-Wno-unused-variable") 82 | .flag("-Wno-unused-parameter") 83 | .flag("-Wno-unused-private-field") 84 | .flag("-Wno-reorder") 85 | .cpp_link_stdlib("stdc++") 86 | .cpp(true); 87 | } else if target.contains("windows") && target.contains("gnu") { 88 | build 89 | .flag("-std=c++17") 90 | .flag("-Wno-missing-field-initializers") 91 | .flag("-Wno-unused-variable") 92 | .flag("-Wno-unused-parameter") 93 | .flag("-Wno-unused-private-field") 94 | .flag("-Wno-reorder") 95 | .flag("-Wno-type-limits") 96 | .cpp_link_stdlib("stdc++") 97 | .cpp(true); 98 | } 99 | 100 | build.compile("vma"); 101 | 102 | generate_bindings("src/ffi.rs"); 103 | } 104 | 105 | #[cfg(feature = "generate_bindings")] 106 | fn generate_bindings(output_file: &str) { 107 | let bindings = bindgen::Builder::default() 108 | .clang_arg("-I./wrapper") 109 | .clang_arg("-I./vendor/Vulkan-Headers/include") 110 | .header("vendor/VulkanMemoryAllocator/include/vk_mem_alloc.h") 111 | .formatter(bindgen::Formatter::Rustfmt) 112 | .size_t_is_usize(true) 113 | .blocklist_type("__darwin_.*") 114 | .allowlist_function("vma.*") 115 | .allowlist_function("PFN_vma.*") 116 | .allowlist_type("Vma.*") 117 | .parse_callbacks(Box::new(FixAshTypes)) 118 | .blocklist_type("Vk.*") 119 | .blocklist_type("PFN_vk.*") 120 | .raw_line("#![allow(non_camel_case_types)]") 121 | .raw_line("#![allow(non_snake_case)]") 122 | .raw_line("#![allow(dead_code)]") 123 | .raw_line("use ash::vk::*;") 124 | .trust_clang_mangling(false) 125 | .layout_tests(false) 126 | .rustified_enum("Vma.*") 127 | .generate() 128 | .expect("Unable to generate bindings!"); 129 | 130 | bindings 131 | .write_to_file(std::path::Path::new(output_file)) 132 | .expect("Unable to write bindings!"); 133 | } 134 | 135 | #[cfg(not(feature = "generate_bindings"))] 136 | fn generate_bindings(_: &str) {} 137 | 138 | #[cfg(feature = "generate_bindings")] 139 | #[derive(Debug)] 140 | struct FixAshTypes; 141 | 142 | #[cfg(feature = "generate_bindings")] 143 | impl bindgen::callbacks::ParseCallbacks for FixAshTypes { 144 | fn item_name(&self, original_item_name: &str) -> Option { 145 | if original_item_name.starts_with("Vk") { 146 | // Strip `Vk` prefix, will use `ash::vk::*` instead 147 | let trimmed_name = original_item_name.trim_start_matches("Vk"); 148 | if ["AllocationCallbacks"].contains(&trimmed_name) { 149 | Some(format!("{}<'static>", trimmed_name)) 150 | } else { 151 | Some(trimmed_name.to_string()) 152 | } 153 | } else if original_item_name.starts_with("PFN_vk") && original_item_name.ends_with("KHR") { 154 | // VMA uses a few extensions like `PFN_vkGetBufferMemoryRequirements2KHR`, 155 | // ash keeps these as `PFN_vkGetBufferMemoryRequirements2` 156 | Some(original_item_name.trim_end_matches("KHR").to_string()) 157 | } else { 158 | None 159 | } 160 | } 161 | 162 | // When ignoring `Vk` types, bindgen loses derives for some type. Quick workaround. 163 | fn add_derives(&self, info: &bindgen::callbacks::DeriveInfo<'_>) -> Vec { 164 | if info.name.starts_with("VmaAllocationInfo") 165 | || info.name.starts_with("VmaDefragmentationStats") 166 | { 167 | vec!["Debug".into(), "Copy".into(), "Clone".into()] 168 | } else { 169 | vec![] 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /examples/demo.rs: -------------------------------------------------------------------------------- 1 | extern crate ash; 2 | extern crate vk_mem; 3 | 4 | /* 5 | use ash::extensions::DebugReport; 6 | use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0}; 7 | use std::os::raw::{c_char, c_void}; 8 | 9 | fn extension_names() -> Vec<*const i8> { 10 | vec![DebugReport::name().as_ptr()] 11 | } 12 | 13 | unsafe extern "system" fn vulkan_debug_callback( 14 | _: ash::vk::DebugReportFlagsEXT, 15 | _: ash::vk::DebugReportObjectTypeEXT, 16 | _: u64, 17 | _: usize, 18 | _: i32, 19 | _: *const c_char, 20 | p_message: *const c_char, 21 | _: *mut c_void, 22 | ) -> u32 { 23 | println!("{:?}", ::std::ffi::CStr::from_ptr(p_message)); 24 | ash::vk::FALSE 25 | } 26 | 27 | fn verify_result(result: ash::vk::Result) { 28 | match result { 29 | ash::vk::Result::SUCCESS => { 30 | // Success 31 | } 32 | _ => { 33 | panic!(format!("Vulkan/Allocator error occurred! {}", result)); 34 | } 35 | } 36 | } 37 | */ 38 | 39 | fn main() { 40 | println!("TODO - port sample from VMA"); 41 | } 42 | -------------------------------------------------------------------------------- /src/definitions.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::{self}; 2 | use ash::vk; 3 | use ash::vk::PhysicalDevice; 4 | use ash::{Device, Instance}; 5 | use bitflags::bitflags; 6 | use std::marker::PhantomData; 7 | 8 | /// Intended usage of memory. 9 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)] 10 | pub enum MemoryUsage { 11 | /// No intended memory usage specified. 12 | /// Use other members of `AllocationCreateInfo` to specify your requirements. 13 | Unknown, 14 | 15 | /// Memory will be used on device only, so fast access from the device is preferred. 16 | /// It usually means device-local GPU (video) memory. 17 | /// No need to be mappable on host. 18 | /// It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`. 19 | /// 20 | /// Usage: 21 | /// 22 | /// - Resources written and read by device, e.g. images used as attachments. 23 | /// - Resources transferred from host once (immutable) or infrequently and read by 24 | /// device multiple times, e.g. textures to be sampled, vertex buffers, uniform 25 | /// (constant) buffers, and majority of other types of resources used on GPU. 26 | /// 27 | /// Allocation may still end up in `ash::vk::MemoryPropertyFlags::HOST_VISIBLE` memory on some implementations. 28 | /// In such case, you are free to map it. 29 | /// You can use `AllocationCreateFlags::MAPPED` with this usage type. 30 | #[deprecated(since = "0.3")] 31 | GpuOnly, 32 | 33 | /// Memory will be mappable on host. 34 | /// It usually means CPU (system) memory. 35 | /// Guarantees to be `ash::vk::MemoryPropertyFlags::HOST_VISIBLE` and `ash::vk::MemoryPropertyFlags::HOST_COHERENT`. 36 | /// CPU access is typically uncached. Writes may be write-combined. 37 | /// Resources created in this pool may still be accessible to the device, but access to them can be slow. 38 | /// It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`. 39 | /// 40 | /// Usage: Staging copy of resources used as transfer source. 41 | #[deprecated(since = "0.3")] 42 | CpuOnly, 43 | 44 | /// Memory that is both mappable on host (guarantees to be `ash::vk::MemoryPropertyFlags::HOST_VISIBLE`) and preferably fast to access by GPU. 45 | /// CPU access is typically uncached. Writes may be write-combined. 46 | /// 47 | /// Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, 48 | /// uniform buffers updated every frame or every draw call. 49 | #[deprecated(since = "0.3")] 50 | CpuToGpu, 51 | 52 | /// Memory mappable on host (guarantees to be `ash::vk::MemoryPropertFlags::HOST_VISIBLE`) and cached. 53 | /// It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`. 54 | /// 55 | /// Usage: 56 | /// 57 | /// - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping. 58 | /// - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection. 59 | #[deprecated(since = "0.3")] 60 | GpuToCpu, 61 | 62 | /// Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. 63 | #[deprecated(since = "0.3")] 64 | CpuCopy, 65 | 66 | /// Lazily allocated GPU memory having (guarantees to be `ash::vk::MemoryPropertFlags::LAZILY_ALLOCATED`). 67 | /// Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation. 68 | /// 69 | /// Usage: 70 | /// 71 | /// - Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`. 72 | /// Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. 73 | GpuLazy, 74 | 75 | /// Selects best memory type automatically. 76 | /// This flag is recommended for most common use cases. 77 | /// 78 | /// When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), 79 | /// you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT 80 | /// in VmaAllocationCreateInfo::flags. 81 | /// 82 | /// It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. 83 | /// vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() 84 | /// and not with generic memory allocation functions. 85 | Auto, 86 | 87 | /// Selects best memory type automatically with preference for GPU (device) memory. 88 | /// 89 | /// When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), 90 | /// you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT 91 | /// in VmaAllocationCreateInfo::flags. 92 | /// 93 | /// It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. 94 | /// vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() 95 | /// and not with generic memory allocation functions. 96 | AutoPreferDevice, 97 | 98 | /// Selects best memory type automatically with preference for CPU (host) memory. 99 | /// 100 | /// When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), 101 | /// you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT 102 | /// in VmaAllocationCreateInfo::flags. 103 | /// 104 | /// It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. 105 | /// vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() 106 | /// and not with generic memory allocation functions. 107 | AutoPreferHost, 108 | } 109 | 110 | bitflags! { 111 | /// Flags for configuring `Allocator` construction. 112 | pub struct AllocatorCreateFlags: u32 { 113 | /// No allocator configuration other than defaults. 114 | const NONE = 0; 115 | 116 | /// Allocator and all objects created from it will not be synchronized internally, 117 | /// so you must guarantee they are used from only one thread at a time or synchronized 118 | /// externally by you. Using this flag may increase performance because internal 119 | /// mutexes are not used. 120 | const EXTERNALLY_SYNCHRONIZED = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT as u32; 121 | 122 | /// Enables usage of `VK_KHR_dedicated_allocation` extension. 123 | /// 124 | /// Using this extenion will automatically allocate dedicated blocks of memory for 125 | /// some buffers and images instead of suballocating place for them out of bigger 126 | /// memory blocks (as if you explicitly used `AllocationCreateFlags::DEDICATED_MEMORY` flag) when it is 127 | /// recommended by the driver. It may improve performance on some GPUs. 128 | /// 129 | /// You may set this flag only if you found out that following device extensions are 130 | /// supported, you enabled them while creating Vulkan device passed as 131 | /// `AllocatorCreateInfo::device`, and you want them to be used internally by this 132 | /// library: 133 | /// 134 | /// - VK_KHR_get_memory_requirements2 135 | /// - VK_KHR_dedicated_allocation 136 | /// 137 | /// When this flag is set, you can experience following warnings reported by Vulkan 138 | /// validation layer. You can ignore them. 139 | /// `> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.` 140 | const KHR_DEDICATED_ALLOCATION = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT as u32; 141 | 142 | /// Enables usage of VK_KHR_bind_memory2 extension. 143 | /// 144 | /// The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. 145 | /// When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. 146 | /// 147 | /// You may set this flag only if you found out that this device extension is supported, 148 | /// you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, 149 | /// and you want it to be used internally by this library. 150 | /// 151 | /// The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`, 152 | /// which allow to pass a chain of `pNext` structures while binding. 153 | /// This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2(). 154 | const KHR_BIND_MEMORY2 = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT as u32; 155 | 156 | /// Enables usage of VK_EXT_memory_budget extension. 157 | /// 158 | /// You may set this flag only if you found out that this device extension is supported, 159 | /// you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, 160 | /// and you want it to be used internally by this library, along with another instance extension 161 | /// VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted). 162 | /// 163 | /// The extension provides query for current memory usage and budget, which will probably 164 | /// be more accurate than an estimation used by the library otherwise. 165 | const EXT_MEMORY_BUDGET = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT as u32; 166 | 167 | /// Enables usage of VK_AMD_device_coherent_memory extension. 168 | /// 169 | /// You may set this flag only if you: 170 | /// 171 | /// - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, 172 | /// - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device, 173 | /// - want it to be used internally by this library. 174 | /// 175 | /// The extension and accompanying device feature provide access to memory types with 176 | /// `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags. 177 | /// They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR. 178 | /// 179 | /// When the extension is not enabled, such memory types are still enumerated, but their usage is illegal. 180 | /// To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type, 181 | /// returning `VK_ERROR_FEATURE_NOT_PRESENT`. 182 | const AMD_DEVICE_COHERENT_MEMORY = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT as u32; 183 | 184 | /// You may set this flag only if you: 185 | /// 186 | /// 1. (For Vulkan version < 1.2) Found as available and enabled device extension 187 | /// VK_KHR_buffer_device_address. 188 | /// This extension is promoted to core Vulkan 1.2. 189 | /// 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`. 190 | /// 191 | /// When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA. 192 | /// The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to 193 | /// allocated memory blocks wherever it might be needed. 194 | /// 195 | /// For more information, see documentation chapter \ref enabling_buffer_device_address. 196 | const BUFFER_DEVICE_ADDRESS = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT as u32; 197 | 198 | /// Enables usage of VK_EXT_memory_priority extension in the library. 199 | /// 200 | /// You may set this flag only if you found available and enabled this device extension, 201 | /// along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`, 202 | /// while creating Vulkan device passed as VmaAllocatorCreateInfo::device. 203 | /// 204 | /// When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority 205 | /// are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored. 206 | /// 207 | /// A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. 208 | /// Larger values are higher priority. The granularity of the priorities is implementation-dependent. 209 | /// It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`. 210 | /// The value to be used for default priority is 0.5. 211 | /// For more details, see the documentation of the VK_EXT_memory_priority extension. 212 | const EXT_MEMORY_PRIORITY = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT as u32; 213 | 214 | /// Enables usage of VK_KHR_maintenance4 extension in the library. 215 | /// 216 | /// You may set this flag only if you found available and enabled this device extension, 217 | /// while creating Vulkan device passed as VmaAllocatorCreateInfo::device. 218 | const KHR_MAINTENANCE4 = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT as u32; 219 | /// Enables usage of VK_KHR_maintenance5 extension in the library. 220 | /// 221 | /// You may set this flag only if you found available and enabled this device extension, 222 | /// while creating Vulkan device passed as VmaAllocatorCreateInfo::device. 223 | const KHR_MAINTENANCE5 = ffi::VmaAllocatorCreateFlagBits::VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT as u32; 224 | } 225 | } 226 | 227 | bitflags! { 228 | /// Flags for configuring `Allocation` construction. 229 | #[derive(Clone, Copy)] 230 | pub struct AllocationCreateFlags: u32 { 231 | /// Set this flag if the allocation should have its own memory block. 232 | /// 233 | /// Use it for special, big resources, like fullscreen images used as attachments. 234 | const DEDICATED_MEMORY = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT as u32; 235 | 236 | /// Set this flag to only try to allocate from existing `ash::vk::DeviceMemory` blocks and never create new such block. 237 | /// 238 | /// If new allocation cannot be placed in any of the existing blocks, allocation 239 | /// fails with `ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY` error. 240 | /// 241 | /// You should not use `AllocationCreateFlags::DEDICATED_MEMORY` and `AllocationCreateFlags::NEVER_ALLOCATE` at the same time. It makes no sense. 242 | const NEVER_ALLOCATE = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT as u32; 243 | 244 | /// Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. 245 | /// 246 | /// Pointer to mapped memory will be returned through `Allocation::get_mapped_data()`. 247 | /// 248 | /// Is it valid to use this flag for allocation made from memory type that is not 249 | /// `ash::vk::MemoryPropertyFlags::HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is 250 | /// useful if you need an allocation that is efficient to use on GPU 251 | /// (`ash::vk::MemoryPropertyFlags::DEVICE_LOCAL`) and still want to map it directly if possible on platforms that 252 | /// support it (e.g. Intel GPU). 253 | /// 254 | /// You should not use this flag together with `AllocationCreateFlags::CAN_BECOME_LOST`. 255 | const MAPPED = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT as u32; 256 | 257 | /// Set this flag to treat `AllocationCreateInfo::user_data` as pointer to a 258 | /// null-terminated string. Instead of copying pointer value, a local copy of the 259 | /// string is made and stored in allocation's user data. The string is automatically 260 | /// freed together with the allocation. It is also used in `Allocator::build_stats_string`. 261 | #[deprecated(since = "0.3", note = "Consider using vmaSetAllocationName() instead.")] 262 | const USER_DATA_COPY_STRING = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT as u32; 263 | 264 | /// Allocation will be created from upper stack in a double stack pool. 265 | /// 266 | /// This flag is only allowed for custom pools created with `AllocatorPoolCreateFlags::LINEAR_ALGORITHM` flag. 267 | const UPPER_ADDRESS = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT as u32; 268 | 269 | /// Create both buffer/image and allocation, but don't bind them together. 270 | /// It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions. 271 | /// The flag is meaningful only with functions that bind by default, such as `Allocator::create_buffer` 272 | /// or `Allocator::create_image`. Otherwise it is ignored. 273 | /// 274 | /// If you want to make sure the new buffer/image is not tied to the new memory allocation 275 | /// through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block, 276 | /// use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT. 277 | const DONT_BIND = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_DONT_BIND_BIT as u32; 278 | 279 | /// Create allocation only if additional device memory required for it, if any, won't exceed 280 | /// memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. 281 | const WITHIN_BUDGET = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT as u32; 282 | 283 | /// Set this flag if the allocated memory will have aliasing resources. 284 | /// 285 | /// Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified. 286 | /// Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors. 287 | const CAN_ALIAS = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT as u32; 288 | 289 | /// Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). 290 | /// 291 | /// - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, 292 | /// you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. 293 | /// - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. 294 | /// This includes allocations created in custom_memory_pools. 295 | /// 296 | /// Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number, 297 | /// never read or accessed randomly, so a memory type can be selected that is uncached and write-combined. 298 | /// 299 | /// Violating this declaration may work correctly, but will likely be very slow. 300 | /// Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;` 301 | /// Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once. 302 | const HOST_ACCESS_SEQUENTIAL_WRITE = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT as u32; 303 | 304 | /// Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). 305 | /// 306 | /// - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, 307 | /// you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. 308 | /// - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. 309 | /// This includes allocations created in custom_memory_pools. 310 | /// 311 | /// Declares that mapped memory can be read, written, and accessed in random order, 312 | /// so a `HOST_CACHED` memory type is required. 313 | const HOST_ACCESS_RANDOM = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT as u32; 314 | 315 | /// Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, 316 | /// it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected 317 | /// if it may improve performance. 318 | /// 319 | /// By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type 320 | /// (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and 321 | /// issue an explicit transfer to write/read your data. 322 | /// To prepare for this possibility, don't forget to add appropriate flags like 323 | /// `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image. 324 | const HOST_ACCESS_ALLOW_TRANSFER_INSTEAD = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT as u32; 325 | 326 | /// Allocation strategy that chooses smallest possible free range for the allocation 327 | /// to minimize memory usage and fragmentation, possibly at the expense of allocation time. 328 | const STRATEGY_MIN_MEMORY = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as u32; 329 | 330 | /// Alias to `STRATEGY_MIN_MEMORY`. 331 | const STRATEGY_BEST_FIT = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as u32; 332 | 333 | /// Allocation strategy that chooses first suitable free range for the allocation - 334 | /// not necessarily in terms of the smallest offset but the one that is easiest and fastest to find 335 | /// to minimize allocation time, possibly at the expense of allocation quality. 336 | const STRATEGY_MIN_TIME = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT as u32; 337 | 338 | /// Alias to `STRATEGY_MIN_TIME`. 339 | const STRATEGY_FIRST_FIT = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT as u32; 340 | 341 | /// Allocation strategy that chooses always the lowest offset in available space. 342 | /// This is not the most efficient strategy but achieves highly packed data. 343 | /// Used internally by defragmentation, not recomended in typical usage. 344 | const STRATEGY_MIN_OFFSET = ffi::VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT as u32; 345 | } 346 | } 347 | 348 | bitflags! { 349 | /// Flags for configuring `AllocatorPool` construction. 350 | #[derive(Default, Clone, Copy)] 351 | pub struct AllocatorPoolCreateFlags: u32 { 352 | /// Use this flag if you always allocate only buffers and linear images or only optimal images 353 | /// out of this pool and so buffer-image granularity can be ignored. 354 | /// 355 | /// This is an optional optimization flag. 356 | /// 357 | /// If you always allocate using `Allocator::create_buffer`, `Allocator::create_image`, 358 | /// `Allocator::allocate_memory_for_buffer`, then you don't need to use it because allocator 359 | /// knows exact type of your allocations so it can handle buffer-image granularity 360 | /// in the optimal way. 361 | /// 362 | /// If you also allocate using `Allocator::allocate_memory_for_image` or `Allocator::allocate_memory`, 363 | /// exact type of such allocations is not known, so allocator must be conservative 364 | /// in handling buffer-image granularity, which can lead to suboptimal allocation 365 | /// (wasted memory). In that case, if you can make sure you always allocate only 366 | /// buffers and linear images or only optimal images out of this pool, use this flag 367 | /// to make allocator disregard buffer-image granularity and so make allocations 368 | /// faster and more optimal. 369 | const IGNORE_BUFFER_IMAGE_GRANULARITY = ffi::VmaPoolCreateFlagBits::VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT as u32; 370 | 371 | /// Enables alternative, linear allocation algorithm in this pool. 372 | /// 373 | /// Specify this flag to enable linear allocation algorithm, which always creates 374 | /// new allocations after last one and doesn't reuse space from allocations freed in 375 | /// between. It trades memory consumption for simplified algorithm and data 376 | /// structure, which has better performance and uses less memory for metadata. 377 | /// 378 | /// By using this flag, you can achieve behavior of free-at-once, stack, 379 | /// ring buffer, and double stack. 380 | /// 381 | /// When using this flag, you must specify PoolCreateInfo::max_block_count == 1 (or 0 for default). 382 | const LINEAR_ALGORITHM = ffi::VmaPoolCreateFlagBits::VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT as u32; 383 | 384 | /// Bit mask to extract only `*_ALGORITHM` bits from entire set of flags. 385 | const ALGORITHM_MASK = ffi::VmaPoolCreateFlagBits::VMA_POOL_CREATE_ALGORITHM_MASK as u32; 386 | } 387 | } 388 | 389 | pub struct AllocatorCreateInfo<'a> { 390 | /// Vulkan physical device. It must be valid throughout whole lifetime of created allocator. 391 | pub(crate) physical_device: PhysicalDevice, 392 | 393 | /// Vulkan device. 394 | /// It must be valid throughout the whole lifetime of created allocator. 395 | pub(crate) device: &'a Device, 396 | 397 | /// Handle to Vulkan instance object. 398 | /// Must be valid throughout the whole lifetime of created allocator. 399 | pub(crate) instance: &'a Instance, 400 | 401 | /// Flags for created allocator. 402 | pub flags: AllocatorCreateFlags, 403 | 404 | /// Preferred size of a single [`vk::DeviceMemory`] block to be allocated from large heaps > 1 GiB. Optional. 405 | /// Set to 0 to use default, which is currently 256 MiB. 406 | pub preferred_large_heap_block_size: vk::DeviceSize, 407 | 408 | /// Custom CPU memory allocation callbacks. Optional. 409 | /// When specified, will also be used for all CPU-side memory allocations. 410 | pub allocation_callbacks: Option<&'a vk::AllocationCallbacks<'a>>, 411 | 412 | /// Informative callbacks for [`vk::AllocateMemory`], [`vk::FreeMemory`]. Optional. 413 | pub device_memory_callbacks: Option<&'a ffi::VmaDeviceMemoryCallbacks>, 414 | 415 | /// An empty array, or an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. 416 | /// When it is not empty, it must be an array of [`vk::PhysicalDeviceMemoryProperties::memoryHeapCount`] elements, defining limit on 417 | /// maximum number of bytes that can be allocated out of particular Vulkan memory 418 | /// heap. 419 | /// 420 | /// Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that 421 | /// heap. This is also the default in case of `pHeapSizeLimit` = NULL. 422 | /// 423 | /// If there is a limit defined for a heap: 424 | /// - If user tries to allocate more memory from that heap using this allocator, 425 | /// the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. 426 | /// - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the 427 | /// value of this limit will be reported instead when using vmaGetMemoryProperties(). 428 | /// 429 | /// Warning! Using this feature may not be equivalent to installing a GPU with 430 | /// smaller amount of memory, because graphics driver doesn't necessary fail new 431 | /// allocations with [`VK_ERROR_OUT_OF_DEVICE_MEMORY`] result when memory capacity is 432 | /// exceeded. It may return success and just silently migrate some device memory 433 | /// blocks to system RAM. This driver behavior can also be controlled using 434 | /// VK_AMD_memory_overallocation_behavior extension. 435 | pub heap_size_limits: &'a [ash::vk::DeviceSize], 436 | /// Optional. Vulkan version that the application uses. 437 | /// It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: 438 | /// `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`. 439 | /// The patch version number specified is ignored. Only the major and minor versions are considered. 440 | /// Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation. 441 | /// Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`. 442 | /// It must match the Vulkan version used by the application and supported on the selected physical device, 443 | /// so it must be no higher than `VkApplicationInfo::apiVersion` passed to `vkCreateInstance` 444 | /// and no higher than `VkPhysicalDeviceProperties::apiVersion` found on the physical device used. 445 | pub vulkan_api_version: u32, 446 | /// Either an empty array or an array of external memory handle types for each Vulkan memory type. 447 | /// If not empty, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount` 448 | /// elements, defining external memory handle types of particular Vulkan memory type, 449 | /// to be passed using `VkExportMemoryAllocateInfoKHR`. 450 | /// 451 | /// Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type. 452 | /// This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL. 453 | pub type_external_memory_handle_types: &'a [vk::ExternalMemoryHandleTypeFlagsKHR], 454 | } 455 | 456 | impl<'a> AllocatorCreateInfo<'a> { 457 | pub fn new( 458 | instance: &'a ash::Instance, 459 | device: &'a ash::Device, 460 | physical_device: ash::vk::PhysicalDevice, 461 | ) -> AllocatorCreateInfo<'a> { 462 | AllocatorCreateInfo { 463 | physical_device, 464 | device, 465 | instance, 466 | flags: AllocatorCreateFlags::empty(), 467 | preferred_large_heap_block_size: 0, 468 | allocation_callbacks: None, 469 | device_memory_callbacks: None, 470 | heap_size_limits: &[], 471 | vulkan_api_version: 0, 472 | type_external_memory_handle_types: &[], 473 | } 474 | } 475 | } 476 | 477 | #[derive(Clone)] 478 | pub struct PoolCreateInfo<'a> { 479 | /// Vulkan memory type index to allocate this pool from. 480 | pub memory_type_index: u32, 481 | pub flags: AllocatorPoolCreateFlags, 482 | /// Size of a single [`vk::DeviceMemory`] block to be allocated as part of this pool, in bytes. Optional. 483 | /// Specify nonzero to set explicit, constant size of memory blocks used by this pool. 484 | /// Leave 0 to use default and let the library manage block sizes automatically. 485 | /// Sizes of particular blocks may vary. 486 | /// In this case, the pool will also support dedicated allocations. 487 | pub block_size: vk::DeviceSize, 488 | /// Minimum number of blocks to be always allocated in this pool, even if they stay empty. 489 | /// Set to 0 to have no preallocated blocks and allow the pool be completely empty. 490 | pub min_block_count: usize, 491 | /// Maximum number of blocks that can be allocated in this pool. Optional. 492 | /// Set to 0 to use default, which is [`usize::MAX`], which means no limit. 493 | /// Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated 494 | /// throughout whole lifetime of this pool. 495 | pub max_block_count: usize, 496 | /// A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations. 497 | /// It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object. 498 | /// Otherwise, this variable is ignored. 499 | pub priority: f32, 500 | /// Additional minimum alignment to be used for all allocations created from this pool. Can be 0. 501 | /// Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two. 502 | /// It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough, 503 | /// e.g. when doing interop with OpenGL. 504 | pub min_allocation_alignment: vk::DeviceSize, 505 | /// Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional. 506 | /// If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`. 507 | /// It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`. 508 | /// Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool. 509 | /// Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`, 510 | /// can be attached automatically by this library when using other, more convenient of its features. 511 | pub memory_allocate_next: *const std::ffi::c_void, 512 | pub _marker: PhantomData<&'a mut ()>, 513 | } 514 | impl<'a> PoolCreateInfo<'a> { 515 | pub fn push_next(&mut self, next: &'a mut T) { 516 | let info = vk::MemoryAllocateInfo { 517 | p_next: self.memory_allocate_next, 518 | ..Default::default() 519 | }; 520 | let info = info.push_next(next); 521 | self.memory_allocate_next = info.p_next; 522 | } 523 | } 524 | impl Default for PoolCreateInfo<'_> { 525 | fn default() -> Self { 526 | Self { 527 | memory_type_index: 0, 528 | flags: AllocatorPoolCreateFlags::empty(), 529 | block_size: 0, 530 | min_block_count: 0, 531 | max_block_count: 0, 532 | priority: 0.0, 533 | min_allocation_alignment: 0, 534 | memory_allocate_next: std::ptr::null_mut(), 535 | _marker: PhantomData, 536 | } 537 | } 538 | } 539 | 540 | #[derive(Clone)] 541 | pub struct AllocationCreateInfo { 542 | pub flags: AllocationCreateFlags, 543 | /// Intended usage of memory. 544 | /// 545 | /// You can leave `MemoryUsage::Unknown` if you specify memory requirements in other way. 546 | /// 547 | /// If `pool` is not null, this member is ignored. 548 | pub usage: MemoryUsage, 549 | /// Flags that must be set in a Memory Type chosen for an allocation. 550 | /// 551 | /// Leave 0 if you specify memory requirements in other way. 552 | /// 553 | /// If `pool` is not null, this member is ignored. 554 | pub required_flags: vk::MemoryPropertyFlags, 555 | /// Flags that preferably should be set in a memory type chosen for an allocation."] 556 | /// 557 | /// Set to 0 if no additional flags are preferred. 558 | /// If `pool` is not null, this member is ignored. 559 | pub preferred_flags: vk::MemoryPropertyFlags, 560 | /// Bitmask containing one bit set for every memory type acceptable for this allocation. 561 | /// 562 | /// Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if 563 | /// it meets other requirements specified by this structure, with no further 564 | /// restrictions on memory type index. 565 | /// 566 | /// If `pool` is not null, this member is ignored. 567 | pub memory_type_bits: u32, 568 | /// Custom general-purpose pointer that will be stored in `Allocation`, 569 | /// can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). 570 | /// 571 | /// If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either 572 | /// null or pointer to a null-terminated string. The string will be then copied to 573 | /// internal buffer, so it doesn't need to be valid after allocation call. 574 | pub user_data: usize, 575 | /// A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. 576 | /// 577 | /// It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object 578 | /// and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. 579 | /// Otherwise, it has the priority of a memory block where it is placed and this variable is ignored. 580 | pub priority: f32, 581 | } 582 | 583 | impl Default for AllocationCreateInfo { 584 | fn default() -> Self { 585 | Self { 586 | flags: AllocationCreateFlags::empty(), 587 | usage: MemoryUsage::Unknown, 588 | required_flags: vk::MemoryPropertyFlags::empty(), 589 | preferred_flags: vk::MemoryPropertyFlags::empty(), 590 | memory_type_bits: 0, 591 | user_data: 0, 592 | priority: 0.0, 593 | } 594 | } 595 | } 596 | 597 | impl From<&AllocationCreateInfo> for ffi::VmaAllocationCreateInfo { 598 | fn from(info: &AllocationCreateInfo) -> Self { 599 | let usage = match info.usage { 600 | MemoryUsage::Unknown => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_UNKNOWN, 601 | #[allow(deprecated)] 602 | MemoryUsage::GpuOnly => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY, 603 | #[allow(deprecated)] 604 | MemoryUsage::CpuOnly => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_ONLY, 605 | #[allow(deprecated)] 606 | MemoryUsage::CpuToGpu => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_TO_GPU, 607 | #[allow(deprecated)] 608 | MemoryUsage::GpuToCpu => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_TO_CPU, 609 | #[allow(deprecated)] 610 | MemoryUsage::CpuCopy => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_COPY, 611 | MemoryUsage::GpuLazy => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED, 612 | MemoryUsage::Auto => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO, 613 | MemoryUsage::AutoPreferDevice => { 614 | ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE 615 | } 616 | MemoryUsage::AutoPreferHost => ffi::VmaMemoryUsage::VMA_MEMORY_USAGE_AUTO_PREFER_HOST, 617 | }; 618 | ffi::VmaAllocationCreateInfo { 619 | flags: info.flags.bits(), 620 | usage, 621 | requiredFlags: info.required_flags, 622 | preferredFlags: info.preferred_flags, 623 | memoryTypeBits: info.memory_type_bits, 624 | pool: std::ptr::null_mut(), 625 | pUserData: info.user_data as _, 626 | priority: info.priority, 627 | } 628 | } 629 | } 630 | 631 | /// Parameters of `Allocation` objects, that can be retrieved using `Allocator::get_allocation_info`. 632 | #[derive(Debug, Clone)] 633 | pub struct AllocationInfo { 634 | /// Memory type index that this allocation was allocated from. It never changes. 635 | pub memory_type: u32, 636 | /// Handle to Vulkan memory object. 637 | /// 638 | /// Same memory object can be shared by multiple allocations. 639 | /// 640 | /// It can change after the allocation is moved during \\ref defragmentation. 641 | pub device_memory: vk::DeviceMemory, 642 | /// Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation. 643 | /// 644 | /// You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function 645 | /// vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image, 646 | /// not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation 647 | /// and apply this offset automatically. 648 | /// 649 | /// It can change after the allocation is moved during \\ref defragmentation. 650 | pub offset: vk::DeviceSize, 651 | /// Size of this allocation, in bytes. It never changes. 652 | /// 653 | /// Allocation size returned in this variable may be greater than the size 654 | /// requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the 655 | /// allocation is accessible for operations on memory e.g. using a pointer after 656 | /// mapping with vmaMapMemory(), but operations on the resource e.g. using 657 | /// `vkCmdCopyBuffer` must be limited to the size of the resource. 658 | pub size: vk::DeviceSize, 659 | /// Pointer to the beginning of this allocation as mapped data. 660 | /// 661 | /// If the allocation hasn't been mapped using vmaMapMemory() and hasn't been 662 | /// created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null. 663 | /// 664 | /// It can change after call to vmaMapMemory(), vmaUnmapMemory(). 665 | /// It can also change after the allocation is moved during defragmentation. 666 | pub mapped_data: *mut ::std::os::raw::c_void, 667 | /// Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). 668 | /// 669 | /// It can change after call to vmaSetAllocationUserData() for this allocation. 670 | pub user_data: usize, 671 | } 672 | 673 | impl From<&ffi::VmaAllocationInfo> for AllocationInfo { 674 | fn from(info: &ffi::VmaAllocationInfo) -> Self { 675 | Self { 676 | memory_type: info.memoryType, 677 | device_memory: info.deviceMemory, 678 | offset: info.offset, 679 | size: info.size, 680 | mapped_data: info.pMappedData, 681 | user_data: info.pUserData as _, 682 | } 683 | } 684 | } 685 | impl From for AllocationInfo { 686 | fn from(info: ffi::VmaAllocationInfo) -> Self { 687 | (&info).into() 688 | } 689 | } 690 | 691 | /// Extended parameters of a VmaAllocation object that can be retrieved using `Allocation::get_allocation_info2`. 692 | #[derive(Debug, Clone)] 693 | pub struct AllocationInfo2 { 694 | /// Basic parameters of the allocation. 695 | /// 696 | /// If you need only these, you can use function vmaGetAllocationInfo() and structure #VmaAllocationInfo instead. 697 | pub allocation_info: AllocationInfo, 698 | /// Size of the `VkDeviceMemory` block that the allocation belongs to. 699 | /// 700 | /// In case of an allocation with dedicated memory, it will be equal to `allocationInfo.size`. 701 | pub block_size: u64, 702 | /// `true` if the allocation has dedicated memory, `false` if it was placed as part of a larger memory block. 703 | /// 704 | /// When `true`, it also means `VkMemoryDedicatedAllocateInfo` was used when creating the allocation 705 | /// (if VK_KHR_dedicated_allocation extension or Vulkan version >= 1.1 is enabled). 706 | pub dedicated_memory: bool, 707 | } 708 | 709 | impl From<&ffi::VmaAllocationInfo2> for AllocationInfo2 { 710 | fn from(info: &ffi::VmaAllocationInfo2) -> Self { 711 | Self { 712 | allocation_info: info.allocationInfo.into(), 713 | block_size: info.blockSize, 714 | dedicated_memory: info.dedicatedMemory != 0 715 | } 716 | } 717 | } 718 | impl From for AllocationInfo2 { 719 | fn from(info: ffi::VmaAllocationInfo2) -> Self { 720 | (&info).into() 721 | } 722 | } 723 | 724 | bitflags! { 725 | /// Flags for configuring `VirtualBlock` construction 726 | #[derive(Default)] 727 | pub struct VirtualBlockCreateFlags: u32 { 728 | /// Enables alternative, linear allocation algorithm in this pool. 729 | /// 730 | /// Specify this flag to enable linear allocation algorithm, which always creates 731 | /// new allocations after last one and doesn't reuse space from allocations freed in 732 | /// between. It trades memory consumption for simplified algorithm and data 733 | /// structure, which has better performance and uses less memory for metadata. 734 | /// 735 | /// By using this flag, you can achieve behavior of free-at-once, stack, 736 | /// ring buffer, and double stack. 737 | const VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = ffi::VmaVirtualBlockCreateFlagBits::VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT as u32; 738 | } 739 | } 740 | 741 | bitflags! { 742 | /// Flags for configuring `VirtualBlock` construction 743 | #[derive(Clone, Copy, Debug)] 744 | pub struct VirtualAllocationCreateFlags: u32 { 745 | /// Allocation will be created from upper stack in a double stack pool. 746 | /// 747 | /// This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag. 748 | const VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = ffi::VmaVirtualAllocationCreateFlagBits::VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT as u32; 749 | /// Allocation strategy that tries to minimize memory usage. 750 | const VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = ffi::VmaVirtualAllocationCreateFlagBits::VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as u32; 751 | /// Allocation strategy that tries to minimize allocation time. 752 | const VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = ffi::VmaVirtualAllocationCreateFlagBits::VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT as u32; 753 | /// Allocation strategy that chooses always the lowest offset in available space. 754 | /// This is not the most efficient strategy but achieves highly packed data. 755 | const VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = ffi::VmaVirtualAllocationCreateFlagBits::VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT as u32; 756 | /// A bit mask to extract only `STRATEGY` bits from entire set of flags. 757 | /// 758 | /// These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits. 759 | const VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = ffi::VmaVirtualAllocationCreateFlagBits::VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK as u32; 760 | } 761 | } 762 | 763 | #[derive(Debug, Clone, Copy)] 764 | pub struct VirtualAllocationCreateInfo { 765 | /// Size of the allocation. 766 | /// 767 | /// Cannot be zero. 768 | pub size: u64, 769 | /// Required alignment of the allocation. Optional. 770 | /// 771 | /// Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset. 772 | pub alignment: u64, 773 | /// Custom pointer to be associated with the allocation. Optional. 774 | /// 775 | /// It can be any value and can be used for user-defined purposes. It can be fetched or changed later. 776 | pub user_data: usize, 777 | /// Flags to configure allocation behavior for this allocation 778 | pub flags: VirtualAllocationCreateFlags, 779 | } 780 | 781 | /// Parameters of created VirtualBlock, to be passed to VirtualBlock::new() 782 | #[derive(Default)] 783 | pub struct VirtualBlockCreateInfo<'a> { 784 | /// Total size of the virtual block. 785 | /// 786 | /// Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. 787 | /// For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. 788 | pub size: vk::DeviceSize, 789 | 790 | pub flags: VirtualBlockCreateFlags, 791 | /// Custom CPU memory allocation callbacks. Optional. 792 | /// When specified, they will be used for all CPU-side memory allocations. 793 | pub allocation_callbacks: Option<&'a vk::AllocationCallbacks<'a>>, 794 | } 795 | 796 | /// Parameters of `VirtualAllocation` objects, that can be retrieved using `VirtualBlock::get_allocation_info`. 797 | #[derive(Debug, Clone, Copy)] 798 | pub struct VirtualAllocationInfo { 799 | /// Offset of the allocation. 800 | /// 801 | /// Offset at which the allocation was made. 802 | pub offset: vk::DeviceSize, 803 | /// Size of the allocation. 804 | /// 805 | /// Same value as passed in VirtualAllocationCreateInfo::size. 806 | pub size: vk::DeviceSize, 807 | /// Custom pointer associated with the allocation 808 | /// 809 | /// It can change after call to vmaSetAllocationUserData() for this allocation. 810 | pub user_data: usize, 811 | } 812 | 813 | impl From<&ffi::VmaVirtualAllocationInfo> for VirtualAllocationInfo { 814 | fn from(info: &ffi::VmaVirtualAllocationInfo) -> Self { 815 | Self { 816 | offset: info.offset, 817 | size: info.size, 818 | user_data: info.pUserData as _, 819 | } 820 | } 821 | } 822 | impl From for VirtualAllocationInfo { 823 | fn from(info: ffi::VmaVirtualAllocationInfo) -> Self { 824 | (&info).into() 825 | } 826 | } 827 | 828 | impl From<&VirtualAllocationCreateInfo> for ffi::VmaVirtualAllocationCreateInfo { 829 | fn from(info: &VirtualAllocationCreateInfo) -> Self { 830 | ffi::VmaVirtualAllocationCreateInfo { 831 | size: info.size, 832 | alignment: info.alignment, 833 | flags: info.flags.bits(), 834 | pUserData: info.user_data as _, 835 | } 836 | } 837 | } 838 | 839 | impl From for ffi::VmaVirtualAllocationCreateInfo { 840 | fn from(info: VirtualAllocationCreateInfo) -> Self { 841 | (&info).into() 842 | } 843 | } 844 | -------------------------------------------------------------------------------- /src/defragmentation.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use crate::Allocator; 3 | use ash::prelude::VkResult; 4 | use ash::vk; 5 | 6 | pub use ffi::VmaDefragmentationMove as DefragmentationMove; 7 | pub use ffi::VmaDefragmentationStats as DefragmentationStats; 8 | pub struct DefragmentationContext<'a> { 9 | allocator: &'a Allocator, 10 | raw: ffi::VmaDefragmentationContext, 11 | } 12 | 13 | impl<'a> Drop for DefragmentationContext<'a> { 14 | fn drop(&mut self) { 15 | unsafe { 16 | ffi::vmaEndDefragmentation(self.allocator.internal, self.raw, std::ptr::null_mut()); 17 | } 18 | } 19 | } 20 | 21 | impl<'a> DefragmentationContext<'a> { 22 | /// Ends defragmentation process. 23 | pub fn end(self) -> DefragmentationStats { 24 | let mut stats = DefragmentationStats { 25 | bytesMoved: 0, 26 | bytesFreed: 0, 27 | allocationsMoved: 0, 28 | deviceMemoryBlocksFreed: 0, 29 | }; 30 | unsafe { 31 | ffi::vmaEndDefragmentation(self.allocator.internal, self.raw, &mut stats); 32 | } 33 | std::mem::forget(self); 34 | stats 35 | } 36 | 37 | /// Returns `false` if no more moves are possible or `true` if more defragmentations are possible. 38 | pub fn begin_pass(&self, mover: impl FnOnce(&mut [DefragmentationMove]) -> ()) -> bool { 39 | let mut pass_info = ffi::VmaDefragmentationPassMoveInfo { 40 | moveCount: 0, 41 | pMoves: std::ptr::null_mut(), 42 | }; 43 | let result = unsafe { 44 | ffi::vmaBeginDefragmentationPass(self.allocator.internal, self.raw, &mut pass_info) 45 | }; 46 | if result == vk::Result::SUCCESS { 47 | return false; 48 | } 49 | debug_assert_eq!(result, vk::Result::INCOMPLETE); 50 | let moves = unsafe { 51 | std::slice::from_raw_parts_mut(pass_info.pMoves, pass_info.moveCount as usize) 52 | }; 53 | mover(moves); 54 | 55 | let result = unsafe { 56 | ffi::vmaEndDefragmentationPass(self.allocator.internal, self.raw, &mut pass_info) 57 | }; 58 | 59 | return result == vk::Result::INCOMPLETE; 60 | } 61 | } 62 | 63 | impl Allocator { 64 | /// Begins defragmentation process. 65 | /// 66 | /// ## Returns 67 | /// `VK_SUCCESS` if defragmentation can begin. 68 | /// `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported. 69 | pub unsafe fn begin_defragmentation( 70 | &self, 71 | info: &ffi::VmaDefragmentationInfo, 72 | ) -> VkResult { 73 | let mut context: ffi::VmaDefragmentationContext = std::ptr::null_mut(); 74 | 75 | ffi::vmaBeginDefragmentation(self.internal, info, &mut context).result()?; 76 | 77 | Ok(DefragmentationContext { 78 | allocator: self, 79 | raw: context, 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Easy to use, high performance memory manager for Vulkan. 2 | 3 | mod definitions; 4 | mod defragmentation; 5 | mod ffi; 6 | mod pool; 7 | mod virtual_block; 8 | pub use definitions::*; 9 | pub use defragmentation::*; 10 | pub use pool::*; 11 | pub use virtual_block::*; 12 | 13 | use ash::prelude::VkResult; 14 | use ash::vk; 15 | use std::mem; 16 | 17 | /// Main allocator object 18 | pub struct Allocator { 19 | /// Pointer to internal VmaAllocator instance 20 | internal: ffi::VmaAllocator, 21 | } 22 | 23 | // Allocator is internally thread safe unless AllocatorCreateFlags::EXTERNALLY_SYNCHRONIZED is used (then you need to add synchronization!) 24 | unsafe impl Send for Allocator {} 25 | unsafe impl Sync for Allocator {} 26 | 27 | /// Represents single memory allocation. 28 | /// 29 | /// It may be either dedicated block of `vk::DeviceMemory` or a specific region of a 30 | /// bigger block of this type plus unique offset. 31 | /// 32 | /// Although the library provides convenience functions that create a Vulkan buffer or image, 33 | /// allocate memory for it and bind them together, binding of the allocation to a buffer or an 34 | /// image is out of scope of the allocation itself. 35 | /// 36 | /// Allocation object can exist without buffer/image bound, binding can be done manually by 37 | /// the user, and destruction of it can be done independently of destruction of the allocation. 38 | /// 39 | /// The object also remembers its size and some other information. To retrieve this information, 40 | /// use `Allocator::get_allocation_info`. 41 | /// 42 | /// Some kinds allocations can be in lost state. 43 | #[derive(Clone, Copy, Debug)] 44 | pub struct Allocation(ffi::VmaAllocation); 45 | unsafe impl Send for Allocation {} 46 | unsafe impl Sync for Allocation {} 47 | 48 | impl Allocator { 49 | /// Construct a new `Allocator` using the provided options. 50 | /// 51 | /// # Safety 52 | /// [`AllocatorCreateInfo::instance`], [`AllocatorCreateInfo::device`] and 53 | /// [`AllocatorCreateInfo::physical_device`] must be valid throughout the lifetime of the allocator. 54 | pub unsafe fn new(create_info: AllocatorCreateInfo) -> VkResult { 55 | unsafe extern "system" fn get_instance_proc_addr_stub( 56 | _instance: vk::Instance, 57 | _p_name: *const ::std::os::raw::c_char, 58 | ) -> vk::PFN_vkVoidFunction { 59 | panic!("VMA_DYNAMIC_VULKAN_FUNCTIONS is unsupported") 60 | } 61 | 62 | unsafe extern "system" fn get_get_device_proc_stub( 63 | _device: vk::Device, 64 | _p_name: *const ::std::os::raw::c_char, 65 | ) -> vk::PFN_vkVoidFunction { 66 | panic!("VMA_DYNAMIC_VULKAN_FUNCTIONS is unsupported") 67 | } 68 | 69 | let mut raw_create_info = ffi::VmaAllocatorCreateInfo { 70 | flags: create_info.flags.bits(), 71 | physicalDevice: create_info.physical_device, 72 | device: create_info.device.handle(), 73 | preferredLargeHeapBlockSize: create_info.preferred_large_heap_block_size, 74 | pAllocationCallbacks: create_info 75 | .allocation_callbacks 76 | .map(|a| unsafe { std::mem::transmute(a) }) 77 | .unwrap_or(std::ptr::null()), 78 | pDeviceMemoryCallbacks: create_info 79 | .device_memory_callbacks 80 | .map(|a| a as *const _) 81 | .unwrap_or(std::ptr::null()), 82 | pHeapSizeLimit: if create_info.heap_size_limits.is_empty() { 83 | std::ptr::null() 84 | } else { 85 | create_info.heap_size_limits.as_ptr() 86 | }, 87 | instance: create_info.instance.handle(), 88 | vulkanApiVersion: create_info.vulkan_api_version, 89 | pVulkanFunctions: std::ptr::null(), 90 | pTypeExternalMemoryHandleTypes: if create_info 91 | .type_external_memory_handle_types 92 | .is_empty() 93 | { 94 | std::ptr::null() 95 | } else { 96 | create_info.type_external_memory_handle_types.as_ptr() 97 | }, 98 | }; 99 | 100 | #[cfg(feature = "loaded")] 101 | let routed_functions = ffi::VmaVulkanFunctions { 102 | vkGetInstanceProcAddr: get_instance_proc_addr_stub, 103 | vkGetDeviceProcAddr: get_get_device_proc_stub, 104 | vkGetPhysicalDeviceProperties: create_info 105 | .instance 106 | .fp_v1_0() 107 | .get_physical_device_properties, 108 | vkGetPhysicalDeviceMemoryProperties: create_info 109 | .instance 110 | .fp_v1_0() 111 | .get_physical_device_memory_properties, 112 | vkAllocateMemory: create_info.device.fp_v1_0().allocate_memory, 113 | vkFreeMemory: create_info.device.fp_v1_0().free_memory, 114 | vkMapMemory: create_info.device.fp_v1_0().map_memory, 115 | vkUnmapMemory: create_info.device.fp_v1_0().unmap_memory, 116 | vkFlushMappedMemoryRanges: create_info.device.fp_v1_0().flush_mapped_memory_ranges, 117 | vkInvalidateMappedMemoryRanges: create_info 118 | .device 119 | .fp_v1_0() 120 | .invalidate_mapped_memory_ranges, 121 | vkBindBufferMemory: create_info.device.fp_v1_0().bind_buffer_memory, 122 | vkBindImageMemory: create_info.device.fp_v1_0().bind_image_memory, 123 | vkGetBufferMemoryRequirements: create_info 124 | .device 125 | .fp_v1_0() 126 | .get_buffer_memory_requirements, 127 | vkGetImageMemoryRequirements: create_info 128 | .device 129 | .fp_v1_0() 130 | .get_image_memory_requirements, 131 | vkCreateBuffer: create_info.device.fp_v1_0().create_buffer, 132 | vkDestroyBuffer: create_info.device.fp_v1_0().destroy_buffer, 133 | vkCreateImage: create_info.device.fp_v1_0().create_image, 134 | vkDestroyImage: create_info.device.fp_v1_0().destroy_image, 135 | vkCmdCopyBuffer: create_info.device.fp_v1_0().cmd_copy_buffer, 136 | vkGetBufferMemoryRequirements2KHR: create_info 137 | .device 138 | .fp_v1_1() 139 | .get_buffer_memory_requirements2, 140 | vkGetImageMemoryRequirements2KHR: create_info 141 | .device 142 | .fp_v1_1() 143 | .get_image_memory_requirements2, 144 | vkBindBufferMemory2KHR: create_info.device.fp_v1_1().bind_buffer_memory2, 145 | vkBindImageMemory2KHR: create_info.device.fp_v1_1().bind_image_memory2, 146 | vkGetPhysicalDeviceMemoryProperties2KHR: create_info 147 | .instance 148 | .fp_v1_1() 149 | .get_physical_device_memory_properties2, 150 | vkGetDeviceBufferMemoryRequirements: create_info 151 | .device 152 | .fp_v1_3() 153 | .get_device_buffer_memory_requirements, 154 | vkGetDeviceImageMemoryRequirements: create_info 155 | .device 156 | .fp_v1_3() 157 | .get_device_image_memory_requirements, 158 | }; 159 | #[cfg(feature = "loaded")] 160 | { 161 | raw_create_info.pVulkanFunctions = &routed_functions; 162 | } 163 | unsafe { 164 | let mut internal: ffi::VmaAllocator = mem::zeroed(); 165 | ffi::vmaCreateAllocator(&raw_create_info, &mut internal).result()?; 166 | 167 | Ok(Allocator { internal }) 168 | } 169 | } 170 | 171 | /// The allocator fetches `vk::PhysicalDeviceProperties` from the physical device. 172 | /// You can get it here, without fetching it again on your own. 173 | pub unsafe fn get_physical_device_properties(&self) -> VkResult { 174 | let mut properties = vk::PhysicalDeviceProperties::default(); 175 | ffi::vmaGetPhysicalDeviceProperties( 176 | self.internal, 177 | &mut properties as *mut _ as *mut *const _, 178 | ); 179 | 180 | Ok(properties) 181 | } 182 | 183 | /// The allocator fetches `vk::PhysicalDeviceMemoryProperties` from the physical device. 184 | /// You can get it here, without fetching it again on your own. 185 | pub unsafe fn get_memory_properties(&self) -> &vk::PhysicalDeviceMemoryProperties { 186 | let mut properties: *const vk::PhysicalDeviceMemoryProperties = std::ptr::null(); 187 | ffi::vmaGetMemoryProperties(self.internal, &mut properties); 188 | 189 | &*properties 190 | } 191 | 192 | /// Sets index of the current frame. 193 | /// 194 | /// This function must be used if you make allocations with `AllocationCreateFlags::CAN_BECOME_LOST` and 195 | /// `AllocationCreateFlags::CAN_MAKE_OTHER_LOST` flags to inform the allocator when a new frame begins. 196 | /// Allocations queried using `Allocator::get_allocation_info` cannot become lost 197 | /// in the current frame. 198 | pub unsafe fn set_current_frame_index(&self, frame_index: u32) { 199 | ffi::vmaSetCurrentFrameIndex(self.internal, frame_index); 200 | } 201 | 202 | /// Retrieves statistics from current state of the `Allocator`. 203 | pub fn calculate_statistics(&self) -> VkResult { 204 | unsafe { 205 | let mut vma_stats: ffi::VmaTotalStatistics = mem::zeroed(); 206 | ffi::vmaCalculateStatistics(self.internal, &mut vma_stats); 207 | Ok(vma_stats) 208 | } 209 | } 210 | 211 | /// Retrieves information about current memory usage and budget for all memory heaps. 212 | /// 213 | /// This function is called "get" not "calculate" because it is very fast, suitable to be called 214 | /// every frame or every allocation. For more detailed statistics use vmaCalculateStatistics(). 215 | /// 216 | /// Note that when using allocator from multiple threads, returned information may immediately 217 | /// become outdated. 218 | pub fn get_heap_budgets(&self) -> VkResult> { 219 | unsafe { 220 | let len = self.get_memory_properties().memory_heap_count as usize; 221 | let mut vma_budgets: Vec = Vec::with_capacity(len); 222 | ffi::vmaGetHeapBudgets(self.internal, vma_budgets.as_mut_ptr()); 223 | vma_budgets.set_len(len); 224 | Ok(vma_budgets) 225 | } 226 | } 227 | 228 | /// Frees memory previously allocated using `Allocator::allocate_memory`, 229 | /// `Allocator::allocate_memory_for_buffer`, or `Allocator::allocate_memory_for_image`. 230 | pub unsafe fn free_memory(&self, allocation: &mut Allocation) { 231 | ffi::vmaFreeMemory(self.internal, allocation.0); 232 | } 233 | 234 | /// Frees memory and destroys multiple allocations. 235 | /// 236 | /// Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. 237 | /// It is just a general purpose function to free memory and destroy allocations made using e.g. `Allocator::allocate_memory', 238 | /// 'Allocator::allocate_memory_pages` and other functions. 239 | /// 240 | /// It may be internally optimized to be more efficient than calling 'Allocator::free_memory` `allocations.len()` times. 241 | /// 242 | /// Allocations in 'allocations' slice can come from any memory pools and types. 243 | pub unsafe fn free_memory_pages(&self, allocations: &mut [Allocation]) { 244 | ffi::vmaFreeMemoryPages( 245 | self.internal, 246 | allocations.len(), 247 | allocations.as_ptr() as *mut _, 248 | ); 249 | } 250 | 251 | /// Returns current information about specified allocation and atomically marks it as used in current frame. 252 | /// 253 | /// Current parameters of given allocation are returned in the result object, available through accessors. 254 | /// 255 | /// This function also atomically "touches" allocation - marks it as used in current frame, 256 | /// just like `Allocator::touch_allocation`. 257 | /// 258 | /// If the allocation is in lost state, `allocation.get_device_memory` returns `vk::DeviceMemory::null()`. 259 | /// 260 | /// Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient, 261 | /// you can avoid calling it too often. 262 | /// 263 | /// If you just want to check if allocation is not lost, `Allocator::touch_allocation` will work faster. 264 | pub fn get_allocation_info(&self, allocation: &Allocation) -> AllocationInfo { 265 | unsafe { 266 | let mut allocation_info: ffi::VmaAllocationInfo = mem::zeroed(); 267 | ffi::vmaGetAllocationInfo(self.internal, allocation.0, &mut allocation_info); 268 | allocation_info.into() 269 | } 270 | } 271 | 272 | /// Returns extended information about specified allocation. 273 | /// 274 | /// Extended parameters in structure AllocationInfo2 include memory block size 275 | /// and a flag telling whether the allocation has dedicated memory. 276 | /// It can be useful e.g. for interop with OpenGL. 277 | pub fn get_allocation_info2(&self, allocation: &Allocation) -> AllocationInfo2 { 278 | unsafe { 279 | let mut allocation_info: ffi::VmaAllocationInfo2 = mem::zeroed(); 280 | ffi::vmaGetAllocationInfo2(self.internal, allocation.0, &mut allocation_info); 281 | allocation_info.into() 282 | } 283 | } 284 | 285 | /// Sets user data in given allocation to new value. 286 | /// 287 | /// If the allocation was created with `AllocationCreateFlags::USER_DATA_COPY_STRING`, 288 | /// `user_data` must be either null, or pointer to a null-terminated string. The function 289 | /// makes local copy of the string and sets it as allocation's user data. String 290 | /// passed as user data doesn't need to be valid for whole lifetime of the allocation - 291 | /// you can free it after this call. String previously pointed by allocation's 292 | /// user data is freed from memory. 293 | /// 294 | /// If the flag was not used, the value of pointer `user_data` is just copied to 295 | /// allocation's user data. It is opaque, so you can use it however you want - e.g. 296 | /// as a pointer, ordinal number or some handle to you own data. 297 | pub unsafe fn set_allocation_user_data( 298 | &self, 299 | allocation: &mut Allocation, 300 | user_data: *mut ::std::os::raw::c_void, 301 | ) { 302 | ffi::vmaSetAllocationUserData(self.internal, allocation.0, user_data); 303 | } 304 | 305 | /// Maps memory represented by given allocation and returns pointer to it. 306 | /// 307 | /// Maps memory represented by given allocation to make it accessible to CPU code. 308 | /// When succeeded, result is a pointer to first byte of this memory. 309 | /// 310 | /// If the allocation is part of bigger `vk::DeviceMemory` block, the pointer is 311 | /// correctly offseted to the beginning of region assigned to this particular 312 | /// allocation. 313 | /// 314 | /// Mapping is internally reference-counted and synchronized, so despite raw Vulkan 315 | /// function `vk::Device::MapMemory` cannot be used to map same block of 316 | /// `vk::DeviceMemory` multiple times simultaneously, it is safe to call this 317 | /// function on allocations assigned to the same memory block. Actual Vulkan memory 318 | /// will be mapped on first mapping and unmapped on last unmapping. 319 | /// 320 | /// If the function succeeded, you must call `Allocator::unmap_memory` to unmap the 321 | /// allocation when mapping is no longer needed or before freeing the allocation, at 322 | /// the latest. 323 | /// 324 | /// It also safe to call this function multiple times on the same allocation. You 325 | /// must call `Allocator::unmap_memory` same number of times as you called 326 | /// `Allocator::map_memory`. 327 | /// 328 | /// It is also safe to call this function on allocation created with 329 | /// `AllocationCreateFlags::MAPPED` flag. Its memory stays mapped all the time. 330 | /// You must still call `Allocator::unmap_memory` same number of times as you called 331 | /// `Allocator::map_memory`. You must not call `Allocator::unmap_memory` additional 332 | /// time to free the "0-th" mapping made automatically due to `AllocationCreateFlags::MAPPED` flag. 333 | /// 334 | /// This function fails when used on allocation made in memory type that is not 335 | /// `vk::MemoryPropertyFlags::HOST_VISIBLE`. 336 | /// 337 | /// This function always fails when called for allocation that was created with 338 | /// `AllocationCreateFlags::CAN_BECOME_LOST` flag. Such allocations cannot be mapped. 339 | pub unsafe fn map_memory(&self, allocation: &mut Allocation) -> VkResult<*mut u8> { 340 | let mut mapped_data: *mut ::std::os::raw::c_void = ::std::ptr::null_mut(); 341 | ffi::vmaMapMemory(self.internal, allocation.0, &mut mapped_data).result()?; 342 | 343 | Ok(mapped_data as *mut u8) 344 | } 345 | 346 | /// Unmaps memory represented by given allocation, mapped previously using `Allocator::map_memory`. 347 | pub unsafe fn unmap_memory(&self, allocation: &mut Allocation) { 348 | ffi::vmaUnmapMemory(self.internal, allocation.0); 349 | } 350 | 351 | /// Flushes memory of given allocation. 352 | /// 353 | /// Calls `vk::Device::FlushMappedMemoryRanges` for memory associated with given range of given allocation. 354 | /// 355 | /// - `offset` must be relative to the beginning of allocation. 356 | /// - `size` can be `vk::WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. 357 | /// - `offset` and `size` don't have to be aligned; hey are internally rounded down/up to multiple of `nonCoherentAtomSize`. 358 | /// - If `size` is 0, this call is ignored. 359 | /// - If memory type that the `allocation` belongs to is not `vk::MemoryPropertyFlags::HOST_VISIBLE` or it is `vk::MemoryPropertyFlags::HOST_COHERENT`, this call is ignored. 360 | pub fn flush_allocation( 361 | &self, 362 | allocation: &Allocation, 363 | offset: vk::DeviceSize, 364 | size: vk::DeviceSize, 365 | ) -> VkResult<()> { 366 | unsafe { ffi::vmaFlushAllocation(self.internal, allocation.0, offset, size).result() } 367 | } 368 | 369 | /// Invalidates memory of given allocation. 370 | /// 371 | /// Calls `vk::Device::invalidate_mapped_memory_ranges` for memory associated with given range of given allocation. 372 | /// 373 | /// - `offset` must be relative to the beginning of allocation. 374 | /// - `size` can be `vk::WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. 375 | /// - `offset` and `size` don't have to be aligned. They are internally rounded down/up to multiple of `nonCoherentAtomSize`. 376 | /// - If `size` is 0, this call is ignored. 377 | /// - If memory type that the `allocation` belongs to is not `vk::MemoryPropertyFlags::HOST_VISIBLE` or it is `vk::MemoryPropertyFlags::HOST_COHERENT`, this call is ignored. 378 | pub fn invalidate_allocation( 379 | &self, 380 | allocation: &Allocation, 381 | offset: vk::DeviceSize, 382 | size: vk::DeviceSize, 383 | ) -> VkResult<()> { 384 | unsafe { ffi::vmaInvalidateAllocation(self.internal, allocation.0, offset, size).result() } 385 | } 386 | 387 | /// Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. 388 | /// 389 | /// `memory_type_bits` bit mask, where each bit set means that a memory type with that index should be checked. 390 | /// 391 | /// Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, 392 | /// `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are `HOST_VISIBLE` and `HOST_COHERENT`. 393 | /// 394 | /// Possible error values: 395 | /// 396 | /// - `vk::Result::ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. 397 | /// - `vk::Result::ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations. 398 | /// `VMA_ASSERT` is also fired in that case. 399 | /// - Other value: Error returned by Vulkan, e.g. memory mapping failure. 400 | pub unsafe fn check_corruption(&self, memory_types: vk::MemoryPropertyFlags) -> VkResult<()> { 401 | ffi::vmaCheckCorruption(self.internal, memory_types.as_raw()).result() 402 | } 403 | 404 | /// Binds buffer to allocation. 405 | /// 406 | /// Binds specified buffer to region of memory represented by specified allocation. 407 | /// Gets `vk::DeviceMemory` handle and offset from the allocation. 408 | /// 409 | /// If you want to create a buffer, allocate memory for it and bind them together separately, 410 | /// you should use this function for binding instead of `vk::Device::bind_buffer_memory`, 411 | /// because it ensures proper synchronization so that when a `vk::DeviceMemory` object is 412 | /// used by multiple allocations, calls to `vk::Device::bind_buffer_memory()` or 413 | /// `vk::Device::map_memory()` won't happen from multiple threads simultaneously 414 | /// (which is illegal in Vulkan). 415 | /// 416 | /// It is recommended to use function `Allocator::create_buffer` instead of this one. 417 | pub unsafe fn bind_buffer_memory( 418 | &self, 419 | allocation: &Allocation, 420 | buffer: vk::Buffer, 421 | ) -> VkResult<()> { 422 | ffi::vmaBindBufferMemory(self.internal, allocation.0, buffer).result() 423 | } 424 | 425 | /// Binds buffer to allocation with additional parameters. 426 | /// 427 | /// * `allocation` 428 | /// * `allocation_local_offset` - Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. 429 | /// * `buffer` 430 | /// * `next` - A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null. 431 | /// 432 | /// This function is similar to vmaBindImageMemory(), but it provides additional parameters. 433 | /// 434 | /// If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag 435 | /// or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. 436 | pub unsafe fn bind_buffer_memory2( 437 | &self, 438 | allocation: &Allocation, 439 | allocation_local_offset: vk::DeviceSize, 440 | buffer: vk::Buffer, 441 | next: *const ::std::os::raw::c_void, 442 | ) -> VkResult<()> { 443 | ffi::vmaBindBufferMemory2( 444 | self.internal, 445 | allocation.0, 446 | allocation_local_offset, 447 | buffer, 448 | next, 449 | ) 450 | .result() 451 | } 452 | 453 | /// Binds image to allocation. 454 | /// 455 | /// Binds specified image to region of memory represented by specified allocation. 456 | /// Gets `vk::DeviceMemory` handle and offset from the allocation. 457 | /// 458 | /// If you want to create a image, allocate memory for it and bind them together separately, 459 | /// you should use this function for binding instead of `vk::Device::bind_image_memory`, 460 | /// because it ensures proper synchronization so that when a `vk::DeviceMemory` object is 461 | /// used by multiple allocations, calls to `vk::Device::bind_image_memory()` or 462 | /// `vk::Device::map_memory()` won't happen from multiple threads simultaneously 463 | /// (which is illegal in Vulkan). 464 | /// 465 | /// It is recommended to use function `Allocator::create_image` instead of this one. 466 | pub unsafe fn bind_image_memory( 467 | &self, 468 | allocation: &Allocation, 469 | image: vk::Image, 470 | ) -> VkResult<()> { 471 | ffi::vmaBindImageMemory(self.internal, allocation.0, image).result() 472 | } 473 | 474 | /// Binds image to allocation with additional parameters. 475 | /// 476 | /// * `allocation` 477 | /// * `allocation_local_offset` - Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. 478 | /// * `image` 479 | /// * `next` - A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null. 480 | /// 481 | /// This function is similar to vmaBindImageMemory(), but it provides additional parameters. 482 | /// 483 | /// If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag 484 | /// or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. 485 | pub unsafe fn bind_image_memory2( 486 | &self, 487 | allocation: &Allocation, 488 | allocation_local_offset: vk::DeviceSize, 489 | image: vk::Image, 490 | next: *const ::std::os::raw::c_void, 491 | ) -> VkResult<()> { 492 | ffi::vmaBindImageMemory2( 493 | self.internal, 494 | allocation.0, 495 | allocation_local_offset, 496 | image, 497 | next, 498 | ) 499 | .result() 500 | } 501 | 502 | /// Destroys Vulkan buffer and frees allocated memory. 503 | /// 504 | /// This is just a convenience function equivalent to: 505 | /// 506 | /// ```ignore 507 | /// vk::Device::destroy_buffer(buffer, None); 508 | /// Allocator::free_memory(allocator, allocation); 509 | /// ``` 510 | /// 511 | /// It it safe to pass null as `buffer` and/or `allocation`. 512 | pub unsafe fn destroy_buffer(&self, buffer: vk::Buffer, allocation: &mut Allocation) { 513 | ffi::vmaDestroyBuffer(self.internal, buffer, allocation.0); 514 | } 515 | 516 | /// Destroys Vulkan image and frees allocated memory. 517 | /// 518 | /// This is just a convenience function equivalent to: 519 | /// 520 | /// ```ignore 521 | /// vk::Device::destroy_image(image, None); 522 | /// Allocator::free_memory(allocator, allocation); 523 | /// ``` 524 | /// 525 | /// It it safe to pass null as `image` and/or `allocation`. 526 | pub unsafe fn destroy_image(&self, image: vk::Image, allocation: &mut Allocation) { 527 | ffi::vmaDestroyImage(self.internal, image, allocation.0); 528 | } 529 | /// Flushes memory of given set of allocations."] 530 | /// 531 | /// Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations."] 532 | /// For more information, see documentation of vmaFlushAllocation()."] 533 | /// 534 | /// * `allocations` 535 | /// * `offsets` - If not None, it must be a slice of offsets of regions to flush, relative to the beginning of respective allocations. None means all ofsets are zero. 536 | /// * `sizes` - If not None, it must be a slice of sizes of regions to flush in respective allocations. None means `VK_WHOLE_SIZE` for all allocations. 537 | pub unsafe fn flush_allocations<'a>( 538 | &self, 539 | allocations: impl IntoIterator, 540 | offsets: Option<&[vk::DeviceSize]>, 541 | sizes: Option<&[vk::DeviceSize]>, 542 | ) -> VkResult<()> { 543 | let allocations: Vec = allocations.into_iter().map(|a| a.0).collect(); 544 | ffi::vmaFlushAllocations( 545 | self.internal, 546 | allocations.len() as u32, 547 | allocations.as_ptr() as *mut _, 548 | offsets.map_or(std::ptr::null(), |offsets| offsets.as_ptr()), 549 | sizes.map_or(std::ptr::null(), |sizes| sizes.as_ptr()), 550 | ) 551 | .result() 552 | } 553 | 554 | /// Invalidates memory of given set of allocations."] 555 | /// 556 | /// Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations."] 557 | /// For more information, see documentation of vmaInvalidateAllocation()."] 558 | /// 559 | /// * `allocations` 560 | /// * `offsets` - If not None, it must be a slice of offsets of regions to flush, relative to the beginning of respective allocations. None means all ofsets are zero. 561 | /// * `sizes` - If not None, it must be a slice of sizes of regions to flush in respective allocations. None means `VK_WHOLE_SIZE` for all allocations. 562 | pub unsafe fn invalidate_allocations<'a>( 563 | &self, 564 | allocations: impl IntoIterator, 565 | offsets: Option<&[vk::DeviceSize]>, 566 | sizes: Option<&[vk::DeviceSize]>, 567 | ) -> VkResult<()> { 568 | let allocations: Vec = allocations.into_iter().map(|a| a.0).collect(); 569 | ffi::vmaInvalidateAllocations( 570 | self.internal, 571 | allocations.len() as u32, 572 | allocations.as_ptr() as *mut _, 573 | offsets.map_or(std::ptr::null(), |offsets| offsets.as_ptr()), 574 | sizes.map_or(std::ptr::null(), |sizes| sizes.as_ptr()), 575 | ) 576 | .result() 577 | } 578 | } 579 | 580 | /// Custom `Drop` implementation to clean up internal allocation instance 581 | impl Drop for Allocator { 582 | fn drop(&mut self) { 583 | unsafe { 584 | ffi::vmaDestroyAllocator(self.internal); 585 | self.internal = std::ptr::null_mut(); 586 | } 587 | } 588 | } 589 | -------------------------------------------------------------------------------- /src/pool.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::sync::Arc; 3 | 4 | use crate::ffi; 5 | use crate::Allocation; 6 | use crate::AllocationCreateInfo; 7 | use crate::Allocator; 8 | use crate::PoolCreateInfo; 9 | use ash::prelude::VkResult; 10 | use ash::vk; 11 | #[derive(Clone, Copy)] 12 | pub struct PoolHandle(ffi::VmaPool); 13 | 14 | /// Represents custom memory pool handle. 15 | pub struct AllocatorPool { 16 | allocator: Arc, 17 | pub(crate) pool: PoolHandle, 18 | } 19 | unsafe impl Send for AllocatorPool {} 20 | unsafe impl Sync for AllocatorPool {} 21 | 22 | impl Allocator { 23 | /// Allocates Vulkan device memory and creates `AllocatorPool` object. 24 | pub fn create_pool(self: &Arc, create_info: &PoolCreateInfo) -> VkResult { 25 | unsafe { 26 | let mut ffi_pool: ffi::VmaPool = std::mem::zeroed(); 27 | let raw_info = ffi::VmaPoolCreateInfo { 28 | memoryTypeIndex: create_info.memory_type_index, 29 | flags: create_info.flags.bits(), 30 | blockSize: create_info.block_size, 31 | minBlockCount: create_info.min_block_count, 32 | maxBlockCount: create_info.max_block_count, 33 | priority: create_info.priority, 34 | minAllocationAlignment: create_info.min_allocation_alignment, 35 | pMemoryAllocateNext: create_info.memory_allocate_next as *mut std::ffi::c_void, 36 | }; 37 | ffi::vmaCreatePool(self.internal, &raw_info, &mut ffi_pool).result()?; 38 | Ok(AllocatorPool { 39 | pool: PoolHandle(ffi_pool), 40 | allocator: self.clone(), 41 | }) 42 | } 43 | } 44 | 45 | pub fn default_pool(self: &Arc) -> AllocatorPool { 46 | AllocatorPool { 47 | pool: PoolHandle(std::ptr::null_mut()), 48 | allocator: self.clone(), 49 | } 50 | } 51 | } 52 | 53 | impl Drop for AllocatorPool { 54 | fn drop(&mut self) { 55 | unsafe { 56 | ffi::vmaDestroyPool(self.allocator.internal, self.pool.0); 57 | } 58 | } 59 | } 60 | 61 | impl AllocatorPool { 62 | pub fn set_name(&self, name: Option<&CStr>) { 63 | if self.pool.0.is_null() { 64 | return; 65 | } 66 | unsafe { 67 | ffi::vmaSetPoolName( 68 | self.allocator.internal, 69 | self.pool.0, 70 | name.map_or(std::ptr::null(), CStr::as_ptr), 71 | ); 72 | } 73 | } 74 | pub fn name(&self) -> Option<&CStr> { 75 | if self.pool.0.is_null() { 76 | return None; 77 | } 78 | let mut ptr: *const ::std::os::raw::c_char = std::ptr::null(); 79 | unsafe { 80 | ffi::vmaGetPoolName(self.allocator.internal, self.pool.0, &mut ptr); 81 | if ptr.is_null() { 82 | return None; 83 | } 84 | Some(CStr::from_ptr(ptr)) 85 | } 86 | } 87 | /// Retrieves statistics of existing `AllocatorPool` object. 88 | pub fn get_statistics(&self) -> VkResult { 89 | unsafe { 90 | let mut pool_stats: ffi::VmaStatistics = std::mem::zeroed(); 91 | ffi::vmaGetPoolStatistics(self.allocator.internal, self.pool.0, &mut pool_stats); 92 | Ok(pool_stats) 93 | } 94 | } 95 | 96 | /// Retrieves statistics of existing `AllocatorPool` object. 97 | pub fn calculate_statistics(&self) -> VkResult { 98 | unsafe { 99 | let mut pool_stats: ffi::VmaDetailedStatistics = std::mem::zeroed(); 100 | ffi::vmaCalculatePoolStatistics(self.allocator.internal, self.pool.0, &mut pool_stats); 101 | Ok(pool_stats) 102 | } 103 | } 104 | 105 | /// Checks magic number in margins around all allocations in given memory pool in search for corruptions. 106 | /// 107 | /// Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, 108 | /// `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is 109 | /// `ash::vk::MemoryPropertyFlags::HOST_VISIBLE` and `ash::vk::MemoryPropertyFlags::HOST_COHERENT`. 110 | /// 111 | /// Possible error values: 112 | /// 113 | /// - `ash::vk::Result::ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. 114 | /// - `ash::vk::Result::ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations. 115 | /// `VMA_ASSERT` is also fired in that case. 116 | /// - Other value: Error returned by Vulkan, e.g. memory mapping failure. 117 | pub fn check_corruption(&self) -> VkResult<()> { 118 | unsafe { ffi::vmaCheckPoolCorruption(self.allocator.internal, self.pool.0).result() } 119 | } 120 | } 121 | 122 | pub trait Alloc { 123 | fn allocator(&self) -> &Allocator; 124 | fn pool(&self) -> PoolHandle; 125 | /// Helps to find memory type index, given memory type bits and allocation info. 126 | /// 127 | /// This algorithm tries to find a memory type that: 128 | /// 129 | /// - Is allowed by memory type bits. 130 | /// - Contains all the flags from `allocation_info.required_flags`. 131 | /// - Matches intended usage. 132 | /// - Has as many flags from `allocation_info.preferred_flags` as possible. 133 | /// 134 | /// Returns ash::vk::Result::ERROR_FEATURE_NOT_PRESENT if not found. Receiving such a result 135 | /// from this function or any other allocating function probably means that your 136 | /// device doesn't support any memory type with requested features for the specific 137 | /// type of resource you want to use it for. Please check parameters of your 138 | /// resource, like image layout (OPTIMAL versus LINEAR) or mip level count. 139 | unsafe fn find_memory_type_index( 140 | &self, 141 | memory_type_bits: u32, 142 | allocation_info: &AllocationCreateInfo, 143 | ) -> VkResult { 144 | let mut memory_type_index: u32 = 0; 145 | let mut allocation_info: ffi::VmaAllocationCreateInfo = allocation_info.into(); 146 | allocation_info.pool = self.pool().0; 147 | ffi::vmaFindMemoryTypeIndex( 148 | self.allocator().internal, 149 | memory_type_bits, 150 | &allocation_info, 151 | &mut memory_type_index, 152 | ) 153 | .result()?; 154 | 155 | Ok(memory_type_index) 156 | } 157 | 158 | /// Helps to find memory type index, given buffer info and allocation info. 159 | /// 160 | /// It can be useful e.g. to determine value to be used as `AllocatorPoolCreateInfo::memory_type_index`. 161 | /// It internally creates a temporary, dummy buffer that never has memory bound. 162 | /// It is just a convenience function, equivalent to calling: 163 | /// 164 | /// - `ash::vk::Device::create_buffer` 165 | /// - `ash::vk::Device::get_buffer_memory_requirements` 166 | /// - `Allocator::find_memory_type_index` 167 | /// - `ash::vk::Device::destroy_buffer` 168 | unsafe fn find_memory_type_index_for_buffer_info( 169 | &self, 170 | buffer_info: &ash::vk::BufferCreateInfo, 171 | allocation_info: &AllocationCreateInfo, 172 | ) -> VkResult { 173 | let mut allocation_info: ffi::VmaAllocationCreateInfo = allocation_info.into(); 174 | allocation_info.pool = self.pool().0; 175 | let mut memory_type_index: u32 = 0; 176 | ffi::vmaFindMemoryTypeIndexForBufferInfo( 177 | self.allocator().internal, 178 | buffer_info, 179 | &allocation_info, 180 | &mut memory_type_index, 181 | ) 182 | .result()?; 183 | 184 | Ok(memory_type_index) 185 | } 186 | 187 | /// Helps to find memory type index, given image info and allocation info. 188 | /// 189 | /// It can be useful e.g. to determine value to be used as `AllocatorPoolCreateInfo::memory_type_index`. 190 | /// It internally creates a temporary, dummy image that never has memory bound. 191 | /// It is just a convenience function, equivalent to calling: 192 | /// 193 | /// - `ash::vk::Device::create_image` 194 | /// - `ash::vk::Device::get_image_memory_requirements` 195 | /// - `Allocator::find_memory_type_index` 196 | /// - `ash::vk::Device::destroy_image` 197 | unsafe fn find_memory_type_index_for_image_info( 198 | &self, 199 | image_info: ash::vk::ImageCreateInfo, 200 | allocation_info: &AllocationCreateInfo, 201 | ) -> VkResult { 202 | let mut allocation_info: ffi::VmaAllocationCreateInfo = allocation_info.into(); 203 | allocation_info.pool = self.pool().0; 204 | let mut memory_type_index: u32 = 0; 205 | ffi::vmaFindMemoryTypeIndexForImageInfo( 206 | self.allocator().internal, 207 | &image_info, 208 | &allocation_info, 209 | &mut memory_type_index, 210 | ) 211 | .result()?; 212 | 213 | Ok(memory_type_index) 214 | } 215 | 216 | /// General purpose memory allocation. 217 | /// 218 | /// You should free the memory using `Allocator::free_memory` or 'Allocator::free_memory_pages'. 219 | /// 220 | /// It is recommended to use `Allocator::allocate_memory_for_buffer`, `Allocator::allocate_memory_for_image`, 221 | /// `Allocator::create_buffer`, `Allocator::create_image` instead whenever possible. 222 | unsafe fn allocate_memory( 223 | &self, 224 | memory_requirements: &ash::vk::MemoryRequirements, 225 | create_info: &AllocationCreateInfo, 226 | ) -> VkResult { 227 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 228 | create_info.pool = self.pool().0; 229 | let mut allocation: ffi::VmaAllocation = std::mem::zeroed(); 230 | ffi::vmaAllocateMemory( 231 | self.allocator().internal, 232 | memory_requirements, 233 | &create_info, 234 | &mut allocation, 235 | std::ptr::null_mut(), 236 | ) 237 | .result()?; 238 | 239 | Ok(Allocation(allocation)) 240 | } 241 | 242 | /// General purpose memory allocation for multiple allocation objects at once. 243 | /// 244 | /// You should free the memory using `Allocator::free_memory` or `Allocator::free_memory_pages`. 245 | /// 246 | /// Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. 247 | /// It is just a general purpose allocation function able to make multiple allocations at once. 248 | /// It may be internally optimized to be more efficient than calling `Allocator::allocate_memory` `allocations.len()` times. 249 | /// 250 | /// All allocations are made using same parameters. All of them are created out of the same memory pool and type. 251 | unsafe fn allocate_memory_pages( 252 | &self, 253 | memory_requirements: &ash::vk::MemoryRequirements, 254 | create_info: &AllocationCreateInfo, 255 | allocation_count: usize, 256 | ) -> VkResult> { 257 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 258 | create_info.pool = self.pool().0; 259 | let mut allocations: Vec = vec![std::mem::zeroed(); allocation_count]; 260 | ffi::vmaAllocateMemoryPages( 261 | self.allocator().internal, 262 | memory_requirements, 263 | &create_info, 264 | allocation_count, 265 | allocations.as_mut_ptr(), 266 | std::ptr::null_mut(), 267 | ) 268 | .result()?; 269 | 270 | let allocations: Vec = allocations 271 | .into_iter() 272 | .map(|alloc| Allocation(alloc)) 273 | .collect(); 274 | 275 | Ok(allocations) 276 | } 277 | 278 | /// Buffer specialized memory allocation. 279 | /// 280 | /// You should free the memory using `Allocator::free_memory` or 'Allocator::free_memory_pages'. 281 | unsafe fn allocate_memory_for_buffer( 282 | &self, 283 | buffer: ash::vk::Buffer, 284 | create_info: &AllocationCreateInfo, 285 | ) -> VkResult { 286 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 287 | create_info.pool = self.pool().0; 288 | let mut allocation: ffi::VmaAllocation = std::mem::zeroed(); 289 | let mut allocation_info: ffi::VmaAllocationInfo = std::mem::zeroed(); 290 | ffi::vmaAllocateMemoryForBuffer( 291 | self.allocator().internal, 292 | buffer, 293 | &create_info, 294 | &mut allocation, 295 | &mut allocation_info, 296 | ) 297 | .result()?; 298 | 299 | Ok(Allocation(allocation)) 300 | } 301 | 302 | /// Image specialized memory allocation. 303 | /// 304 | /// You should free the memory using `Allocator::free_memory` or 'Allocator::free_memory_pages'. 305 | unsafe fn allocate_memory_for_image( 306 | &self, 307 | image: ash::vk::Image, 308 | create_info: &AllocationCreateInfo, 309 | ) -> VkResult { 310 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 311 | create_info.pool = self.pool().0; 312 | let mut allocation: ffi::VmaAllocation = std::mem::zeroed(); 313 | ffi::vmaAllocateMemoryForImage( 314 | self.allocator().internal, 315 | image, 316 | &create_info, 317 | &mut allocation, 318 | std::ptr::null_mut(), 319 | ) 320 | .result()?; 321 | 322 | Ok(Allocation(allocation)) 323 | } 324 | 325 | /// This function automatically creates a buffer, allocates appropriate memory 326 | /// for it, and binds the buffer with the memory. 327 | /// 328 | /// If the function succeeded, you must destroy both buffer and allocation when you 329 | /// no longer need them using either convenience function `Allocator::destroy_buffer` or 330 | /// separately, using `ash::Device::destroy_buffer` and `Allocator::free_memory`. 331 | /// 332 | /// If `AllocatorCreateFlags::KHR_DEDICATED_ALLOCATION` flag was used, 333 | /// VK_KHR_dedicated_allocation extension is used internally to query driver whether 334 | /// it requires or prefers the new buffer to have dedicated allocation. If yes, 335 | /// and if dedicated allocation is possible (AllocationCreateInfo::pool is null 336 | /// and `AllocationCreateFlags::NEVER_ALLOCATE` is not used), it creates dedicated 337 | /// allocation for this buffer, just like when using `AllocationCreateFlags::DEDICATED_MEMORY`. 338 | unsafe fn create_buffer( 339 | &self, 340 | buffer_info: &ash::vk::BufferCreateInfo, 341 | create_info: &AllocationCreateInfo, 342 | ) -> VkResult<(ash::vk::Buffer, Allocation)> { 343 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 344 | create_info.pool = self.pool().0; 345 | let mut buffer = vk::Buffer::null(); 346 | let mut allocation: ffi::VmaAllocation = std::mem::zeroed(); 347 | ffi::vmaCreateBuffer( 348 | self.allocator().internal, 349 | &*buffer_info, 350 | &create_info, 351 | &mut buffer, 352 | &mut allocation, 353 | std::ptr::null_mut(), 354 | ) 355 | .result()?; 356 | 357 | Ok((buffer, Allocation(allocation))) 358 | } 359 | /// brief Creates a buffer with additional minimum alignment. 360 | /// 361 | /// Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom, 362 | /// minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g. 363 | /// for interop with OpenGL. 364 | unsafe fn create_buffer_with_alignment( 365 | &self, 366 | buffer_info: &ash::vk::BufferCreateInfo, 367 | create_info: &AllocationCreateInfo, 368 | min_alignment: vk::DeviceSize, 369 | ) -> VkResult<(ash::vk::Buffer, Allocation)> { 370 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 371 | create_info.pool = self.pool().0; 372 | let mut buffer = vk::Buffer::null(); 373 | let mut allocation: ffi::VmaAllocation = std::mem::zeroed(); 374 | ffi::vmaCreateBufferWithAlignment( 375 | self.allocator().internal, 376 | &*buffer_info, 377 | &create_info, 378 | min_alignment, 379 | &mut buffer, 380 | &mut allocation, 381 | std::ptr::null_mut(), 382 | ) 383 | .result()?; 384 | 385 | Ok((buffer, Allocation(allocation))) 386 | } 387 | /// This function automatically creates an image, allocates appropriate memory 388 | /// for it, and binds the image with the memory. 389 | /// 390 | /// If the function succeeded, you must destroy both image and allocation when you 391 | /// no longer need them using either convenience function `Allocator::destroy_image` or 392 | /// separately, using `ash::Device::destroy_image` and `Allocator::free_memory`. 393 | /// 394 | /// If `AllocatorCreateFlags::KHR_DEDICATED_ALLOCATION` flag was used, 395 | /// `VK_KHR_dedicated_allocation extension` is used internally to query driver whether 396 | /// it requires or prefers the new image to have dedicated allocation. If yes, 397 | /// and if dedicated allocation is possible (AllocationCreateInfo::pool is null 398 | /// and `AllocationCreateFlags::NEVER_ALLOCATE` is not used), it creates dedicated 399 | /// allocation for this image, just like when using `AllocationCreateFlags::DEDICATED_MEMORY`. 400 | /// 401 | /// If `VK_ERROR_VALIDAITON_FAILED_EXT` is returned, VMA may have encountered a problem 402 | /// that is not caught by the validation layers. One example is if you try to create a 0x0 403 | /// image, a panic will occur and `VK_ERROR_VALIDAITON_FAILED_EXT` is thrown. 404 | unsafe fn create_image( 405 | &self, 406 | image_info: &ash::vk::ImageCreateInfo, 407 | create_info: &AllocationCreateInfo, 408 | ) -> VkResult<(ash::vk::Image, Allocation)> { 409 | let mut create_info: ffi::VmaAllocationCreateInfo = create_info.into(); 410 | create_info.pool = self.pool().0; 411 | let mut image = vk::Image::null(); 412 | let mut allocation: ffi::VmaAllocation = std::mem::zeroed(); 413 | ffi::vmaCreateImage( 414 | self.allocator().internal, 415 | &*image_info, 416 | &create_info, 417 | &mut image, 418 | &mut allocation, 419 | std::ptr::null_mut(), 420 | ) 421 | .result()?; 422 | 423 | Ok((image, Allocation(allocation))) 424 | } 425 | } 426 | 427 | impl Alloc for AllocatorPool { 428 | fn allocator(&self) -> &Allocator { 429 | self.allocator.as_ref() 430 | } 431 | 432 | fn pool(&self) -> PoolHandle { 433 | self.pool 434 | } 435 | } 436 | impl Alloc for Allocator { 437 | fn allocator(&self) -> &Allocator { 438 | self 439 | } 440 | 441 | fn pool(&self) -> PoolHandle { 442 | PoolHandle(std::ptr::null_mut()) 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /src/virtual_block.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use ash::prelude::VkResult; 3 | use std::mem; 4 | 5 | use crate::definitions::*; 6 | 7 | /// Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory. 8 | /// 9 | /// For more info: 10 | pub struct VirtualBlock { 11 | internal: ffi::VmaVirtualBlock, 12 | } 13 | 14 | /// Represents single memory allocation done inside VirtualBlock. 15 | #[derive(Debug)] 16 | pub struct VirtualAllocation(ffi::VmaVirtualAllocation); 17 | unsafe impl Send for VirtualAllocation {} 18 | unsafe impl Sync for VirtualAllocation {} 19 | 20 | impl VirtualBlock { 21 | /// Creates new VirtualBlock object. 22 | pub fn new(create_info: VirtualBlockCreateInfo) -> VkResult { 23 | unsafe { 24 | let mut internal: ffi::VmaVirtualBlock = mem::zeroed(); 25 | let raw_info = ffi::VmaVirtualBlockCreateInfo { 26 | size: create_info.size, 27 | flags: create_info.flags.bits(), 28 | pAllocationCallbacks: create_info 29 | .allocation_callbacks 30 | .map(|a| std::mem::transmute(a)) 31 | .unwrap_or(std::ptr::null()), 32 | }; 33 | ffi::vmaCreateVirtualBlock(&raw_info, &mut internal).result()?; 34 | 35 | Ok(VirtualBlock { internal }) 36 | } 37 | } 38 | 39 | /// Allocates new virtual allocation inside given VirtualBlock. 40 | /// 41 | /// Possible error values: 42 | /// 43 | /// - `ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY` - Allocation failed due to not enough free space in the virtual block. 44 | /// (despite the function doesn't ever allocate actual GPU memory) 45 | pub unsafe fn allocate( 46 | &mut self, 47 | allocation_info: VirtualAllocationCreateInfo, 48 | ) -> VkResult<(VirtualAllocation, u64)> { 49 | let create_info: ffi::VmaVirtualAllocationCreateInfo = allocation_info.into(); 50 | let mut allocation: ffi::VmaVirtualAllocation = std::mem::zeroed(); 51 | let mut offset = 0; 52 | ffi::vmaVirtualAllocate(self.internal, &create_info, &mut allocation, &mut offset) 53 | .result()?; 54 | Ok((VirtualAllocation(allocation), offset)) 55 | } 56 | 57 | /// Frees virtual allocation inside given VirtualBlock. 58 | /// 59 | /// It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing. 60 | pub unsafe fn free(&mut self, allocation: &mut VirtualAllocation) { 61 | ffi::vmaVirtualFree(self.internal, allocation.0); 62 | } 63 | 64 | /// Frees all virtual allocations inside given VirtualBlock. 65 | /// 66 | /// You must either call this function or free each virtual allocation individually with vmaVirtualFree() 67 | /// before destroying a virtual block. Otherwise, an assert is called. 68 | /// 69 | /// If you keep pointer to some additional metadata associated with your virtual allocation in its `user_data`, 70 | /// don't forget to free it as well. 71 | /// 72 | /// Any VirtualAllocations created previously in the VirtualBlock will no longer be valid! 73 | pub unsafe fn clear(&mut self) { 74 | ffi::vmaClearVirtualBlock(self.internal); 75 | } 76 | 77 | /// Returns information about a specific virtual allocation within a virtual block, like its size and user_data pointer. 78 | pub unsafe fn get_allocation_info( 79 | &self, 80 | allocation: &VirtualAllocation, 81 | ) -> VkResult { 82 | let mut allocation_info: ffi::VmaVirtualAllocationInfo = mem::zeroed(); 83 | ffi::vmaGetVirtualAllocationInfo(self.internal, allocation.0, &mut allocation_info); 84 | Ok(allocation_info.into()) 85 | } 86 | 87 | /// Changes custom pointer associated with given virtual allocation. 88 | pub unsafe fn set_allocation_user_data( 89 | &self, 90 | allocation: &mut VirtualAllocation, 91 | user_data: *mut ::std::os::raw::c_void, 92 | ) { 93 | ffi::vmaSetVirtualAllocationUserData(self.internal, allocation.0, user_data); 94 | } 95 | } 96 | 97 | /// Custom `Drop` implementation to clean up internal VirtualBlock instance 98 | impl Drop for VirtualBlock { 99 | fn drop(&mut self) { 100 | unsafe { 101 | ffi::vmaDestroyVirtualBlock(self.internal); 102 | self.internal = std::ptr::null_mut(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /tests/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate ash; 2 | extern crate vk_mem; 3 | 4 | use ash::{ext::debug_utils, vk}; 5 | use std::{os::raw::c_void, sync::Arc}; 6 | use vk_mem::Alloc; 7 | 8 | fn extension_names() -> Vec<*const i8> { 9 | vec![debug_utils::NAME.as_ptr()] 10 | } 11 | 12 | unsafe extern "system" fn vulkan_debug_callback( 13 | _message_severity: ash::vk::DebugUtilsMessageSeverityFlagsEXT, 14 | _message_types: ash::vk::DebugUtilsMessageTypeFlagsEXT, 15 | p_callback_data: *const ash::vk::DebugUtilsMessengerCallbackDataEXT, 16 | _p_user_data: *mut c_void, 17 | ) -> ash::vk::Bool32 { 18 | let p_callback_data = &*p_callback_data; 19 | println!( 20 | "{:?}", 21 | ::std::ffi::CStr::from_ptr(p_callback_data.p_message) 22 | ); 23 | ash::vk::FALSE 24 | } 25 | 26 | pub struct TestHarness { 27 | pub entry: ash::Entry, 28 | pub instance: ash::Instance, 29 | pub device: ash::Device, 30 | pub physical_device: ash::vk::PhysicalDevice, 31 | pub debug_callback: ash::vk::DebugUtilsMessengerEXT, 32 | pub debug_report_loader: debug_utils::Instance, 33 | } 34 | 35 | impl Drop for TestHarness { 36 | fn drop(&mut self) { 37 | unsafe { 38 | self.device.device_wait_idle().unwrap(); 39 | self.device.destroy_device(None); 40 | self.debug_report_loader 41 | .destroy_debug_utils_messenger(self.debug_callback, None); 42 | self.instance.destroy_instance(None); 43 | } 44 | } 45 | } 46 | impl TestHarness { 47 | pub fn new() -> Self { 48 | let app_name = ::std::ffi::CString::new("vk-mem testing").unwrap(); 49 | let app_info = ash::vk::ApplicationInfo::default() 50 | .application_name(&app_name) 51 | .application_version(0) 52 | .engine_name(&app_name) 53 | .engine_version(0) 54 | .api_version(ash::vk::make_api_version(0, 1, 3, 0)); 55 | 56 | let layer_names = [::std::ffi::CString::new("VK_LAYER_KHRONOS_validation").unwrap()]; 57 | let layers_names_raw: Vec<*const i8> = layer_names 58 | .iter() 59 | .map(|raw_name| raw_name.as_ptr()) 60 | .collect(); 61 | 62 | let extension_names_raw = extension_names(); 63 | let create_info = ash::vk::InstanceCreateInfo::default() 64 | .application_info(&app_info) 65 | .enabled_layer_names(&layers_names_raw) 66 | .enabled_extension_names(&extension_names_raw); 67 | 68 | let entry = unsafe { ash::Entry::load().unwrap() }; 69 | let instance: ash::Instance = unsafe { 70 | entry 71 | .create_instance(&create_info, None) 72 | .expect("Instance creation error") 73 | }; 74 | 75 | let debug_info = ash::vk::DebugUtilsMessengerCreateInfoEXT::default() 76 | .message_severity( 77 | ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR 78 | | ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING, 79 | ) 80 | .message_type( 81 | vk::DebugUtilsMessageTypeFlagsEXT::GENERAL 82 | | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE 83 | | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION, 84 | ) 85 | .pfn_user_callback(Some(vulkan_debug_callback)); 86 | 87 | let debug_report_loader = debug_utils::Instance::new(&entry, &instance); 88 | let debug_callback = unsafe { 89 | debug_report_loader 90 | .create_debug_utils_messenger(&debug_info, None) 91 | .unwrap() 92 | }; 93 | 94 | let physical_devices = unsafe { 95 | instance 96 | .enumerate_physical_devices() 97 | .expect("Physical device error") 98 | }; 99 | 100 | let physical_device = unsafe { 101 | *physical_devices 102 | .iter() 103 | .filter(|physical_device| { 104 | let version = instance 105 | .get_physical_device_properties(**physical_device) 106 | .api_version; 107 | ash::vk::api_version_major(version) == 1 108 | && ash::vk::api_version_minor(version) == 3 109 | }) 110 | .next() 111 | .expect("Couldn't find suitable device.") 112 | }; 113 | 114 | let priorities = [1.0]; 115 | 116 | let queue_info = [ash::vk::DeviceQueueCreateInfo::default() 117 | .queue_family_index(0) 118 | .queue_priorities(&priorities)]; 119 | 120 | let device_create_info = 121 | ash::vk::DeviceCreateInfo::default().queue_create_infos(&queue_info); 122 | 123 | let device: ash::Device = unsafe { 124 | instance 125 | .create_device(physical_device, &device_create_info, None) 126 | .unwrap() 127 | }; 128 | 129 | TestHarness { 130 | entry, 131 | instance, 132 | device, 133 | physical_device, 134 | debug_report_loader, 135 | debug_callback, 136 | } 137 | } 138 | 139 | pub fn create_allocator(&self) -> vk_mem::Allocator { 140 | let create_info = 141 | vk_mem::AllocatorCreateInfo::new(&self.instance, &self.device, self.physical_device); 142 | vk_mem::Allocator::new(create_info).unwrap() 143 | } 144 | } 145 | 146 | #[test] 147 | fn create_harness() { 148 | let _ = TestHarness::new(); 149 | } 150 | 151 | #[test] 152 | fn create_allocator() { 153 | let harness = TestHarness::new(); 154 | let _ = harness.create_allocator(); 155 | } 156 | 157 | #[test] 158 | fn create_gpu_buffer() { 159 | let harness = TestHarness::new(); 160 | let allocator = harness.create_allocator(); 161 | let allocation_info = vk_mem::AllocationCreateInfo { 162 | usage: vk_mem::MemoryUsage::Auto, 163 | ..Default::default() 164 | }; 165 | 166 | unsafe { 167 | let (buffer, mut allocation) = allocator 168 | .create_buffer( 169 | &ash::vk::BufferCreateInfo::default().size(16 * 1024).usage( 170 | ash::vk::BufferUsageFlags::VERTEX_BUFFER 171 | | ash::vk::BufferUsageFlags::TRANSFER_DST, 172 | ), 173 | &allocation_info, 174 | ) 175 | .unwrap(); 176 | let allocation_info = allocator.get_allocation_info(&allocation); 177 | assert_eq!(allocation_info.mapped_data, std::ptr::null_mut()); 178 | allocator.destroy_buffer(buffer, &mut allocation); 179 | } 180 | } 181 | 182 | #[test] 183 | fn create_cpu_buffer_preferred() { 184 | let harness = TestHarness::new(); 185 | let allocator = harness.create_allocator(); 186 | let allocation_info = vk_mem::AllocationCreateInfo { 187 | required_flags: ash::vk::MemoryPropertyFlags::HOST_VISIBLE, 188 | preferred_flags: ash::vk::MemoryPropertyFlags::HOST_COHERENT 189 | | ash::vk::MemoryPropertyFlags::HOST_CACHED, 190 | flags: vk_mem::AllocationCreateFlags::MAPPED, 191 | ..Default::default() 192 | }; 193 | unsafe { 194 | let (buffer, mut allocation) = allocator 195 | .create_buffer( 196 | &ash::vk::BufferCreateInfo::default().size(16 * 1024).usage( 197 | ash::vk::BufferUsageFlags::VERTEX_BUFFER 198 | | ash::vk::BufferUsageFlags::TRANSFER_DST, 199 | ), 200 | &allocation_info, 201 | ) 202 | .unwrap(); 203 | let allocation_info = allocator.get_allocation_info(&allocation); 204 | assert_ne!(allocation_info.mapped_data, std::ptr::null_mut()); 205 | allocator.destroy_buffer(buffer, &mut allocation); 206 | } 207 | } 208 | 209 | #[test] 210 | fn create_gpu_buffer_pool() { 211 | let harness = TestHarness::new(); 212 | let allocator = harness.create_allocator(); 213 | let allocator = Arc::new(allocator); 214 | 215 | let buffer_info = ash::vk::BufferCreateInfo::default() 216 | .size(16 * 1024) 217 | .usage(ash::vk::BufferUsageFlags::UNIFORM_BUFFER | ash::vk::BufferUsageFlags::TRANSFER_DST); 218 | 219 | let allocation_info = vk_mem::AllocationCreateInfo { 220 | required_flags: ash::vk::MemoryPropertyFlags::HOST_VISIBLE, 221 | preferred_flags: ash::vk::MemoryPropertyFlags::HOST_COHERENT 222 | | ash::vk::MemoryPropertyFlags::HOST_CACHED, 223 | flags: vk_mem::AllocationCreateFlags::MAPPED, 224 | 225 | ..Default::default() 226 | }; 227 | unsafe { 228 | let memory_type_index = allocator 229 | .find_memory_type_index_for_buffer_info(&buffer_info, &allocation_info) 230 | .unwrap(); 231 | 232 | // Create a pool that can have at most 2 blocks, 128 MiB each. 233 | let pool_info = vk_mem::PoolCreateInfo { 234 | memory_type_index, 235 | block_size: 128 * 1024 * 1024, 236 | max_block_count: 2, 237 | ..Default::default() 238 | }; 239 | 240 | let pool = allocator.create_pool(&pool_info).unwrap(); 241 | 242 | let (buffer, mut allocation) = pool.create_buffer(&buffer_info, &allocation_info).unwrap(); 243 | let allocation_info = allocator.get_allocation_info(&allocation); 244 | assert_ne!(allocation_info.mapped_data, std::ptr::null_mut()); 245 | allocator.destroy_buffer(buffer, &mut allocation); 246 | } 247 | } 248 | 249 | #[test] 250 | fn test_gpu_stats() { 251 | let harness = TestHarness::new(); 252 | let allocator = harness.create_allocator(); 253 | let allocation_info = vk_mem::AllocationCreateInfo { 254 | usage: vk_mem::MemoryUsage::Auto, 255 | ..Default::default() 256 | }; 257 | 258 | unsafe { 259 | let stats_1 = allocator.calculate_statistics().unwrap(); 260 | assert_eq!(stats_1.total.statistics.blockCount, 0); 261 | assert_eq!(stats_1.total.statistics.allocationCount, 0); 262 | assert_eq!(stats_1.total.statistics.allocationBytes, 0); 263 | 264 | let (buffer, mut allocation) = allocator 265 | .create_buffer( 266 | &ash::vk::BufferCreateInfo::default().size(16 * 1024).usage( 267 | ash::vk::BufferUsageFlags::VERTEX_BUFFER 268 | | ash::vk::BufferUsageFlags::TRANSFER_DST, 269 | ), 270 | &allocation_info, 271 | ) 272 | .unwrap(); 273 | 274 | let stats_2 = allocator.calculate_statistics().unwrap(); 275 | assert_eq!(stats_2.total.statistics.blockCount, 1); 276 | assert_eq!(stats_2.total.statistics.allocationCount, 1); 277 | assert_eq!(stats_2.total.statistics.allocationBytes, 16 * 1024); 278 | 279 | allocator.destroy_buffer(buffer, &mut allocation); 280 | 281 | let stats_3 = allocator.calculate_statistics().unwrap(); 282 | assert_eq!(stats_3.total.statistics.blockCount, 1); 283 | assert_eq!(stats_3.total.statistics.allocationCount, 0); 284 | assert_eq!(stats_3.total.statistics.allocationBytes, 0); 285 | } 286 | } 287 | 288 | #[test] 289 | fn create_virtual_block() { 290 | let create_info = vk_mem::VirtualBlockCreateInfo { 291 | size: 16 * 1024 * 1024, 292 | flags: vk_mem::VirtualBlockCreateFlags::VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT, 293 | allocation_callbacks: None, 294 | }; // 16MB block 295 | let _virtual_block = 296 | vk_mem::VirtualBlock::new(create_info).expect("Couldn't create VirtualBlock"); 297 | } 298 | 299 | #[test] 300 | fn virtual_allocate_and_free() { 301 | let create_info = vk_mem::VirtualBlockCreateInfo { 302 | size: 16 * 1024 * 1024, 303 | flags: vk_mem::VirtualBlockCreateFlags::VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT, 304 | allocation_callbacks: None, 305 | }; // 16MB block 306 | let mut virtual_block = 307 | vk_mem::VirtualBlock::new(create_info).expect("Couldn't create VirtualBlock"); 308 | 309 | let allocation_info = vk_mem::VirtualAllocationCreateInfo { 310 | size: 8 * 1024 * 1024, 311 | alignment: 0, 312 | user_data: 0, 313 | flags: vk_mem::VirtualAllocationCreateFlags::empty(), 314 | }; 315 | 316 | // Fully allocate the VirtualBlock and then free both allocations 317 | unsafe { 318 | let (mut virtual_alloc_0, offset_0) = virtual_block.allocate(allocation_info).unwrap(); 319 | let (mut virtual_alloc_1, offset_1) = virtual_block.allocate(allocation_info).unwrap(); 320 | assert_ne!(offset_0, offset_1); 321 | virtual_block.free(&mut virtual_alloc_0); 322 | virtual_block.free(&mut virtual_alloc_1); 323 | } 324 | 325 | // Fully allocate it again and then clear it 326 | unsafe { 327 | let (_virtual_alloc_0, offset_0) = virtual_block.allocate(allocation_info).unwrap(); 328 | let (_virtual_alloc_1, offset_1) = virtual_block.allocate(allocation_info).unwrap(); 329 | assert_ne!(offset_0, offset_1); 330 | virtual_block.clear(); 331 | } 332 | 333 | // VMA should trigger an assert when the VirtualBlock is dropped, if any 334 | // allocations have not been freed, or the block not cleared instead 335 | } 336 | 337 | #[test] 338 | fn virtual_allocation_user_data() { 339 | let create_info = vk_mem::VirtualBlockCreateInfo { 340 | size: 16 * 1024 * 1024, 341 | ..Default::default() 342 | }; // 16MB block 343 | let mut virtual_block = 344 | vk_mem::VirtualBlock::new(create_info).expect("Couldn't create VirtualBlock"); 345 | 346 | let user_data = Box::new(vec![12, 34, 56, 78, 90]); 347 | let allocation_info = vk_mem::VirtualAllocationCreateInfo { 348 | size: 8 * 1024 * 1024, 349 | alignment: 0, 350 | user_data: user_data.as_ptr() as usize, 351 | flags: vk_mem::VirtualAllocationCreateFlags::empty(), 352 | }; 353 | 354 | unsafe { 355 | let (mut virtual_alloc_0, _) = virtual_block.allocate(allocation_info).unwrap(); 356 | let queried_info = virtual_block 357 | .get_allocation_info(&virtual_alloc_0) 358 | .expect("Couldn't get VirtualAllocationInfo from VirtualBlock"); 359 | let queried_user_data = std::slice::from_raw_parts(queried_info.user_data as *const i32, 5); 360 | assert_eq!(queried_user_data, &*user_data); 361 | virtual_block.free(&mut virtual_alloc_0); 362 | } 363 | } 364 | 365 | #[test] 366 | fn virtual_block_out_of_space() { 367 | let create_info = vk_mem::VirtualBlockCreateInfo { 368 | size: 16 * 1024 * 1024, 369 | ..Default::default() 370 | }; // 16MB block 371 | let mut virtual_block = 372 | vk_mem::VirtualBlock::new(create_info).expect("Couldn't create VirtualBlock"); 373 | 374 | let allocation_info = vk_mem::VirtualAllocationCreateInfo { 375 | size: 16 * 1024 * 1024 + 1, 376 | alignment: 0, 377 | user_data: 0, 378 | flags: vk_mem::VirtualAllocationCreateFlags::empty(), 379 | }; 380 | 381 | unsafe { 382 | match virtual_block.allocate(allocation_info) { 383 | Ok(_) => panic!("Created VirtualAllocation larger than VirtualBlock"), 384 | Err(ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => {} 385 | Err(_) => panic!("Unexpected VirtualBlock error"), 386 | } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /wrapper.cpp: -------------------------------------------------------------------------------- 1 | #define VMA_IMPLEMENTATION 2 | #include "vk_mem_alloc.h" 3 | --------------------------------------------------------------------------------