librashader_runtime/
scaling.rs

1use crate::filter_pass::FilterPassMeta;
2use crate::scaling;
3use librashader_common::{ImageFormat, Size};
4use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
5use num_traits::AsPrimitive;
6use std::ops::Mul;
7
8pub const MAX_TEXEL_SIZE: f32 = 16384f32;
9
10/// Trait for size scaling relative to the viewport.
11pub trait ViewportSize<T>
12where
13    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
14    f32: AsPrimitive<T>,
15{
16    /// Produce a `Size<T>` scaled with the input scaling options.
17    /// The size will at minimum be 1x1, and at a maximum of the specified clamp
18    /// value, or 16384x16384 if the clamp value is not specified.
19    fn scale_viewport(
20        self,
21        scaling: Scale2D,
22        viewport: Size<T>,
23        original: Size<T>,
24        clamp: Option<T>,
25    ) -> Size<T>;
26}
27
28impl<T> ViewportSize<T> for Size<T>
29where
30    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
31    f32: AsPrimitive<T>,
32{
33    fn scale_viewport(
34        self,
35        scaling: Scale2D,
36        viewport: Size<T>,
37        original: Size<T>,
38        clamp: Option<T>,
39    ) -> Size<T>
40    where
41        T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
42        f32: AsPrimitive<T>,
43    {
44        scaling::scale(scaling, self, viewport, original, clamp)
45    }
46}
47
48/// Trait for size scaling relating to mipmap generation.
49pub trait MipmapSize<T> {
50    /// Calculate the number of mipmap levels for a given size.
51    fn calculate_miplevels(self) -> T;
52
53    /// Scale the size according to the given mipmap level.
54    /// The size will at minimum be 1x1, and at a maximum 16384x16384.
55    fn scale_mipmap(self, miplevel: T) -> Size<T>;
56}
57
58impl MipmapSize<u32> for Size<u32> {
59    fn calculate_miplevels(self) -> u32 {
60        let mut size = std::cmp::max(self.width, self.height);
61        let mut levels = 0;
62        while size != 0 {
63            levels += 1;
64            size >>= 1;
65        }
66
67        levels
68    }
69
70    fn scale_mipmap(self, miplevel: u32) -> Size<u32> {
71        let scaled_width = std::cmp::max(self.width >> miplevel, 1);
72        let scaled_height = std::cmp::max(self.height >> miplevel, 1);
73        Size::new(scaled_width, scaled_height)
74    }
75}
76
77fn scale<T>(
78    scaling: Scale2D,
79    source: Size<T>,
80    viewport: Size<T>,
81    original: Size<T>,
82    clamp: Option<T>,
83) -> Size<T>
84where
85    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
86    f32: AsPrimitive<T>,
87{
88    let width = match scaling.x {
89        Scaling {
90            scale_type: ScaleType::Input,
91            factor,
92        } => source.width * factor,
93        Scaling {
94            scale_type: ScaleType::Absolute,
95            factor,
96        } => factor.into(),
97        Scaling {
98            scale_type: ScaleType::Viewport,
99            factor,
100        } => viewport.width * factor,
101        Scaling {
102            scale_type: ScaleType::Original,
103            factor,
104        } => original.width * factor,
105    };
106
107    let height = match scaling.y {
108        Scaling {
109            scale_type: ScaleType::Input,
110            factor,
111        } => source.height * factor,
112        Scaling {
113            scale_type: ScaleType::Absolute,
114            factor,
115        } => factor.into(),
116        Scaling {
117            scale_type: ScaleType::Viewport,
118            factor,
119        } => viewport.height * factor,
120        Scaling {
121            scale_type: ScaleType::Original,
122            factor,
123        } => original.height * factor,
124    };
125
126    Size {
127        width: std::cmp::min(
128            std::cmp::max(width.round().as_(), 1f32.as_()),
129            clamp.unwrap_or(MAX_TEXEL_SIZE.as_()),
130        ),
131        height: std::cmp::min(
132            std::cmp::max(height.round().as_(), 1f32.as_()),
133            clamp.unwrap_or(MAX_TEXEL_SIZE.as_()),
134        ),
135    }
136}
137
138/// Trait for owned framebuffer objects that can be scaled.
139pub trait ScaleFramebuffer<T = ()> {
140    type Error;
141    type Context;
142    /// Scale the framebuffer according to the provided parameters, returning the new size.
143    fn scale(
144        &mut self,
145        scaling: Scale2D,
146        format: ImageFormat,
147        viewport_size: &Size<u32>,
148        source_size: &Size<u32>,
149        original_size: &Size<u32>,
150        should_mipmap: bool,
151        context: &Self::Context,
152    ) -> Result<Size<u32>, Self::Error>;
153
154    /// Scale framebuffers with default context.
155    #[inline(always)]
156    fn scale_framebuffers<P>(
157        source_size: Size<u32>,
158        viewport_size: Size<u32>,
159        original_size: Size<u32>,
160        output: &mut [Self],
161        feedback: &mut [Self],
162        passes: &[P],
163        callback: Option<&mut dyn FnMut(usize, &P, &Self, &Self) -> Result<(), Self::Error>>,
164    ) -> Result<(), Self::Error>
165    where
166        Self: Sized,
167        Self::Context: Default,
168        P: FilterPassMeta,
169    {
170        scale_framebuffers_with_context_callback::<T, Self, Self::Error, Self::Context, _>(
171            source_size,
172            viewport_size,
173            original_size,
174            output,
175            feedback,
176            passes,
177            &Self::Context::default(),
178            callback,
179        )
180    }
181
182    /// Scale framebuffers with user provided context.
183    #[inline(always)]
184    fn scale_framebuffers_with_context<P>(
185        source_size: Size<u32>,
186        viewport_size: Size<u32>,
187        original_size: Size<u32>,
188        output: &mut [Self],
189        feedback: &mut [Self],
190        passes: &[P],
191        context: &Self::Context,
192        callback: Option<&mut dyn FnMut(usize, &P, &Self, &Self) -> Result<(), Self::Error>>,
193    ) -> Result<(), Self::Error>
194    where
195        Self: Sized,
196        P: FilterPassMeta,
197    {
198        scale_framebuffers_with_context_callback::<T, Self, Self::Error, Self::Context, _>(
199            source_size,
200            viewport_size,
201            original_size,
202            output,
203            feedback,
204            passes,
205            context,
206            callback,
207        )
208    }
209}
210
211/// Scale framebuffers according to the pass configs, source and viewport size
212/// passing a context into the scale function and a callback for each framebuffer rescale.
213#[inline(always)]
214fn scale_framebuffers_with_context_callback<T, F, E, C, P>(
215    source_size: Size<u32>,
216    viewport_size: Size<u32>,
217    original_size: Size<u32>,
218    output: &mut [F],
219    feedback: &mut [F],
220    passes: &[P],
221    context: &C,
222    mut callback: Option<&mut dyn FnMut(usize, &P, &F, &F) -> Result<(), E>>,
223) -> Result<(), E>
224where
225    F: ScaleFramebuffer<T, Context = C, Error = E>,
226    P: FilterPassMeta,
227{
228    assert_eq!(output.len(), feedback.len());
229    let mut iterator = passes.iter().enumerate().peekable();
230    let mut target_size = source_size;
231    while let Some((index, pass)) = iterator.next() {
232        let should_mipmap = iterator
233            .peek()
234            .map_or(false, |(_, p)| p.meta().mipmap_input);
235
236        let next_size = output[index].scale(
237            pass.meta().scaling.clone(),
238            pass.get_format(),
239            &viewport_size,
240            &target_size,
241            &original_size,
242            should_mipmap,
243            context,
244        )?;
245
246        feedback[index].scale(
247            pass.meta().scaling.clone(),
248            pass.get_format(),
249            &viewport_size,
250            &target_size,
251            &original_size,
252            should_mipmap,
253            context,
254        )?;
255
256        target_size = next_size;
257
258        if let Some(callback) = callback.as_mut() {
259            callback(index, pass, &output[index], &feedback[index])?;
260        }
261    }
262
263    Ok(())
264}