Skip to main content

ferroui_vello/
framebuffer_render_target.rs

1use crate::drawing_context_impl::{CreateInfo, DrawingContextImpl, VelloBackdrop};
2use crate::helpers::pixel_format_helper::{from_premul_rgba, scene_size, to_image, to_premul_rgba};
3use crate::scene::{create_scene_sink, VelloSceneBrush, VelloSceneImage};
4use crate::vello_options::VelloRenderingMode;
5use ferroui_base::platform::surfaces::{IFramebufferPlatformSurface, IFramebufferRenderTarget};
6use ferroui_base::platform::{
7    IDrawingContextImpl, IRenderTarget, PlatformRenderTargetState, RenderTargetDrawingContextProperties,
8    RenderTargetProperties, RenderTargetSceneInfo,
9};
10use std::cell::RefCell;
11use std::rc::Rc;
12
13/// Vello render target that renders to a framebuffer surface. No GPU
14/// acceleration available: a scene is rendered into memory and written to
15/// the framebuffer in its format.
16///
17/// The Skia backend draws into the framebuffer itself when Skia has its
18/// format and through a conversion shim otherwise; the renderers of the
19/// Vello project draw one format (premultiplied RGBA without padding), so
20/// every frame goes the way of the shim. Drawing a framebuffer of that very
21/// format in place is an optimization left open (design document, risks).
22pub struct FramebufferRenderTarget {
23    use_scaled_drawing: bool,
24    rendering_modes: Vec<VelloRenderingMode>,
25    render_target: RefCell<Option<Rc<dyn IFramebufferRenderTarget>>>,
26    /// Whether the framebuffer is the surface of a platform: its frames are
27    /// the frames [`crate::perf`] times. A bitmap that is drawn into is not.
28    presents_frames: bool,
29}
30
31impl FramebufferRenderTarget {
32    /// Creates a framebuffer render target for a framebuffer surface.
33    pub fn new(
34        platform_surface: &dyn IFramebufferPlatformSurface,
35        use_scaled_drawing: bool,
36        rendering_modes: Vec<VelloRenderingMode>,
37    ) -> Self {
38        Self {
39            presents_frames: true,
40            ..Self::from_render_target(
41                platform_surface.create_framebuffer_render_target(),
42                use_scaled_drawing,
43                rendering_modes,
44            )
45        }
46    }
47
48    /// Creates a framebuffer render target over the render target of a
49    /// framebuffer surface.
50    pub fn from_render_target(
51        render_target: Rc<dyn IFramebufferRenderTarget>,
52        use_scaled_drawing: bool,
53        rendering_modes: Vec<VelloRenderingMode>,
54    ) -> Self {
55        Self { use_scaled_drawing, rendering_modes, render_target: RefCell::new(Some(render_target)), presents_frames: false }
56    }
57}
58
59impl IRenderTarget for FramebufferRenderTarget {
60    fn properties(&self) -> RenderTargetProperties {
61        RenderTargetProperties {
62            retains_previous_frame_contents: self
63                .render_target
64                .borrow()
65                .as_ref()
66                .is_some_and(|target| target.retains_frame_contents()),
67            is_suitable_for_direct_rendering: true,
68        }
69    }
70
71    fn create_drawing_context(
72        &self,
73        scene_info: &RenderTargetSceneInfo,
74    ) -> (Box<dyn IDrawingContextImpl>, RenderTargetDrawingContextProperties) {
75        let render_target =
76            self.render_target.borrow().clone().unwrap_or_else(|| panic!("FramebufferRenderTarget has been disposed"));
77
78        let frame_start = if self.presents_frames { crate::perf::frame_start() } else { None };
79
80        let (framebuffer, lock_properties) = render_target.lock(scene_info);
81        let size = framebuffer.size();
82        if size.width <= 0 || size.height <= 0 {
83            panic!("Unable to create a surface with size {}x{}", size.width, size.height);
84        }
85
86        let (format, alpha_format) = (framebuffer.format(), framebuffer.alpha_format());
87        let row_bytes = framebuffer.row_bytes().max(0) as usize;
88        let (width, height) = scene_size(size);
89
90        // What the framebuffer holds is what the scene is drawn over,
91        // whether it is the frame before or not: the Skia backend draws
92        // into the framebuffer itself, and a render target bitmap is drawn
93        // into again and again. A framebuffer that holds nothing (every
94        // byte is zero) needs no backdrop.
95        //
96        // A framebuffer of the very format the renderers draw (premultiplied
97        // RGBA without padding) is drawn into where it is by the CPU sink,
98        // which composes the scene over it: nothing is converted or copied.
99        // Any other is given to the scene as an image, converted.
100        let sink = create_scene_sink(&self.rendering_modes, width, height);
101        let in_place = sink.rendering_mode() == VelloRenderingMode::Cpu
102            && format == ferroui_base::platform::PixelFormats::RGBA8888
103            && alpha_format == ferroui_base::platform::AlphaFormat::Premul
104            && row_bytes == width as usize * 4;
105
106        let mut has_content = false;
107        framebuffer.with_data(&mut |data| has_content = data.iter().any(|byte| *byte != 0));
108        let backdrop = has_content.then(|| {
109            let framebuffer = framebuffer.clone();
110            VelloBackdrop {
111                composes_over_target: in_place,
112                brush: Box::new(move || {
113                    let mut image = None;
114                    framebuffer.with_data(&mut |data| {
115                        let _perf = crate::perf::scope(crate::perf::Phase::PixelCopy, data.len() as u64);
116                        image = Some(to_image(to_premul_rgba(data, row_bytes, size, format, alpha_format), size));
117                    });
118                    image.map(|image| {
119                        VelloSceneBrush::Image(VelloSceneImage {
120                            image,
121                            x_extend: peniko::Extend::Pad,
122                            y_extend: peniko::Extend::Pad,
123                            quality: peniko::ImageQuality::Low,
124                            alpha: 1.0,
125                        })
126                    })
127                }),
128            }
129        });
130
131        let properties =
132            RenderTargetDrawingContextProperties { previous_frame_is_retained: lock_properties.previous_frame_is_retained };
133
134        let presented = framebuffer.clone();
135        let create_info = CreateInfo {
136            sink,
137            backdrop,
138            on_finished: Box::new(move |sink| {
139                if in_place {
140                    let length = width as usize * height as usize * 4;
141                    presented.with_data(&mut |data| sink.render_to_pixels(&mut data[..length]));
142                    return;
143                }
144                let mut rgba = vec![0u8; width as usize * height as usize * 4];
145                sink.render_to_pixels(&mut rgba);
146                let _perf = crate::perf::scope(crate::perf::Phase::PixelCopy, rgba.len() as u64);
147                presented.with_data(&mut |data| from_premul_rgba(&rgba, data, row_bytes, size, format, alpha_format));
148            }),
149            scale_drawing_to_dpi: self.use_scaled_drawing,
150            dpi: framebuffer.dpi(),
151            rendering_modes: self.rendering_modes.clone(),
152        };
153
154        let disposables: Vec<Box<dyn FnOnce()>> = vec![Box::new(move || {
155            {
156                let _perf = crate::perf::scope(crate::perf::Phase::Present, 0);
157                framebuffer.dispose();
158            }
159            crate::perf::frame_end(frame_start, "framebuffer", u32::from(width), u32::from(height));
160        })];
161
162        (Box::new(DrawingContextImpl::new(create_info, disposables)), properties)
163    }
164
165    fn platform_render_target_state(&self) -> PlatformRenderTargetState {
166        match &*self.render_target.borrow() {
167            Some(render_target) => render_target.state(),
168            None => PlatformRenderTargetState::DISPOSED,
169        }
170    }
171
172    fn dispose(&self) {
173        if let Some(render_target) = self.render_target.borrow_mut().take() {
174            render_target.dispose();
175        }
176    }
177}