Skip to content

Instantly share code, notes, and snippets.

@nical
Last active January 30, 2024 20:21
Show Gist options
  • Save nical/c5d88aaf97f20815756a36dc5c94b5a3 to your computer and use it in GitHub Desktop.
Save nical/c5d88aaf97f20815756a36dc5c94b5a3 to your computer and use it in GitHub Desktop.
wgpu-core dyn trait experiment
// In wgpu_core
pub mod core {
use crate::hal::HalDeviceFactory;
use super::hal;
use std::{cell::UnsafeCell, sync::Arc};
pub struct Error;
impl From<hal::Error> for Error {
fn from(e: hal::Error) -> Self { Error }
}
/// Resources that can be snatched. We can't have an `Option<dyn Trait>` so the ability to snatch
/// has to be done below the abstraction.
pub trait SnatchableResource {
fn get(&self, _guard: &SnatchGuard) -> Option<&dyn hal::Resource>;
fn snatch(&self, _guard: ExclusiveSnatchGuard) -> Option<Box<dyn hal::Resource>> { None }
}
pub struct DeviceResource<Hal: ?Sized> {
pub raw: Hal
}
pub struct BindGroupLayoutResource<Hal: ?Sized> {
pub label: String,
// etc.
pub raw: Hal
}
pub struct TextureResource<Hal: ?Sized> {
pub label: String,
// etc.
pub raw: Hal
}
// These are the alias used in most places.
pub type Device = DeviceResource<dyn hal::Resource>;
pub type BindGroupLayout = BindGroupLayoutResource<dyn hal::Resource>;
pub type Texture = TextureResource<dyn SnatchableResource>;
// The details of the snatching logic are not very interesting
pub struct SnatchGuard;
pub struct ExclusiveSnatchGuard;
pub struct Snatchable<T> {
value: UnsafeCell<Option<T>>,
}
impl<T: hal::Resource> Snatchable<T> {
pub fn new(val: T) -> Self {
Snatchable {
value: UnsafeCell::new(Some(val))
}
}
}
impl<T> SnatchableResource for Snatchable<T> where T: hal::Resource {
fn get(&self, _guard: &SnatchGuard) -> Option<&dyn hal::Resource> {
unsafe {
(*self.value.get()).as_ref().map(|resource| resource as &dyn hal::Resource)
}
}
fn snatch(&self, _guard: ExclusiveSnatchGuard) -> Option<Box<dyn hal::Resource>> {
unsafe {
(*self.value.get()).take().map(|resource| {
let typed = Box::new(resource);
typed as _
})
}
}
}
/// The object-safe equivalent to `HalDeviceFactory`
pub trait CoreDeviceFactory {
fn create_texture(&self, desc: &hal::TextureDescriptor) -> Result<Arc<Texture>, Error>;
fn create_bind_group_layout(&self, desc: &hal::BindGroupLayoutDescriptor) -> Result<Arc<BindGroupLayout>, Error>;
}
impl<T> CoreDeviceFactory for T where T: hal::HalDeviceFactory {
fn create_texture(&self, desc: &hal::TextureDescriptor) -> Result<Arc<Texture>, Error> {
let typed_texture = Arc::new(TextureResource {
label: String::new(),
raw: Snatchable::new(self.create_hal_texture(desc)?),
});
Ok(typed_texture)
}
fn create_bind_group_layout(&self, desc: &hal::BindGroupLayoutDescriptor) -> Result<Arc<BindGroupLayout>, Error> {
let typed_bgl = Arc::new(BindGroupLayoutResource {
label: String::new(),
raw: self.create_hal_bind_group_layout(desc)?,
});
Ok(typed_bgl)
}
}
}
// In wgpu_hal
pub mod hal {
/// Implemented by most hal types
pub trait Resource: 'static {
fn as_any(&self) -> &dyn std::any::Any;
}
pub struct TextureDescriptor;
pub struct BindGroupLayoutDescriptor;
pub struct Error;
// A helper
pub fn downcast<T:'static>(resource: &dyn Resource) -> &T {
resource.as_any().downcast_ref::<T>().unwrap()
}
/// Object safe trait.
pub trait DeviceApi {
fn buffer_map(&self, buffer: &dyn Resource) -> Result<(), Error>;
}
/// Parts of the API that we couldn't make object-safe are split into this "Factory" trait.
pub trait HalDeviceFactory {
type Texture: Resource;
type Buffer: Resource;
type BindGroupLayout: Resource;
fn create_hal_texture(&self, desc: &TextureDescriptor) -> Result<Self::Texture, Error>;
fn create_hal_bind_group_layout(&self, desc: &BindGroupLayoutDescriptor) -> Result<Self::BindGroupLayout, Error>;
}
pub mod vulkan {
use super::{downcast, DeviceApi, HalDeviceFactory, Error, Resource};
use std::any::Any;
pub struct Device {
// ...
}
impl DeviceApi for Device {
fn buffer_map(&self, buffer: &dyn Resource) -> Result<(), Error> {
let buffer: &Buffer = downcast(buffer);
Ok(())
}
}
impl HalDeviceFactory for Device {
type Texture = Texture;
type Buffer = Buffer;
type BindGroupLayout = BindGroupLayout;
fn create_hal_texture(&self, desc: &super::TextureDescriptor) -> Result<Self::Texture, Error> {
unimplemented!()
}
fn create_hal_bind_group_layout(&self, desc: &super::BindGroupLayoutDescriptor) -> Result<Self::BindGroupLayout, Error> {
unimplemented!()
}
}
pub struct Texture {
// ...
}
pub struct Buffer {
// ...
}
pub struct BindGroupLayout {
// ...
}
impl Resource for Device {
fn as_any(&self) -> &dyn Any { self }
}
impl Resource for Texture {
fn as_any(&self) -> &dyn Any { self }
}
impl Resource for Buffer {
fn as_any(&self) -> &dyn Any { self }
}
impl Resource for BindGroupLayout {
fn as_any(&self) -> &dyn Any { self }
}
// etc.
pub fn texture_do_something(texture: &dyn Resource) {
let texture = downcast::<super::vulkan::Texture>(texture);
}
}
}
fn main() {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment