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
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;
58pub use settings::Settings;
59
60#[cfg(feature = "geometry")]
61pub use geometry::Geometry;
62
63use crate::core::{
64 Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
65 Vector,
66};
67use crate::graphics::Viewport;
68use crate::graphics::text::{Editor, Paragraph};
69
70#[allow(missing_debug_implementations)]
75pub struct Renderer {
76 default_font: Font,
77 default_text_size: Pixels,
78 layers: layer::Stack,
79
80 triangle_storage: triangle::Storage,
81 text_storage: text::Storage,
82 text_viewport: text::Viewport,
83
84 #[cfg(any(feature = "svg", feature = "image"))]
86 image_cache: std::cell::RefCell<image::Cache>,
87}
88
89impl Renderer {
90 pub fn new(
91 device: &wgpu::Device,
92 engine: &Engine,
93 default_font: Font,
94 default_text_size: Pixels,
95 ) -> Self {
96 Self {
97 default_font,
98 default_text_size,
99 layers: layer::Stack::new(),
100
101 triangle_storage: triangle::Storage::new(),
102 text_storage: text::Storage::new(),
103 text_viewport: engine.text_pipeline.create_viewport(device),
104
105 #[cfg(any(feature = "svg", feature = "image"))]
106 image_cache: std::cell::RefCell::new(
107 engine.create_image_cache(device),
108 ),
109 }
110 }
111
112 pub fn present<T: AsRef<str>>(
113 &mut self,
114 engine: &mut Engine,
115 device: &wgpu::Device,
116 queue: &wgpu::Queue,
117 encoder: &mut wgpu::CommandEncoder,
118 clear_color: Option<Color>,
119 format: wgpu::TextureFormat,
120 frame: &wgpu::TextureView,
121 viewport: &Viewport,
122 overlay: &[T],
123 ) {
124 self.draw_overlay(overlay, viewport);
125 self.prepare(engine, device, queue, format, encoder, viewport);
126 self.render(engine, encoder, frame, clear_color, viewport);
127
128 self.triangle_storage.trim();
129 self.text_storage.trim();
130
131 #[cfg(any(feature = "svg", feature = "image"))]
132 self.image_cache.borrow_mut().trim();
133 }
134
135 fn prepare(
136 &mut self,
137 engine: &mut Engine,
138 device: &wgpu::Device,
139 queue: &wgpu::Queue,
140 _format: wgpu::TextureFormat,
141 encoder: &mut wgpu::CommandEncoder,
142 viewport: &Viewport,
143 ) {
144 let scale_factor = viewport.scale_factor() as f32;
145
146 self.text_viewport.update(queue, viewport.physical_size());
147
148 let physical_bounds = Rectangle::<f32>::from(Rectangle::with_size(
149 viewport.physical_size(),
150 ));
151
152 for layer in self.layers.iter_mut() {
153 if physical_bounds
154 .intersection(&(layer.bounds * scale_factor))
155 .and_then(Rectangle::snap)
156 .is_none()
157 {
158 continue;
159 }
160
161 if !layer.quads.is_empty() {
162 engine.quad_pipeline.prepare(
163 device,
164 encoder,
165 &mut engine.staging_belt,
166 &layer.quads,
167 viewport.projection(),
168 scale_factor,
169 );
170 }
171
172 if !layer.triangles.is_empty() {
173 engine.triangle_pipeline.prepare(
174 device,
175 encoder,
176 &mut engine.staging_belt,
177 &mut self.triangle_storage,
178 &layer.triangles,
179 Transformation::scale(scale_factor),
180 viewport.physical_size(),
181 );
182 }
183
184 if !layer.primitives.is_empty() {
185 for instance in &layer.primitives {
186 instance.primitive.prepare(
187 device,
188 queue,
189 engine.format,
190 &mut engine.primitive_storage,
191 &instance.bounds,
192 viewport,
193 );
194 }
195 }
196
197 #[cfg(any(feature = "svg", feature = "image"))]
198 if !layer.images.is_empty() {
199 engine.image_pipeline.prepare(
200 device,
201 encoder,
202 &mut engine.staging_belt,
203 &mut self.image_cache.borrow_mut(),
204 &layer.images,
205 viewport.projection(),
206 scale_factor,
207 );
208 }
209
210 if !layer.text.is_empty() {
211 engine.text_pipeline.prepare(
212 device,
213 queue,
214 &self.text_viewport,
215 encoder,
216 &mut self.text_storage,
217 &layer.text,
218 layer.bounds,
219 Transformation::scale(scale_factor),
220 );
221 }
222 }
223 }
224
225 fn render(
226 &mut self,
227 engine: &mut Engine,
228 encoder: &mut wgpu::CommandEncoder,
229 frame: &wgpu::TextureView,
230 clear_color: Option<Color>,
231 viewport: &Viewport,
232 ) {
233 use std::mem::ManuallyDrop;
234
235 let mut render_pass = ManuallyDrop::new(encoder.begin_render_pass(
236 &wgpu::RenderPassDescriptor {
237 label: Some("iced_wgpu render pass"),
238 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
239 view: frame,
240 resolve_target: None,
241 ops: wgpu::Operations {
242 load: match clear_color {
243 Some(background_color) => wgpu::LoadOp::Clear({
244 let [r, g, b, a] =
245 graphics::color::pack(background_color)
246 .components();
247
248 wgpu::Color {
249 r: f64::from(r),
250 g: f64::from(g),
251 b: f64::from(b),
252 a: f64::from(a),
253 }
254 }),
255 None => wgpu::LoadOp::Load,
256 },
257 store: wgpu::StoreOp::Store,
258 },
259 })],
260 depth_stencil_attachment: None,
261 timestamp_writes: None,
262 occlusion_query_set: None,
263 },
264 ));
265
266 let mut quad_layer = 0;
267 let mut mesh_layer = 0;
268 let mut text_layer = 0;
269
270 #[cfg(any(feature = "svg", feature = "image"))]
271 let mut image_layer = 0;
272 #[cfg(any(feature = "svg", feature = "image"))]
273 let image_cache = self.image_cache.borrow();
274
275 let scale_factor = viewport.scale_factor() as f32;
276 let physical_bounds = Rectangle::<f32>::from(Rectangle::with_size(
277 viewport.physical_size(),
278 ));
279
280 let scale = Transformation::scale(scale_factor);
281
282 for layer in self.layers.iter() {
283 let Some(physical_bounds) =
284 physical_bounds.intersection(&(layer.bounds * scale_factor))
285 else {
286 continue;
287 };
288
289 let Some(scissor_rect) = physical_bounds.snap() else {
290 continue;
291 };
292
293 if !layer.quads.is_empty() {
294 engine.quad_pipeline.render(
295 quad_layer,
296 scissor_rect,
297 &layer.quads,
298 &mut render_pass,
299 );
300
301 quad_layer += 1;
302 }
303
304 if !layer.triangles.is_empty() {
305 let _ = ManuallyDrop::into_inner(render_pass);
306
307 mesh_layer += engine.triangle_pipeline.render(
308 encoder,
309 frame,
310 &self.triangle_storage,
311 mesh_layer,
312 &layer.triangles,
313 physical_bounds,
314 scale,
315 );
316
317 render_pass = ManuallyDrop::new(encoder.begin_render_pass(
318 &wgpu::RenderPassDescriptor {
319 label: Some("iced_wgpu render pass"),
320 color_attachments: &[Some(
321 wgpu::RenderPassColorAttachment {
322 view: frame,
323 resolve_target: None,
324 ops: wgpu::Operations {
325 load: wgpu::LoadOp::Load,
326 store: wgpu::StoreOp::Store,
327 },
328 },
329 )],
330 depth_stencil_attachment: None,
331 timestamp_writes: None,
332 occlusion_query_set: None,
333 },
334 ));
335 }
336
337 if !layer.primitives.is_empty() {
338 let _ = ManuallyDrop::into_inner(render_pass);
339
340 for instance in &layer.primitives {
341 if let Some(clip_bounds) = (instance.bounds * scale)
342 .intersection(&physical_bounds)
343 .and_then(Rectangle::snap)
344 {
345 instance.primitive.render(
346 encoder,
347 &engine.primitive_storage,
348 frame,
349 &clip_bounds,
350 );
351 }
352 }
353
354 render_pass = ManuallyDrop::new(encoder.begin_render_pass(
355 &wgpu::RenderPassDescriptor {
356 label: Some("iced_wgpu render pass"),
357 color_attachments: &[Some(
358 wgpu::RenderPassColorAttachment {
359 view: frame,
360 resolve_target: None,
361 ops: wgpu::Operations {
362 load: wgpu::LoadOp::Load,
363 store: wgpu::StoreOp::Store,
364 },
365 },
366 )],
367 depth_stencil_attachment: None,
368 timestamp_writes: None,
369 occlusion_query_set: None,
370 },
371 ));
372 }
373
374 #[cfg(any(feature = "svg", feature = "image"))]
375 if !layer.images.is_empty() {
376 engine.image_pipeline.render(
377 &image_cache,
378 image_layer,
379 scissor_rect,
380 &mut render_pass,
381 );
382
383 image_layer += 1;
384 }
385
386 if !layer.text.is_empty() {
387 text_layer += engine.text_pipeline.render(
388 &self.text_viewport,
389 &self.text_storage,
390 text_layer,
391 &layer.text,
392 scissor_rect,
393 &mut render_pass,
394 );
395 }
396 }
397
398 let _ = ManuallyDrop::into_inner(render_pass);
399 }
400
401 fn draw_overlay(
402 &mut self,
403 overlay: &[impl AsRef<str>],
404 viewport: &Viewport,
405 ) {
406 use crate::core::Renderer as _;
407 use crate::core::alignment;
408 use crate::core::text::Renderer as _;
409
410 self.with_layer(
411 Rectangle::with_size(viewport.logical_size()),
412 |renderer| {
413 for (i, line) in overlay.iter().enumerate() {
414 let text = crate::core::Text {
415 content: line.as_ref().to_owned(),
416 bounds: viewport.logical_size(),
417 size: Pixels(20.0),
418 line_height: core::text::LineHeight::default(),
419 font: Font::MONOSPACE,
420 align_x: core::text::Alignment::Default,
421 align_y: alignment::Vertical::Top,
422 shaping: core::text::Shaping::Basic,
423 wrapping: core::text::Wrapping::Word,
424 };
425
426 renderer.fill_text(
427 text.clone(),
428 Point::new(11.0, 11.0 + 25.0 * i as f32),
429 Color::from_rgba(0.9, 0.9, 0.9, 1.0),
430 Rectangle::with_size(Size::INFINITY),
431 );
432
433 renderer.fill_text(
434 text,
435 Point::new(11.0, 11.0 + 25.0 * i as f32)
436 + Vector::new(-1.0, -1.0),
437 Color::BLACK,
438 Rectangle::with_size(Size::INFINITY),
439 );
440 }
441 },
442 );
443 }
444}
445
446impl core::Renderer for Renderer {
447 fn start_layer(&mut self, bounds: Rectangle) {
448 self.layers.push_clip(bounds);
449 }
450
451 fn end_layer(&mut self) {
452 self.layers.pop_clip();
453 }
454
455 fn start_transformation(&mut self, transformation: Transformation) {
456 self.layers.push_transformation(transformation);
457 }
458
459 fn end_transformation(&mut self) {
460 self.layers.pop_transformation();
461 }
462
463 fn fill_quad(
464 &mut self,
465 quad: core::renderer::Quad,
466 background: impl Into<Background>,
467 ) {
468 let (layer, transformation) = self.layers.current_mut();
469 layer.draw_quad(quad, background.into(), transformation);
470 }
471
472 fn clear(&mut self) {
473 self.layers.clear();
474 }
475}
476
477impl core::text::Renderer for Renderer {
478 type Font = Font;
479 type Paragraph = Paragraph;
480 type Editor = Editor;
481
482 const ICON_FONT: Font = Font::with_name("Iced-Icons");
483 const CHECKMARK_ICON: char = '\u{f00c}';
484 const ARROW_DOWN_ICON: char = '\u{e800}';
485
486 fn default_font(&self) -> Self::Font {
487 self.default_font
488 }
489
490 fn default_size(&self) -> Pixels {
491 self.default_text_size
492 }
493
494 fn fill_paragraph(
495 &mut self,
496 text: &Self::Paragraph,
497 position: Point,
498 color: Color,
499 clip_bounds: Rectangle,
500 ) {
501 let (layer, transformation) = self.layers.current_mut();
502
503 layer.draw_paragraph(
504 text,
505 position,
506 color,
507 clip_bounds,
508 transformation,
509 );
510 }
511
512 fn fill_editor(
513 &mut self,
514 editor: &Self::Editor,
515 position: Point,
516 color: Color,
517 clip_bounds: Rectangle,
518 ) {
519 let (layer, transformation) = self.layers.current_mut();
520 layer.draw_editor(editor, position, color, clip_bounds, transformation);
521 }
522
523 fn fill_text(
524 &mut self,
525 text: core::Text,
526 position: Point,
527 color: Color,
528 clip_bounds: Rectangle,
529 ) {
530 let (layer, transformation) = self.layers.current_mut();
531 layer.draw_text(text, position, color, clip_bounds, transformation);
532 }
533}
534
535#[cfg(feature = "image")]
536impl core::image::Renderer for Renderer {
537 type Handle = core::image::Handle;
538
539 fn measure_image(&self, handle: &Self::Handle) -> Size<u32> {
540 self.image_cache.borrow_mut().measure_image(handle)
541 }
542
543 fn draw_image(&mut self, image: core::Image, bounds: Rectangle) {
544 let (layer, transformation) = self.layers.current_mut();
545 layer.draw_raster(image, bounds, transformation);
546 }
547}
548
549#[cfg(feature = "svg")]
550impl core::svg::Renderer for Renderer {
551 fn measure_svg(&self, handle: &core::svg::Handle) -> Size<u32> {
552 self.image_cache.borrow_mut().measure_svg(handle)
553 }
554
555 fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) {
556 let (layer, transformation) = self.layers.current_mut();
557 layer.draw_svg(svg, bounds, transformation);
558 }
559}
560
561impl graphics::mesh::Renderer for Renderer {
562 fn draw_mesh(&mut self, mesh: graphics::Mesh) {
563 debug_assert!(
564 !mesh.indices().is_empty(),
565 "Mesh must not have empty indices"
566 );
567
568 debug_assert!(
569 mesh.indices().len() % 3 == 0,
570 "Mesh indices length must be a multiple of 3"
571 );
572
573 let (layer, transformation) = self.layers.current_mut();
574 layer.draw_mesh(mesh, transformation);
575 }
576}
577
578#[cfg(feature = "geometry")]
579impl graphics::geometry::Renderer for Renderer {
580 type Geometry = Geometry;
581 type Frame = geometry::Frame;
582
583 fn new_frame(&self, size: Size) -> Self::Frame {
584 geometry::Frame::new(size)
585 }
586
587 fn draw_geometry(&mut self, geometry: Self::Geometry) {
588 let (layer, transformation) = self.layers.current_mut();
589
590 match geometry {
591 Geometry::Live {
592 meshes,
593 images,
594 text,
595 } => {
596 layer.draw_mesh_group(meshes, transformation);
597
598 for image in images {
599 layer.draw_image(image, transformation);
600 }
601
602 layer.draw_text_group(text, transformation);
603 }
604 Geometry::Cached(cache) => {
605 if let Some(meshes) = cache.meshes {
606 layer.draw_mesh_cache(meshes, transformation);
607 }
608
609 if let Some(images) = cache.images {
610 for image in images.iter().cloned() {
611 layer.draw_image(image, transformation);
612 }
613 }
614
615 if let Some(text) = cache.text {
616 layer.draw_text_cache(text, transformation);
617 }
618 }
619 }
620 }
621}
622
623impl primitive::Renderer for Renderer {
624 fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive) {
625 let (layer, transformation) = self.layers.current_mut();
626 layer.draw_primitive(bounds, Box::new(primitive), transformation);
627 }
628}
629
630impl graphics::compositor::Default for crate::Renderer {
631 type Compositor = window::Compositor;
632}