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