use crate::error::{FilterChainError, Result}; use crate::select_optimal_pixel_format; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_presets::Scale2D; use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize}; use objc2::rc::Retained; use objc2::runtime::ProtocolObject; use objc2_metal::{ MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLPixelFormat, MTLStorageMode, MTLTexture, MTLTextureDescriptor, MTLTextureUsage, }; pub type MetalTexture = Retained>; /// Alias to an `id`. pub type MetalTextureRef<'a> = &'a ProtocolObject; pub struct OwnedTexture { pub(crate) texture: MetalTexture, pub(crate) max_miplevels: u32, size: Size, } pub struct InputTexture { pub texture: MetalTexture, pub wrap_mode: WrapMode, pub filter_mode: FilterMode, pub mip_filter: FilterMode, } impl InputTexture { pub fn try_clone(&self) -> Result { Ok(Self { texture: self .texture .newTextureViewWithPixelFormat(self.texture.pixelFormat()) .ok_or(FilterChainError::FailedToCreateTexture)?, wrap_mode: self.wrap_mode, filter_mode: self.filter_mode, mip_filter: self.mip_filter, }) } } impl AsRef for InputTexture { fn as_ref(&self) -> &InputTexture { &self } } impl OwnedTexture { pub fn new( device: &ProtocolObject, size: Size, max_miplevels: u32, format: MTLPixelFormat, ) -> Result { let descriptor = unsafe { let descriptor = MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped( select_optimal_pixel_format(format), size.width as usize, size.height as usize, max_miplevels > 1, ); descriptor.setSampleCount(1); descriptor.setMipmapLevelCount(if max_miplevels > 1 { size.calculate_miplevels() as usize } else { 1 }); descriptor.setUsage( MTLTextureUsage::ShaderRead | MTLTextureUsage::ShaderWrite | MTLTextureUsage::RenderTarget | MTLTextureUsage::PixelFormatView, ); descriptor }; Ok(Self { texture: device .newTextureWithDescriptor(&descriptor) .ok_or(FilterChainError::FailedToCreateTexture)?, max_miplevels, size, }) } pub fn scale( &mut self, device: &ProtocolObject, scaling: Scale2D, format: MTLPixelFormat, viewport_size: &Size, source_size: &Size, original_size: &Size, mipmap: bool, ) -> Result> { let size = source_size.scale_viewport(scaling, *viewport_size, *original_size); if self.size != size || (mipmap && self.max_miplevels == 1) || (!mipmap && self.max_miplevels != 1) || self.texture.pixelFormat() != select_optimal_pixel_format(format) { let mut new = OwnedTexture::new( device, size, self.max_miplevels, select_optimal_pixel_format(format), )?; std::mem::swap(self, &mut new); } Ok(size) } pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> Result { Ok(InputTexture { texture: self .texture .newTextureViewWithPixelFormat(self.texture.pixelFormat()) .ok_or(FilterChainError::FailedToCreateTexture)?, wrap_mode, filter_mode: filter, mip_filter: filter, }) } pub fn copy_from( &self, encoder: &ProtocolObject, other: &ProtocolObject, ) -> Result<()> { unsafe { encoder.copyFromTexture_toTexture(other, &self.texture); } if self.texture.mipmapLevelCount() > 1 { encoder.generateMipmapsForTexture(&self.texture); } Ok(()) } pub fn generate_mipmaps(&self, cmd: &ProtocolObject) -> Result<()> { let mipmapper = cmd .blitCommandEncoder() .ok_or(FilterChainError::FailedToCreateCommandBuffer)?; if self.texture.mipmapLevelCount() > 1 { mipmapper.generateMipmapsForTexture(&self.texture); } mipmapper.endEncoding(); Ok(()) } } impl ScaleFramebuffer for OwnedTexture { type Error = FilterChainError; type Context = ProtocolObject; fn scale( &mut self, scaling: Scale2D, format: ImageFormat, viewport_size: &Size, source_size: &Size, original_size: &Size, should_mipmap: bool, context: &Self::Context, ) -> std::result::Result, Self::Error> { Ok(self.scale( &context, scaling, format.into(), viewport_size, source_size, original_size, should_mipmap, )?) } } pub(crate) fn get_texture_size(texture: &ProtocolObject) -> Size { let height = texture.height(); let width = texture.width(); Size { height: height as u32, width: width as u32, } }