librashader_runtime_mtl/
texture.rs1use crate::error::{FilterChainError, Result};
2use crate::select_optimal_pixel_format;
3use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
4use librashader_presets::Scale2D;
5use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
6use objc2::rc::Retained;
7use objc2::runtime::ProtocolObject;
8use objc2_metal::{
9 MTLBlitCommandEncoder, MTLCommandBuffer, MTLCommandEncoder, MTLDevice, MTLPixelFormat,
10 MTLTexture, MTLTextureDescriptor, MTLTextureUsage,
11};
12
13pub type MetalTexture = Retained<ProtocolObject<dyn MTLTexture>>;
14
15pub type MetalTextureRef<'a> = &'a ProtocolObject<dyn MTLTexture>;
17
18pub struct OwnedTexture {
19 pub(crate) texture: MetalTexture,
20 pub(crate) max_miplevels: u32,
21 size: Size<u32>,
22}
23
24pub struct InputTexture {
25 pub texture: MetalTexture,
26 pub wrap_mode: WrapMode,
27 pub filter_mode: FilterMode,
28 pub mip_filter: FilterMode,
29}
30
31impl InputTexture {
32 pub fn try_clone(&self) -> Result<Self> {
33 Ok(Self {
34 texture: self
35 .texture
36 .newTextureViewWithPixelFormat(self.texture.pixelFormat())
37 .ok_or(FilterChainError::FailedToCreateTexture)?,
38 wrap_mode: self.wrap_mode,
39 filter_mode: self.filter_mode,
40 mip_filter: self.mip_filter,
41 })
42 }
43}
44impl AsRef<InputTexture> for InputTexture {
45 fn as_ref(&self) -> &InputTexture {
46 &self
47 }
48}
49
50impl OwnedTexture {
51 pub fn new(
52 device: &ProtocolObject<dyn MTLDevice>,
53 size: Size<u32>,
54 max_miplevels: u32,
55 format: MTLPixelFormat,
56 ) -> Result<Self> {
57 let descriptor = unsafe {
58 let descriptor =
59 MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
60 select_optimal_pixel_format(format),
61 size.width as usize,
62 size.height as usize,
63 max_miplevels > 1,
64 );
65
66 descriptor.setSampleCount(1);
67 descriptor.setMipmapLevelCount(if max_miplevels > 1 {
68 size.calculate_miplevels() as usize
69 } else {
70 1
71 });
72
73 descriptor.setUsage(
74 MTLTextureUsage::ShaderRead
75 | MTLTextureUsage::ShaderWrite
76 | MTLTextureUsage::RenderTarget
77 | MTLTextureUsage::PixelFormatView,
78 );
79
80 descriptor
81 };
82
83 Ok(Self {
84 texture: device
85 .newTextureWithDescriptor(&descriptor)
86 .ok_or(FilterChainError::FailedToCreateTexture)?,
87 max_miplevels,
88 size,
89 })
90 }
91
92 pub fn scale(
93 &mut self,
94 device: &ProtocolObject<dyn MTLDevice>,
95 scaling: Scale2D,
96 format: MTLPixelFormat,
97 viewport_size: &Size<u32>,
98 source_size: &Size<u32>,
99 original_size: &Size<u32>,
100 mipmap: bool,
101 ) -> Result<Size<u32>> {
102 let size = source_size.scale_viewport(scaling, *viewport_size, *original_size, None);
103
104 if self.size != size
105 || (mipmap && self.max_miplevels == 1)
106 || (!mipmap && self.max_miplevels != 1)
107 || self.texture.pixelFormat() != select_optimal_pixel_format(format)
108 {
109 let mut new = OwnedTexture::new(
110 device,
111 size,
112 self.max_miplevels,
113 select_optimal_pixel_format(format),
114 )?;
115 std::mem::swap(self, &mut new);
116 }
117 Ok(size)
118 }
119
120 pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> Result<InputTexture> {
121 Ok(InputTexture {
122 texture: self
123 .texture
124 .newTextureViewWithPixelFormat(self.texture.pixelFormat())
125 .ok_or(FilterChainError::FailedToCreateTexture)?,
126 wrap_mode,
127 filter_mode: filter,
128 mip_filter: filter,
129 })
130 }
131
132 pub fn copy_from(
133 &self,
134 encoder: &ProtocolObject<dyn MTLBlitCommandEncoder>,
135 other: &ProtocolObject<dyn MTLTexture>,
136 ) -> Result<()> {
137 unsafe {
138 encoder.copyFromTexture_toTexture(other, &self.texture);
139 }
140
141 if self.texture.mipmapLevelCount() > 1 {
142 encoder.generateMipmapsForTexture(&self.texture);
143 }
144
145 Ok(())
146 }
147
148 pub fn generate_mipmaps(&self, cmd: &ProtocolObject<dyn MTLCommandBuffer>) -> Result<()> {
149 let mipmapper = cmd
150 .blitCommandEncoder()
151 .ok_or(FilterChainError::FailedToCreateCommandBuffer)?;
152 if self.texture.mipmapLevelCount() > 1 {
153 mipmapper.generateMipmapsForTexture(&self.texture);
154 }
155 mipmapper.endEncoding();
156 Ok(())
157 }
158}
159
160impl ScaleFramebuffer for OwnedTexture {
161 type Error = FilterChainError;
162 type Context = ProtocolObject<dyn MTLDevice>;
163
164 fn scale(
165 &mut self,
166 scaling: Scale2D,
167 format: ImageFormat,
168 viewport_size: &Size<u32>,
169 source_size: &Size<u32>,
170 original_size: &Size<u32>,
171 should_mipmap: bool,
172 context: &Self::Context,
173 ) -> std::result::Result<Size<u32>, Self::Error> {
174 Ok(self.scale(
175 &context,
176 scaling,
177 format.into(),
178 viewport_size,
179 source_size,
180 original_size,
181 should_mipmap,
182 )?)
183 }
184}
185
186pub(crate) fn get_texture_size(texture: &ProtocolObject<dyn MTLTexture>) -> Size<u32> {
187 let height = texture.height();
188 let width = texture.width();
189 Size {
190 height: height as u32,
191 width: width as u32,
192 }
193}