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