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