├── .gitignore ├── .rustfmt.toml ├── .vscode └── settings.json ├── Cargo.toml ├── LICENSE ├── README.md ├── crates ├── util │ ├── .gitignore │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── target │ │ ├── .rustc_info.json │ │ ├── CACHEDIR.TAG │ │ └── debug │ │ └── .cargo-lock └── vkfft-sys │ ├── .gitignore │ ├── .rustfmt.toml │ ├── .vscode │ └── settings.json │ ├── Cargo.toml │ ├── LICENSE │ ├── build.rs │ ├── src │ └── lib.rs │ └── wrapper.cpp ├── examples └── convolution.rs ├── rust-toolchain.toml └── src ├── app.rs ├── config.rs ├── error.rs ├── lib.rs └── version.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .idea/ 4 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "/usr/bin/python3" 3 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vkfft" 3 | version = "0.1.3" 4 | authors = ["Semio AI, Inc."] 5 | edition = "2018" 6 | resolver = "2" 7 | license = "BSD-3-Clause" 8 | description = "Rust bindings for VkFFT" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | vkfft-sys = { path = "./crates/vkfft-sys", version = "0.1.1" } 14 | vulkano = "0.22" 15 | derive_more = "0.99" 16 | vk-sys = "0.6" 17 | 18 | [dev-dependencies] 19 | util = { path = "./crates/util" } 20 | smallvec = "1.6" 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Semio AI, Inc. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vkfft-rs 2 | 3 | `vkfft-rs` allows high-performance execution of 1, 2, or 3D FFTs on the GPU using Vulkan in Rust, with built-in support for convolutions. 4 | 5 | `vkfft-rs` is a binding for [VkFFT](https://github.com/DTolm/VkFFT) that assumes usage with [vulkano](https://vulkano.rs/). While VkFFT, despite the name, supports multiple backends, this wrapper requires usage with Vulkan. 6 | 7 | While `vkfft-rs` attempts to maintain a safe API, it's very likely there are some safe functions in this codebase that can still cause unsafe behavior. VkFFT's API and associated data structures are unsafe and stateful, which presents difficulties in ensuring Rust's safety guarantees. Until its safety properties can be properly verified it is recommend to proceed with caution. PRs welcome! 8 | 9 | ## Building 10 | 11 | ```.sh 12 | # Clone VkFFT 13 | git clone https://github.com/DTolm/VkFFT.git 14 | 15 | # Navigate into the folder 16 | cd VkFFT 17 | 18 | # Create a build directory (this currently must be named "build"!) 19 | mkdir build && cd build 20 | 21 | # Configure build 22 | cmake .. 23 | 24 | # Build 25 | make 26 | 27 | # Build vkfft-rs 28 | cd vkfft-rs 29 | 30 | # VKFFT_ROOT must be set to the root directory of VkFFT! 31 | export VKFFT_ROOT=/path/to/VkFFT 32 | 33 | # Build 34 | cargo build --examples 35 | 36 | # Run convolution example 37 | cargo run --example convolution 38 | ``` 39 | 40 | ### IMPORTANT 41 | 42 | If your system already has `libSPIRV.a` in the library search path and are encountering strange segmentation faults 43 | in SPIRV at runtime, it's possible Rust has linked against the system `libSPIRV.a` rather than the one in VkFFT's `build` 44 | directory. These different libraries might be ABI incompatible. 45 | 46 | This is unfortunately a limitation of cargo/rustc's ability for a crate to specify absolute paths for static libraries. It is recommended to, unfortunately, remove the other `libSPIRV.a` from the system library path. 47 | 48 | For example, on Ubuntu: 49 | ```.sh 50 | sudo mv /usr/lib/x86_64-linux-gnu/libSPIRV.a /usr/lib/x86_64-linux-gnu/libSPIRV.a.backup 51 | ``` 52 | -------------------------------------------------------------------------------- /crates/util/.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /crates/util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "util" 3 | version = "0.1.0" 4 | authors = ["Braden McDorman "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | vulkano = "0.22" 11 | -------------------------------------------------------------------------------- /crates/util/src/lib.rs: -------------------------------------------------------------------------------- 1 | use vulkano::{buffer::{BufferAccess, CpuAccessibleBuffer}, command_buffer::pool::{UnsafeCommandPool, UnsafeCommandPoolAlloc}}; 2 | use vulkano::command_buffer::{submit::SubmitCommandBufferBuilder, sys::UnsafeCommandBuffer}; 3 | use vulkano::device::{Device, DeviceExtensions, Features, Queue}; 4 | use vulkano::instance::debug::{DebugCallback, Message, MessageSeverity, MessageType}; 5 | use vulkano::instance::{Instance, PhysicalDevice}; 6 | use vulkano::sync::Fence; 7 | 8 | use std::{error::Error, fmt::{Display, Formatter}, sync::Arc}; 9 | 10 | const MESSAGE_SEVERITIES: MessageSeverity = MessageSeverity { 11 | error: true, 12 | warning: true, 13 | information: true, 14 | verbose: true, 15 | }; 16 | 17 | fn on_debug_message(msg: &Message) { 18 | if msg.ty.general && msg.severity.verbose { 19 | return; 20 | } 21 | 22 | let severity = if msg.severity.error { 23 | "error" 24 | } else if msg.severity.warning { 25 | "warning" 26 | } else if msg.severity.information { 27 | "information" 28 | } else if msg.severity.verbose { 29 | "verbose" 30 | } else { 31 | panic!("no-impl"); 32 | }; 33 | 34 | let ty = if msg.ty.general { 35 | "general" 36 | } else if msg.ty.validation { 37 | "validation" 38 | } else if msg.ty.performance { 39 | "performance" 40 | } else { 41 | panic!("no-impl"); 42 | }; 43 | 44 | eprintln!( 45 | "{} {} {}: {}", 46 | msg.layer_prefix.unwrap_or("unknown"), 47 | ty, 48 | severity, 49 | msg.description 50 | ); 51 | } 52 | 53 | pub struct Context<'a> { 54 | pub instance: &'a Arc, 55 | pub physical: PhysicalDevice<'a>, 56 | pub device: Arc, 57 | pub queue: Arc, 58 | pub pool: Arc, 59 | pub fence: Fence, 60 | _debug_cb: Option, 61 | } 62 | 63 | impl<'a> Context<'a> { 64 | pub fn new(instance: &'a Arc) -> Result> { 65 | let debug_cb = DebugCallback::new( 66 | &instance, 67 | MESSAGE_SEVERITIES, 68 | MessageType::all(), 69 | on_debug_message, 70 | ) 71 | .ok(); 72 | 73 | let physical = PhysicalDevice::enumerate(&instance) 74 | .next() 75 | .ok_or("No device available")?; 76 | 77 | println!("Using {}", physical.name()); 78 | 79 | let queue_family = physical 80 | .queue_families() 81 | .find(|&q| q.supports_compute() && q.supports_graphics()) 82 | .ok_or("Couldn't find a compute queue family")?; 83 | 84 | let (device, mut queues) = Device::new( 85 | physical, 86 | &Features::none(), 87 | &DeviceExtensions::none(), 88 | [(queue_family, 0.5)].iter().cloned(), 89 | )?; 90 | 91 | let queue = queues.next().unwrap(); 92 | let pool = Arc::new(UnsafeCommandPool::new( 93 | device.clone(), 94 | queue_family, 95 | false, 96 | true, 97 | )?); 98 | 99 | let fence = Fence::alloc(device.clone())?; 100 | 101 | Ok(Self { 102 | instance, 103 | physical, 104 | queue, 105 | device, 106 | pool, 107 | fence, 108 | _debug_cb: debug_cb, 109 | }) 110 | } 111 | 112 | pub fn submit( 113 | &mut self, 114 | command_buffer: UnsafeCommandBuffer, 115 | ) -> Result<(), Box> { 116 | unsafe { 117 | let mut submit = SubmitCommandBufferBuilder::new(); 118 | submit.add_command_buffer(&command_buffer); 119 | submit.set_fence_signal(&self.fence); 120 | 121 | submit.submit(&self.queue)?; 122 | 123 | self.fence.wait(None)?; 124 | 125 | self.fence.reset()?; 126 | } 127 | 128 | Ok(()) 129 | } 130 | 131 | pub fn alloc_cmd_buffer( 132 | &self, 133 | secondary: bool, 134 | ) -> Result> { 135 | Ok( 136 | self 137 | .pool 138 | .alloc_command_buffers(secondary, 1)? 139 | .next() 140 | .ok_or("Failed to allocate cmd buffer")?, 141 | ) 142 | } 143 | 144 | pub fn alloc_primary_cmd_buffer(&self) -> Result> { 145 | self.alloc_cmd_buffer(false) 146 | } 147 | 148 | pub fn alloc_secondary_cmd_buffer(&self) -> Result> { 149 | self.alloc_cmd_buffer(true) 150 | } 151 | } 152 | 153 | pub struct SizeIterator<'a> { 154 | size: &'a [u32; 2], 155 | pos: [u32; 2], 156 | total: u32, 157 | iter: u32 158 | } 159 | 160 | impl<'a> SizeIterator<'a> { 161 | pub fn new(size: &'a [u32; 2]) -> Self { 162 | let total = size.iter().cloned().reduce(|a, b| a * b).unwrap(); 163 | Self { size, pos: [0; 2], total, iter: 0 } 164 | } 165 | } 166 | 167 | impl<'a> Iterator for SizeIterator<'a> { 168 | type Item = [u32; 2]; 169 | 170 | fn next(&mut self) -> Option { 171 | if self.iter >= self.total - 1 { 172 | return None; 173 | } 174 | 175 | let ret = Some([self.iter % self.size[0], self.iter / self.size[0]]); 176 | 177 | self.iter += 1; 178 | 179 | ret 180 | } 181 | } 182 | 183 | pub trait Digits { 184 | fn digits(&self) -> usize; 185 | } 186 | 187 | impl Digits for i64 { 188 | fn digits(&self) -> usize { 189 | let mut this = *self; 190 | let mut ret = 1; 191 | while this / 10 != 0 { 192 | ret += 1; 193 | this /= 10; 194 | } 195 | ret 196 | } 197 | } 198 | 199 | pub struct MatrixFormatter<'a> { 200 | size: &'a [u32; 2], 201 | data: &'a Arc> 202 | } 203 | 204 | impl<'a> MatrixFormatter<'a> { 205 | pub fn new(size: &'a [u32; 2], data: &'a Arc>) -> Self { 206 | Self { 207 | size, 208 | data 209 | } 210 | } 211 | } 212 | 213 | impl<'a> Display for MatrixFormatter<'a> 214 | { 215 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 216 | let data = self.data.read().unwrap(); 217 | for j in 0..self.size[1] { 218 | for i in 0..self.size[0] { 219 | let value = data[(j * self.size[0] + i) as usize]; 220 | if value >= 0.0f32 { 221 | write!(f, " ")?; 222 | } 223 | 224 | let spaces = 3 - (value.floor() as i64).digits(); 225 | for _ in 0..spaces { 226 | write!(f, " ")?; 227 | } 228 | 229 | write!(f, "{:.1}", value)?; 230 | } 231 | writeln!(f)?; 232 | } 233 | 234 | Ok(()) 235 | } 236 | } 237 | 238 | #[cfg(test)] 239 | mod tests { 240 | use super::*; 241 | 242 | #[test] 243 | fn check_digits() { 244 | assert_eq!(100i64.digits(), 3); 245 | assert_eq!(1000i64.digits(), 4); 246 | assert_eq!((-1000i64).digits(), 4); 247 | assert_eq!((-1i64).digits(), 1); 248 | assert_eq!(0i64.digits(), 1); 249 | assert_eq!((-9.6f32.floor() as i64).digits(), 1) 250 | } 251 | } -------------------------------------------------------------------------------- /crates/util/target/.rustc_info.json: -------------------------------------------------------------------------------- 1 | {"rustc_fingerprint":16169262166453339448,"outputs":{"17598535894874457435":{"success":true,"status":"","code":0,"stdout":"rustc 1.53.0-nightly (07e0e2ec2 2021-03-24)\nbinary: rustc\ncommit-hash: 07e0e2ec268c140e607e1ac7f49f145612d0f597\ncommit-date: 2021-03-24\nhost: x86_64-unknown-linux-gnu\nrelease: 1.53.0-nightly\nLLVM version: 12.0.0\n","stderr":""},"2797684049618456168":{"success":false,"status":"exit code: 1","code":1,"stdout":"","stderr":"error: `-Csplit-debuginfo` is unstable on this platform\n\n"},"931469667778813386":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/beta/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}} -------------------------------------------------------------------------------- /crates/util/target/CACHEDIR.TAG: -------------------------------------------------------------------------------- 1 | Signature: 8a477f597d28d172789f06886806bc55 2 | # This file is a cache directory tag created by cargo. 3 | # For information about cache directory tags see https://bford.info/cachedir/ 4 | -------------------------------------------------------------------------------- /crates/util/target/debug/.cargo-lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semio-ai/vkfft-rs/e5341d47b1519ed2c2f28769ae6a5ee1f098a708/crates/util/target/debug/.cargo-lock -------------------------------------------------------------------------------- /crates/vkfft-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /crates/vkfft-sys/.rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 -------------------------------------------------------------------------------- /crates/vkfft-sys/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.default.includePath": [ 3 | "${env:VKFFT_ROOT}/vkFFT", 4 | "${env:VKFFT_ROOT}/glslang-main/glslang/Include", 5 | ], 6 | "C_Cpp.default.defines": [ 7 | "VKFFT_BACKEND=0" 8 | ] 9 | } -------------------------------------------------------------------------------- /crates/vkfft-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vkfft-sys" 3 | version = "0.1.1" 4 | authors = ["Semio AI, Inc."] 5 | edition = "2018" 6 | license = "BSD-3-Clause" 7 | description = "Rust bindings for VkFFT" 8 | 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | 14 | [build-dependencies] 15 | bindgen = "0.58" 16 | cc = "1.0" -------------------------------------------------------------------------------- /crates/vkfft-sys/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Semio AI, Inc. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /crates/vkfft-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | extern crate cc; 3 | 4 | use std::error::Error; 5 | use std::path::{Path, PathBuf}; 6 | 7 | use bindgen::Bindings; 8 | 9 | fn build_lib(out_dir: O, library_dirs: LD, libraries: L, defines: &[(&str, &str); N], include_dirs: &[String; M]) -> Result<(), Box> 10 | where 11 | O: AsRef, 12 | LD: Iterator, 13 | LD::Item: AsRef, 14 | L: Iterator, 15 | L::Item: AsRef 16 | { 17 | let mut build = cc::Build::default(); 18 | 19 | build 20 | .cpp(true) 21 | .file("wrapper.cpp") 22 | .include(out_dir) 23 | .flag("-std=c++11") 24 | .flag("-w"); 25 | 26 | for library_dir in library_dirs { 27 | build.flag(format!("-L{}", library_dir.as_ref()).as_str()); 28 | } 29 | 30 | for library in libraries { 31 | build.flag(format!("-l{}", library.as_ref()).as_str()); 32 | } 33 | 34 | build 35 | .cargo_metadata(true) 36 | .static_flag(true); 37 | 38 | for (key, value) in defines.iter() { 39 | build.define(*key, Some(*value)); 40 | } 41 | 42 | for include_dir in include_dirs.iter() { 43 | build.include(include_dir); 44 | } 45 | 46 | 47 | build.compile("vkfft"); 48 | 49 | Ok(()) 50 | } 51 | 52 | fn gen_wrapper(file: F, defines: &[(&str, &str); N], include_dirs: &[String; M]) -> Result> 53 | where 54 | F: AsRef, 55 | { 56 | let base_args = [ 57 | "-std=c++11".to_string(), 58 | ]; 59 | 60 | let defines: Vec = defines.iter().map(|(k, v)| { 61 | format!("-D{}={}", k, v) 62 | }).collect(); 63 | 64 | let include_dirs: Vec = include_dirs.iter() 65 | .map(|s| format!("-I{}", s)) 66 | .collect(); 67 | 68 | let clang_args = base_args 69 | .iter() 70 | .chain(defines.iter()) 71 | .chain(include_dirs.iter()); 72 | 73 | println!("{:?}", clang_args); 74 | 75 | 76 | 77 | 78 | let res = bindgen::Builder::default() 79 | .clang_args(clang_args) 80 | .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 81 | .header(file.as_ref().to_str().unwrap()) 82 | .allowlist_recursively(true) 83 | .allowlist_type("VkFFTConfiguration") 84 | .allowlist_type("VkFFTLaunchParams") 85 | .allowlist_type("VkFFTResult") 86 | .allowlist_type("VkFFTSpecializationConstantsLayout") 87 | .allowlist_type("VkFFTPushConstantsLayout") 88 | .allowlist_type("VkFFTAxis") 89 | .allowlist_type("VkFFTPlan") 90 | .allowlist_type("VkFFTApplication") 91 | .allowlist_function("VkFFTSync") 92 | .allowlist_function("VkFFTAppend") 93 | .allowlist_function("VkFFTPlanAxis") 94 | .allowlist_function("initializeVkFFT") 95 | .allowlist_function("deleteVkFFT") 96 | .allowlist_function("VkFFTGetVersion") 97 | 98 | .generate(); 99 | 100 | let bindings = match res { 101 | Ok(x) => x, 102 | Err(_) => { 103 | eprintln!("Failed to generate bindings."); 104 | std::process::exit(1); 105 | } 106 | }; 107 | 108 | Ok(bindings) 109 | } 110 | 111 | fn main() -> Result<(), Box> { 112 | let vkfft_root = std::env::var("VKFFT_ROOT")?; 113 | let out_dir = std::env::var("OUT_DIR")?; 114 | let out_dir = PathBuf::from(out_dir); 115 | 116 | let library_dirs = [ 117 | format!("{}/build/glslang-main/glslang", vkfft_root), 118 | format!("{}/build/glslang-main/glslang/OSDependent/Unix", vkfft_root), 119 | format!("{}/build/glslang-main/glslang/OGLCompilersDLL", vkfft_root), 120 | format!("{}/build/glslang-main/SPIRV", vkfft_root), 121 | ]; 122 | 123 | let libraries = [ 124 | "glslang", 125 | "MachineIndependent", 126 | "OSDependent", 127 | "GenericCodeGen", 128 | "OGLCompiler", 129 | "vulkan", 130 | "SPIRV" 131 | ]; 132 | 133 | for library_dir in library_dirs.iter() { 134 | println!("cargo:rustc-link-search={}", library_dir); 135 | } 136 | 137 | for library in libraries.iter() { 138 | println!("cargo:rustc-link-lib={}", library); 139 | } 140 | 141 | 142 | println!("cargo:rerun-if-changed=wrapper.cpp"); 143 | println!("cargo:rerun-if-changed=build.rs"); 144 | 145 | let include_dirs = [ 146 | format!("{}/vkFFT", &vkfft_root), 147 | format!("{}/glslang-main/glslang/Include", vkfft_root) 148 | ]; 149 | 150 | let defines = [ 151 | ("VKFFT_BACKEND", "0"), 152 | ("VK_API_VERSION", "11") 153 | ]; 154 | 155 | let wrapper = std::fs::read_to_string(format!("{}/vkFFT/vkFFT.h", vkfft_root))? 156 | .replace("static inline", ""); 157 | 158 | let rw = out_dir.join("vkfft_rw.hpp"); 159 | std::fs::write(&rw, wrapper.as_str())?; 160 | 161 | build_lib(&out_dir, library_dirs.iter(), libraries.iter(), &defines, &include_dirs)?; 162 | 163 | let bindings = gen_wrapper(&rw, &defines, &include_dirs)?; 164 | bindings.write_to_file(out_dir.join("bindings.rs"))?; 165 | 166 | Ok(()) 167 | } 168 | -------------------------------------------------------------------------------- /crates/vkfft-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | 5 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 6 | -------------------------------------------------------------------------------- /crates/vkfft-sys/wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /examples/convolution.rs: -------------------------------------------------------------------------------- 1 | use vkfft::app::App; 2 | use vkfft::app::LaunchParams; 3 | use vkfft::config::Config; 4 | 5 | use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer}; 6 | use vulkano::command_buffer::{ 7 | sys::{Flags, UnsafeCommandBufferBuilder}, 8 | Kind, 9 | }; 10 | 11 | use vulkano::instance::{Instance, InstanceExtensions}; 12 | 13 | use std::{error::Error, sync::Arc}; 14 | 15 | use util::{Context, SizeIterator, MatrixFormatter}; 16 | 17 | const DEFAULT_BUFFER_USAGE: BufferUsage = BufferUsage { 18 | storage_buffer: true, 19 | transfer_source: true, 20 | transfer_destination: true, 21 | ..BufferUsage::none() 22 | }; 23 | 24 | 25 | 26 | 27 | /// Transform a kernel from spatial data to frequency data 28 | pub fn transform_kernel( 29 | context: &mut Context, 30 | coordinate_features: u32, 31 | batch_count: u32, 32 | size: &[u32; 2], 33 | kernel: &Arc>, 34 | ) -> Result<(), Box> { 35 | // Configure kernel FFT 36 | let config = Config::builder() 37 | .physical_device(context.physical) 38 | .device(context.device.clone()) 39 | .fence(&context.fence) 40 | .queue(context.queue.clone()) 41 | .buffer(kernel.clone()) 42 | .command_pool(context.pool.clone()) 43 | .kernel_convolution() 44 | .normalize() 45 | .coordinate_features(coordinate_features) 46 | .batch_count(1) 47 | .r2c() 48 | .disable_reorder_four_step() 49 | .dim(&size) 50 | .build()?; 51 | 52 | // Allocate a command buffer 53 | let primary_cmd_buffer = context.alloc_primary_cmd_buffer()?; 54 | 55 | // Create command buffer handle 56 | let builder = 57 | unsafe { UnsafeCommandBufferBuilder::new(&primary_cmd_buffer, Kind::primary(), Flags::None)? }; 58 | 59 | // Configure FFT launch parameters 60 | let mut params = LaunchParams::builder().command_buffer(&builder).build()?; 61 | 62 | // Construct FFT "Application" 63 | let mut app = App::new(config)?; 64 | 65 | // Run forward FFT 66 | app.forward(&mut params)?; 67 | // app.inverse(&mut params)?; 68 | 69 | // Dispatch command buffer and wait for completion 70 | let command_buffer = builder.build()?; 71 | context.submit(command_buffer)?; 72 | 73 | Ok(()) 74 | } 75 | 76 | pub fn convolve( 77 | context: &mut Context, 78 | coordinate_features: u32, 79 | size: &[u32; 2], 80 | kernel: &Arc>, 81 | ) -> Result<(), Box> { 82 | let input_buffer_size = coordinate_features * 2 * (size[0] / 2 + 1) * size[1]; 83 | let buffer_size = coordinate_features * 2 * (size[0] / 2 + 1) * size[1]; 84 | 85 | let input_buffer = CpuAccessibleBuffer::from_iter( 86 | context.device.clone(), 87 | DEFAULT_BUFFER_USAGE, 88 | false, 89 | (0..input_buffer_size).map(|_| 0.0f32), 90 | )?; 91 | 92 | let buffer = CpuAccessibleBuffer::from_iter( 93 | context.device.clone(), 94 | DEFAULT_BUFFER_USAGE, 95 | false, 96 | (0..buffer_size).map(|_| 0.0f32), 97 | )?; 98 | 99 | { 100 | let mut buffer = input_buffer.write()?; 101 | 102 | for v in 0..coordinate_features { 103 | for [i, j] in SizeIterator::new(size) { 104 | let _0 = i + j * (size[0] / 2) + v * (size[0] / 2) * size[1]; 105 | buffer[_0 as usize] = 1.0f32; 106 | } 107 | } 108 | } 109 | 110 | println!("Buffer:"); 111 | println!("{}", MatrixFormatter::new(size, &input_buffer)); 112 | println!(); 113 | 114 | // Configure kernel FFT 115 | let conv_config = Config::builder() 116 | .physical_device(context.physical) 117 | .device(context.device.clone()) 118 | .fence(&context.fence) 119 | .queue(context.queue.clone()) 120 | .input_buffer(input_buffer) 121 | .buffer(buffer.clone()) 122 | .command_pool(context.pool.clone()) 123 | .convolution() 124 | .kernel(kernel.clone()) 125 | .normalize() 126 | .coordinate_features(coordinate_features) 127 | .batch_count(1) 128 | .r2c() 129 | .disable_reorder_four_step() 130 | .input_formatted(true) 131 | .dim(&size) 132 | .build()?; 133 | 134 | // Allocate a command buffer 135 | let primary_cmd_buffer = context.alloc_primary_cmd_buffer()?; 136 | 137 | // Create command buffer handle 138 | let builder = 139 | unsafe { UnsafeCommandBufferBuilder::new(&primary_cmd_buffer, Kind::primary(), Flags::None)? }; 140 | 141 | // Configure FFT launch parameters 142 | let mut params = LaunchParams::builder().command_buffer(&builder).build()?; 143 | 144 | // Construct FFT "Application" 145 | let mut app = App::new(conv_config)?; 146 | 147 | // Run forward FFT 148 | app.forward(&mut params)?; 149 | 150 | // Dispatch command buffer and wait for completion 151 | let command_buffer = builder.build()?; 152 | context.submit(command_buffer)?; 153 | 154 | println!("Result:"); 155 | println!("{}", MatrixFormatter::new(size, &buffer)); 156 | println!(); 157 | 158 | Ok(()) 159 | } 160 | 161 | fn main() -> Result<(), Box> { 162 | println!("VkFFT version: {}", vkfft::version()); 163 | 164 | let instance = Instance::new( 165 | None, 166 | &InstanceExtensions { 167 | ext_debug_utils: true, 168 | ..InstanceExtensions::none() 169 | }, 170 | None, 171 | )?; 172 | 173 | let mut context = Context::new(&instance)?; 174 | 175 | let batch_count = 2; 176 | let coordinate_features = 2; 177 | let size = [32, 32]; 178 | 179 | let kernel_size = batch_count * coordinate_features * 2 * (size[0] / 2 + 1) * size[1]; 180 | 181 | let kernel = CpuAccessibleBuffer::from_iter( 182 | context.device.clone(), 183 | DEFAULT_BUFFER_USAGE, 184 | false, 185 | (0..kernel_size).map(|_| 0.0f32), 186 | )?; 187 | 188 | { 189 | let mut kernel_input = kernel.write()?; 190 | 191 | let mut range = size; 192 | range[0] = range[0] / 2 + 1; 193 | 194 | for f in 0..batch_count { 195 | for v in 0..coordinate_features { 196 | for [i, j] in SizeIterator::new(&range) { 197 | println!("{} {}", i, j); 198 | let _0 = 2 * i 199 | + j * (size[0] + 2) 200 | + v * (size[0] + 2) * size[1] 201 | + f * coordinate_features * (size[0] + 2) * size[1]; 202 | let _1 = 2 * i 203 | + 1 204 | + j * (size[0] + 2) 205 | + v * (size[0] + 2) * size[1] 206 | + f * coordinate_features * (size[0] + 2) * size[1]; 207 | kernel_input[_0 as usize] = (f * coordinate_features + v + 1) as f32; 208 | kernel_input[_1 as usize] = 0.0f32; 209 | } 210 | } 211 | } 212 | } 213 | 214 | println!("Kernel:"); 215 | println!("{}", &MatrixFormatter::new(&size, &kernel)); 216 | println!(); 217 | 218 | 219 | transform_kernel( 220 | &mut context, 221 | coordinate_features, 222 | batch_count, 223 | &size, 224 | &kernel, 225 | )?; 226 | 227 | println!("Transformed Kernel:"); 228 | println!("{}", &MatrixFormatter::new(&size, &kernel)); 229 | println!(); 230 | 231 | convolve(&mut context, coordinate_features, &size, &kernel)?; 232 | 233 | Ok(()) 234 | } 235 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /src/app.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use error::check_error; 4 | use vulkano::{buffer::BufferAccess, VulkanHandle, VulkanObject}; 5 | 6 | use crate::{ 7 | config::{Config, ConfigGuard}, 8 | error, 9 | }; 10 | 11 | use std::pin::Pin; 12 | use vk_sys as vk; 13 | 14 | use std::ptr::addr_of_mut; 15 | 16 | use derive_more::{Display, Error}; 17 | 18 | #[derive(Display, Debug, Error)] 19 | pub enum BuildError { 20 | NoCommandBuffer, 21 | // NoBuffer, 22 | // NoTempBuffer, 23 | // NoInputBuffer, 24 | // NoOutputBuffer, 25 | // NoKernel, 26 | } 27 | 28 | #[derive(Display, Debug, Error)] 29 | pub enum LaunchError { 30 | ConfigSpecifiesBuffer, 31 | ConfigSpecifiesTempBuffer, 32 | ConfigSpecifiesInputBuffer, 33 | ConfigSpecifiesOutputBuffer, 34 | ConfigSpecifiesKernel, 35 | } 36 | 37 | pub struct LaunchParamsBuilder { 38 | command_buffer: Option, 39 | buffer: Option>, 40 | temp_buffer: Option>, 41 | input_buffer: Option>, 42 | output_buffer: Option>, 43 | kernel: Option>, 44 | } 45 | 46 | impl LaunchParamsBuilder { 47 | pub fn new() -> Self { 48 | Self { 49 | buffer: None, 50 | command_buffer: None, 51 | input_buffer: None, 52 | kernel: None, 53 | output_buffer: None, 54 | temp_buffer: None, 55 | } 56 | } 57 | 58 | pub fn command_buffer(mut self, command_buffer: &C) -> Self 59 | where 60 | C: VulkanObject, 61 | { 62 | self.command_buffer = Some(command_buffer.internal_object()); 63 | self 64 | } 65 | 66 | pub fn buffer(mut self, buffer: Arc) -> Self { 67 | self.buffer = Some(buffer); 68 | self 69 | } 70 | 71 | pub fn temp_buffer(mut self, temp_buffer: Arc) -> Self { 72 | self.temp_buffer = Some(temp_buffer); 73 | self 74 | } 75 | 76 | pub fn input_buffer(mut self, input_buffer: Arc) -> Self { 77 | self.input_buffer = Some(input_buffer); 78 | self 79 | } 80 | 81 | pub fn output_buffer(mut self, output_buffer: Arc) -> Self { 82 | self.output_buffer = Some(output_buffer); 83 | self 84 | } 85 | 86 | pub fn kernel(mut self, kernel: Arc) -> Self { 87 | self.kernel = Some(kernel); 88 | self 89 | } 90 | 91 | pub fn build(self) -> Result { 92 | let command_buffer = match self.command_buffer { 93 | Some(command_buffer) => command_buffer, 94 | None => return Err(BuildError::NoCommandBuffer), 95 | }; 96 | 97 | Ok(LaunchParams { 98 | buffer: self.buffer, 99 | command_buffer, 100 | input_buffer: self.input_buffer, 101 | output_buffer: self.output_buffer, 102 | temp_buffer: self.temp_buffer, 103 | kernel: self.kernel, 104 | }) 105 | } 106 | } 107 | 108 | #[repr(C)] 109 | pub(crate) struct LaunchParamsGuard { 110 | pub(crate) params: vkfft_sys::VkFFTLaunchParams, 111 | pub(crate) command_buffer: vk_sys::CommandBuffer, 112 | pub(crate) buffer: Option, 113 | pub(crate) temp_buffer: Option, 114 | pub(crate) input_buffer: Option, 115 | pub(crate) output_buffer: Option, 116 | pub(crate) kernel: Option, 117 | } 118 | 119 | pub struct LaunchParams { 120 | pub command_buffer: vk::CommandBuffer, 121 | pub buffer: Option>, 122 | pub temp_buffer: Option>, 123 | pub input_buffer: Option>, 124 | pub output_buffer: Option>, 125 | pub kernel: Option>, 126 | } 127 | 128 | impl LaunchParams { 129 | fn buffer_object(buffer: B) -> u64 130 | where 131 | B: AsRef, 132 | { 133 | buffer.as_ref().inner().buffer.internal_object().value() 134 | } 135 | 136 | pub(crate) fn as_sys(&self) -> Pin> { 137 | use std::mem::{transmute, zeroed}; 138 | 139 | unsafe { 140 | let mut res = Box::pin(LaunchParamsGuard { 141 | params: zeroed(), 142 | command_buffer: self.command_buffer, 143 | buffer: self.buffer.as_ref().map(Self::buffer_object), 144 | temp_buffer: self.temp_buffer.as_ref().map(Self::buffer_object), 145 | input_buffer: self.input_buffer.as_ref().map(Self::buffer_object), 146 | output_buffer: self.output_buffer.as_ref().map(Self::buffer_object), 147 | kernel: self.kernel.as_ref().map(Self::buffer_object), 148 | }); 149 | 150 | res.params.commandBuffer = transmute(addr_of_mut!(res.command_buffer)); 151 | 152 | if let Some(b) = &res.buffer { 153 | res.params.buffer = transmute(b); 154 | } 155 | 156 | if let Some(b) = &res.temp_buffer { 157 | res.params.tempBuffer = transmute(b); 158 | } 159 | 160 | if let Some(b) = &res.input_buffer { 161 | res.params.inputBuffer = transmute(b); 162 | } 163 | 164 | if let Some(b) = &res.output_buffer { 165 | res.params.outputBuffer = transmute(b); 166 | } 167 | 168 | if let Some(k) = &res.kernel { 169 | res.params.kernel = transmute(k); 170 | } 171 | 172 | res 173 | } 174 | } 175 | 176 | pub fn builder() -> LaunchParamsBuilder { 177 | LaunchParamsBuilder::new() 178 | } 179 | } 180 | 181 | pub struct App { 182 | app: vkfft_sys::VkFFTApplication, 183 | 184 | // Safety: We must keep a copy of the config to ensure our resources are kept alive 185 | config: Pin>, 186 | } 187 | 188 | impl App { 189 | pub fn new(config: Config) -> error::Result>> { 190 | use vkfft_sys::*; 191 | 192 | let app: VkFFTApplication = unsafe { std::mem::zeroed() }; 193 | 194 | let sys_config = config.as_sys()?; 195 | 196 | let mut res = Box::pin(Self { 197 | app, 198 | config: sys_config, 199 | }); 200 | 201 | check_error(unsafe { initializeVkFFT(std::ptr::addr_of_mut!(res.app), res.config.config) })?; 202 | 203 | Ok(res) 204 | } 205 | 206 | pub fn launch(&mut self, params: &mut LaunchParams, inverse: bool) -> error::Result<()> { 207 | use vkfft_sys::VkFFTAppend; 208 | 209 | let mut params = params.as_sys(); 210 | 211 | if self.config.buffer.is_some() && params.buffer.is_some() { 212 | return Err(LaunchError::ConfigSpecifiesBuffer.into()); 213 | } 214 | 215 | if self.config.temp_buffer.is_some() && params.temp_buffer.is_some() { 216 | return Err(LaunchError::ConfigSpecifiesTempBuffer.into()); 217 | } 218 | 219 | if self.config.input_buffer.is_some() && params.input_buffer.is_some() { 220 | return Err(LaunchError::ConfigSpecifiesInputBuffer.into()); 221 | } 222 | 223 | if self.config.output_buffer.is_some() && params.output_buffer.is_some() { 224 | return Err(LaunchError::ConfigSpecifiesOutputBuffer.into()); 225 | } 226 | 227 | check_error(unsafe { 228 | VkFFTAppend( 229 | std::ptr::addr_of_mut!(self.app), 230 | if inverse { 1 } else { -1 }, 231 | std::ptr::addr_of_mut!(params.params), 232 | ) 233 | })?; 234 | 235 | Ok(()) 236 | } 237 | 238 | pub fn forward(&mut self, params: &mut LaunchParams) -> error::Result<()> { 239 | self.launch(params, false) 240 | } 241 | 242 | pub fn inverse(&mut self, params: &mut LaunchParams) -> error::Result<()> { 243 | self.launch(params, true) 244 | } 245 | } 246 | 247 | impl Drop for App { 248 | fn drop(&mut self) { 249 | use vkfft_sys::*; 250 | 251 | unsafe { 252 | deleteVkFFT(std::ptr::addr_of_mut!(self.app)); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use derive_more::{Display, Error}; 4 | use std::pin::Pin; 5 | use vulkano::{ 6 | buffer::BufferAccess, 7 | command_buffer::pool::UnsafeCommandPool, 8 | device::{Device, Queue}, 9 | instance::PhysicalDevice, 10 | sync::Fence, 11 | SynchronizedVulkanObject, VulkanHandle, VulkanObject, 12 | }; 13 | 14 | use std::ptr::addr_of_mut; 15 | 16 | #[derive(Display, Debug, Error)] 17 | pub enum BuildError { 18 | NoPhysicalDevice, 19 | NoDevice, 20 | NoQueue, 21 | NoFence, 22 | NoCommandPool, 23 | NoBuffer, 24 | } 25 | 26 | pub struct ConfigBuilder<'a> { 27 | fft_dim: u32, 28 | size: [u32; 3usize], 29 | 30 | physical_device: Option>, 31 | device: Option>, 32 | queue: Option>, 33 | fence: Option<&'a Fence>, 34 | command_pool: Option>, 35 | buffer: Option, 36 | input_buffer: Option, 37 | output_buffer: Option, 38 | temp_buffer: Option, 39 | kernel: Option, 40 | normalize: bool, 41 | zero_padding: [bool; 3usize], 42 | zeropad_left: [u32; 3usize], 43 | zeropad_right: [u32; 3usize], 44 | kernel_convolution: bool, 45 | convolution: bool, 46 | r2c: bool, 47 | coordinate_features: u32, 48 | disable_reorder_four_step: bool, 49 | batch_count: Option, 50 | precision: Precision, 51 | use_lut: bool, 52 | symmetric_kernel: bool, 53 | input_formatted: Option, 54 | output_formatted: Option, 55 | } 56 | 57 | impl<'a> ConfigBuilder<'a> { 58 | pub fn new() -> Self { 59 | Self { 60 | fft_dim: 1, 61 | size: [1, 1, 1], 62 | physical_device: None, 63 | device: None, 64 | queue: None, 65 | fence: None, 66 | command_pool: None, 67 | normalize: false, 68 | zero_padding: [false, false, false], 69 | zeropad_left: [0, 0, 0], 70 | zeropad_right: [0, 0, 0], 71 | kernel_convolution: false, 72 | r2c: false, 73 | coordinate_features: 1, 74 | disable_reorder_four_step: false, 75 | buffer: None, 76 | temp_buffer: None, 77 | input_buffer: None, 78 | output_buffer: None, 79 | batch_count: None, 80 | precision: Precision::Single, 81 | convolution: false, 82 | use_lut: false, 83 | symmetric_kernel: false, 84 | input_formatted: None, 85 | output_formatted: None, 86 | kernel: None, 87 | } 88 | } 89 | 90 | pub fn dim(mut self, dim: &[u32; N]) -> Self { 91 | let len = dim.len(); 92 | assert!(len <= 3); 93 | 94 | self.fft_dim = len as u32; 95 | if len > 0 { 96 | self.size[0] = dim[0]; 97 | } 98 | if len > 1 { 99 | self.size[1] = dim[1]; 100 | } 101 | if len > 2 { 102 | self.size[2] = dim[2]; 103 | } 104 | self 105 | } 106 | 107 | pub fn physical_device(mut self, physical_device: PhysicalDevice<'a>) -> Self { 108 | self.physical_device = Some(physical_device); 109 | self 110 | } 111 | 112 | pub fn device(mut self, device: Arc) -> Self { 113 | self.device = Some(device); 114 | self 115 | } 116 | 117 | pub fn queue(mut self, queue: Arc) -> Self { 118 | self.queue = Some(queue); 119 | self 120 | } 121 | 122 | pub fn command_pool(mut self, command_pool: Arc) -> Self { 123 | self.command_pool = Some(command_pool); 124 | self 125 | } 126 | 127 | pub fn fence(mut self, fence: &'a Fence) -> Self { 128 | self.fence = Some(fence); 129 | self 130 | } 131 | 132 | pub fn buffer(mut self, buffer: B) -> Self 133 | where 134 | B: Into, 135 | { 136 | self.buffer = Some(buffer.into()); 137 | self 138 | } 139 | 140 | pub fn temp_buffer(mut self, temp_buffer: B) -> Self 141 | where 142 | B: Into, 143 | { 144 | self.temp_buffer = Some(temp_buffer.into()); 145 | self 146 | } 147 | 148 | pub fn input_buffer(mut self, input_buffer: B) -> Self 149 | where 150 | B: Into, 151 | { 152 | self.input_buffer = Some(input_buffer.into()); 153 | self 154 | } 155 | 156 | pub fn output_buffer(mut self, output_buffer: B) -> Self 157 | where 158 | B: Into, 159 | { 160 | self.output_buffer = Some(output_buffer.into()); 161 | self 162 | } 163 | 164 | pub fn kernel(mut self, kernel: B) -> Self 165 | where 166 | B: Into, 167 | { 168 | self.kernel = Some(kernel.into()); 169 | self 170 | } 171 | 172 | pub fn normalize(mut self) -> Self { 173 | self.normalize = true; 174 | self 175 | } 176 | 177 | pub fn kernel_convolution(mut self) -> Self { 178 | self.kernel_convolution = true; 179 | self 180 | } 181 | 182 | pub fn symmetric_kernel(mut self) -> Self { 183 | self.symmetric_kernel = true; 184 | self 185 | } 186 | 187 | pub fn convolution(mut self) -> Self { 188 | self.convolution = true; 189 | self 190 | } 191 | 192 | pub fn r2c(mut self) -> Self { 193 | self.r2c = true; 194 | self 195 | } 196 | 197 | pub fn use_lut(mut self) -> Self { 198 | self.use_lut = true; 199 | self 200 | } 201 | 202 | pub fn coordinate_features(mut self, coordinate_features: u32) -> Self { 203 | self.coordinate_features = coordinate_features; 204 | self 205 | } 206 | 207 | pub fn disable_reorder_four_step(mut self) -> Self { 208 | self.disable_reorder_four_step = true; 209 | self 210 | } 211 | 212 | pub fn zero_padding(mut self, zero_padding: &[bool; N]) -> Self { 213 | let len = zero_padding.len(); 214 | assert!(len <= 3); 215 | 216 | if len > 0 { 217 | self.zero_padding[0] = zero_padding[0]; 218 | } 219 | if len > 1 { 220 | self.zero_padding[1] = zero_padding[1]; 221 | } 222 | if len > 2 { 223 | self.zero_padding[2] = zero_padding[2]; 224 | } 225 | self 226 | } 227 | 228 | pub fn zeropad_left(mut self, zeropad_left: &[u32; N]) -> Self { 229 | let len = zeropad_left.len(); 230 | assert!(len <= 3); 231 | 232 | if len > 0 { 233 | self.zeropad_left[0] = zeropad_left[0]; 234 | } 235 | if len > 1 { 236 | self.zeropad_left[1] = zeropad_left[1]; 237 | } 238 | if len > 2 { 239 | self.zeropad_left[2] = zeropad_left[2]; 240 | } 241 | self 242 | } 243 | 244 | pub fn zeropad_right(mut self, zeropad_right: &[u32; N]) -> Self { 245 | let len = zeropad_right.len(); 246 | assert!(len <= 3); 247 | 248 | if len > 0 { 249 | self.zeropad_right[0] = zeropad_right[0]; 250 | } 251 | if len > 1 { 252 | self.zeropad_right[1] = zeropad_right[1]; 253 | } 254 | if len > 2 { 255 | self.zeropad_right[2] = zeropad_right[2]; 256 | } 257 | self 258 | } 259 | 260 | pub fn batch_count(mut self, batch_count: u32) -> Self { 261 | self.batch_count = Some(batch_count); 262 | self 263 | } 264 | 265 | pub fn input_formatted(mut self, input_formatted: bool) -> Self { 266 | self.input_formatted = Some(input_formatted); 267 | self 268 | } 269 | 270 | pub fn output_formatted(mut self, output_formatted: bool) -> Self { 271 | self.output_formatted = Some(output_formatted); 272 | self 273 | } 274 | 275 | pub fn build(self) -> Result, BuildError> { 276 | let physical_device = match self.physical_device { 277 | Some(v) => v, 278 | None => return Err(BuildError::NoPhysicalDevice), 279 | }; 280 | 281 | let device = match self.device { 282 | Some(v) => v, 283 | None => return Err(BuildError::NoDevice), 284 | }; 285 | 286 | let queue = match self.queue { 287 | Some(v) => v, 288 | None => return Err(BuildError::NoQueue), 289 | }; 290 | 291 | let fence = match self.fence { 292 | Some(v) => v, 293 | None => return Err(BuildError::NoFence), 294 | }; 295 | 296 | let command_pool = match self.command_pool { 297 | Some(v) => v, 298 | None => return Err(BuildError::NoCommandPool), 299 | }; 300 | 301 | Ok(Config { 302 | fft_dim: self.fft_dim, 303 | size: self.size, 304 | physical_device, 305 | device, 306 | queue, 307 | fence, 308 | command_pool, 309 | normalize: self.normalize, 310 | zero_padding: self.zero_padding, 311 | zeropad_left: self.zeropad_left, 312 | zeropad_right: self.zeropad_right, 313 | kernel_convolution: self.kernel_convolution, 314 | r2c: self.r2c, 315 | coordinate_features: self.coordinate_features, 316 | disable_reorder_four_step: self.disable_reorder_four_step, 317 | buffer: self.buffer, 318 | batch_count: self.batch_count, 319 | precision: self.precision, 320 | convolution: self.convolution, 321 | use_lut: self.use_lut, 322 | symmetric_kernel: self.symmetric_kernel, 323 | input_formatted: self.input_formatted, 324 | output_formatted: self.output_formatted, 325 | kernel: self.kernel, 326 | temp_buffer: self.temp_buffer, 327 | input_buffer: self.input_buffer, 328 | output_buffer: self.output_buffer, 329 | }) 330 | } 331 | } 332 | 333 | pub enum Precision { 334 | /// Perform calculations in single precision (32-bit) 335 | Single, 336 | /// Perform calculations in double precision (64-bit) 337 | Double, 338 | /// Perform calculations in half precision (16-bit) 339 | Half, 340 | /// Use half precision only as input/output buffer. Input/Output have to be allocated as half, 341 | /// buffer/tempBuffer have to be allocated as float (out of place mode only). 342 | HalfMemory, 343 | } 344 | 345 | pub enum BufferDesc { 346 | Buffer(Arc), 347 | BufferSize(usize), 348 | } 349 | 350 | impl From> for BufferDesc 351 | where 352 | T: 'static + BufferAccess, 353 | { 354 | fn from(value: Arc) -> Self { 355 | Self::Buffer(value as Arc) 356 | } 357 | } 358 | 359 | impl From for BufferDesc { 360 | fn from(value: usize) -> Self { 361 | Self::BufferSize(value) 362 | } 363 | } 364 | 365 | impl BufferDesc { 366 | pub fn size(&self) -> usize { 367 | match self { 368 | Self::Buffer(b) => b.size(), 369 | Self::BufferSize(b) => *b, 370 | } 371 | } 372 | 373 | pub fn as_buffer(&self) -> Option<&Arc> { 374 | match self { 375 | Self::Buffer(b) => Some(b), 376 | Self::BufferSize(_) => None, 377 | } 378 | } 379 | 380 | pub fn as_buffer_size(&self) -> Option<&usize> { 381 | match self { 382 | Self::Buffer(_) => None, 383 | Self::BufferSize(b) => Some(b), 384 | } 385 | } 386 | } 387 | 388 | pub struct Config<'a> { 389 | pub fft_dim: u32, 390 | pub size: [u32; 3usize], 391 | 392 | pub physical_device: PhysicalDevice<'a>, 393 | pub device: Arc, 394 | pub queue: Arc, 395 | pub fence: &'a Fence, 396 | pub command_pool: Arc, 397 | 398 | pub buffer: Option, 399 | pub input_buffer: Option, 400 | pub output_buffer: Option, 401 | pub temp_buffer: Option, 402 | pub kernel: Option, 403 | 404 | /// Normalize inverse transform 405 | pub normalize: bool, 406 | 407 | /// Don't read some data/perform computations if some input sequences are zeropadded for each axis 408 | pub zero_padding: [bool; 3usize], 409 | 410 | /// Specify start boundary of zero block in the system for each axis 411 | pub zeropad_left: [u32; 3usize], 412 | 413 | /// Specify end boundary of zero block in the system for each axis 414 | pub zeropad_right: [u32; 3usize], 415 | 416 | /// Specify if this application is used to create kernel for convolution, so it has the same properties 417 | pub kernel_convolution: bool, 418 | 419 | /// Perform convolution in this application (0 - off, 1 - on). Disables reorderFourStep parameter 420 | pub convolution: bool, 421 | 422 | /// Perform R2C/C2R decomposition 423 | pub r2c: bool, 424 | 425 | /// C - coordinate, or dimension of features vector. In matrix convolution - size of vector 426 | pub coordinate_features: u32, 427 | 428 | /// Disables unshuffling of four step algorithm. Requires `temp_buffer` allocation. 429 | pub disable_reorder_four_step: bool, 430 | 431 | /// Used to perform multiple batches of initial data 432 | pub batch_count: Option, 433 | 434 | pub precision: Precision, 435 | 436 | /// Switches from calculating sincos to using precomputed LUT tables 437 | pub use_lut: bool, 438 | 439 | /// Specify if kernel in 2x2 or 3x3 matrix convolution is symmetric 440 | pub symmetric_kernel: bool, 441 | 442 | /// specify if input buffer is padded - false is padded, true is not padded. 443 | /// For example if it is not padded for R2C if out-of-place mode is selected 444 | /// (only if numberBatches==1 and numberKernels==1) 445 | pub input_formatted: Option, 446 | 447 | /// specify if output buffer is padded - false is padded, true is not padded. 448 | /// For example if it is not padded for R2C if out-of-place mode is selected 449 | /// (only if numberBatches==1 and numberKernels==1) 450 | pub output_formatted: Option, 451 | } 452 | 453 | #[derive(Display, Debug, Error)] 454 | pub enum ConfigError { 455 | InvalidConfig, 456 | } 457 | 458 | pub(crate) struct KeepAlive { 459 | pub device: Arc, 460 | pub queue: Arc, 461 | pub command_pool: Arc, 462 | 463 | pub buffer: Option>, 464 | pub input_buffer: Option>, 465 | pub output_buffer: Option>, 466 | pub temp_buffer: Option>, 467 | pub kernel: Option>, 468 | } 469 | 470 | #[repr(C)] 471 | pub(crate) struct ConfigGuard { 472 | pub(crate) keep_alive: KeepAlive, 473 | pub(crate) config: vkfft_sys::VkFFTConfiguration, 474 | pub(crate) physical_device: vk_sys::PhysicalDevice, 475 | pub(crate) device: vk_sys::Device, 476 | pub(crate) queue: vk_sys::Queue, 477 | pub(crate) command_pool: vk_sys::CommandPool, 478 | pub(crate) fence: vk_sys::Fence, 479 | pub(crate) buffer_size: u64, 480 | pub(crate) buffer: Option, 481 | pub(crate) input_buffer_size: u64, 482 | pub(crate) input_buffer: Option, 483 | pub(crate) output_buffer_size: u64, 484 | pub(crate) output_buffer: Option, 485 | pub(crate) temp_buffer_size: u64, 486 | pub(crate) temp_buffer: Option, 487 | pub(crate) kernel_size: u64, 488 | pub(crate) kernel: Option, 489 | } 490 | 491 | impl<'a> Config<'a> { 492 | pub fn builder() -> ConfigBuilder<'a> { 493 | ConfigBuilder::new() 494 | } 495 | 496 | pub fn buffer_size(&self) -> usize { 497 | self.buffer.as_ref().map(|b| b.size()).unwrap_or(0) 498 | } 499 | 500 | pub fn buffer(&self) -> Option<&BufferDesc> { 501 | self.buffer.as_ref() 502 | } 503 | 504 | pub fn temp_buffer(&self) -> Option<&BufferDesc> { 505 | self.temp_buffer.as_ref() 506 | } 507 | 508 | pub fn input_buffer(&self) -> Option<&BufferDesc> { 509 | self.input_buffer.as_ref() 510 | } 511 | 512 | pub fn output_buffer(&self) -> Option<&BufferDesc> { 513 | self.output_buffer.as_ref() 514 | } 515 | 516 | pub fn kernel_convolution(&self) -> bool { 517 | self.kernel_convolution 518 | } 519 | 520 | pub fn symmetric_kernel(&self) -> bool { 521 | self.kernel_convolution 522 | } 523 | 524 | pub fn convolution(&self) -> bool { 525 | self.convolution 526 | } 527 | 528 | pub fn r2c(&self) -> bool { 529 | self.r2c 530 | } 531 | 532 | pub fn normalize(&self) -> bool { 533 | self.normalize 534 | } 535 | 536 | pub fn coordinate_features(&self) -> u32 { 537 | self.coordinate_features 538 | } 539 | 540 | pub fn batch_count(&self) -> Option { 541 | self.batch_count 542 | } 543 | 544 | pub fn use_lut(&self) -> bool { 545 | self.use_lut 546 | } 547 | 548 | pub(crate) fn as_sys(&self) -> Result>, ConfigError> { 549 | use std::mem::{transmute, zeroed}; 550 | 551 | unsafe { 552 | let keep_alive = KeepAlive { 553 | device: self.device.clone(), 554 | buffer: self.buffer.as_ref().map(|b| b.as_buffer().cloned()).flatten(), 555 | input_buffer: self.input_buffer.as_ref().map(|b| b.as_buffer().cloned()).flatten(), 556 | output_buffer: self.output_buffer.as_ref().map(|b| b.as_buffer().cloned()).flatten(), 557 | kernel: self.kernel.as_ref().map(|b| b.as_buffer().cloned()).flatten(), 558 | command_pool: self.command_pool.clone(), 559 | queue: self.queue.clone(), 560 | temp_buffer: self.temp_buffer.as_ref().map(|b| b.as_buffer().cloned()).flatten() 561 | }; 562 | 563 | let mut res = Box::pin(ConfigGuard { 564 | keep_alive, 565 | config: zeroed(), 566 | physical_device: self.physical_device.internal_object(), 567 | device: self.device.internal_object().value() as usize, 568 | queue: self.queue.internal_object_guard().value() as usize, 569 | command_pool: self.command_pool.internal_object().value(), 570 | fence: self.fence.internal_object().value(), 571 | buffer_size: self.buffer.as_ref().map(|b| b.size()).unwrap_or(0) as u64, 572 | temp_buffer_size: self.temp_buffer.as_ref().map(|b| b.size()).unwrap_or(0) as u64, 573 | input_buffer_size: self.input_buffer.as_ref().map(|b| b.size()).unwrap_or(0) as u64, 574 | output_buffer_size: self.output_buffer.as_ref().map(|b| b.size()).unwrap_or(0) as u64, 575 | kernel_size: self.kernel.as_ref().map(|b| b.size()).unwrap_or(0) as u64, 576 | buffer: self 577 | .buffer 578 | .as_ref() 579 | .map(|b| b.as_buffer()) 580 | .flatten() 581 | .map(|b| b.inner().buffer.internal_object().value()), 582 | temp_buffer: self 583 | .temp_buffer 584 | .as_ref() 585 | .map(|b| b.as_buffer()) 586 | .flatten() 587 | .map(|b| b.inner().buffer.internal_object().value()), 588 | input_buffer: self 589 | .input_buffer 590 | .as_ref() 591 | .map(|b| b.as_buffer()) 592 | .flatten() 593 | .map(|b| b.inner().buffer.internal_object().value()), 594 | output_buffer: self 595 | .output_buffer 596 | .as_ref() 597 | .map(|b| b.as_buffer()) 598 | .flatten() 599 | .map(|b| b.inner().buffer.internal_object().value()), 600 | kernel: self 601 | .kernel 602 | .as_ref() 603 | .map(|b| b.as_buffer()) 604 | .flatten() 605 | .map(|b| b.inner().buffer.internal_object().value()), 606 | }); 607 | 608 | res.config.FFTdim = self.fft_dim as u64; 609 | res.config.size = self.size.map(u64::from); 610 | 611 | res.config.physicalDevice = transmute(addr_of_mut!(res.physical_device)); 612 | res.config.device = transmute(addr_of_mut!(res.device)); 613 | res.config.queue = transmute(addr_of_mut!(res.queue)); 614 | res.config.commandPool = transmute(addr_of_mut!(res.command_pool)); 615 | res.config.fence = transmute(addr_of_mut!(res.fence)); 616 | res.config.normalize = self.normalize.into(); 617 | 618 | if res.kernel_size != 0 { 619 | res.config.kernelNum = 1; 620 | res.config.kernelSize = transmute(addr_of_mut!(res.kernel_size)); 621 | } 622 | 623 | if let Some(t) = &res.kernel { 624 | println!("K: {:#0x}", t); 625 | res.config.kernel = transmute(t); 626 | } 627 | 628 | if res.buffer_size != 0 { 629 | res.config.bufferNum = 1; 630 | res.config.bufferSize = transmute(addr_of_mut!(res.buffer_size)); 631 | } 632 | 633 | if let Some(t) = &res.buffer { 634 | println!("B: {:#0x}", *t); 635 | res.config.buffer = transmute(t); 636 | } 637 | 638 | if res.temp_buffer_size != 0 { 639 | res.config.tempBufferNum = 1; 640 | res.config.tempBufferSize = transmute(addr_of_mut!(res.temp_buffer_size)); 641 | } 642 | 643 | if let Some(t) = &res.temp_buffer { 644 | println!("T: {:#0x}", *t); 645 | res.config.tempBuffer = transmute(t); 646 | } 647 | 648 | if res.input_buffer_size != 0 { 649 | res.config.inputBufferNum = 1; 650 | res.config.inputBufferSize = transmute(addr_of_mut!(res.input_buffer_size)); 651 | } 652 | 653 | if let Some(t) = &res.input_buffer { 654 | println!("I: {:#0x}", *t); 655 | res.config.inputBuffer = transmute(t); 656 | } 657 | 658 | if res.output_buffer_size != 0 { 659 | res.config.outputBufferNum = 1; 660 | res.config.outputBufferSize = transmute(addr_of_mut!(res.output_buffer_size)); 661 | 662 | } 663 | 664 | if let Some(t) = &res.output_buffer { 665 | println!("O: {:#0x}", *t); 666 | res.config.outputBuffer = transmute(t); 667 | } 668 | 669 | res.config.performZeropadding[0] = self.zero_padding[0].into(); 670 | res.config.performZeropadding[1] = self.zero_padding[1].into(); 671 | res.config.performZeropadding[2] = self.zero_padding[2].into(); 672 | 673 | res.config.fft_zeropad_left = self.zeropad_left.map(u64::from); 674 | res.config.fft_zeropad_right = self.zeropad_right.map(u64::from); 675 | 676 | res.config.kernelConvolution = self.kernel_convolution.into(); 677 | res.config.performR2C = self.r2c.into(); 678 | res.config.coordinateFeatures = self.coordinate_features as u64; 679 | res.config.disableReorderFourStep = self.disable_reorder_four_step.into(); 680 | 681 | res.config.symmetricKernel = self.symmetric_kernel.into(); 682 | 683 | if let Some(input_formatted) = self.input_formatted { 684 | res.config.isInputFormatted = input_formatted.into(); 685 | } 686 | 687 | if let Some(output_formatted) = self.output_formatted { 688 | res.config.isOutputFormatted = output_formatted.into(); 689 | } 690 | 691 | match self.precision { 692 | Precision::Double => { 693 | res.config.doublePrecision = true.into(); 694 | } 695 | Precision::Half => res.config.halfPrecision = true.into(), 696 | Precision::HalfMemory => { 697 | res.config.halfPrecisionMemoryOnly = true.into(); 698 | 699 | if let Some(false) = self.input_formatted { 700 | return Err(ConfigError::InvalidConfig); 701 | } 702 | 703 | if let Some(false) = self.output_formatted { 704 | return Err(ConfigError::InvalidConfig); 705 | } 706 | 707 | res.config.isInputFormatted = true.into(); 708 | res.config.isOutputFormatted = true.into(); 709 | } 710 | _ => {} 711 | } 712 | 713 | if let Some(batch_count) = &self.batch_count { 714 | res.config.numberBatches = *batch_count as u64; 715 | } 716 | 717 | Ok(res) 718 | } 719 | } 720 | } 721 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::convert::{TryFrom, TryInto}; 2 | 3 | use derive_more::{Display, Error}; 4 | 5 | use crate::{app::LaunchError, config::ConfigError}; 6 | 7 | #[derive(Display, Debug, Error)] 8 | pub enum Error { 9 | InvalidPhysicalDevice, 10 | InvalidDevice, 11 | InvalidQueue, 12 | InvalidCommandPool, 13 | InvalidFence, 14 | OnlyForwardFftInitialized, 15 | OnlyInverseFftInitialized, 16 | InvalidContext, 17 | InvalidPlatform, 18 | EmptyFftDim, 19 | EmptySize, 20 | EmptyBufferSize, 21 | EmptyBuffer, 22 | EmptyTempBufferSize, 23 | EmptyTempBuffer, 24 | EmptyInputBufferSize, 25 | EmptyInputBuffer, 26 | EmptyOutputBufferSize, 27 | EmptyOutputBuffer, 28 | EmptyKernelSize, 29 | EmptyKernel, 30 | UnsupportedRadix, 31 | UnsupportedFftLength, 32 | UnsupportedFftLengthR2C, 33 | FailedToAllocate, 34 | FailedToMapMemory, 35 | FailedToAllocateCommandBuffers, 36 | FailedToBeginCommandBuffer, 37 | FailedToEndCommandBuffer, 38 | FailedToSubmitQueue, 39 | FailedToWaitForFences, 40 | FailedToResetFences, 41 | FailedToCreateDescriptorPool, 42 | FailedToCreatedDescriptorSetLayout, 43 | FailedToAllocateDescriptorSets, 44 | FailedToCreatePipelineLayout, 45 | FailedShaderPreprocess, 46 | FailedShaderParse, 47 | FailedShaderLink, 48 | FailedSpirvGenerate, 49 | FailedToCreateShaderModule, 50 | FailedToCreateInstance, 51 | FailedToSetupDebugMessenger, 52 | FailedToFindPhysicalDevice, 53 | FailedToCreateDevice, 54 | FailedToCreateFence, 55 | FailedToCreateCommandPool, 56 | FailedToCreateBuffer, 57 | FailedToAllocateMemory, 58 | FailedToBindBufferMemory, 59 | FailedToFindMemory, 60 | FailedToSynchronize, 61 | FailedToCopy, 62 | FailedToCreateProgram, 63 | FailedToCompileProgram, 64 | FailedToGetCodeSize, 65 | FailedToGetCode, 66 | FailedToDestroyProgram, 67 | FailedToLoadModule, 68 | FailedToGetFunction, 69 | FailedToSetDynamicSharedMemory, 70 | FailedToModuleGetGlobal, 71 | FailedToLaunchKernel, 72 | FailedToEventRecord, 73 | FailedToAddNameExpression, 74 | FailedToInitialize, 75 | FailedToSetDeviceId, 76 | FailedToGetDevice, 77 | FailedToCreateContext, 78 | FailedToCreatePipeline, 79 | FailedToSetKernelArg, 80 | FailedToCreateCommandQueue, 81 | FailedToReleaseCommandQueue, 82 | FailedToEnumerateDevices, 83 | Config(ConfigError), 84 | Launch(LaunchError), 85 | } 86 | 87 | impl TryFrom for Error { 88 | type Error = (); 89 | 90 | #[allow(non_upper_case_globals)] 91 | fn try_from(value: vkfft_sys::VkFFTResult) -> std::result::Result { 92 | use vkfft_sys::*; 93 | 94 | match value { 95 | VkFFTResult_VKFFT_ERROR_INVALID_PHYSICAL_DEVICE => Ok(Self::InvalidPhysicalDevice), 96 | VkFFTResult_VKFFT_ERROR_INVALID_DEVICE => Ok(Self::InvalidDevice), 97 | VkFFTResult_VKFFT_ERROR_INVALID_QUEUE => Ok(Self::InvalidQueue), 98 | VkFFTResult_VKFFT_ERROR_INVALID_COMMAND_POOL => Ok(Self::InvalidCommandPool), 99 | VkFFTResult_VKFFT_ERROR_INVALID_FENCE => Ok(Self::InvalidFence), 100 | VkFFTResult_VKFFT_ERROR_ONLY_FORWARD_FFT_INITIALIZED => Ok(Self::OnlyForwardFftInitialized), 101 | VkFFTResult_VKFFT_ERROR_ONLY_INVERSE_FFT_INITIALIZED => Ok(Self::OnlyInverseFftInitialized), 102 | VkFFTResult_VKFFT_ERROR_INVALID_CONTEXT => Ok(Self::InvalidContext), 103 | VkFFTResult_VKFFT_ERROR_INVALID_PLATFORM => Ok(Self::InvalidPlatform), 104 | VkFFTResult_VKFFT_ERROR_EMPTY_FFTdim => Ok(Self::EmptyFftDim), 105 | VkFFTResult_VKFFT_ERROR_EMPTY_size => Ok(Self::EmptySize), 106 | VkFFTResult_VKFFT_ERROR_EMPTY_bufferSize => Ok(Self::EmptyBufferSize), 107 | VkFFTResult_VKFFT_ERROR_EMPTY_buffer => Ok(Self::EmptyBuffer), 108 | VkFFTResult_VKFFT_ERROR_EMPTY_tempBufferSize => Ok(Self::EmptyTempBufferSize), 109 | VkFFTResult_VKFFT_ERROR_EMPTY_tempBuffer => Ok(Self::EmptyTempBuffer), 110 | VkFFTResult_VKFFT_ERROR_EMPTY_inputBufferSize => Ok(Self::EmptyInputBufferSize), 111 | VkFFTResult_VKFFT_ERROR_EMPTY_inputBuffer => Ok(Self::EmptyInputBuffer), 112 | VkFFTResult_VKFFT_ERROR_EMPTY_outputBufferSize => Ok(Self::EmptyOutputBufferSize), 113 | VkFFTResult_VKFFT_ERROR_EMPTY_outputBuffer => Ok(Self::EmptyOutputBuffer), 114 | VkFFTResult_VKFFT_ERROR_EMPTY_kernelSize => Ok(Self::EmptyKernelSize), 115 | VkFFTResult_VKFFT_ERROR_EMPTY_kernel => Ok(Self::EmptyKernel), 116 | VkFFTResult_VKFFT_ERROR_UNSUPPORTED_RADIX => Ok(Self::UnsupportedRadix), 117 | VkFFTResult_VKFFT_ERROR_UNSUPPORTED_FFT_LENGTH => Ok(Self::UnsupportedFftLength), 118 | VkFFTResult_VKFFT_ERROR_UNSUPPORTED_FFT_LENGTH_R2C => Ok(Self::UnsupportedFftLengthR2C), 119 | VkFFTResult_VKFFT_ERROR_FAILED_TO_ALLOCATE => Ok(Self::FailedToAllocate), 120 | VkFFTResult_VKFFT_ERROR_FAILED_TO_MAP_MEMORY => Ok(Self::FailedToMapMemory), 121 | VkFFTResult_VKFFT_ERROR_FAILED_TO_ALLOCATE_COMMAND_BUFFERS => { 122 | Ok(Self::FailedToAllocateCommandBuffers) 123 | } 124 | VkFFTResult_VKFFT_ERROR_FAILED_TO_BEGIN_COMMAND_BUFFER => { 125 | Ok(Self::FailedToBeginCommandBuffer) 126 | } 127 | VkFFTResult_VKFFT_ERROR_FAILED_TO_END_COMMAND_BUFFER => Ok(Self::FailedToEndCommandBuffer), 128 | VkFFTResult_VKFFT_ERROR_FAILED_TO_SUBMIT_QUEUE => Ok(Self::FailedToSubmitQueue), 129 | VkFFTResult_VKFFT_ERROR_FAILED_TO_WAIT_FOR_FENCES => Ok(Self::FailedToWaitForFences), 130 | VkFFTResult_VKFFT_ERROR_FAILED_TO_RESET_FENCES => Ok(Self::FailedToResetFences), 131 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_DESCRIPTOR_POOL => { 132 | Ok(Self::FailedToCreateDescriptorPool) 133 | } 134 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_DESCRIPTOR_SET_LAYOUT => { 135 | Ok(Self::FailedToCreatedDescriptorSetLayout) 136 | } 137 | VkFFTResult_VKFFT_ERROR_FAILED_TO_ALLOCATE_DESCRIPTOR_SETS => { 138 | Ok(Self::FailedToAllocateDescriptorSets) 139 | } 140 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_PIPELINE_LAYOUT => { 141 | Ok(Self::FailedToCreatePipelineLayout) 142 | } 143 | VkFFTResult_VKFFT_ERROR_FAILED_SHADER_PREPROCESS => Ok(Self::FailedShaderPreprocess), 144 | VkFFTResult_VKFFT_ERROR_FAILED_SHADER_PARSE => Ok(Self::FailedShaderParse), 145 | VkFFTResult_VKFFT_ERROR_FAILED_SHADER_LINK => Ok(Self::FailedShaderLink), 146 | VkFFTResult_VKFFT_ERROR_FAILED_SPIRV_GENERATE => Ok(Self::FailedSpirvGenerate), 147 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_SHADER_MODULE => { 148 | Ok(Self::FailedToCreateShaderModule) 149 | } 150 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_INSTANCE => Ok(Self::FailedToCreateInstance), 151 | VkFFTResult_VKFFT_ERROR_FAILED_TO_SETUP_DEBUG_MESSENGER => { 152 | Ok(Self::FailedToSetupDebugMessenger) 153 | } 154 | VkFFTResult_VKFFT_ERROR_FAILED_TO_FIND_PHYSICAL_DEVICE => { 155 | Ok(Self::FailedToFindPhysicalDevice) 156 | } 157 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_DEVICE => Ok(Self::FailedToCreateDevice), 158 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_FENCE => Ok(Self::FailedToCreateFence), 159 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_POOL => Ok(Self::FailedToCreateCommandPool), 160 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_BUFFER => Ok(Self::FailedToCreateBuffer), 161 | VkFFTResult_VKFFT_ERROR_FAILED_TO_ALLOCATE_MEMORY => Ok(Self::FailedToAllocateMemory), 162 | VkFFTResult_VKFFT_ERROR_FAILED_TO_BIND_BUFFER_MEMORY => Ok(Self::FailedToBindBufferMemory), 163 | VkFFTResult_VKFFT_ERROR_FAILED_TO_FIND_MEMORY => Ok(Self::FailedToFindMemory), 164 | VkFFTResult_VKFFT_ERROR_FAILED_TO_SYNCHRONIZE => Ok(Self::FailedToSynchronize), 165 | VkFFTResult_VKFFT_ERROR_FAILED_TO_COPY => Ok(Self::FailedToCopy), 166 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_PROGRAM => Ok(Self::FailedToCreateProgram), 167 | VkFFTResult_VKFFT_ERROR_FAILED_TO_COMPILE_PROGRAM => Ok(Self::FailedToCompileProgram), 168 | VkFFTResult_VKFFT_ERROR_FAILED_TO_GET_CODE_SIZE => Ok(Self::FailedToGetCodeSize), 169 | VkFFTResult_VKFFT_ERROR_FAILED_TO_GET_CODE => Ok(Self::FailedToGetCode), 170 | VkFFTResult_VKFFT_ERROR_FAILED_TO_DESTROY_PROGRAM => Ok(Self::FailedToDestroyProgram), 171 | VkFFTResult_VKFFT_ERROR_FAILED_TO_LOAD_MODULE => Ok(Self::FailedToLoadModule), 172 | VkFFTResult_VKFFT_ERROR_FAILED_TO_GET_FUNCTION => Ok(Self::FailedToGetFunction), 173 | VkFFTResult_VKFFT_ERROR_FAILED_TO_SET_DYNAMIC_SHARED_MEMORY => { 174 | Ok(Self::FailedToSetDynamicSharedMemory) 175 | } 176 | VkFFTResult_VKFFT_ERROR_FAILED_TO_MODULE_GET_GLOBAL => Ok(Self::FailedToModuleGetGlobal), 177 | VkFFTResult_VKFFT_ERROR_FAILED_TO_LAUNCH_KERNEL => Ok(Self::FailedToLaunchKernel), 178 | VkFFTResult_VKFFT_ERROR_FAILED_TO_EVENT_RECORD => Ok(Self::FailedToEventRecord), 179 | VkFFTResult_VKFFT_ERROR_FAILED_TO_ADD_NAME_EXPRESSION => Ok(Self::FailedToAddNameExpression), 180 | VkFFTResult_VKFFT_ERROR_FAILED_TO_INITIALIZE => Ok(Self::FailedToInitialize), 181 | VkFFTResult_VKFFT_ERROR_FAILED_TO_SET_DEVICE_ID => Ok(Self::FailedToSetDeviceId), 182 | VkFFTResult_VKFFT_ERROR_FAILED_TO_GET_DEVICE => Ok(Self::FailedToGetDevice), 183 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_CONTEXT => Ok(Self::FailedToCreateContext), 184 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_PIPELINE => Ok(Self::FailedToCreatePipeline), 185 | VkFFTResult_VKFFT_ERROR_FAILED_TO_SET_KERNEL_ARG => Ok(Self::FailedToSetKernelArg), 186 | VkFFTResult_VKFFT_ERROR_FAILED_TO_CREATE_COMMAND_QUEUE => { 187 | Ok(Self::FailedToCreateCommandQueue) 188 | } 189 | VkFFTResult_VKFFT_ERROR_FAILED_TO_RELEASE_COMMAND_QUEUE => { 190 | Ok(Self::FailedToReleaseCommandQueue) 191 | } 192 | VkFFTResult_VKFFT_ERROR_FAILED_TO_ENUMERATE_DEVICES => Ok(Self::FailedToEnumerateDevices), 193 | _ => Err(()), 194 | } 195 | } 196 | } 197 | 198 | impl From for Error { 199 | fn from(e: ConfigError) -> Self { 200 | Self::Config(e) 201 | } 202 | } 203 | 204 | impl From for Error { 205 | fn from(e: LaunchError) -> Self { 206 | Self::Launch(e) 207 | } 208 | } 209 | 210 | pub(crate) fn check_error(result: vkfft_sys::VkFFTResult) -> Result<()> { 211 | match result.try_into() { 212 | Ok(err) => Err(err), 213 | Err(_) => Ok(()), 214 | } 215 | } 216 | 217 | pub type Result = std::result::Result; 218 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(core_intrinsics)] 2 | 3 | pub mod app; 4 | pub mod config; 5 | pub mod error; 6 | mod version; 7 | 8 | pub use version::*; 9 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 3 | pub struct Version { 4 | major: u32, 5 | minor: u32, 6 | patch: u32, 7 | } 8 | 9 | impl Version { 10 | #[inline] 11 | pub fn major(&self) -> u32 { 12 | self.major 13 | } 14 | 15 | #[inline] 16 | pub fn minor(&self) -> u32 { 17 | self.minor 18 | } 19 | 20 | #[inline] 21 | pub fn patch(&self) -> u32 { 22 | self.patch 23 | } 24 | } 25 | 26 | impl Display for Version { 27 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 28 | write!(f, "{}.{}.{}", self.major, self.minor, self.patch) 29 | } 30 | } 31 | 32 | pub fn version() -> Version { 33 | let ver = unsafe { vkfft_sys::VkFFTGetVersion() }; 34 | 35 | Version { 36 | major: (ver / 10000) as u32, 37 | minor: (ver % 10000 / 100) as u32, 38 | patch: (ver % 100) as u32, 39 | } 40 | } 41 | --------------------------------------------------------------------------------