floem_vello_renderer/
lib.rs

1use std::collections::HashMap;
2use std::mem;
3use std::sync::mpsc::sync_channel;
4use std::sync::Arc;
5
6use anyhow::Result;
7use floem_renderer::gpu_resources::GpuResources;
8use floem_renderer::text::fontdb::ID;
9use floem_renderer::text::{LayoutGlyph, LayoutRun, FONT_SYSTEM};
10use floem_renderer::{Img, Renderer};
11use peniko::kurbo::Size;
12use peniko::{
13    color::palette,
14    kurbo::{Affine, Point, Rect, Shape},
15    Blob, BrushRef, Color,
16};
17use peniko::{Compose, Fill, Mix};
18use vello::kurbo::Stroke;
19use vello::{AaConfig, RendererOptions, Scene};
20use wgpu::{
21    Device, DeviceType, Queue, Surface, SurfaceConfiguration, TextureAspect, TextureFormat,
22};
23
24pub struct VelloRenderer {
25    device: Arc<Device>,
26    #[allow(unused)]
27    queue: Arc<Queue>,
28    surface: Surface<'static>,
29    renderer: vello::Renderer,
30    scene: Scene,
31    alt_scene: Option<Scene>,
32    config: SurfaceConfiguration,
33    window_scale: f64,
34    transform: Affine,
35    capture: bool,
36    font_cache: HashMap<ID, vello::peniko::Font>,
37}
38
39impl VelloRenderer {
40    pub fn new(
41        gpu_resources: GpuResources,
42        width: u32,
43        height: u32,
44        scale: f64,
45        _font_embolden: f32,
46    ) -> Result<Self> {
47        let GpuResources {
48            surface,
49            adapter,
50            device,
51            queue,
52        } = gpu_resources;
53
54        if adapter.get_info().device_type == DeviceType::Cpu {
55            return Err(anyhow::anyhow!("only cpu adapter found"));
56        }
57
58        let mut required_downlevel_flags = wgpu::DownlevelFlags::empty();
59        required_downlevel_flags.set(wgpu::DownlevelFlags::VERTEX_STORAGE, true);
60
61        if !adapter
62            .get_downlevel_capabilities()
63            .flags
64            .contains(required_downlevel_flags)
65        {
66            return Err(anyhow::anyhow!(
67                "adapter doesn't support required downlevel flags"
68            ));
69        }
70
71        let device = Arc::new(device);
72        let queue = Arc::new(queue);
73
74        let surface_caps = surface.get_capabilities(&adapter);
75        let texture_format = surface_caps
76            .formats
77            .into_iter()
78            .find(|it| matches!(it, TextureFormat::Rgba8Unorm | TextureFormat::Bgra8Unorm))
79            .ok_or_else(|| anyhow::anyhow!("surface should support Rgba8Unorm or Bgra8Unorm"))?;
80
81        let config = wgpu::SurfaceConfiguration {
82            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
83            format: texture_format,
84            width,
85            height,
86            present_mode: wgpu::PresentMode::Fifo,
87            alpha_mode: wgpu::CompositeAlphaMode::Auto,
88            view_formats: vec![],
89            desired_maximum_frame_latency: 2,
90        };
91        surface.configure(&device, &config);
92
93        let scene = Scene::new();
94        let renderer = vello::Renderer::new(
95            &device,
96            RendererOptions {
97                surface_format: Some(texture_format),
98                use_cpu: false,
99                antialiasing_support: vello::AaSupport::all(),
100                num_init_threads: None,
101            },
102        )
103        .unwrap();
104
105        Ok(Self {
106            device,
107            queue,
108            surface,
109            renderer,
110            scene,
111            alt_scene: None,
112            window_scale: scale,
113            config,
114            transform: Affine::IDENTITY,
115            capture: false,
116            font_cache: HashMap::new(),
117        })
118    }
119
120    pub fn resize(&mut self, width: u32, height: u32, scale: f64) {
121        if width != self.config.width || height != self.config.height {
122            self.config.width = width;
123            self.config.height = height;
124            self.surface.configure(&self.device, &self.config);
125        }
126        self.window_scale = scale;
127    }
128
129    pub fn set_scale(&mut self, scale: f64) {
130        self.window_scale = scale;
131    }
132
133    pub const fn scale(&self) -> f64 {
134        self.window_scale
135    }
136
137    pub const fn size(&self) -> Size {
138        Size::new(self.config.width as f64, self.config.height as f64)
139    }
140}
141
142impl Renderer for VelloRenderer {
143    fn begin(&mut self, capture: bool) {
144        if self.capture == capture {
145            self.scene.reset();
146        } else {
147            self.capture = capture;
148            if self.alt_scene.is_none() {
149                self.alt_scene = Some(Scene::new());
150            }
151            if let Some(scene) = self.alt_scene.as_mut() {
152                scene.reset();
153            }
154            self.scene.reset();
155            mem::swap(&mut self.scene, self.alt_scene.as_mut().unwrap());
156        }
157        self.transform = Affine::IDENTITY;
158    }
159
160    fn stroke<'b, 's>(
161        &mut self,
162        shape: &impl Shape,
163        brush: impl Into<BrushRef<'b>>,
164        stroke: &'s Stroke,
165    ) {
166        if stroke.width * self.window_scale < 2. {
167            let brush: BrushRef = brush.into();
168            match &brush {
169                BrushRef::Solid(color) => {
170                    let mut stroke = stroke.clone();
171                    // this special handling is done to make thin strokes look better
172                    stroke.width *= 1.5;
173                    let color = color.multiply_alpha(0.5);
174                    self.scene.stroke(
175                        &stroke,
176                        self.transform.then_scale(self.window_scale),
177                        BrushRef::Solid(color),
178                        None,
179                        shape,
180                    );
181                }
182
183                _ => {
184                    self.scene.stroke(
185                        stroke,
186                        self.transform.then_scale(self.window_scale),
187                        brush,
188                        None,
189                        shape,
190                    );
191                }
192            }
193        } else {
194            self.scene.stroke(
195                stroke,
196                self.transform.then_scale(self.window_scale),
197                brush,
198                None,
199                shape,
200            );
201        }
202    }
203
204    fn fill<'b>(&mut self, path: &impl Shape, brush: impl Into<BrushRef<'b>>, blur_radius: f64) {
205        let brush: BrushRef<'b> = brush.into();
206        if let Some(rounded) = path.as_rounded_rect() {
207            let BrushRef::Solid(color) = brush else {
208                return;
209            };
210            let rect_radius = rounded.radii().top_left;
211            let rect = rounded.rect();
212            self.scene.draw_blurred_rounded_rect(
213                self.transform.then_scale(self.window_scale),
214                rect,
215                color,
216                rect_radius,
217                blur_radius,
218            );
219        } else if let Some(rect) = path.as_rect() {
220            let BrushRef::Solid(color) = brush else {
221                return;
222            };
223            self.scene.draw_blurred_rounded_rect(
224                self.transform.then_scale(self.window_scale),
225                rect,
226                color,
227                0.,
228                blur_radius,
229            );
230        } else {
231            self.scene.fill(
232                vello::peniko::Fill::NonZero,
233                self.transform.then_scale(self.window_scale),
234                brush,
235                None,
236                path,
237            );
238        }
239    }
240
241    fn draw_text_with_layout<'b>(
242        &mut self,
243        layout: impl Iterator<Item = LayoutRun<'b>>,
244        pos: impl Into<Point>,
245    ) {
246        let pos: Point = pos.into();
247        let transform = self
248            .transform
249            .pre_translate((pos.x, pos.y).into())
250            .then_scale(self.window_scale);
251
252        for line in layout {
253            let mut current_run: Option<GlyphRun> = None;
254
255            for glyph in line.glyphs {
256                let color = glyph.color_opt.map_or(palette::css::BLACK, |c| {
257                    Color::from_rgba8(c.r(), c.g(), c.b(), c.a())
258                });
259                let font_size = glyph.font_size;
260                let font_id = glyph.font_id;
261                let metadata = glyph.metadata;
262
263                if current_run.as_ref().map_or(true, |run| {
264                    run.color != color
265                        || run.font_size != font_size
266                        || run.font_id != font_id
267                        || run.metadata != metadata
268                }) {
269                    if let Some(run) = current_run.take() {
270                        self.draw_glyph_run(
271                            run,
272                            transform.pre_translate((0., line.line_y.into()).into()),
273                        );
274                    }
275                    current_run = Some(GlyphRun {
276                        color,
277                        font_size,
278                        font_id,
279                        metadata,
280                        glyphs: Vec::new(),
281                    });
282                }
283
284                if let Some(run) = &mut current_run {
285                    run.glyphs.push(glyph);
286                }
287            }
288
289            if let Some(run) = current_run.take() {
290                self.draw_glyph_run(
291                    run,
292                    transform.pre_translate((0., line.line_y.into()).into()),
293                );
294            }
295        }
296    }
297
298    fn draw_img(&mut self, img: Img<'_>, rect: Rect) {
299        let rect_width = rect.width().max(1.);
300        let rect_height = rect.height().max(1.);
301
302        let scale_x = rect_width / img.img.width as f64;
303        let scale_y = rect_height / img.img.height as f64;
304
305        let translate_x = rect.min_x();
306        let translate_y = rect.min_y();
307
308        self.scene.draw_image(
309            &img.img,
310            self.transform
311                .pre_scale_non_uniform(scale_x, scale_y)
312                .then_translate((translate_x, translate_y).into())
313                .then_scale(self.window_scale),
314        );
315    }
316
317    fn draw_svg<'b>(
318        &mut self,
319        svg: floem_renderer::Svg<'b>,
320        rect: Rect,
321        brush: Option<impl Into<BrushRef<'b>>>,
322    ) {
323        let rect_width = rect.width().max(1.);
324        let rect_height = rect.height().max(1.);
325
326        let svg_size = svg.tree.size();
327
328        let scale_x = rect_width / f64::from(svg_size.width());
329        let scale_y = rect_height / f64::from(svg_size.height());
330
331        let translate_x = rect.min_x();
332        let translate_y = rect.min_y();
333
334        let new = brush.map_or_else(
335            || vello_svg::render_tree(svg.tree),
336            |brush| {
337                let brush = brush.into();
338                alpha_mask_scene(
339                    rect.size(),
340                    |scene| {
341                        scene.append(&vello_svg::render_tree(svg.tree), None);
342                    },
343                    move |scene| {
344                        scene.fill(Fill::NonZero, Affine::IDENTITY, brush, None, &rect);
345                    },
346                )
347            },
348        );
349
350        // Apply transformations to fit the SVG within the provided rectangle
351        self.scene.append(
352            &new,
353            Some(
354                self.transform
355                    .pre_scale_non_uniform(scale_x, scale_y)
356                    .pre_translate((translate_x, translate_y).into())
357                    .then_scale(self.window_scale),
358            ),
359        );
360    }
361
362    fn transform(&mut self, transform: Affine) {
363        self.transform = transform;
364    }
365
366    fn set_z_index(&mut self, _z_index: i32) {}
367
368    fn clip(&mut self, shape: &impl Shape) {
369        if shape.bounding_box().is_zero_area() {
370            return;
371        }
372        self.scene.pop_layer();
373        self.scene.push_layer(
374            vello::peniko::BlendMode::default(),
375            1.,
376            // Affine::IDENTITY,
377            self.transform.then_scale(self.window_scale),
378            shape,
379        );
380    }
381
382    fn clear_clip(&mut self) {
383        self.scene.pop_layer();
384    }
385
386    fn finish(&mut self) -> Option<vello::peniko::Image> {
387        if self.capture {
388            self.render_capture_image()
389        } else {
390            if let Ok(frame) = self.surface.get_current_texture() {
391                // Render the scene using Vello's `render_to_surface` function
392                self.renderer
393                    .render_to_surface(
394                        &self.device,
395                        &self.queue,
396                        &self.scene,
397                        &frame,
398                        &vello::RenderParams {
399                            base_color: palette::css::BLACK, // Background color
400                            width: self.config.width,
401                            height: self.config.height,
402                            antialiasing_method: vello::AaConfig::Area,
403                        },
404                    )
405                    .unwrap();
406
407                frame.present();
408            }
409            None
410        }
411    }
412}
413
414impl VelloRenderer {
415    fn render_capture_image(&mut self) -> Option<peniko::Image> {
416        let width_align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - 1;
417        let width = (self.config.width + width_align) & !width_align;
418        let height = self.config.height;
419        let texture_desc = wgpu::TextureDescriptor {
420            size: wgpu::Extent3d {
421                width: self.config.width,
422                height,
423                depth_or_array_layers: 1,
424            },
425            mip_level_count: 1,
426            sample_count: 1,
427            dimension: wgpu::TextureDimension::D2,
428            format: wgpu::TextureFormat::Rgba8Unorm,
429            usage: wgpu::TextureUsages::RENDER_ATTACHMENT
430                | wgpu::TextureUsages::COPY_SRC
431                | wgpu::TextureUsages::STORAGE_BINDING,
432            label: Some("render_texture"),
433            view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
434        };
435        let texture = self.device.create_texture(&texture_desc);
436        let view = texture.create_view(&wgpu::TextureViewDescriptor {
437            label: Some("Floem Inspector Preview"),
438            format: Some(TextureFormat::Rgba8Unorm),
439            dimension: Some(wgpu::TextureViewDimension::D2),
440            aspect: TextureAspect::default(),
441            base_mip_level: 0,
442            mip_level_count: None,
443            base_array_layer: 0,
444            array_layer_count: None,
445        });
446
447        self.renderer
448            .render_to_texture(
449                &self.device,
450                &self.queue,
451                &self.scene,
452                &view,
453                &vello::RenderParams {
454                    base_color: palette::css::BLACK, // Background color
455                    width: self.config.width * self.window_scale as u32,
456                    height: self.config.height * self.window_scale as u32,
457                    antialiasing_method: AaConfig::Area,
458                },
459            )
460            .unwrap();
461
462        let bytes_per_pixel = 4;
463        let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
464            label: None,
465            size: (u64::from(width * height) * bytes_per_pixel),
466            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
467            mapped_at_creation: false,
468        });
469        let bytes_per_row = width * bytes_per_pixel as u32;
470        assert!(bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0);
471
472        let mut encoder = self
473            .device
474            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
475        encoder.copy_texture_to_buffer(
476            texture.as_image_copy(),
477            wgpu::ImageCopyBuffer {
478                buffer: &buffer,
479                layout: wgpu::ImageDataLayout {
480                    offset: 0,
481                    bytes_per_row: Some(bytes_per_row),
482                    rows_per_image: None,
483                },
484            },
485            texture_desc.size,
486        );
487        let command_buffer = encoder.finish();
488        self.queue.submit(Some(command_buffer));
489        self.device.poll(wgpu::Maintain::Wait);
490
491        let slice = buffer.slice(..);
492        let (tx, rx) = sync_channel(1);
493        slice.map_async(wgpu::MapMode::Read, move |r| tx.send(r).unwrap());
494
495        loop {
496            if let Ok(r) = rx.try_recv() {
497                break r.ok()?;
498            }
499            if matches!(
500                self.device.poll(wgpu::MaintainBase::Wait),
501                wgpu::MaintainResult::Ok
502            ) {
503                rx.recv().ok()?.ok()?;
504                break;
505            }
506        }
507
508        let mut cropped_buffer = Vec::new();
509        let buffer: Vec<u8> = slice.get_mapped_range().to_owned();
510
511        let mut cursor = 0;
512        let row_size = self.config.width as usize * bytes_per_pixel as usize;
513        for _ in 0..height {
514            cropped_buffer.extend_from_slice(&buffer[cursor..(cursor + row_size)]);
515            cursor += bytes_per_row as usize;
516        }
517
518        Some(vello::peniko::Image::new(
519            Blob::new(Arc::new(cropped_buffer)),
520            vello::peniko::ImageFormat::Rgba8,
521            self.config.width,
522            height,
523        ))
524    }
525}
526
527fn common_alpha_mask_scene(
528    size: Size,
529    alpha_mask: impl FnOnce(&mut Scene),
530    item: impl FnOnce(&mut Scene),
531    compose_mode: Compose,
532) -> Scene {
533    let mut scene = Scene::new();
534    scene.push_layer(
535        Mix::Normal,
536        1.0,
537        Affine::IDENTITY,
538        &Rect::from_origin_size((0., 0.), size),
539    );
540
541    alpha_mask(&mut scene);
542
543    scene.push_layer(
544        vello::peniko::BlendMode {
545            mix: Mix::Clip,
546            compose: compose_mode,
547        },
548        1.,
549        Affine::IDENTITY,
550        &Rect::from_origin_size((0., 0.), size),
551    );
552
553    item(&mut scene);
554
555    scene.pop_layer();
556    scene.pop_layer();
557    scene
558}
559
560fn alpha_mask_scene(
561    size: Size,
562    alpha_mask: impl FnOnce(&mut Scene),
563    item: impl FnOnce(&mut Scene),
564) -> Scene {
565    common_alpha_mask_scene(size, alpha_mask, item, Compose::SrcIn)
566}
567#[allow(unused)]
568fn invert_alpha_mask_scene(
569    size: Size,
570    alpha_mask: impl FnOnce(&mut Scene),
571    item: impl FnOnce(&mut Scene),
572) -> Scene {
573    common_alpha_mask_scene(size, alpha_mask, item, Compose::SrcOut)
574}
575
576struct GlyphRun<'a> {
577    color: Color,
578    font_size: f32,
579    font_id: ID,
580    metadata: usize,
581    glyphs: Vec<&'a LayoutGlyph>,
582}
583
584impl VelloRenderer {
585    fn get_font(&mut self, font_id: ID) -> vello::peniko::Font {
586        self.font_cache.get(&font_id).cloned().unwrap_or_else(|| {
587            let mut font_system = FONT_SYSTEM.lock();
588            let font = font_system.get_font(font_id).unwrap();
589            let face = font_system.db().face(font_id).unwrap();
590            let font_data = font.data();
591            let font_index = face.index;
592            drop(font_system);
593            let font =
594                vello::peniko::Font::new(Blob::new(Arc::new(font_data.to_vec())), font_index);
595            self.font_cache.insert(font_id, font.clone());
596            font
597        })
598    }
599
600    fn draw_glyph_run(&mut self, run: GlyphRun, transform: Affine) {
601        let font = self.get_font(run.font_id);
602        self.scene
603            .draw_glyphs(&font)
604            .font_size(run.font_size)
605            .brush(run.color)
606            .hint(false)
607            .transform(transform)
608            .draw(
609                Fill::NonZero,
610                run.glyphs.into_iter().map(|glyph| vello::Glyph {
611                    id: glyph.glyph_id.into(),
612                    x: glyph.x,
613                    y: glyph.y,
614                }),
615            );
616    }
617}