diff --git a/src/vulkan/meson.build b/src/vulkan/meson.build index 614a33a47c6..7bb1a101611 100644 --- a/src/vulkan/meson.build +++ b/src/vulkan/meson.build @@ -86,6 +86,7 @@ vulkan_wsi_deps += idep_vulkan_wsi_defines subdir('util') subdir('runtime') subdir('wsi') +subdir('rust') if with_vulkan_overlay_layer subdir('overlay-layer') endif diff --git a/src/vulkan/rust/.rustfmt.toml b/src/vulkan/rust/.rustfmt.toml new file mode 100644 index 00000000000..df99c69198f --- /dev/null +++ b/src/vulkan/rust/.rustfmt.toml @@ -0,0 +1 @@ +max_width = 80 diff --git a/src/vulkan/rust/boxed.rs b/src/vulkan/rust/boxed.rs new file mode 100644 index 00000000000..6709547efe6 --- /dev/null +++ b/src/vulkan/rust/boxed.rs @@ -0,0 +1,335 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ + +use vulkan_h::*; + +use crate::Result; + +use std::alloc::Layout; +use std::convert::{AsMut, AsRef}; +use std::mem::MaybeUninit; +use std::ops::{Deref, DerefMut}; +use std::os::raw::c_void; +use std::ptr::NonNull; + +trait VkAllocScoped { + unsafe fn alloc_scoped_raw( + &self, + layout: Layout, + scope: VkSystemAllocationScope, + ) -> *mut u8; + + unsafe fn alloc_scoped(&self, scope: VkSystemAllocationScope) -> *mut T { + self.alloc_scoped_raw(Layout::new::(), scope) as *mut T + } +} + +trait VkFree { + unsafe fn free_raw(&self, mem: *mut u8); + + unsafe fn free(&self, mem: *mut T) { + self.free_raw(mem as *mut u8); + } +} + +impl VkAllocScoped for VkAllocationCallbacks { + unsafe fn alloc_scoped_raw( + &self, + layout: Layout, + scope: VkSystemAllocationScope, + ) -> *mut u8 { + self.pfnAllocation.unwrap()( + self.pUserData, + layout.align(), + layout.size(), + scope, + ) as *mut u8 + } +} + +impl VkFree for VkAllocationCallbacks { + unsafe fn free_raw(&self, mem: *mut u8) { + self.pfnFree.unwrap()(self.pUserData, mem as *mut c_void); + } +} + +#[derive(Copy, Clone)] +struct FreeCb { + user_data: *mut c_void, + free: unsafe extern "C" fn(pUserData: *mut c_void, pMemory: *mut c_void), +} + +impl FreeCb { + pub fn new(alloc: &VkAllocationCallbacks) -> FreeCb { + FreeCb { + user_data: alloc.pUserData, + free: alloc.pfnFree.unwrap(), + } + } +} + +impl VkFree for FreeCb { + unsafe fn free_raw(&self, mem: *mut u8) { + (self.free)(self.user_data, mem as *mut c_void); + } +} + +pub struct VkBox { + ptr: NonNull, + free: FreeCb, +} + +impl VkBox> { + pub unsafe fn assume_init(self) -> VkBox { + let ub = std::mem::ManuallyDrop::new(self); + VkBox { + ptr: NonNull::new_unchecked(ub.ptr.as_ptr() as *mut T), + free: ub.free, + } + } + + pub fn new_uninit( + alloc: &VkAllocationCallbacks, + scope: VkSystemAllocationScope, + ) -> Result>> { + unsafe { + let ptr = alloc.alloc_scoped::>(scope); + if let Some(ptr) = NonNull::new(ptr) { + Ok(VkBox { + ptr: ptr, + free: FreeCb::new(alloc), + }) + } else { + Err(VK_ERROR_OUT_OF_HOST_MEMORY) + } + } + } + + pub fn write(self, x: T) -> VkBox { + unsafe { + (self.ptr.as_ptr() as *mut T).write(x); + self.assume_init() + } + } +} + +impl VkBox { + pub fn new( + x: T, + alloc: &VkAllocationCallbacks, + scope: VkSystemAllocationScope, + ) -> Result> { + match VkBox::>::new_uninit(alloc, scope) { + Ok(b) => Ok(b.write(x)), + Err(e) => Err(e), + } + } + + pub unsafe fn new_cb) -> VkResult>( + alloc: &VkAllocationCallbacks, + scope: VkSystemAllocationScope, + f: F, + ) -> Result> { + match VkBox::>::new_uninit(alloc, scope) { + Ok(b) => { + match f(NonNull::new_unchecked(b.ptr.as_ptr() as *mut T)) { + VK_SUCCESS => Ok(b.assume_init()), + e => Err(e), + } + }, + Err(e) => Err(e), + } + } + + pub fn new2( + x: T, + parent_alloc: &VkAllocationCallbacks, + alloc: *const VkAllocationCallbacks, + scope: VkSystemAllocationScope, + ) -> Result> { + let alloc = if alloc.is_null() { + parent_alloc + } else { + unsafe {&*alloc } + }; + VkBox::new(x, alloc, scope) + } +} + +impl Drop for VkBox { + fn drop(&mut self) { + unsafe { + std::ptr::drop_in_place(self.ptr.as_ptr()); + self.free.free(self.ptr.as_ptr() as *mut c_void); + } + } +} + +impl AsRef for VkBox { + fn as_ref(&self) -> &T { + unsafe { self.ptr.as_ref() } + } +} + +impl AsMut for VkBox { + fn as_mut(&mut self) -> &mut T { + unsafe { self.ptr.as_mut() } + } +} + +impl Deref for VkBox { + type Target = T; + + fn deref(&self) -> &T { + self.as_ref() + } +} + +impl DerefMut for VkBox { + fn deref_mut(&mut self) -> &mut T { + self.as_mut() + } +} + +type VkFinishFn = unsafe extern "C" fn(obj: *mut V); + +#[repr(C)] +pub struct VkObj { + vk: V, + finish: VkFinishFn, + data: Option, + _pin: std::marker::PhantomPinned, +} + +impl VkObj { + unsafe fn init_ptr) -> VkResult>( + mut ptr: NonNull, + finish: VkFinishFn, + f: F, + ) -> VkResult { + let vk_ptr = (&mut ptr.as_mut().vk) as *mut V; + let finish_ptr = (&mut ptr.as_mut().finish) as *mut VkFinishFn; + let data_ptr = (&mut ptr.as_mut().data) as *mut Option; + + match f(NonNull::new_unchecked(vk_ptr)) { + VK_SUCCESS => { + finish_ptr.write(finish); + data_ptr.write(None); + VK_SUCCESS + } + err => err, + } + } + + pub fn vk(&self) -> &V { + &self.vk + } + + pub fn vk_mut(&mut self) -> &mut V { + &mut self.vk + } + + pub unsafe fn vk_ptr(&self) -> *mut V { + &self.vk as *const V as *mut V + } +} + +impl Drop for VkObj { + fn drop(&mut self) { + self.data.take(); + unsafe { (self.finish)(&mut self.vk) }; + } +} + +impl Deref for VkObj { + type Target = T; + + fn deref(&self) -> &T { + self.data.as_ref().unwrap() + } +} + +impl DerefMut for VkObj { + fn deref_mut(&mut self) -> &mut T { + self.data.as_mut().unwrap() + } +} + +pub struct VkObjBaseBox { + obj: VkBox>, +} + +impl VkObjBaseBox { + pub fn new_cb) -> VkResult>( + alloc: &VkAllocationCallbacks, + finish: unsafe extern "C" fn(obj: *mut V), + f: F, + ) -> Result> { + let obj = unsafe { + VkBox::new_cb(alloc, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, |ptr| { + VkObj::init_ptr(ptr, finish, f) + }) + }?; + Ok(VkObjBaseBox { obj: obj }) + } + + pub fn new2_cb) -> VkResult>( + parent_alloc: &VkAllocationCallbacks, + alloc: *const VkAllocationCallbacks, + finish: unsafe extern "C" fn(obj: *mut V), + f: F, + ) -> Result> { + let alloc = if alloc.is_null() { + parent_alloc + } else { + unsafe {&*alloc } + }; + let obj = unsafe { + VkBox::new_cb(alloc, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, |ptr| { + VkObj::init_ptr(ptr, finish, f) + }) + }?; + Ok(VkObjBaseBox { obj: obj }) + } +} + +impl Deref for VkObjBaseBox { + type Target = V; + + fn deref(&self) -> &V { + &self.obj.vk + } +} + +impl DerefMut for VkObjBaseBox { + fn deref_mut(&mut self) -> &mut V { + &mut self.obj.vk + } +} + +pub struct VkObjBox { + base: VkObjBaseBox, +} + +impl VkObjBox { + pub fn new(mut base: VkObjBaseBox, x: T) -> VkObjBox { + base.obj.data.replace(x); + VkObjBox { base: base } + } +} + +impl Deref for VkObjBox { + type Target = VkObj; + + fn deref(&self) -> &VkObj { + &self.base.obj + } +} + +impl DerefMut for VkObjBox { + fn deref_mut(&mut self) -> &mut VkObj { + &mut self.base.obj + } +} diff --git a/src/vulkan/rust/examples.rs b/src/vulkan/rust/examples.rs new file mode 100644 index 00000000000..7f708eacdba --- /dev/null +++ b/src/vulkan/rust/examples.rs @@ -0,0 +1,43 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ + +use vulkan_h::*; +use vulkan_runtime::*; + +use crate::Result; +use crate::boxed::*; + +use std::ptr::NonNull; + +struct Device { +} + +struct Image<'a> { + dev: &'a VkObj, +} + +fn create_image( + dev: &VkObj, + info: *const VkImageCreateInfo, + alloc: *const VkAllocationCallbacks, +) -> Result> { + let vk = unsafe { + VkObjBaseBox::new2_cb( + &dev.vk().alloc, + alloc, + vk_image_finish, + &|vk: NonNull| { + vk_image_init(dev.vk_ptr(), vk.as_ptr(), info); + VK_SUCCESS + }, + ) + }?; + + /* Stuff which may use vk */ + + Ok(VkObjBox::new(vk, Image { + dev: dev, + })) +} diff --git a/src/vulkan/rust/meson.build b/src/vulkan/rust/meson.build new file mode 100644 index 00000000000..b274cae0d1d --- /dev/null +++ b/src/vulkan/rust/meson.build @@ -0,0 +1,83 @@ +# Copyright © 2022 Collabora, Ltd. +# SPDX-License-Identifier: MIT + +add_languages('rust', required: true) +rust = import('unstable-rust') + +assert( + meson.version().version_compare('>= 1.0.0'), + 'Meson 1.0 is required for Rust Vulkan bindings' +) + +_vulkan_h_rs = rust.bindgen( + input : ['vulkan_h_bindgen.h'], + output : 'vulkan_h.rs', + include_directories : [inc_include, inc_src], + c_args : [ + pre_args, + ], + args : [ + '--allowlist-type', 'PFN_vk.*', + '--allowlist-type', 'Vk.*', + '--allowlist-var', 'VK_.*', + '--no-prepend-enum-name', + '--raw-line', '#![allow(non_camel_case_types)]', + '--raw-line', '#![allow(non_snake_case)]', + '--raw-line', '#![allow(non_upper_case_globals)]', + '--size_t-is-usize', + ], +) + +libvulkan_h_rs = static_library( + 'vulkan_h', + _vulkan_h_rs, + gnu_symbol_visibility : 'hidden', + rust_crate_type : 'rlib', +) + +_vulkan_runtime_rs = rust.bindgen( + input : ['vulkan_runtime_bindgen.h'], + output : 'vulkan_runtime.rs', + c_args : [ + pre_args, + ], + args : [ + '--allowlist-function', 'vk_.*', + '--allowlist-type', 'vk_.*', + '--allowlist-var', 'vk_.*', + '--blocklist-type', 'PFN_vk.*', + '--blocklist-type', 'Vk.*', + '--no-prepend-enum-name', + '--raw-line', '#![allow(non_camel_case_types)]', + '--raw-line', '#![allow(non_snake_case)]', + '--raw-line', '#![allow(non_upper_case_globals)]', + '--raw-line', 'extern crate vulkan_h;', + '--raw-line', 'use vulkan_h::*;', + '--size_t-is-usize', + ], + dependencies : [ + idep_mesautil, + idep_vulkan_runtime_headers, + idep_vulkan_runtime, + idep_vulkan_util_headers, + ], +) + +libvulkan_runtime_rs = static_library( + 'vulkan_runtime', + _vulkan_runtime_rs, + gnu_symbol_visibility : 'hidden', + link_with : [libvulkan_h_rs], + rust_crate_type : 'rlib', +) + +libvk_rs = static_library( + 'vk_rs', + 'vk.rs', + gnu_symbol_visibility : 'hidden', + rust_crate_type : 'rlib', + link_with : [ + libvulkan_h_rs, + libvulkan_runtime_rs, + ] +) diff --git a/src/vulkan/rust/vk.rs b/src/vulkan/rust/vk.rs new file mode 100644 index 00000000000..13b6d03bcc8 --- /dev/null +++ b/src/vulkan/rust/vk.rs @@ -0,0 +1,14 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ + +extern crate vulkan_h; +extern crate vulkan_runtime; + +mod boxed; +mod examples; + +use vulkan_h::VkResult; + +pub type Result = std::result::Result; diff --git a/src/vulkan/rust/vulkan_h_bindgen.h b/src/vulkan/rust/vulkan_h_bindgen.h new file mode 100644 index 00000000000..976d6a24279 --- /dev/null +++ b/src/vulkan/rust/vulkan_h_bindgen.h @@ -0,0 +1,5 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ +#include "vulkan/vulkan_core.h" diff --git a/src/vulkan/rust/vulkan_runtime_bindgen.h b/src/vulkan/rust/vulkan_runtime_bindgen.h new file mode 100644 index 00000000000..918378c8ab5 --- /dev/null +++ b/src/vulkan/rust/vulkan_runtime_bindgen.h @@ -0,0 +1,8 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ +#include "vk_device.h" +#include "vk_image.h" +#include "vk_instance.h" +#include "vk_physical_device.h"