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