1#![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
69pub 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 #[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 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 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 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 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; let alignment = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize; 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 pub fn finish(&mut self) {
649 self.staging_belt.finish();
650 }
651
652 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}