Skip to main content

librashader_runtime/
scaling.rs

1use crate::filter_pass::FilterPassMeta;
2use crate::framebuffer::FramebufferPool;
3use crate::scaling;
4use librashader_common::{ImageFormat, Size};
5use librashader_presets::{Scale2D, ScaleFactor, ScaleType, Scaling};
6use num_traits::AsPrimitive;
7use std::ops::Mul;
8
9pub const MAX_TEXEL_SIZE: f32 = 16384f32;
10
11/// Trait for size scaling relative to the viewport.
12pub trait ViewportSize<T>
13where
14    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
15    f32: AsPrimitive<T>,
16{
17    /// Produce a `Size<T>` scaled with the input scaling options.
18    /// The size will at minimum be 1x1, and at a maximum of the specified clamp
19    /// value, or 16384x16384 if the clamp value is not specified.
20    fn scale_viewport(
21        self,
22        scaling: Scale2D,
23        viewport: Size<T>,
24        original: Size<T>,
25        clamp: Option<T>,
26    ) -> Size<T>;
27}
28
29impl<T> ViewportSize<T> for Size<T>
30where
31    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
32    f32: AsPrimitive<T>,
33{
34    fn scale_viewport(
35        self,
36        scaling: Scale2D,
37        viewport: Size<T>,
38        original: Size<T>,
39        clamp: Option<T>,
40    ) -> Size<T>
41    where
42        T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
43        f32: AsPrimitive<T>,
44    {
45        scaling::scale(scaling, self, viewport, original, clamp)
46    }
47}
48
49/// Trait for size scaling relating to mipmap generation.
50pub trait MipmapSize<T> {
51    /// Calculate the number of mipmap levels for a given size.
52    fn calculate_miplevels(self) -> T;
53
54    /// Scale the size according to the given mipmap level.
55    /// The size will at minimum be 1x1, and at a maximum 16384x16384.
56    fn scale_mipmap(self, miplevel: T) -> Size<T>;
57}
58
59impl MipmapSize<u32> for Size<u32> {
60    fn calculate_miplevels(self) -> u32 {
61        let mut size = std::cmp::max(self.width, self.height);
62        let mut levels = 0;
63        while size != 0 {
64            levels += 1;
65            size >>= 1;
66        }
67
68        levels
69    }
70
71    fn scale_mipmap(self, miplevel: u32) -> Size<u32> {
72        let scaled_width = std::cmp::max(self.width >> miplevel, 1);
73        let scaled_height = std::cmp::max(self.height >> miplevel, 1);
74        Size::new(scaled_width, scaled_height)
75    }
76}
77
78fn scale<T>(
79    scaling: Scale2D,
80    source: Size<T>,
81    viewport: Size<T>,
82    original: Size<T>,
83    clamp: Option<T>,
84) -> Size<T>
85where
86    T: Mul<ScaleFactor, Output = f32> + Copy + Ord + 'static,
87    f32: AsPrimitive<T>,
88{
89    let width = match scaling.x {
90        Scaling {
91            scale_type: ScaleType::Input,
92            factor,
93        } => source.width * factor,
94        Scaling {
95            scale_type: ScaleType::Absolute,
96            factor,
97        } => factor.into(),
98        Scaling {
99            scale_type: ScaleType::Viewport,
100            factor,
101        } => viewport.width * factor,
102        Scaling {
103            scale_type: ScaleType::Original,
104            factor,
105        } => original.width * factor,
106    };
107
108    let height = match scaling.y {
109        Scaling {
110            scale_type: ScaleType::Input,
111            factor,
112        } => source.height * factor,
113        Scaling {
114            scale_type: ScaleType::Absolute,
115            factor,
116        } => factor.into(),
117        Scaling {
118            scale_type: ScaleType::Viewport,
119            factor,
120        } => viewport.height * factor,
121        Scaling {
122            scale_type: ScaleType::Original,
123            factor,
124        } => original.height * factor,
125    };
126
127    Size {
128        width: std::cmp::min(
129            std::cmp::max(width.round().as_(), 1f32.as_()),
130            clamp.unwrap_or(MAX_TEXEL_SIZE.as_()),
131        ),
132        height: std::cmp::min(
133            std::cmp::max(height.round().as_(), 1f32.as_()),
134            clamp.unwrap_or(MAX_TEXEL_SIZE.as_()),
135        ),
136    }
137}
138
139/// Trait for owned framebuffer objects that can be scaled.
140pub trait ScaleFramebuffer<T = ()> {
141    type Error;
142    type Context;
143    /// Scale the framebuffer according to the provided parameters, returning the new size.
144    fn scale(
145        &mut self,
146        scaling: Scale2D,
147        format: ImageFormat,
148        viewport_size: &Size<u32>,
149        source_size: &Size<u32>,
150        original_size: &Size<u32>,
151        should_mipmap: bool,
152        context: &Self::Context,
153    ) -> Result<Size<u32>, Self::Error>;
154
155    /// Scale the sparse feedback framebuffers, invoking `callback` for each pass that is
156    /// referenced as feedback so the runtime can refresh its bound feedback texture.
157    #[inline(always)]
158    fn scale_feedback_framebuffers<P>(
159        source_size: Size<u32>,
160        viewport_size: Size<u32>,
161        original_size: Size<u32>,
162        feedback: &mut FramebufferPool<Self>,
163        passes: &[P],
164        callback: impl FnMut(usize, &P, &Self) -> Result<(), Self::Error>,
165    ) -> Result<(), Self::Error>
166    where
167        Self: Sized,
168        Self::Context: Default,
169        P: FilterPassMeta,
170    {
171        Self::scale_feedback_framebuffers_with_context(
172            source_size,
173            viewport_size,
174            original_size,
175            feedback,
176            passes,
177            &Self::Context::default(),
178            callback,
179        )
180    }
181
182    /// Scale the sparse feedback framebuffers with a user provided context.
183    #[inline(always)]
184    fn scale_feedback_framebuffers_with_context<P>(
185        source_size: Size<u32>,
186        viewport_size: Size<u32>,
187        original_size: Size<u32>,
188        feedback: &mut FramebufferPool<Self>,
189        passes: &[P],
190        context: &Self::Context,
191        callback: impl FnMut(usize, &P, &Self) -> Result<(), Self::Error>,
192    ) -> Result<(), Self::Error>
193    where
194        Self: Sized,
195        P: FilterPassMeta,
196    {
197        scale_feedback_framebuffers_callback::<T, Self, Self::Error, Self::Context, P, _>(
198            source_size,
199            viewport_size,
200            original_size,
201            feedback,
202            passes,
203            context,
204            callback,
205        )
206    }
207
208    /// Scale the pooled output framebuffers, invoking `callback` for each pass with its
209    /// routed render target and scaled output size so the runtime can draw it.
210    #[inline(always)]
211    fn scale_output_framebuffers<P>(
212        source_size: Size<u32>,
213        viewport_size: Size<u32>,
214        original_size: Size<u32>,
215        output: &mut FramebufferPool<Self>,
216        passes: &mut [P],
217        callback: impl FnMut(usize, &mut P, &Self, Size<u32>) -> Result<(), Self::Error>,
218    ) -> Result<(), Self::Error>
219    where
220        Self: Sized,
221        Self::Context: Default,
222        P: FilterPassMeta,
223    {
224        Self::scale_output_framebuffers_with_context(
225            source_size,
226            viewport_size,
227            original_size,
228            output,
229            passes,
230            &Self::Context::default(),
231            callback,
232        )
233    }
234
235    /// Scale the pooled output framebuffers with a user provided context.
236    #[inline(always)]
237    fn scale_output_framebuffers_with_context<P>(
238        source_size: Size<u32>,
239        viewport_size: Size<u32>,
240        original_size: Size<u32>,
241        output: &mut FramebufferPool<Self>,
242        passes: &mut [P],
243        context: &Self::Context,
244        callback: impl FnMut(usize, &mut P, &Self, Size<u32>) -> Result<(), Self::Error>,
245    ) -> Result<(), Self::Error>
246    where
247        Self: Sized,
248        P: FilterPassMeta,
249    {
250        scale_output_framebuffers_callback::<T, Self, Self::Error, Self::Context, P, _>(
251            source_size,
252            viewport_size,
253            original_size,
254            output,
255            passes,
256            context,
257            callback,
258        )
259    }
260}
261
262#[inline(always)]
263fn scale_feedback_framebuffers_callback<T, F, E, C, P, CB>(
264    source_size: Size<u32>,
265    viewport_size: Size<u32>,
266    original_size: Size<u32>,
267    feedback: &mut FramebufferPool<F>,
268    passes: &[P],
269    context: &C,
270    mut callback: CB,
271) -> Result<(), E>
272where
273    F: ScaleFramebuffer<T, Context = C, Error = E>,
274    P: FilterPassMeta,
275    CB: FnMut(usize, &P, &F) -> Result<(), E>,
276{
277    let mut iterator = passes.iter().enumerate().peekable();
278    let mut target_size = source_size;
279    while let Some((index, pass)) = iterator.next() {
280        let should_mipmap = iterator
281            .peek()
282            .map_or(false, |(_, p)| p.meta().mipmap_input);
283
284        let next_size = target_size.scale_viewport(
285            pass.meta().scaling.clone(),
286            viewport_size,
287            original_size,
288            None,
289        );
290
291        if feedback.contains(index) {
292            feedback[index].scale(
293                pass.meta().scaling.clone(),
294                pass.get_format(),
295                &viewport_size,
296                &target_size,
297                &original_size,
298                should_mipmap,
299                context,
300            )?;
301            callback(index, pass, &feedback[index])?;
302        }
303
304        target_size = next_size;
305    }
306
307    Ok(())
308}
309
310#[inline(always)]
311fn scale_output_framebuffers_callback<T, F, E, C, P, CB>(
312    source_size: Size<u32>,
313    viewport_size: Size<u32>,
314    original_size: Size<u32>,
315    output: &mut FramebufferPool<F>,
316    passes: &mut [P],
317    context: &C,
318    mut callback: CB,
319) -> Result<(), E>
320where
321    F: ScaleFramebuffer<T, Context = C, Error = E>,
322    P: FilterPassMeta,
323    CB: FnMut(usize, &mut P, &F, Size<u32>) -> Result<(), E>,
324{
325    let len = passes.len();
326
327    // Compute every pass's output size up front so the pool can be colored by liveness
328    // before any buffer is touched.
329    let mut sizes = Vec::with_capacity(len);
330    let mut target_size = source_size;
331    for pass in passes.iter() {
332        target_size = target_size.scale_viewport(
333            pass.meta().scaling.clone(),
334            viewport_size,
335            original_size,
336            None,
337        );
338        sizes.push(target_size);
339    }
340
341    output.prepare(&sizes);
342
343    for index in 0..len {
344        let scaling = passes[index].meta().scaling.clone();
345        let format = passes[index].get_format();
346        let should_mipmap = passes
347            .get(index + 1)
348            .map_or(false, |p| p.meta().mipmap_input);
349        let prev = if index == 0 {
350            source_size
351        } else {
352            sizes[index - 1]
353        };
354
355        let size = output[index].scale(
356            scaling,
357            format,
358            &viewport_size,
359            &prev,
360            &original_size,
361            should_mipmap,
362            context,
363        )?;
364
365        callback(index, &mut passes[index], &output[index], size)?;
366    }
367
368    Ok(())
369}