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