Merge branch 'vulkan/rust' into 'main'

Draft: Add infrastructure for Vulkan drivers written in Rust

See merge request mesa/mesa!20298
This commit is contained in:
Faith Ekstrand 2025-12-20 00:50:00 +00:00
commit e6135d2e28
8 changed files with 490 additions and 0 deletions

View file

@ -86,6 +86,7 @@ vulkan_wsi_deps += idep_vulkan_wsi_defines
subdir('util') subdir('util')
subdir('runtime') subdir('runtime')
subdir('wsi') subdir('wsi')
subdir('rust')
if with_vulkan_overlay_layer if with_vulkan_overlay_layer
subdir('overlay-layer') subdir('overlay-layer')
endif endif

View file

@ -0,0 +1 @@
max_width = 80

335
src/vulkan/rust/boxed.rs Normal file
View file

@ -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<T>(&self, scope: VkSystemAllocationScope) -> *mut T {
self.alloc_scoped_raw(Layout::new::<T>(), scope) as *mut T
}
}
trait VkFree {
unsafe fn free_raw(&self, mem: *mut u8);
unsafe fn free<T>(&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<T> {
ptr: NonNull<T>,
free: FreeCb,
}
impl<T> VkBox<MaybeUninit<T>> {
pub unsafe fn assume_init(self) -> VkBox<T> {
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<VkBox<MaybeUninit<T>>> {
unsafe {
let ptr = alloc.alloc_scoped::<MaybeUninit<T>>(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<T> {
unsafe {
(self.ptr.as_ptr() as *mut T).write(x);
self.assume_init()
}
}
}
impl<T> VkBox<T> {
pub fn new(
x: T,
alloc: &VkAllocationCallbacks,
scope: VkSystemAllocationScope,
) -> Result<VkBox<T>> {
match VkBox::<MaybeUninit<T>>::new_uninit(alloc, scope) {
Ok(b) => Ok(b.write(x)),
Err(e) => Err(e),
}
}
pub unsafe fn new_cb<F: FnOnce(NonNull<T>) -> VkResult>(
alloc: &VkAllocationCallbacks,
scope: VkSystemAllocationScope,
f: F,
) -> Result<VkBox<T>> {
match VkBox::<MaybeUninit<T>>::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<VkBox<T>> {
let alloc = if alloc.is_null() {
parent_alloc
} else {
unsafe {&*alloc }
};
VkBox::new(x, alloc, scope)
}
}
impl<T> Drop for VkBox<T> {
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<T> AsRef<T> for VkBox<T> {
fn as_ref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
}
impl<T> AsMut<T> for VkBox<T> {
fn as_mut(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
}
impl<T> Deref for VkBox<T> {
type Target = T;
fn deref(&self) -> &T {
self.as_ref()
}
}
impl<T> DerefMut for VkBox<T> {
fn deref_mut(&mut self) -> &mut T {
self.as_mut()
}
}
type VkFinishFn<V> = unsafe extern "C" fn(obj: *mut V);
#[repr(C)]
pub struct VkObj<V, T> {
vk: V,
finish: VkFinishFn<V>,
data: Option<T>,
_pin: std::marker::PhantomPinned,
}
impl<V, T> VkObj<V, T> {
unsafe fn init_ptr<F: FnOnce(NonNull<V>) -> VkResult>(
mut ptr: NonNull<Self>,
finish: VkFinishFn<V>,
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<V>;
let data_ptr = (&mut ptr.as_mut().data) as *mut Option<T>;
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<V, T> Drop for VkObj<V, T> {
fn drop(&mut self) {
self.data.take();
unsafe { (self.finish)(&mut self.vk) };
}
}
impl<V, T> Deref for VkObj<V, T> {
type Target = T;
fn deref(&self) -> &T {
self.data.as_ref().unwrap()
}
}
impl<V, T> DerefMut for VkObj<V, T> {
fn deref_mut(&mut self) -> &mut T {
self.data.as_mut().unwrap()
}
}
pub struct VkObjBaseBox<V, T> {
obj: VkBox<VkObj<V, T>>,
}
impl<V, T> VkObjBaseBox<V, T> {
pub fn new_cb<F: FnOnce(NonNull<V>) -> VkResult>(
alloc: &VkAllocationCallbacks,
finish: unsafe extern "C" fn(obj: *mut V),
f: F,
) -> Result<VkObjBaseBox<V, T>> {
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<F: FnOnce(NonNull<V>) -> VkResult>(
parent_alloc: &VkAllocationCallbacks,
alloc: *const VkAllocationCallbacks,
finish: unsafe extern "C" fn(obj: *mut V),
f: F,
) -> Result<VkObjBaseBox<V, T>> {
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<V, T> Deref for VkObjBaseBox<V, T> {
type Target = V;
fn deref(&self) -> &V {
&self.obj.vk
}
}
impl<V, T> DerefMut for VkObjBaseBox<V, T> {
fn deref_mut(&mut self) -> &mut V {
&mut self.obj.vk
}
}
pub struct VkObjBox<V, T> {
base: VkObjBaseBox<V, T>,
}
impl<V, T> VkObjBox<V, T> {
pub fn new(mut base: VkObjBaseBox<V, T>, x: T) -> VkObjBox<V, T> {
base.obj.data.replace(x);
VkObjBox { base: base }
}
}
impl<V, T> Deref for VkObjBox<V, T> {
type Target = VkObj<V, T>;
fn deref(&self) -> &VkObj<V, T> {
&self.base.obj
}
}
impl<V, T> DerefMut for VkObjBox<V, T> {
fn deref_mut(&mut self) -> &mut VkObj<V, T> {
&mut self.base.obj
}
}

View file

@ -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<vk_device, Device>,
}
fn create_image(
dev: &VkObj<vk_device, Device>,
info: *const VkImageCreateInfo,
alloc: *const VkAllocationCallbacks,
) -> Result<VkObjBox<vk_image, Image>> {
let vk = unsafe {
VkObjBaseBox::new2_cb(
&dev.vk().alloc,
alloc,
vk_image_finish,
&|vk: NonNull<vk_image>| {
vk_image_init(dev.vk_ptr(), vk.as_ptr(), info);
VK_SUCCESS
},
)
}?;
/* Stuff which may use vk */
Ok(VkObjBox::new(vk, Image {
dev: dev,
}))
}

View file

@ -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,
]
)

14
src/vulkan/rust/vk.rs Normal file
View file

@ -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<T> = std::result::Result<T, VkResult>;

View file

@ -0,0 +1,5 @@
/*
* Copyright © 2022 Collabora, Ltd.
* SPDX-License-Identifier: MIT
*/
#include "vulkan/vulkan_core.h"

View file

@ -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"