Last active
January 30, 2024 20:21
-
-
Save nical/c5d88aaf97f20815756a36dc5c94b5a3 to your computer and use it in GitHub Desktop.
wgpu-core dyn trait experiment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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