use crate::error::{FilterChainError, Result}; use icrate::Metal::{ MTLBlitCommandEncoder, MTLCommandBuffer, MTLDevice, MTLPixelFormat, MTLTexture, MTLTextureDescriptor, MTLTextureUsageRenderTarget, MTLTextureUsageShaderRead, MTLTextureUsageShaderWrite, }; use librashader_common::{FilterMode, ImageFormat, Size, WrapMode}; use librashader_presets::Scale2D; use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize}; use objc2::rc::Id; use objc2::runtime::ProtocolObject; pub type MetalTexture = Id>; 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( 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( MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget, ); 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, mipmap: bool, ) -> Result> { let size = source_size.scale_viewport(scaling, *viewport_size); if self.size != size || (mipmap && self.max_miplevels == 1) || (!mipmap && self.max_miplevels != 1) || format != self.texture.pixelFormat() { let mut new = OwnedTexture::new(device, size, self.max_miplevels, 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); } encoder.generateMipmapsForTexture(&self.texture); Ok(()) } pub fn clear(&self, cmd: &ProtocolObject) { // let render = cmd.renderCommandEncoder() // .ok_or(FilterChainError::FailedToCreateCommandBuffer)?; // render. // cmd.clear_texture(&self.image, &wgpu::ImageSubresourceRange::default()); } /// caller must end the blit encoder after. pub fn generate_mipmaps(&self, mipmapper: &ProtocolObject) { mipmapper.generateMipmapsForTexture(&self.texture); } } impl ScaleFramebuffer for OwnedTexture { type Error = FilterChainError; type Context = ProtocolObject; fn scale( &mut self, scaling: Scale2D, format: ImageFormat, viewport_size: &Size, source_size: &Size, should_mipmap: bool, context: &Self::Context, ) -> std::result::Result, Self::Error> { Ok(self.scale( &context, scaling, format.into(), viewport_size, source_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, } }