Skip to main content

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