avenger_wgpu/
canvas.rs

1use image::imageops::crop_imm;
2use std::sync::Arc;
3use wgpu::{
4    Adapter, Buffer, BufferAddress, BufferDescriptor, BufferUsages, CommandBuffer,
5    CommandEncoderDescriptor, Device, DeviceDescriptor, Extent3d, ImageCopyBuffer,
6    ImageCopyTexture, ImageDataLayout, LoadOp, MapMode, Operations, Origin3d, PowerPreference,
7    Queue, RenderPassColorAttachment, RenderPassDescriptor, RequestAdapterOptions, StoreOp,
8    Surface, SurfaceConfiguration, Texture, TextureAspect, TextureDescriptor, TextureDimension,
9    TextureFormat, TextureFormatFeatureFlags, TextureUsages, TextureView, TextureViewDescriptor,
10};
11use winit::dpi::Size;
12use winit::event::WindowEvent;
13use winit::window::Window;
14
15use crate::error::AvengerWgpuError;
16use crate::marks::instanced_mark::InstancedMarkRenderer;
17use crate::marks::multi::MultiMarkRenderer;
18use crate::marks::symbol::SymbolShader;
19use crate::marks::text::TextAtlasBuilderTrait;
20use avenger::marks::arc::ArcMark;
21use avenger::marks::area::AreaMark;
22use avenger::marks::group::Clip;
23use avenger::marks::image::ImageMark;
24use avenger::marks::line::LineMark;
25use avenger::marks::path::PathMark;
26use avenger::marks::trail::TrailMark;
27use avenger::{
28    marks::group::SceneGroup, marks::mark::SceneMark, marks::rect::RectMark, marks::rule::RuleMark,
29    marks::symbol::SymbolMark, marks::text::TextMark, scene_graph::SceneGraph,
30};
31
32pub enum MarkRenderer {
33    Instanced(InstancedMarkRenderer),
34    Multi(Box<MultiMarkRenderer>),
35}
36
37#[derive(Debug, Copy, Clone)]
38pub struct CanvasDimensions {
39    pub size: [f32; 2],
40    pub scale: f32,
41}
42
43impl CanvasDimensions {
44    pub fn to_physical_width(&self) -> u32 {
45        (self.size[0] * self.scale) as u32
46    }
47
48    pub fn to_physical_height(&self) -> u32 {
49        (self.size[1] * self.scale) as u32
50    }
51
52    pub fn to_physical_size(&self) -> winit::dpi::PhysicalSize<u32> {
53        winit::dpi::PhysicalSize {
54            width: self.to_physical_width(),
55            height: self.to_physical_height(),
56        }
57    }
58}
59
60pub type TextBuildCtor = Arc<fn() -> Box<dyn TextAtlasBuilderTrait>>;
61
62#[derive(Default)]
63pub struct CanvasConfig {
64    pub text_builder_ctor: Option<TextBuildCtor>,
65}
66
67pub trait Canvas {
68    fn add_mark_renderer(&mut self, mark_renderer: MarkRenderer);
69    fn clear_mark_renderer(&mut self);
70    fn device(&self) -> &Device;
71    fn queue(&self) -> &Queue;
72    fn dimensions(&self) -> CanvasDimensions;
73
74    fn texture_format(&self) -> TextureFormat;
75
76    fn sample_count(&self) -> u32;
77
78    fn get_multi_renderer(&mut self) -> &mut MultiMarkRenderer;
79
80    fn add_arc_mark(
81        &mut self,
82        mark: &ArcMark,
83        origin: [f32; 2],
84        group_clip: &Clip,
85    ) -> Result<(), AvengerWgpuError> {
86        self.get_multi_renderer()
87            .add_arc_mark(mark, origin, group_clip)?;
88        Ok(())
89    }
90
91    fn add_path_mark(
92        &mut self,
93        mark: &PathMark,
94        origin: [f32; 2],
95        group_clip: &Clip,
96    ) -> Result<(), AvengerWgpuError> {
97        self.get_multi_renderer()
98            .add_path_mark(mark, origin, group_clip)?;
99        Ok(())
100    }
101
102    fn add_line_mark(
103        &mut self,
104        mark: &LineMark,
105        origin: [f32; 2],
106        group_clip: &Clip,
107    ) -> Result<(), AvengerWgpuError> {
108        self.get_multi_renderer()
109            .add_line_mark(mark, origin, group_clip)?;
110        Ok(())
111    }
112
113    fn add_trail_mark(
114        &mut self,
115        mark: &TrailMark,
116        origin: [f32; 2],
117        group_clip: &Clip,
118    ) -> Result<(), AvengerWgpuError> {
119        self.get_multi_renderer()
120            .add_trail_mark(mark, origin, group_clip)?;
121        Ok(())
122    }
123
124    fn add_area_mark(
125        &mut self,
126        mark: &AreaMark,
127        origin: [f32; 2],
128        group_clip: &Clip,
129    ) -> Result<(), AvengerWgpuError> {
130        self.get_multi_renderer()
131            .add_area_mark(mark, origin, group_clip)?;
132        Ok(())
133    }
134
135    fn add_symbol_mark(
136        &mut self,
137        mark: &SymbolMark,
138        origin: [f32; 2],
139        group_clip: &Clip,
140    ) -> Result<(), AvengerWgpuError> {
141        if mark.len >= 10000
142            && mark.gradients.is_empty()
143            && matches!(group_clip, Clip::None | Clip::Rect { .. })
144        {
145            self.add_mark_renderer(MarkRenderer::Instanced(InstancedMarkRenderer::new(
146                self.device(),
147                self.texture_format(),
148                self.sample_count(),
149                Box::new(SymbolShader::from_symbol_mark(
150                    mark,
151                    self.dimensions(),
152                    origin,
153                )?),
154                group_clip.maybe_clip(mark.clip),
155                self.dimensions().scale,
156            )));
157        } else {
158            self.get_multi_renderer()
159                .add_symbol_mark(mark, origin, group_clip)?;
160        }
161
162        Ok(())
163    }
164
165    fn add_rect_mark(
166        &mut self,
167        mark: &RectMark,
168        origin: [f32; 2],
169        group_clip: &Clip,
170    ) -> Result<(), AvengerWgpuError> {
171        self.get_multi_renderer()
172            .add_rect_mark(mark, origin, group_clip)?;
173        Ok(())
174    }
175
176    fn add_rule_mark(
177        &mut self,
178        mark: &RuleMark,
179        origin: [f32; 2],
180        group_clip: &Clip,
181    ) -> Result<(), AvengerWgpuError> {
182        self.get_multi_renderer()
183            .add_rule_mark(mark, origin, group_clip)?;
184        Ok(())
185    }
186
187    fn add_text_mark(
188        &mut self,
189        mark: &TextMark,
190        origin: [f32; 2],
191        group_clip: &Clip,
192    ) -> Result<(), AvengerWgpuError> {
193        self.get_multi_renderer()
194            .add_text_mark(mark, origin, group_clip)?;
195        Ok(())
196    }
197
198    fn add_image_mark(
199        &mut self,
200        mark: &ImageMark,
201        origin: [f32; 2],
202        group_clip: &Clip,
203    ) -> Result<(), AvengerWgpuError> {
204        self.get_multi_renderer()
205            .add_image_mark(mark, origin, group_clip)?;
206        Ok(())
207    }
208
209    fn add_group_mark(
210        &mut self,
211        group: &SceneGroup,
212        parent_origin: [f32; 2],
213        parent_clip: &Clip,
214    ) -> Result<(), AvengerWgpuError> {
215        // Maybe add rect around group boundary
216        if let Some(rect) = group.make_path_mark() {
217            self.add_path_mark(&rect, parent_origin, &group.clip)?;
218        }
219
220        // Add groups in order of zindex
221        let zindex = group.marks.iter().map(|m| m.zindex()).collect::<Vec<_>>();
222        let mut indices: Vec<usize> = (0..zindex.len()).collect();
223        indices.sort_by_key(|i| zindex[*i].unwrap_or(0));
224
225        // Compute new origin
226        let origin = [
227            parent_origin[0] + group.origin[0],
228            parent_origin[1] + group.origin[1],
229        ];
230
231        // Compute new clip
232        let clip = if let Clip::None = group.clip {
233            // No clip defined for this group, propagate parent clip down
234            parent_clip.clone()
235        } else {
236            // Translate clip to absolute coordinates
237            group.clip.translate(origin[0], origin[1])
238        };
239
240        for mark_ind in indices {
241            let mark = &group.marks[mark_ind];
242            match mark {
243                SceneMark::Arc(mark) => {
244                    self.add_arc_mark(mark, origin, &clip)?;
245                }
246                SceneMark::Symbol(mark) => {
247                    self.add_symbol_mark(mark, origin, &clip)?;
248                }
249                SceneMark::Rect(mark) => {
250                    self.add_rect_mark(mark, origin, &clip)?;
251                }
252                SceneMark::Rule(mark) => {
253                    self.add_rule_mark(mark, origin, &clip)?;
254                }
255                SceneMark::Path(mark) => {
256                    self.add_path_mark(mark, origin, &clip)?;
257                }
258                SceneMark::Line(mark) => {
259                    self.add_line_mark(mark, origin, &clip)?;
260                }
261                SceneMark::Trail(mark) => {
262                    self.add_trail_mark(mark, origin, &clip)?;
263                }
264                SceneMark::Area(mark) => {
265                    self.add_area_mark(mark, origin, &clip)?;
266                }
267                SceneMark::Text(mark) => {
268                    self.add_text_mark(mark, origin, &clip)?;
269                }
270                SceneMark::Image(mark) => {
271                    self.add_image_mark(mark, origin, &clip)?;
272                }
273                SceneMark::Group(group) => {
274                    self.add_group_mark(group, origin, &clip)?;
275                }
276            }
277        }
278        Ok(())
279    }
280
281    #[tracing::instrument(skip_all)]
282    fn set_scene(&mut self, scene_graph: &SceneGraph) -> Result<(), AvengerWgpuError> {
283        // Clear existing marks
284        self.clear_mark_renderer();
285
286        // Sort groups by zindex
287        let zindex = scene_graph
288            .groups
289            .iter()
290            .map(|g| g.zindex)
291            .collect::<Vec<_>>();
292        let mut indices: Vec<usize> = (0..zindex.len()).collect();
293        indices.sort_by_key(|i| zindex[*i].unwrap_or(0));
294
295        for group_ind in &indices {
296            let group = &scene_graph.groups[*group_ind];
297            self.add_group_mark(group, scene_graph.origin, &Clip::None)?;
298        }
299
300        Ok(())
301    }
302}
303
304// Private shared canvas logic
305pub(crate) fn make_background_command<C: Canvas>(
306    canvas: &C,
307    texture_view: &TextureView,
308    resolve_target: Option<&TextureView>,
309) -> CommandBuffer {
310    let mut background_encoder =
311        canvas
312            .device()
313            .create_command_encoder(&CommandEncoderDescriptor {
314                label: Some("Render Background Encoder"),
315            });
316
317    {
318        let _render_pass = background_encoder.begin_render_pass(&RenderPassDescriptor {
319            label: Some("Render Pass"),
320            color_attachments: &[Some(RenderPassColorAttachment {
321                view: texture_view,
322                resolve_target,
323                ops: Operations {
324                    load: LoadOp::Clear(wgpu::Color {
325                        r: 1.0,
326                        g: 1.0,
327                        b: 1.0,
328                        a: 1.0,
329                    }),
330                    store: StoreOp::Store,
331                },
332            })],
333            depth_stencil_attachment: None,
334            occlusion_query_set: None,
335            timestamp_writes: None,
336        });
337    }
338    background_encoder.finish()
339}
340
341pub(crate) fn make_wgpu_instance() -> wgpu::Instance {
342    wgpu::Instance::new(wgpu::InstanceDescriptor {
343        backends: wgpu::Backends::all(),
344        ..Default::default()
345    })
346}
347
348pub(crate) async fn make_wgpu_adapter(
349    instance: &wgpu::Instance,
350    compatible_surface: Option<&Surface<'_>>,
351) -> Result<Adapter, AvengerWgpuError> {
352    instance
353        .request_adapter(&RequestAdapterOptions {
354            power_preference: PowerPreference::default(),
355            compatible_surface,
356            force_fallback_adapter: false,
357        })
358        .await
359        .ok_or(AvengerWgpuError::MakeWgpuAdapterError)
360}
361
362pub(crate) async fn request_wgpu_device(
363    adapter: &Adapter,
364) -> Result<(Device, Queue), AvengerWgpuError> {
365    Ok(adapter
366        .request_device(
367            &DeviceDescriptor {
368                label: None,
369                required_features: wgpu::Features::empty(),
370                // WebGL doesn't support all of wgpu's features, so if
371                // we're building for the web we'll have to disable some.
372                required_limits: if cfg!(target_arch = "wasm32") {
373                    wgpu::Limits::downlevel_webgl2_defaults()
374                } else {
375                    wgpu::Limits::default()
376                },
377            },
378            None,
379        )
380        .await?)
381}
382
383pub(crate) fn create_multisampled_framebuffer(
384    device: &Device,
385    width: u32,
386    height: u32,
387    format: TextureFormat,
388    sample_count: u32,
389) -> TextureView {
390    let multisampled_texture_extent = wgpu::Extent3d {
391        width,
392        height,
393        depth_or_array_layers: 1,
394    };
395    let multisampled_frame_descriptor = &TextureDescriptor {
396        size: multisampled_texture_extent,
397        mip_level_count: 1,
398        sample_count,
399        dimension: TextureDimension::D2,
400        format,
401        usage: TextureUsages::RENDER_ATTACHMENT,
402        label: None,
403        view_formats: &[],
404    };
405
406    device
407        .create_texture(multisampled_frame_descriptor)
408        .create_view(&TextureViewDescriptor::default())
409}
410
411pub(crate) fn get_supported_sample_count(sample_flags: TextureFormatFeatureFlags) -> u32 {
412    // Get max supported sample count up to 4
413    if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X4) {
414        4
415    } else if sample_flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X2) {
416        2
417    } else {
418        1
419    }
420}
421
422pub struct WindowCanvas<'window> {
423    sample_count: u32,
424    surface_config: SurfaceConfiguration,
425    dimensions: CanvasDimensions,
426    marks: Vec<MarkRenderer>,
427    multi_renderer: Option<MultiMarkRenderer>,
428    config: CanvasConfig,
429
430    // Order of properties determines drop order.
431    // Device must be dropped after the buffers and textures associated with marks
432    multisampled_framebuffer: TextureView,
433    queue: Queue,
434    device: Device,
435    surface: Surface<'window>,
436    window: Arc<Window>,
437}
438
439impl<'window> WindowCanvas<'window> {
440    pub async fn new(
441        window: Window,
442        dimensions: CanvasDimensions,
443        config: CanvasConfig,
444    ) -> Result<Self, AvengerWgpuError> {
445        let _ = window.request_inner_size(Size::Physical(dimensions.to_physical_size()));
446        let instance = make_wgpu_instance();
447        let window = Arc::new(window);
448        let surface = instance.create_surface(window.clone())?;
449        let adapter = make_wgpu_adapter(&instance, Some(&surface)).await?;
450        let (device, queue) = request_wgpu_device(&adapter).await?;
451
452        let surface_caps = surface.get_capabilities(&adapter);
453
454        // Select first non-srgb texture format
455        let surface_format = surface_caps
456            .formats
457            .iter()
458            .copied()
459            .find(|f| !f.is_srgb())
460            .unwrap_or(surface_caps.formats[0]);
461
462        let surface_config = SurfaceConfiguration {
463            usage: TextureUsages::RENDER_ATTACHMENT,
464            format: surface_format,
465            width: dimensions.to_physical_width(),
466            height: dimensions.to_physical_height(),
467            present_mode: surface_caps.present_modes[0],
468            alpha_mode: surface_caps.alpha_modes[0],
469            view_formats: vec![],
470            desired_maximum_frame_latency: 2,
471        };
472        surface.configure(&device, &surface_config);
473
474        let format_flags = adapter.get_texture_format_features(surface_format).flags;
475        let sample_count = get_supported_sample_count(format_flags);
476        let multisampled_framebuffer = create_multisampled_framebuffer(
477            &device,
478            surface_config.width,
479            surface_config.height,
480            surface_format,
481            sample_count,
482        );
483
484        Ok(Self {
485            surface,
486            device,
487            queue,
488            multisampled_framebuffer,
489            sample_count,
490            surface_config,
491            dimensions,
492            window,
493            marks: Vec::new(),
494            multi_renderer: None,
495            config,
496        })
497    }
498
499    pub fn get_size(&self) -> winit::dpi::PhysicalSize<u32> {
500        self.dimensions.to_physical_size()
501    }
502
503    pub fn window(&self) -> &Window {
504        &self.window
505    }
506
507    pub fn resize(&mut self, _new_size: winit::dpi::PhysicalSize<u32>) {
508        // if new_size.width > 0 && new_size.height > 0 {
509        //     self.size = new_size;
510        //     self.config.width = new_size.width;
511        //     self.config.height = new_size.height;
512        //     self.surface.configure(&self.device, &self.config);
513        // }
514    }
515
516    #[allow(unused_variables)]
517    pub fn input(&mut self, event: &WindowEvent) -> bool {
518        false
519    }
520
521    pub fn update(&mut self) {}
522
523    pub fn render(&mut self) -> Result<(), AvengerWgpuError> {
524        let output = self.surface.get_current_texture()?;
525        let view = output
526            .texture
527            .create_view(&TextureViewDescriptor::default());
528
529        // Commit open multi-renderer
530        if let Some(multi_renderer) = self.multi_renderer.take() {
531            self.marks
532                .push(MarkRenderer::Multi(Box::new(multi_renderer)));
533        }
534
535        let background_command = if self.sample_count > 1 {
536            make_background_command(self, &self.multisampled_framebuffer, Some(&view))
537        } else {
538            make_background_command(self, &view, None)
539        };
540        let mut commands = vec![background_command];
541        let texture_format = self.texture_format();
542        for mark in &mut self.marks {
543            let command = match mark {
544                MarkRenderer::Instanced(renderer) => {
545                    if self.sample_count > 1 {
546                        renderer.render(&self.device, &self.multisampled_framebuffer, Some(&view))
547                    } else {
548                        renderer.render(&self.device, &view, None)
549                    }
550                }
551                MarkRenderer::Multi(renderer) => {
552                    if self.sample_count > 1 {
553                        renderer.render(
554                            &self.device,
555                            &self.queue,
556                            texture_format,
557                            self.sample_count,
558                            &self.multisampled_framebuffer,
559                            Some(&view),
560                        )
561                    } else {
562                        renderer.render(
563                            &self.device,
564                            &self.queue,
565                            texture_format,
566                            self.sample_count,
567                            &view,
568                            None,
569                        )
570                    }
571                }
572            };
573
574            commands.push(command);
575        }
576
577        self.queue.submit(commands);
578        output.present();
579
580        Ok(())
581    }
582}
583
584impl<'window> Canvas for WindowCanvas<'window> {
585    fn get_multi_renderer(&mut self) -> &mut MultiMarkRenderer {
586        if self.multi_renderer.is_none() {
587            self.multi_renderer = Some(MultiMarkRenderer::new(
588                self.dimensions,
589                self.config.text_builder_ctor.clone(),
590            ));
591        }
592        self.multi_renderer.as_mut().unwrap()
593    }
594
595    fn add_mark_renderer(&mut self, mark_renderer: MarkRenderer) {
596        if let Some(multi_renderer) = self.multi_renderer.take() {
597            self.marks
598                .push(MarkRenderer::Multi(Box::new(multi_renderer)));
599        }
600        self.marks.push(mark_renderer);
601    }
602
603    fn clear_mark_renderer(&mut self) {
604        self.marks.clear();
605    }
606
607    fn device(&self) -> &Device {
608        &self.device
609    }
610
611    fn queue(&self) -> &Queue {
612        &self.queue
613    }
614
615    fn dimensions(&self) -> CanvasDimensions {
616        self.dimensions
617    }
618
619    fn texture_format(&self) -> TextureFormat {
620        self.surface_config.format
621    }
622
623    fn sample_count(&self) -> u32 {
624        self.sample_count
625    }
626}
627
628pub struct PngCanvas {
629    sample_count: u32,
630    marks: Vec<MarkRenderer>,
631    dimensions: CanvasDimensions,
632    texture_view: TextureView,
633    output_buffer: Buffer,
634    texture: Texture,
635    texture_size: Extent3d,
636    padded_width: u32,
637    padded_height: u32,
638    multi_renderer: Option<MultiMarkRenderer>,
639    config: CanvasConfig,
640
641    // The order of properties in a struct is the order in which items are dropped.
642    // wgpu seems to require that the device be dropped last, otherwise there is a resouce
643    // leak.
644    multisampled_framebuffer: TextureView,
645    queue: Queue,
646    device: Device,
647}
648
649impl PngCanvas {
650    #[tracing::instrument(skip_all)]
651    pub async fn new(
652        dimensions: CanvasDimensions,
653        config: CanvasConfig,
654    ) -> Result<Self, AvengerWgpuError> {
655        let instance = make_wgpu_instance();
656        let adapter = make_wgpu_adapter(&instance, None).await?;
657        let (device, queue) = request_wgpu_device(&adapter).await?;
658        let texture_format = TextureFormat::Rgba8Unorm;
659        let format_flags = adapter.get_texture_format_features(texture_format).flags;
660        let sample_count = get_supported_sample_count(format_flags);
661        let texture_desc = TextureDescriptor {
662            size: Extent3d {
663                width: dimensions.to_physical_width(),
664                height: dimensions.to_physical_height(),
665                depth_or_array_layers: 1,
666            },
667            mip_level_count: 1,
668            sample_count: 1, // Sample count of output texture is always 1
669            dimension: TextureDimension::D2,
670            format: texture_format,
671            usage: TextureUsages::COPY_SRC | TextureUsages::RENDER_ATTACHMENT,
672            label: None,
673            view_formats: &[texture_format],
674        };
675        let texture_size = texture_desc.size;
676        let texture = device.create_texture(&texture_desc);
677        let texture_view = texture.create_view(&Default::default());
678
679        // we need to store this for later
680        let u32_size = std::mem::size_of::<u32>() as u32;
681
682        // Width and height must be padded to multiple of 256 for copying image buffer
683        // from/to GPU texture
684        let padded_width = (256.0 * (dimensions.to_physical_width() as f32 / 256.0).ceil()) as u32;
685        let padded_height =
686            (256.0 * (dimensions.to_physical_height() as f32 / 256.0).ceil()) as u32;
687
688        let output_buffer_size = (u32_size * padded_width * padded_height) as BufferAddress;
689        let output_buffer_desc = BufferDescriptor {
690            size: output_buffer_size,
691            usage: BufferUsages::COPY_DST
692                // this tells wpgu that we want to read this buffer from the cpu
693                | BufferUsages::MAP_READ,
694            label: None,
695            mapped_at_creation: false,
696        };
697        let output_buffer = device.create_buffer(&output_buffer_desc);
698
699        let multisampled_framebuffer = create_multisampled_framebuffer(
700            &device,
701            dimensions.to_physical_width(),
702            dimensions.to_physical_height(),
703            texture_format,
704            sample_count,
705        );
706
707        Ok(Self {
708            device,
709            queue,
710            multisampled_framebuffer,
711            sample_count,
712            dimensions,
713            texture,
714            texture_view,
715            output_buffer,
716            texture_size,
717            padded_width,
718            padded_height,
719            marks: Vec::new(),
720            multi_renderer: None,
721            config,
722        })
723    }
724
725    #[tracing::instrument(skip_all)]
726    pub async fn render(&mut self) -> Result<image::RgbaImage, AvengerWgpuError> {
727        // Commit open multi mark renderer
728        if let Some(multi_renderer) = self.multi_renderer.take() {
729            self.marks
730                .push(MarkRenderer::Multi(Box::new(multi_renderer)));
731        }
732
733        // Build encoder for chart background
734        let background_command = if self.sample_count > 1 {
735            make_background_command(
736                self,
737                &self.multisampled_framebuffer,
738                Some(&self.texture_view),
739            )
740        } else {
741            make_background_command(self, &self.texture_view, None)
742        };
743
744        let mut commands = vec![background_command];
745        let texture_format = self.texture_format();
746        for mark in &mut self.marks {
747            let command = match mark {
748                MarkRenderer::Instanced(renderer) => {
749                    if self.sample_count > 1 {
750                        renderer.render(
751                            &self.device,
752                            &self.multisampled_framebuffer,
753                            Some(&self.texture_view),
754                        )
755                    } else {
756                        renderer.render(&self.device, &self.texture_view, None)
757                    }
758                }
759                MarkRenderer::Multi(renderer) => {
760                    if self.sample_count > 1 {
761                        renderer.render(
762                            &self.device,
763                            &self.queue,
764                            texture_format,
765                            self.sample_count,
766                            &self.multisampled_framebuffer,
767                            Some(&self.texture_view),
768                        )
769                    } else {
770                        renderer.render(
771                            &self.device,
772                            &self.queue,
773                            texture_format,
774                            self.sample_count,
775                            &self.texture_view,
776                            None,
777                        )
778                    }
779                }
780            };
781
782            commands.push(command);
783        }
784
785        self.queue.submit(commands);
786
787        // Extract texture from GPU
788        let mut extract_encoder = self
789            .device
790            .create_command_encoder(&CommandEncoderDescriptor {
791                label: Some("Extract Texture Encoder"),
792            });
793
794        let u32_size = std::mem::size_of::<u32>() as u32;
795
796        extract_encoder.copy_texture_to_buffer(
797            ImageCopyTexture {
798                aspect: TextureAspect::All,
799                texture: &self.texture,
800                mip_level: 0,
801                origin: Origin3d::ZERO,
802            },
803            ImageCopyBuffer {
804                buffer: &self.output_buffer,
805                layout: ImageDataLayout {
806                    offset: 0,
807                    // bytes_per_row: Some(u32_size * self.width as u32),
808                    bytes_per_row: Some(u32_size * self.padded_width),
809                    rows_per_image: Some(self.padded_height),
810                },
811            },
812            self.texture_size,
813        );
814        self.queue.submit(Some(extract_encoder.finish()));
815
816        // Output to png file
817        let img = {
818            let buffer_slice = self.output_buffer.slice(..);
819
820            // NOTE: We have to create the mapping THEN device.poll() before await
821            // the future. Otherwise the application will freeze.
822            let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
823            buffer_slice.map_async(MapMode::Read, move |result| {
824                tx.send(result).unwrap();
825            });
826            self.device.poll(wgpu::Maintain::Wait);
827
828            // TODO: remove panic
829            rx.receive().await.unwrap().unwrap();
830
831            let data = buffer_slice.get_mapped_range();
832            let img_buf =
833                image::RgbaImage::from_vec(self.padded_width, self.padded_height, data.to_vec())
834                    .unwrap();
835
836            let cropped_img = crop_imm(
837                &img_buf,
838                0,
839                0,
840                self.dimensions.to_physical_width(),
841                self.dimensions.to_physical_height(),
842            );
843            cropped_img.to_image()
844        };
845
846        self.output_buffer.unmap();
847        Ok(img)
848    }
849}
850
851impl Canvas for PngCanvas {
852    fn get_multi_renderer(&mut self) -> &mut MultiMarkRenderer {
853        if self.multi_renderer.is_none() {
854            self.multi_renderer = Some(MultiMarkRenderer::new(
855                self.dimensions,
856                self.config.text_builder_ctor.clone(),
857            ));
858        }
859        self.multi_renderer.as_mut().unwrap()
860    }
861
862    fn add_mark_renderer(&mut self, mark_renderer: MarkRenderer) {
863        if let Some(multi_renderer) = self.multi_renderer.take() {
864            self.marks
865                .push(MarkRenderer::Multi(Box::new(multi_renderer)));
866        }
867        self.marks.push(mark_renderer);
868    }
869
870    fn clear_mark_renderer(&mut self) {
871        self.marks.clear();
872    }
873
874    fn device(&self) -> &Device {
875        &self.device
876    }
877
878    fn queue(&self) -> &Queue {
879        &self.queue
880    }
881
882    fn dimensions(&self) -> CanvasDimensions {
883        self.dimensions
884    }
885
886    fn texture_format(&self) -> TextureFormat {
887        self.texture.format()
888    }
889
890    fn sample_count(&self) -> u32 {
891        self.sample_count
892    }
893}