From 7318f8c286236a6a50655448f7f7b38595ab9f36 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Tue, 13 Dec 2022 07:50:04 -0600 Subject: [PATCH 1/6] vulkan: Add Rust bindings for vulkan_core.h --- src/vulkan/meson.build | 1 + src/vulkan/rust/meson.build | 36 ++++++++++++++++++++++++++++++ src/vulkan/rust/vulkan_h_bindgen.h | 5 +++++ 3 files changed, 42 insertions(+) create mode 100644 src/vulkan/rust/meson.build create mode 100644 src/vulkan/rust/vulkan_h_bindgen.h diff --git a/src/vulkan/meson.build b/src/vulkan/meson.build index 6a95768c5e8..c3c9af30128 100644 --- a/src/vulkan/meson.build +++ b/src/vulkan/meson.build @@ -64,6 +64,7 @@ endif subdir('util') subdir('runtime') subdir('wsi') +subdir('rust') if with_vulkan_overlay_layer subdir('overlay-layer') endif diff --git a/src/vulkan/rust/meson.build b/src/vulkan/rust/meson.build new file mode 100644 index 00000000000..cfaeba4ecac --- /dev/null +++ b/src/vulkan/rust/meson.build @@ -0,0 +1,36 @@ +# 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], + 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', +) 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" From 2721172479ea913c07e65d930dfdea0625b28040 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Tue, 13 Dec 2022 08:05:50 -0600 Subject: [PATCH 2/6] vulkan: Add Rust bindings for the mesa Vulkan runtime --- src/vulkan/rust/meson.build | 38 +++++++++++++++++++++++- src/vulkan/rust/vulkan_runtime_bindgen.h | 7 +++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/vulkan/rust/vulkan_runtime_bindgen.h diff --git a/src/vulkan/rust/meson.build b/src/vulkan/rust/meson.build index cfaeba4ecac..ecce7102724 100644 --- a/src/vulkan/rust/meson.build +++ b/src/vulkan/rust/meson.build @@ -12,7 +12,7 @@ assert( _vulkan_h_rs = rust.bindgen( input : ['vulkan_h_bindgen.h'], output : 'vulkan_h.rs', - include_directories : [inc_include], + include_directories : [inc_include, inc_src], c_args : [ pre_args, ], @@ -34,3 +34,39 @@ libvulkan_h_rs = static_library( 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', +) diff --git a/src/vulkan/rust/vulkan_runtime_bindgen.h b/src/vulkan/rust/vulkan_runtime_bindgen.h new file mode 100644 index 00000000000..eaa5a7e8a27 --- /dev/null +++ b/src/vulkan/rust/vulkan_runtime_bindgen.h @@ -0,0 +1,7 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ +#include "vk_device.h" +#include "vk_instance.h" +#include "vk_physical_device.h" From c2e89b2d6f379a995bca62dc96b76d32117038fe Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Tue, 13 Dec 2022 08:08:49 -0600 Subject: [PATCH 3/6] vulkan: Add a vk crate for Vulkan rust stuff --- src/vulkan/rust/.rustfmt.toml | 1 + src/vulkan/rust/meson.build | 11 +++++++++++ src/vulkan/rust/vk.rs | 4 ++++ 3 files changed, 16 insertions(+) create mode 100644 src/vulkan/rust/.rustfmt.toml create mode 100644 src/vulkan/rust/vk.rs 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/meson.build b/src/vulkan/rust/meson.build index ecce7102724..b274cae0d1d 100644 --- a/src/vulkan/rust/meson.build +++ b/src/vulkan/rust/meson.build @@ -70,3 +70,14 @@ libvulkan_runtime_rs = static_library( 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..bfba6bdb763 --- /dev/null +++ b/src/vulkan/rust/vk.rs @@ -0,0 +1,4 @@ +/* + * Copyright © 2022 Collabora, Ltd. + * SPDX-License-Identifier: MIT + */ From fce779dbb59b6de127868525f8dc9ecb5d179749 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Tue, 13 Dec 2022 09:27:09 -0600 Subject: [PATCH 4/6] Add a VkBox type This is roughly the equivalent of std::boxed::Box except that it takes Vulkan allocator callbacks and a scope in new(). --- src/vulkan/rust/boxed.rs | 174 +++++++++++++++++++++++++++++++++++++++ src/vulkan/rust/vk.rs | 9 ++ 2 files changed, 183 insertions(+) create mode 100644 src/vulkan/rust/boxed.rs diff --git a/src/vulkan/rust/boxed.rs b/src/vulkan/rust/boxed.rs new file mode 100644 index 00000000000..1ff28ac1f11 --- /dev/null +++ b/src/vulkan/rust/boxed.rs @@ -0,0 +1,174 @@ +/* + * 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 fn new2( + x: T, + parent_alloc: *const VkAllocationCallbacks, + alloc: *const VkAllocationCallbacks, + scope: VkSystemAllocationScope, + ) -> Result> { + let alloc = if alloc.is_null() { parent_alloc } else { 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() + } +} diff --git a/src/vulkan/rust/vk.rs b/src/vulkan/rust/vk.rs index bfba6bdb763..f3219bb0f57 100644 --- a/src/vulkan/rust/vk.rs +++ b/src/vulkan/rust/vk.rs @@ -2,3 +2,12 @@ * Copyright © 2022 Collabora, Ltd. * SPDX-License-Identifier: MIT */ + +extern crate vulkan_h; +extern crate vulkan_runtime; + +mod boxed; + +use vulkan_h::VkResult; + +pub type Result = std::result::Result; From 2bb50502f1748baab9d7245b7d382b7c6a18f26c Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Tue, 13 Dec 2022 12:20:36 -0600 Subject: [PATCH 5/6] Add a VkObj and box types Handling Vulkan objects that potentially derive from vk_foo base objects has three parts: 1. VkObj is a container type that contains both a vk_foo (that's the V type) and a driver data type T. The struct is designed so that the vk_foo goes first, followed by the driver data. It also contains a pointer to the vk_foo_finish() function so that it can properly implement Drop. The VkObj struct is mostly private and cannot be constructed by drivers directly. 2. VkObjBaseBox is a box-like container type that contains a vk_foo. It also contains enough memory for the driver's data type but it is left uninitialized. VkObjBaseBox is constructed via an initializer callback constructor new_cb() which takes a callback which initializes the vk_foo. This is to allow the client to invoke vk_foo_init() with the appropriate parameters. 3. VkObjBox is a box-like container type that contains a VkObj. It is constructed via VkObjBox::new() which takes a VkObjBoxBase and a client data struct. The VkObjBoxBase is consumed and the extra memory allocated by VkObjBoxBase is used to store the driver data. All put together, the construction pattern looks like this: fn create_image( dev: &mut VkObjBox, 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 { /* Fields */ })) } --- src/vulkan/rust/boxed.rs | 165 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-) diff --git a/src/vulkan/rust/boxed.rs b/src/vulkan/rust/boxed.rs index 1ff28ac1f11..6709547efe6 100644 --- a/src/vulkan/rust/boxed.rs +++ b/src/vulkan/rust/boxed.rs @@ -127,13 +127,33 @@ impl VkBox { } } + 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: *const VkAllocationCallbacks, + parent_alloc: &VkAllocationCallbacks, alloc: *const VkAllocationCallbacks, scope: VkSystemAllocationScope, ) -> Result> { - let alloc = if alloc.is_null() { parent_alloc } else { alloc }; + let alloc = if alloc.is_null() { + parent_alloc + } else { + unsafe {&*alloc } + }; VkBox::new(x, alloc, scope) } } @@ -172,3 +192,144 @@ impl DerefMut for VkBox { 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 + } +} From e22634359385491733346e4b89d42315b2df9d91 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 14 Dec 2022 14:00:04 -0600 Subject: [PATCH 6/6] Add an examples file --- src/vulkan/rust/examples.rs | 43 ++++++++++++++++++++++++ src/vulkan/rust/vk.rs | 1 + src/vulkan/rust/vulkan_runtime_bindgen.h | 1 + 3 files changed, 45 insertions(+) create mode 100644 src/vulkan/rust/examples.rs 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/vk.rs b/src/vulkan/rust/vk.rs index f3219bb0f57..13b6d03bcc8 100644 --- a/src/vulkan/rust/vk.rs +++ b/src/vulkan/rust/vk.rs @@ -7,6 +7,7 @@ extern crate vulkan_h; extern crate vulkan_runtime; mod boxed; +mod examples; use vulkan_h::VkResult; diff --git a/src/vulkan/rust/vulkan_runtime_bindgen.h b/src/vulkan/rust/vulkan_runtime_bindgen.h index eaa5a7e8a27..918378c8ab5 100644 --- a/src/vulkan/rust/vulkan_runtime_bindgen.h +++ b/src/vulkan/rust/vulkan_runtime_bindgen.h @@ -3,5 +3,6 @@ * SPDX-License-Identifier: MIT */ #include "vk_device.h" +#include "vk_image.h" #include "vk_instance.h" #include "vk_physical_device.h"