diff --git a/wgpu-hal/src/dx12/mod.rs b/wgpu-hal/src/dx12/mod.rs index 8d08b8f72..165a970ef 100644 --- a/wgpu-hal/src/dx12/mod.rs +++ b/wgpu-hal/src/dx12/mod.rs @@ -87,6 +87,25 @@ impl crate::Api for Api { type AccelerationStructure = AccelerationStructure; } +crate::impl_dyn_resource!( + BindGroup, + BindGroupLayout, + Buffer, + CommandBuffer, + CommandEncoder, + ComputePipeline, + Fence, + PipelineCache, + PipelineLayout, + QuerySet, + RenderPipeline, + Sampler, + ShaderModule, + Surface, + Texture, + TextureView +); + // Limited by D3D12's root signature size of 64. Each element takes 1 or 2 entries. const MAX_ROOT_ELEMENTS: usize = 64; const ZERO_BUFFER_SIZE: wgt::BufferAddress = 256 << 10; @@ -407,6 +426,8 @@ pub struct Buffer { unsafe impl Send for Buffer {} unsafe impl Sync for Buffer {} +impl crate::DynBuffer for Buffer {} + impl crate::BufferBinding<'_, Api> { fn resolve_size(&self) -> wgt::BufferAddress { match self.size { diff --git a/wgpu-hal/src/dynamic/mod.rs b/wgpu-hal/src/dynamic/mod.rs new file mode 100644 index 000000000..9b0ffe6d1 --- /dev/null +++ b/wgpu-hal/src/dynamic/mod.rs @@ -0,0 +1,83 @@ +use std::any::Any; + +use wgt::WasmNotSendSync; + +/// Base trait for all resources, allows downcasting via [`Any`]. +pub trait DynResource: Any + WasmNotSendSync + 'static { + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +/// Utility macro for implementing `DynResource` for a list of types. +macro_rules! impl_dyn_resource { + ($($type:ty),*) => { + $( + impl crate::DynResource for $type { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { + self + } + } + )* + }; +} +pub(crate) use impl_dyn_resource; + +/// Extension trait for `DynResource` used by implementations of various dynamic resource traits. +trait DynResourceExt { + /// # Panics + /// + /// - Panics if `self` is not downcastable to `T`. + fn expect_downcast_ref(&self) -> &T; + /// # Panics + /// + /// - Panics if `self` is not downcastable to `T`. + fn expect_downcast_mut(&mut self) -> &mut T; + + /// Unboxes a `Box` to a concrete type. + /// + /// # Safety + /// + /// - `self` must be the correct concrete type. + unsafe fn unbox(self: Box) -> T; +} + +impl DynResourceExt for R { + fn expect_downcast_ref<'a, T: DynResource>(&'a self) -> &'a T { + self.as_any() + .downcast_ref() + .expect("Resource doesn't have the expected backend type.") + } + + fn expect_downcast_mut<'a, T: DynResource>(&'a mut self) -> &'a mut T { + self.as_any_mut() + .downcast_mut() + .expect("Resource doesn't have the expected backend type.") + } + + unsafe fn unbox(self: Box) -> T { + debug_assert!( + ::type_id(self.as_ref()) == std::any::TypeId::of::(), + "Resource doesn't have the expected type, expected {:?}, got {:?}", + std::any::TypeId::of::(), + ::type_id(self.as_ref()) + ); + + let casted_ptr = Box::into_raw(self).cast::(); + // SAFETY: This is adheres to the safety contract of `Box::from_raw` because: + // + // - We are casting the value of a previously `Box`ed value, which guarantees: + // - `casted_ptr` is not null. + // - `casted_ptr` is valid for reads and writes, though by itself this does not mean + // valid reads and writes for `T` (read on for that). + // - We don't change the allocator. + // - The contract of `Box::from_raw` requires that an initialized and aligned `T` is stored + // within `casted_ptr`. + *unsafe { Box::from_raw(casted_ptr) } + } +} + +pub trait DynBuffer: DynResource + std::fmt::Debug {} diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs index 956b7b08a..4cee4501e 100644 --- a/wgpu-hal/src/empty.rs +++ b/wgpu-hal/src/empty.rs @@ -40,6 +40,10 @@ impl crate::Api for Api { type ComputePipeline = Resource; } +crate::impl_dyn_resource!(Context, Encoder, Resource); + +impl crate::DynBuffer for Resource {} + impl crate::Instance for Context { type A = Api; diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 459600df7..bf04c3a63 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -164,6 +164,24 @@ impl crate::Api for Api { type ComputePipeline = ComputePipeline; } +crate::impl_dyn_resource!( + BindGroup, + BindGroupLayout, + Buffer, + CommandBuffer, + CommandEncoder, + ComputePipeline, + Fence, + PipelineLayout, + QuerySet, + RenderPipeline, + Sampler, + ShaderModule, + Surface, + Texture, + TextureView +); + bitflags::bitflags! { /// Flags that affect internal code paths but do not /// change the exposed feature set. @@ -307,6 +325,8 @@ unsafe impl Sync for Buffer {} #[cfg(send_sync)] unsafe impl Send for Buffer {} +impl crate::DynBuffer for Buffer {} + #[derive(Clone, Debug)] pub enum TextureInner { Renderbuffer { diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index bd60b029e..89053b2a7 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -262,6 +262,11 @@ pub mod api { pub use super::vulkan::Api as Vulkan; } +mod dynamic; + +pub use dynamic::DynBuffer; +pub(crate) use dynamic::{impl_dyn_resource, DynResource}; + use std::{ borrow::{Borrow, Cow}, fmt, @@ -399,7 +404,7 @@ pub trait Api: Clone + fmt::Debug + Sized { /// [`CommandEncoder`]: Api::CommandEncoder type CommandBuffer: WasmNotSendSync + fmt::Debug; - type Buffer: fmt::Debug + WasmNotSendSync + 'static; + type Buffer: DynBuffer; type Texture: fmt::Debug + WasmNotSendSync + 'static; type SurfaceTexture: fmt::Debug + WasmNotSendSync + Borrow; type TextureView: fmt::Debug + WasmNotSendSync; diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 000398370..c57137d73 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -71,6 +71,24 @@ impl crate::Api for Api { type AccelerationStructure = AccelerationStructure; } +crate::impl_dyn_resource!( + BindGroup, + BindGroupLayout, + Buffer, + CommandBuffer, + CommandEncoder, + ComputePipeline, + Fence, + PipelineLayout, + QuerySet, + RenderPipeline, + Sampler, + ShaderModule, + Surface, + Texture, + TextureView +); + pub struct Instance { managed_metal_layer_delegate: surface::HalManagedMetalLayerDelegate, } @@ -460,6 +478,8 @@ pub struct Buffer { unsafe impl Send for Buffer {} unsafe impl Sync for Buffer {} +impl crate::DynBuffer for Buffer {} + impl Buffer { fn as_raw(&self) -> BufferPtr { unsafe { NonNull::new_unchecked(self.raw.as_ptr()) } diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index d4be64548..f32955177 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -78,6 +78,25 @@ impl crate::Api for Api { type ComputePipeline = ComputePipeline; } +crate::impl_dyn_resource!( + BindGroup, + BindGroupLayout, + Buffer, + CommandBuffer, + CommandEncoder, + ComputePipeline, + Fence, + PipelineCache, + PipelineLayout, + QuerySet, + RenderPipeline, + Sampler, + ShaderModule, + Surface, + Texture, + TextureView +); + struct DebugUtils { extension: ext::debug_utils::Instance, messenger: vk::DebugUtilsMessengerEXT, @@ -631,6 +650,8 @@ pub struct Buffer { block: Option>>, } +impl crate::DynBuffer for Buffer {} + #[derive(Debug)] pub struct AccelerationStructure { raw: vk::AccelerationStructureKHR,