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
10pub trait ViewportSize<T>
12where
13    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
14    f32: AsPrimitive<T>,
15{
16    fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>, original: Size<T>) -> Size<T>;
19}
20
21impl<T> ViewportSize<T> for Size<T>
22where
23    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
24    f32: AsPrimitive<T>,
25{
26    fn scale_viewport(self, scaling: Scale2D, viewport: Size<T>, original: Size<T>) -> Size<T>
27    where
28        T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
29        f32: AsPrimitive<T>,
30    {
31        scaling::scale(scaling, self, viewport, original)
32    }
33}
34
35pub trait MipmapSize<T> {
37    fn calculate_miplevels(self) -> T;
39
40    fn scale_mipmap(self, miplevel: T) -> Size<T>;
43}
44
45impl MipmapSize<u32> for Size<u32> {
46    fn calculate_miplevels(self) -> u32 {
47        let mut size = std::cmp::max(self.width, self.height);
48        let mut levels = 0;
49        while size != 0 {
50            levels += 1;
51            size >>= 1;
52        }
53
54        levels
55    }
56
57    fn scale_mipmap(self, miplevel: u32) -> Size<u32> {
58        let scaled_width = std::cmp::max(self.width >> miplevel, 1);
59        let scaled_height = std::cmp::max(self.height >> miplevel, 1);
60        Size::new(scaled_width, scaled_height)
61    }
62}
63
64fn scale<T>(scaling: Scale2D, source: Size<T>, viewport: Size<T>, original: Size<T>) -> Size<T>
65where
66    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
67    f32: AsPrimitive<T>,
68{
69    let width = match scaling.x {
70        Scaling {
71            scale_type: ScaleType::Input,
72            factor,
73        } => source.width * factor,
74        Scaling {
75            scale_type: ScaleType::Absolute,
76            factor,
77        } => factor.into(),
78        Scaling {
79            scale_type: ScaleType::Viewport,
80            factor,
81        } => viewport.width * factor,
82        Scaling {
83            scale_type: ScaleType::Original,
84            factor,
85        } => original.width * factor,
86    };
87
88    let height = match scaling.y {
89        Scaling {
90            scale_type: ScaleType::Input,
91            factor,
92        } => source.height * factor,
93        Scaling {
94            scale_type: ScaleType::Absolute,
95            factor,
96        } => factor.into(),
97        Scaling {
98            scale_type: ScaleType::Viewport,
99            factor,
100        } => viewport.height * factor,
101        Scaling {
102            scale_type: ScaleType::Original,
103            factor,
104        } => original.height * factor,
105    };
106
107    Size {
108        width: std::cmp::min(
109            std::cmp::max(width.round().as_(), 1f32.as_()),
110            MAX_TEXEL_SIZE.as_(),
111        ),
112        height: std::cmp::min(
113            std::cmp::max(height.round().as_(), 1f32.as_()),
114            MAX_TEXEL_SIZE.as_(),
115        ),
116    }
117}
118
119pub trait ScaleFramebuffer<T = ()> {
121    type Error;
122    type Context;
123    fn scale(
125        &mut self,
126        scaling: Scale2D,
127        format: ImageFormat,
128        viewport_size: &Size<u32>,
129        source_size: &Size<u32>,
130        original_size: &Size<u32>,
131        should_mipmap: bool,
132        context: &Self::Context,
133    ) -> Result<Size<u32>, Self::Error>;
134
135    #[inline(always)]
137    fn scale_framebuffers<P>(
138        source_size: Size<u32>,
139        viewport_size: Size<u32>,
140        original_size: Size<u32>,
141        output: &mut [Self],
142        feedback: &mut [Self],
143        passes: &[P],
144        callback: Option<&mut dyn FnMut(usize, &P, &Self, &Self) -> Result<(), Self::Error>>,
145    ) -> Result<(), Self::Error>
146    where
147        Self: Sized,
148        Self::Context: Default,
149        P: FilterPassMeta,
150    {
151        scale_framebuffers_with_context_callback::<T, Self, Self::Error, Self::Context, _>(
152            source_size,
153            viewport_size,
154            original_size,
155            output,
156            feedback,
157            passes,
158            &Self::Context::default(),
159            callback,
160        )
161    }
162
163    #[inline(always)]
165    fn scale_framebuffers_with_context<P>(
166        source_size: Size<u32>,
167        viewport_size: Size<u32>,
168        original_size: Size<u32>,
169        output: &mut [Self],
170        feedback: &mut [Self],
171        passes: &[P],
172        context: &Self::Context,
173        callback: Option<&mut dyn FnMut(usize, &P, &Self, &Self) -> Result<(), Self::Error>>,
174    ) -> Result<(), Self::Error>
175    where
176        Self: Sized,
177        P: FilterPassMeta,
178    {
179        scale_framebuffers_with_context_callback::<T, Self, Self::Error, Self::Context, _>(
180            source_size,
181            viewport_size,
182            original_size,
183            output,
184            feedback,
185            passes,
186            context,
187            callback,
188        )
189    }
190}
191
192#[inline(always)]
195fn scale_framebuffers_with_context_callback<T, F, E, C, P>(
196    source_size: Size<u32>,
197    viewport_size: Size<u32>,
198    original_size: Size<u32>,
199    output: &mut [F],
200    feedback: &mut [F],
201    passes: &[P],
202    context: &C,
203    mut callback: Option<&mut dyn FnMut(usize, &P, &F, &F) -> Result<(), E>>,
204) -> Result<(), E>
205where
206    F: ScaleFramebuffer<T, Context = C, Error = E>,
207    P: FilterPassMeta,
208{
209    assert_eq!(output.len(), feedback.len());
210    let mut iterator = passes.iter().enumerate().peekable();
211    let mut target_size = source_size;
212    while let Some((index, pass)) = iterator.next() {
213        let should_mipmap = iterator
214            .peek()
215            .map_or(false, |(_, p)| p.meta().mipmap_input);
216
217        let next_size = output[index].scale(
218            pass.meta().scaling.clone(),
219            pass.get_format(),
220            &viewport_size,
221            &target_size,
222            &original_size,
223            should_mipmap,
224            context,
225        )?;
226
227        feedback[index].scale(
228            pass.meta().scaling.clone(),
229            pass.get_format(),
230            &viewport_size,
231            &target_size,
232            &original_size,
233            should_mipmap,
234            context,
235        )?;
236
237        target_size = next_size;
238
239        if let Some(callback) = callback.as_mut() {
240            callback(index, pass, &output[index], &feedback[index])?;
241        }
242    }
243
244    Ok(())
245}