use dpi::PhysicalSize;
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_io_surface::IOSurfaceRef;
use objc2_metal::{
MTLDevice, MTLPixelFormat, MTLTexture, MTLTextureDescriptor, MTLTextureType, MTLTextureUsage,
};
#[cfg(all(
feature = "wgpu-28",
not(feature = "wgpu-29"),
not(feature = "wgpu-30")
))]
use foreign_types_shared::ForeignType;
use crate::InteropError;
use super::texture_normalizer::ImportedTextureNormalizer;
pub struct MetalImporter {
normalizer: ImportedTextureNormalizer,
}
impl MetalImporter {
pub fn new(device: &wgpu::Device) -> Self {
Self {
normalizer: ImportedTextureNormalizer::new(device),
}
}
pub fn import(
&self,
iosurface: &IOSurfaceRef,
size: PhysicalSize<u32>,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Result<wgpu::Texture, InteropError> {
let metal_texture = self.create_metal_texture(device, iosurface, size)?;
let hal_texture = self.wrap_as_wgpu_hal(device, metal_texture, size)?;
Ok(self.normalizer.normalize(device, queue, &hal_texture, size))
}
fn create_metal_texture(
&self,
wgpu_device: &wgpu::Device,
iosurface: &IOSurfaceRef,
size: PhysicalSize<u32>,
) -> Result<Retained<ProtocolObject<dyn MTLTexture>>, InteropError> {
unsafe {
let metal_device = wgpu_device.as_hal::<wgpu::wgc::api::Metal>().ok_or(
InteropError::BackendMismatch {
expected: "Metal",
actual: "non-Metal",
},
)?;
#[cfg(all(
feature = "wgpu-28",
not(feature = "wgpu-29"),
not(feature = "wgpu-30")
))]
let device_raw = Retained::retain(
metal_device
.raw_device()
.as_ptr()
.cast::<ProtocolObject<dyn MTLDevice>>(),
)
.ok_or_else(|| InteropError::Metal("failed to retain Metal device".into()))?;
#[cfg(any(feature = "wgpu-29", feature = "wgpu-30"))]
let device_raw = metal_device.raw_device().clone();
let descriptor = MTLTextureDescriptor::new();
descriptor.setDepth(1);
descriptor.setSampleCount(1);
descriptor.setWidth(size.width as usize);
descriptor.setHeight(size.height as usize);
descriptor.setMipmapLevelCount(1);
descriptor.setUsage(MTLTextureUsage::ShaderRead);
descriptor.setPixelFormat(MTLPixelFormat::BGRA8Unorm);
descriptor.setTextureType(MTLTextureType::Type2D);
device_raw
.newTextureWithDescriptor_iosurface_plane(&descriptor, iosurface, 0)
.ok_or_else(|| {
InteropError::Metal("failed to create Metal texture from IOSurface".to_string())
})
}
}
fn wrap_as_wgpu_hal(
&self,
wgpu_device: &wgpu::Device,
metal_texture: Retained<ProtocolObject<dyn MTLTexture>>,
size: PhysicalSize<u32>,
) -> Result<wgpu::Texture, InteropError> {
unsafe {
let copy_size = wgpu::hal::CopyExtent {
width: size.width,
height: size.height,
depth: 1,
};
#[cfg(feature = "wgpu-30")]
let hal_texture = wgpu::hal::metal::Device::texture_from_raw(
metal_texture,
wgpu::TextureFormat::Bgra8Unorm,
MTLTextureType::Type2D,
1,
1,
copy_size,
None, );
#[cfg(all(feature = "wgpu-29", not(feature = "wgpu-30")))]
let hal_texture = wgpu::hal::metal::Device::texture_from_raw(
metal_texture,
wgpu::TextureFormat::Bgra8Unorm,
MTLTextureType::Type2D,
1,
1,
copy_size,
);
#[cfg(all(
feature = "wgpu-28",
not(feature = "wgpu-29"),
not(feature = "wgpu-30")
))]
let hal_texture = wgpu::hal::metal::Device::texture_from_raw(
metal::Texture::from_ptr(Retained::into_raw(metal_texture).cast()),
wgpu::TextureFormat::Bgra8Unorm,
metal::MTLTextureType::D2,
1,
1,
copy_size,
);
Ok(
crate::wgpu_compat::create_texture_from_hal::<wgpu_hal::api::Metal>(
wgpu_device,
hal_texture,
&wgpu::TextureDescriptor {
label: Some("iosurface-metal-import"),
size: wgpu::Extent3d {
width: size.width,
height: size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
wgpu::TextureUses::RESOURCE,
),
)
}
}
}