iced_wgpu/
lib.rs

1//! A [`wgpu`] renderer for [Iced].
2//!
3//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
4//!
5//! [`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and
6//! DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the
7//! incoming [WebGPU API].
8//!
9//! Currently, `iced_wgpu` supports the following primitives:
10//! - Text, which is rendered using [`glyphon`].
11//! - Quads or rectangles, with rounded borders and a solid background color.
12//! - Clip areas, useful to implement scrollables or hide overflowing content.
13//! - Images and SVG, loaded from memory or the file system.
14//! - Meshes of triangles, useful to draw geometry freely.
15//!
16//! [Iced]: https://github.com/iced-rs/iced
17//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
18//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
19//! [`glyphon`]: https://github.com/grovesNL/glyphon
20#![doc(
21    html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
22)]
23#![cfg_attr(docsrs, feature(doc_cfg))]
24#![allow(missing_docs)]
25pub mod layer;
26pub mod primitive;
27pub mod settings;
28pub mod window;
29
30#[cfg(feature = "geometry")]
31pub mod geometry;
32
33mod buffer;
34mod color;
35mod engine;
36mod quad;
37mod text;
38mod triangle;
39
40#[cfg(any(feature = "image", feature = "svg"))]
41#[path = "image/mod.rs"]
42mod image;
43
44#[cfg(not(any(feature = "image", feature = "svg")))]
45#[path = "image/null.rs"]
46mod image;
47
48use buffer::Buffer;
49
50use iced_debug as debug;
51pub use iced_graphics as graphics;
52pub use iced_graphics::core;
53
54pub use wgpu;
55
56pub use engine::Engine;
57pub use layer::Layer;
58pub use primitive::Primitive;
59pub use settings::Settings;
60
61#[cfg(feature = "geometry")]
62pub use geometry::Geometry;
63
64use crate::core::renderer;
65use crate::core::{Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation};
66use crate::graphics::mesh;
67use crate::graphics::text::{Editor, Paragraph};
68use crate::graphics::{Shell, Viewport};
69
70/// A [`wgpu`] graphics renderer for [`iced`].
71///
72/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
73/// [`iced`]: https://github.com/iced-rs/iced
74pub struct Renderer {
75    engine: Engine,
76
77    default_font: Font,
78    default_text_size: Pixels,
79    layers: layer::Stack,
80    scale_factor: Option<f32>,
81
82    quad: quad::State,
83    triangle: triangle::State,
84    text: text::State,
85    text_viewport: text::Viewport,
86
87    #[cfg(any(feature = "svg", feature = "image"))]
88    image: image::State,
89
90    // TODO: Centralize all the image feature handling
91    #[cfg(any(feature = "svg", feature = "image"))]
92    image_cache: std::cell::RefCell<image::Cache>,
93
94    staging_belt: wgpu::util::StagingBelt,
95}
96
97impl Renderer {
98    pub fn new(engine: Engine, default_font: Font, default_text_size: Pixels) -> Self {
99        Self {
100            default_font,
101            default_text_size,
102            layers: layer::Stack::new(),
103            scale_factor: None,
104
105            quad: quad::State::new(),
106            triangle: triangle::State::new(&engine.device, &engine.triangle_pipeline),
107            text: text::State::new(),
108            text_viewport: engine.text_pipeline.create_viewport(&engine.device),
109
110            #[cfg(any(feature = "svg", feature = "image"))]
111            image: image::State::new(),
112
113            #[cfg(any(feature = "svg", feature = "image"))]
114            image_cache: std::cell::RefCell::new(engine.create_image_cache()),
115
116            // TODO: Resize belt smartly (?)
117            // It would be great if the `StagingBelt` API exposed methods
118            // for introspection to detect when a resize may be worth it.
119            staging_belt: wgpu::util::StagingBelt::new(buffer::MAX_WRITE_SIZE as u64),
120
121            engine,
122        }
123    }
124
125    fn draw(
126        &mut self,
127        clear_color: Option<Color>,
128        target: &wgpu::TextureView,
129        viewport: &Viewport,
130    ) -> wgpu::CommandEncoder {
131        let mut encoder =
132            self.engine
133                .device
134                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
135                    label: Some("iced_wgpu encoder"),
136                });
137
138        self.prepare(&mut encoder, viewport);
139        self.render(&mut encoder, target, clear_color, viewport);
140
141        self.quad.trim();
142        self.triangle.trim();
143        self.text.trim();
144
145        // TODO: Provide window id (?)
146        self.engine.trim();
147
148        #[cfg(any(feature = "svg", feature = "image"))]
149        {
150            self.image.trim();
151            self.image_cache.borrow_mut().trim();
152        }
153
154        encoder
155    }
156
157    pub fn present(
158        &mut self,
159        clear_color: Option<Color>,
160        _format: wgpu::TextureFormat,
161        frame: &wgpu::TextureView,
162        viewport: &Viewport,
163    ) -> wgpu::SubmissionIndex {
164        let encoder = self.draw(clear_color, frame, viewport);
165
166        self.staging_belt.finish();
167        let submission = self.engine.queue.submit([encoder.finish()]);
168        self.staging_belt.recall();
169        submission
170    }
171
172    /// Renders the current surface to an offscreen buffer.
173    ///
174    /// Returns RGBA bytes of the texture data.
175    pub fn screenshot(&mut self, viewport: &Viewport, background_color: Color) -> Vec<u8> {
176        #[derive(Clone, Copy, Debug)]
177        struct BufferDimensions {
178            width: u32,
179            height: u32,
180            unpadded_bytes_per_row: usize,
181            padded_bytes_per_row: usize,
182        }
183
184        impl BufferDimensions {
185            fn new(size: Size<u32>) -> Self {
186                let unpadded_bytes_per_row = size.width as usize * 4; //slice of buffer per row; always RGBA
187                let alignment = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize; //256
188                let padded_bytes_per_row_padding =
189                    (alignment - unpadded_bytes_per_row % alignment) % alignment;
190                let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
191
192                Self {
193                    width: size.width,
194                    height: size.height,
195                    unpadded_bytes_per_row,
196                    padded_bytes_per_row,
197                }
198            }
199        }
200
201        let dimensions = BufferDimensions::new(viewport.physical_size());
202
203        let texture_extent = wgpu::Extent3d {
204            width: dimensions.width,
205            height: dimensions.height,
206            depth_or_array_layers: 1,
207        };
208
209        let texture = self.engine.device.create_texture(&wgpu::TextureDescriptor {
210            label: Some("iced_wgpu.offscreen.source_texture"),
211            size: texture_extent,
212            mip_level_count: 1,
213            sample_count: 1,
214            dimension: wgpu::TextureDimension::D2,
215            format: self.engine.format,
216            usage: wgpu::TextureUsages::RENDER_ATTACHMENT
217                | wgpu::TextureUsages::COPY_SRC
218                | wgpu::TextureUsages::TEXTURE_BINDING,
219            view_formats: &[],
220        });
221
222        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
223
224        let mut encoder = self.draw(Some(background_color), &view, viewport);
225
226        let texture = crate::color::convert(
227            &self.engine.device,
228            &mut encoder,
229            texture,
230            if graphics::color::GAMMA_CORRECTION {
231                wgpu::TextureFormat::Rgba8UnormSrgb
232            } else {
233                wgpu::TextureFormat::Rgba8Unorm
234            },
235        );
236
237        let output_buffer = self.engine.device.create_buffer(&wgpu::BufferDescriptor {
238            label: Some("iced_wgpu.offscreen.output_texture_buffer"),
239            size: (dimensions.padded_bytes_per_row * dimensions.height as usize) as u64,
240            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
241            mapped_at_creation: false,
242        });
243
244        encoder.copy_texture_to_buffer(
245            texture.as_image_copy(),
246            wgpu::TexelCopyBufferInfo {
247                buffer: &output_buffer,
248                layout: wgpu::TexelCopyBufferLayout {
249                    offset: 0,
250                    bytes_per_row: Some(dimensions.padded_bytes_per_row as u32),
251                    rows_per_image: None,
252                },
253            },
254            texture_extent,
255        );
256
257        self.staging_belt.finish();
258        let index = self.engine.queue.submit([encoder.finish()]);
259        self.staging_belt.recall();
260
261        let slice = output_buffer.slice(..);
262        slice.map_async(wgpu::MapMode::Read, |_| {});
263
264        let _ = self.engine.device.poll(wgpu::PollType::Wait {
265            submission_index: Some(index),
266            timeout: None,
267        });
268
269        let mapped_buffer = slice.get_mapped_range();
270
271        mapped_buffer
272            .chunks(dimensions.padded_bytes_per_row)
273            .fold(vec![], |mut acc, row| {
274                acc.extend(&row[..dimensions.unpadded_bytes_per_row]);
275                acc
276            })
277    }
278
279    fn prepare(&mut self, encoder: &mut wgpu::CommandEncoder, viewport: &Viewport) {
280        let scale_factor = viewport.scale_factor();
281
282        self.text_viewport
283            .update(&self.engine.queue, viewport.physical_size());
284
285        let physical_bounds =
286            Rectangle::<f32>::from(Rectangle::with_size(viewport.physical_size()));
287
288        self.layers.merge();
289
290        for layer in self.layers.iter() {
291            let clip_bounds = layer.bounds * scale_factor;
292
293            if physical_bounds
294                .intersection(&clip_bounds)
295                .and_then(Rectangle::snap)
296                .is_none()
297            {
298                continue;
299            }
300
301            if !layer.quads.is_empty() {
302                let prepare_span = debug::prepare(debug::Primitive::Quad);
303
304                self.quad.prepare(
305                    &self.engine.quad_pipeline,
306                    &self.engine.device,
307                    &mut self.staging_belt,
308                    encoder,
309                    &layer.quads,
310                    viewport.projection(),
311                    scale_factor,
312                );
313
314                prepare_span.finish();
315            }
316
317            if !layer.triangles.is_empty() {
318                let prepare_span = debug::prepare(debug::Primitive::Triangle);
319
320                self.triangle.prepare(
321                    &self.engine.triangle_pipeline,
322                    &self.engine.device,
323                    &mut self.staging_belt,
324                    encoder,
325                    &layer.triangles,
326                    Transformation::scale(scale_factor),
327                    viewport.physical_size(),
328                );
329
330                prepare_span.finish();
331            }
332
333            if !layer.primitives.is_empty() {
334                let prepare_span = debug::prepare(debug::Primitive::Shader);
335
336                let mut primitive_storage = self
337                    .engine
338                    .primitive_storage
339                    .write()
340                    .expect("Write primitive storage");
341
342                for instance in &layer.primitives {
343                    instance.primitive.prepare(
344                        &mut primitive_storage,
345                        &self.engine.device,
346                        &self.engine.queue,
347                        self.engine.format,
348                        &instance.bounds,
349                        viewport,
350                    );
351                }
352
353                prepare_span.finish();
354            }
355
356            #[cfg(any(feature = "svg", feature = "image"))]
357            if !layer.images.is_empty() {
358                let prepare_span = debug::prepare(debug::Primitive::Image);
359
360                self.image.prepare(
361                    &self.engine.image_pipeline,
362                    &self.engine.device,
363                    &mut self.staging_belt,
364                    encoder,
365                    &mut self.image_cache.borrow_mut(),
366                    &layer.images,
367                    viewport.projection(),
368                    scale_factor,
369                );
370
371                prepare_span.finish();
372            }
373
374            if !layer.text.is_empty() {
375                let prepare_span = debug::prepare(debug::Primitive::Text);
376
377                self.text.prepare(
378                    &self.engine.text_pipeline,
379                    &self.engine.device,
380                    &self.engine.queue,
381                    &self.text_viewport,
382                    encoder,
383                    &layer.text,
384                    layer.bounds,
385                    Transformation::scale(scale_factor),
386                );
387
388                prepare_span.finish();
389            }
390        }
391    }
392
393    fn render(
394        &mut self,
395        encoder: &mut wgpu::CommandEncoder,
396        frame: &wgpu::TextureView,
397        clear_color: Option<Color>,
398        viewport: &Viewport,
399    ) {
400        use std::mem::ManuallyDrop;
401
402        let mut render_pass =
403            ManuallyDrop::new(encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
404                label: Some("iced_wgpu render pass"),
405                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
406                    view: frame,
407                    depth_slice: None,
408                    resolve_target: None,
409                    ops: wgpu::Operations {
410                        load: match clear_color {
411                            Some(background_color) => wgpu::LoadOp::Clear({
412                                let [r, g, b, a] =
413                                    graphics::color::pack(background_color).components();
414
415                                wgpu::Color {
416                                    r: f64::from(r),
417                                    g: f64::from(g),
418                                    b: f64::from(b),
419                                    a: f64::from(a),
420                                }
421                            }),
422                            None => wgpu::LoadOp::Load,
423                        },
424                        store: wgpu::StoreOp::Store,
425                    },
426                })],
427                depth_stencil_attachment: None,
428                timestamp_writes: None,
429                occlusion_query_set: None,
430            }));
431
432        let mut quad_layer = 0;
433        let mut mesh_layer = 0;
434        let mut text_layer = 0;
435
436        #[cfg(any(feature = "svg", feature = "image"))]
437        let mut image_layer = 0;
438
439        let scale_factor = viewport.scale_factor();
440        let physical_bounds =
441            Rectangle::<f32>::from(Rectangle::with_size(viewport.physical_size()));
442
443        let scale = Transformation::scale(scale_factor);
444
445        for layer in self.layers.iter() {
446            let Some(physical_bounds) =
447                physical_bounds.intersection(&(layer.bounds * scale_factor))
448            else {
449                continue;
450            };
451
452            let Some(scissor_rect) = physical_bounds.snap() else {
453                continue;
454            };
455
456            if !layer.quads.is_empty() {
457                let render_span = debug::render(debug::Primitive::Quad);
458                self.quad.render(
459                    &self.engine.quad_pipeline,
460                    quad_layer,
461                    scissor_rect,
462                    &layer.quads,
463                    &mut render_pass,
464                );
465                render_span.finish();
466
467                quad_layer += 1;
468            }
469
470            if !layer.triangles.is_empty() {
471                let _ = ManuallyDrop::into_inner(render_pass);
472
473                let render_span = debug::render(debug::Primitive::Triangle);
474                mesh_layer += self.triangle.render(
475                    &self.engine.triangle_pipeline,
476                    encoder,
477                    frame,
478                    mesh_layer,
479                    &layer.triangles,
480                    physical_bounds,
481                    scale,
482                );
483                render_span.finish();
484
485                render_pass =
486                    ManuallyDrop::new(encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
487                        label: Some("iced_wgpu render pass"),
488                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
489                            view: frame,
490                            depth_slice: None,
491                            resolve_target: None,
492                            ops: wgpu::Operations {
493                                load: wgpu::LoadOp::Load,
494                                store: wgpu::StoreOp::Store,
495                            },
496                        })],
497                        depth_stencil_attachment: None,
498                        timestamp_writes: None,
499                        occlusion_query_set: None,
500                    }));
501            }
502
503            if !layer.primitives.is_empty() {
504                let render_span = debug::render(debug::Primitive::Shader);
505
506                let primitive_storage = self
507                    .engine
508                    .primitive_storage
509                    .read()
510                    .expect("Read primitive storage");
511
512                let mut need_render = Vec::new();
513
514                for instance in &layer.primitives {
515                    let bounds = instance.bounds * scale;
516
517                    if let Some(clip_bounds) = (instance.bounds * scale)
518                        .intersection(&physical_bounds)
519                        .and_then(Rectangle::snap)
520                    {
521                        render_pass.set_viewport(
522                            bounds.x,
523                            bounds.y,
524                            bounds.width,
525                            bounds.height,
526                            0.0,
527                            1.0,
528                        );
529
530                        render_pass.set_scissor_rect(
531                            clip_bounds.x,
532                            clip_bounds.y,
533                            clip_bounds.width,
534                            clip_bounds.height,
535                        );
536
537                        let drawn = instance
538                            .primitive
539                            .draw(&primitive_storage, &mut render_pass);
540
541                        if !drawn {
542                            need_render.push((instance, clip_bounds));
543                        }
544                    }
545                }
546
547                render_pass.set_viewport(
548                    0.0,
549                    0.0,
550                    viewport.physical_width() as f32,
551                    viewport.physical_height() as f32,
552                    0.0,
553                    1.0,
554                );
555
556                render_pass.set_scissor_rect(
557                    0,
558                    0,
559                    viewport.physical_width(),
560                    viewport.physical_height(),
561                );
562
563                if !need_render.is_empty() {
564                    let _ = ManuallyDrop::into_inner(render_pass);
565
566                    for (instance, clip_bounds) in need_render {
567                        instance
568                            .primitive
569                            .render(&primitive_storage, encoder, frame, &clip_bounds);
570                    }
571
572                    render_pass =
573                        ManuallyDrop::new(encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
574                            label: Some("iced_wgpu render pass"),
575                            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
576                                view: frame,
577                                depth_slice: None,
578                                resolve_target: None,
579                                ops: wgpu::Operations {
580                                    load: wgpu::LoadOp::Load,
581                                    store: wgpu::StoreOp::Store,
582                                },
583                            })],
584                            depth_stencil_attachment: None,
585                            timestamp_writes: None,
586                            occlusion_query_set: None,
587                        }));
588                }
589
590                render_span.finish();
591            }
592
593            #[cfg(any(feature = "svg", feature = "image"))]
594            if !layer.images.is_empty() {
595                let render_span = debug::render(debug::Primitive::Image);
596                self.image.render(
597                    &self.engine.image_pipeline,
598                    image_layer,
599                    scissor_rect,
600                    &mut render_pass,
601                );
602                render_span.finish();
603
604                image_layer += 1;
605            }
606
607            if !layer.text.is_empty() {
608                let render_span = debug::render(debug::Primitive::Text);
609                text_layer += self.text.render(
610                    &self.engine.text_pipeline,
611                    &self.text_viewport,
612                    text_layer,
613                    &layer.text,
614                    scissor_rect,
615                    &mut render_pass,
616                );
617                render_span.finish();
618            }
619        }
620
621        let _ = ManuallyDrop::into_inner(render_pass);
622
623        debug::layers_rendered(|| {
624            self.layers
625                .iter()
626                .filter(|layer| {
627                    !layer.is_empty()
628                        && physical_bounds
629                            .intersection(&(layer.bounds * scale_factor))
630                            .is_some_and(|viewport| viewport.snap().is_some())
631                })
632                .count()
633        });
634    }
635}
636
637impl core::Renderer for Renderer {
638    fn start_layer(&mut self, bounds: Rectangle) {
639        self.layers.push_clip(bounds);
640    }
641
642    fn end_layer(&mut self) {
643        self.layers.pop_clip();
644    }
645
646    fn start_transformation(&mut self, transformation: Transformation) {
647        self.layers.push_transformation(transformation);
648    }
649
650    fn end_transformation(&mut self) {
651        self.layers.pop_transformation();
652    }
653
654    fn fill_quad(&mut self, quad: core::renderer::Quad, background: impl Into<Background>) {
655        let (layer, transformation) = self.layers.current_mut();
656        layer.draw_quad(quad, background.into(), transformation);
657    }
658
659    fn allocate_image(
660        &mut self,
661        _handle: &core::image::Handle,
662        _callback: impl FnOnce(Result<core::image::Allocation, core::image::Error>) + Send + 'static,
663    ) {
664        #[cfg(feature = "image")]
665        self.image_cache
666            .get_mut()
667            .allocate_image(_handle, _callback);
668    }
669
670    fn hint(&mut self, scale_factor: f32) {
671        self.scale_factor = Some(scale_factor);
672    }
673
674    fn scale_factor(&self) -> Option<f32> {
675        Some(self.scale_factor? * self.layers.transformation().scale_factor())
676    }
677
678    fn tick(&mut self) {
679        #[cfg(feature = "image")]
680        self.image_cache.get_mut().receive();
681    }
682
683    fn reset(&mut self, new_bounds: Rectangle) {
684        self.layers.reset(new_bounds);
685    }
686}
687
688impl core::text::Renderer for Renderer {
689    type Font = Font;
690    type Paragraph = Paragraph;
691    type Editor = Editor;
692
693    const ICON_FONT: Font = Font::with_name("Iced-Icons");
694    const CHECKMARK_ICON: char = '\u{f00c}';
695    const ARROW_DOWN_ICON: char = '\u{e800}';
696    const ICED_LOGO: char = '\u{e801}';
697    const SCROLL_UP_ICON: char = '\u{e802}';
698    const SCROLL_DOWN_ICON: char = '\u{e803}';
699    const SCROLL_LEFT_ICON: char = '\u{e804}';
700    const SCROLL_RIGHT_ICON: char = '\u{e805}';
701
702    fn default_font(&self) -> Self::Font {
703        self.default_font
704    }
705
706    fn default_size(&self) -> Pixels {
707        self.default_text_size
708    }
709
710    fn fill_paragraph(
711        &mut self,
712        text: &Self::Paragraph,
713        position: Point,
714        color: Color,
715        clip_bounds: Rectangle,
716    ) {
717        let (layer, transformation) = self.layers.current_mut();
718
719        layer.draw_paragraph(text, position, color, clip_bounds, transformation);
720    }
721
722    fn fill_editor(
723        &mut self,
724        editor: &Self::Editor,
725        position: Point,
726        color: Color,
727        clip_bounds: Rectangle,
728    ) {
729        let (layer, transformation) = self.layers.current_mut();
730        layer.draw_editor(editor, position, color, clip_bounds, transformation);
731    }
732
733    fn fill_text(
734        &mut self,
735        text: core::Text,
736        position: Point,
737        color: Color,
738        clip_bounds: Rectangle,
739    ) {
740        let (layer, transformation) = self.layers.current_mut();
741        layer.draw_text(text, position, color, clip_bounds, transformation);
742    }
743}
744
745impl graphics::text::Renderer for Renderer {
746    fn fill_raw(&mut self, raw: graphics::text::Raw) {
747        let (layer, transformation) = self.layers.current_mut();
748        layer.draw_text_raw(raw, transformation);
749    }
750}
751
752#[cfg(feature = "image")]
753impl core::image::Renderer for Renderer {
754    type Handle = core::image::Handle;
755
756    fn load_image(
757        &self,
758        handle: &Self::Handle,
759    ) -> Result<core::image::Allocation, core::image::Error> {
760        self.image_cache
761            .borrow_mut()
762            .load_image(&self.engine.device, &self.engine.queue, handle)
763    }
764
765    fn measure_image(&self, handle: &Self::Handle) -> Option<core::Size<u32>> {
766        self.image_cache.borrow_mut().measure_image(handle)
767    }
768
769    fn draw_image(&mut self, image: core::Image, bounds: Rectangle, clip_bounds: Rectangle) {
770        let (layer, transformation) = self.layers.current_mut();
771        layer.draw_raster(image, bounds, clip_bounds, transformation);
772    }
773}
774
775#[cfg(feature = "svg")]
776impl core::svg::Renderer for Renderer {
777    fn measure_svg(&self, handle: &core::svg::Handle) -> core::Size<u32> {
778        self.image_cache.borrow_mut().measure_svg(handle)
779    }
780
781    fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle, clip_bounds: Rectangle) {
782        let (layer, transformation) = self.layers.current_mut();
783        layer.draw_svg(svg, bounds, clip_bounds, transformation);
784    }
785}
786
787impl graphics::mesh::Renderer for Renderer {
788    fn draw_mesh(&mut self, mesh: graphics::Mesh) {
789        debug_assert!(
790            !mesh.indices().is_empty(),
791            "Mesh must not have empty indices"
792        );
793
794        debug_assert!(
795            mesh.indices().len().is_multiple_of(3),
796            "Mesh indices length must be a multiple of 3"
797        );
798
799        let (layer, transformation) = self.layers.current_mut();
800        layer.draw_mesh(mesh, transformation);
801    }
802
803    fn draw_mesh_cache(&mut self, cache: mesh::Cache) {
804        let (layer, transformation) = self.layers.current_mut();
805        layer.draw_mesh_cache(cache, transformation);
806    }
807}
808
809#[cfg(feature = "geometry")]
810impl graphics::geometry::Renderer for Renderer {
811    type Geometry = Geometry;
812    type Frame = geometry::Frame;
813
814    fn new_frame(&self, bounds: Rectangle) -> Self::Frame {
815        geometry::Frame::new(bounds)
816    }
817
818    fn draw_geometry(&mut self, geometry: Self::Geometry) {
819        let (layer, transformation) = self.layers.current_mut();
820
821        match geometry {
822            Geometry::Live {
823                meshes,
824                images,
825                text,
826            } => {
827                layer.draw_mesh_group(meshes, transformation);
828
829                for image in images {
830                    layer.draw_image(image, transformation);
831                }
832
833                layer.draw_text_group(text, transformation);
834            }
835            Geometry::Cached(cache) => {
836                if let Some(meshes) = cache.meshes {
837                    layer.draw_mesh_cache(meshes, transformation);
838                }
839
840                if let Some(images) = cache.images {
841                    for image in images.iter().cloned() {
842                        layer.draw_image(image, transformation);
843                    }
844                }
845
846                if let Some(text) = cache.text {
847                    layer.draw_text_cache(text, transformation);
848                }
849            }
850        }
851    }
852}
853
854impl primitive::Renderer for Renderer {
855    fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive) {
856        let (layer, transformation) = self.layers.current_mut();
857        layer.draw_primitive(bounds, primitive, transformation);
858    }
859}
860
861impl graphics::compositor::Default for crate::Renderer {
862    type Compositor = window::Compositor;
863}
864
865impl renderer::Headless for Renderer {
866    async fn new(
867        default_font: Font,
868        default_text_size: Pixels,
869        backend: Option<&str>,
870    ) -> Option<Self> {
871        if backend.is_some_and(|backend| backend != "wgpu") {
872            return None;
873        }
874
875        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
876            backends: wgpu::Backends::from_env().unwrap_or(wgpu::Backends::PRIMARY),
877            flags: wgpu::InstanceFlags::empty(),
878            ..wgpu::InstanceDescriptor::default()
879        });
880
881        let adapter = instance
882            .request_adapter(&wgpu::RequestAdapterOptions {
883                power_preference: wgpu::PowerPreference::HighPerformance,
884                force_fallback_adapter: false,
885                compatible_surface: None,
886            })
887            .await
888            .ok()?;
889
890        let (device, queue) = adapter
891            .request_device(&wgpu::DeviceDescriptor {
892                label: Some("iced_wgpu [headless]"),
893                required_features: wgpu::Features::empty(),
894                required_limits: wgpu::Limits {
895                    max_bind_groups: 2,
896                    ..wgpu::Limits::default()
897                },
898                memory_hints: wgpu::MemoryHints::MemoryUsage,
899                trace: wgpu::Trace::Off,
900                experimental_features: wgpu::ExperimentalFeatures::disabled(),
901            })
902            .await
903            .ok()?;
904
905        let engine = Engine::new(
906            &adapter,
907            device,
908            queue,
909            if graphics::color::GAMMA_CORRECTION {
910                wgpu::TextureFormat::Rgba8UnormSrgb
911            } else {
912                wgpu::TextureFormat::Rgba8Unorm
913            },
914            Some(graphics::Antialiasing::MSAAx4),
915            Shell::headless(),
916        );
917
918        Some(Self::new(engine, default_font, default_text_size))
919    }
920
921    fn name(&self) -> String {
922        "wgpu".to_owned()
923    }
924
925    fn screenshot(
926        &mut self,
927        size: Size<u32>,
928        scale_factor: f32,
929        background_color: Color,
930    ) -> Vec<u8> {
931        self.screenshot(
932            &Viewport::with_physical_size(size, scale_factor),
933            background_color,
934        )
935    }
936}