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