2023-07-28 19:48:39 +02:00
//! A [`wgpu`] renderer for [Iced].
2019-11-22 22:14:24 +01:00
//!
2021-12-23 09:34:37 +02:00
//! 
2019-11-22 22:14:24 +01:00
//!
//! [`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and
//! DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the
//! incoming [WebGPU API].
//!
//! Currently, `iced_wgpu` supports the following primitives:
2023-07-28 19:48:39 +02:00
//! - Text, which is rendered using [`glyphon`].
2019-11-22 22:14:24 +01:00
//! - Quads or rectangles, with rounded borders and a solid background color.
//! - Clip areas, useful to implement scrollables or hide overflowing content.
2020-04-02 03:11:16 +02:00
//! - Images and SVG, loaded from memory or the file system.
//! - Meshes of triangles, useful to draw geometry freely.
2019-11-22 22:14:24 +01:00
//!
2021-12-23 09:34:37 +02:00
//! [Iced]: https://github.com/iced-rs/iced
2019-11-22 22:14:24 +01:00
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
2023-07-28 19:48:39 +02:00
//! [`glyphon`]: https://github.com/grovesNL/glyphon
2021-12-08 08:04:46 +01:00
#![ doc(
2021-12-09 15:10:38 +07:00
html_logo_url = " https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg "
2021-12-08 08:04:46 +01:00
) ]
2025-10-08 04:37:13 +02:00
#![ cfg_attr(docsrs, feature(doc_cfg)) ]
2024-04-07 14:01:05 +02:00
#![ allow(missing_docs) ]
2023-03-01 21:34:26 +01:00
pub mod layer ;
2023-06-29 07:48:03 +02:00
pub mod primitive ;
2020-02-15 10:08:27 +01:00
pub mod settings ;
2020-02-09 03:25:13 +01:00
pub mod window ;
2019-12-29 10:57:01 +01:00
2023-03-03 04:57:55 +01:00
#[ cfg(feature = " geometry " ) ]
pub mod geometry ;
2023-03-03 04:00:44 +01:00
2022-11-03 05:00:35 +01:00
mod buffer ;
2023-06-27 20:26:13 +02:00
mod color ;
2024-04-03 21:07:54 +02:00
mod engine ;
2019-10-07 03:56:16 +02:00
mod quad ;
2019-11-13 03:54:36 +01:00
mod text ;
2022-11-14 00:02:42 +01:00
mod triangle ;
2019-10-07 03:56:16 +02:00
2024-04-03 21:07:54 +02:00
#[ cfg(any(feature = " image " , feature = " svg " )) ]
#[ path = " image/mod.rs " ]
mod image ;
#[ cfg(not(any(feature = " image " , feature = " svg " ))) ]
#[ path = " image/null.rs " ]
mod image ;
2023-05-19 04:02:18 +02:00
use buffer ::Buffer ;
2025-04-09 21:50:21 +02:00
use iced_debug as debug ;
2023-03-04 05:37:11 +01:00
pub use iced_graphics as graphics ;
pub use iced_graphics ::core ;
2023-03-01 21:34:26 +01:00
2020-02-09 05:31:42 +01:00
pub use wgpu ;
2024-04-03 21:07:54 +02:00
pub use engine ::Engine ;
2023-03-01 21:34:26 +01:00
pub use layer ::Layer ;
2023-06-22 00:38:36 +02:00
pub use primitive ::Primitive ;
2020-01-01 17:49:48 +01:00
pub use settings ::Settings ;
2020-02-09 03:25:13 +01:00
2024-04-03 21:07:54 +02:00
#[ cfg(feature = " geometry " ) ]
pub use geometry ::Geometry ;
2025-03-24 01:28:39 +01:00
use crate ::core ::renderer ;
2024-04-03 21:07:54 +02:00
use crate ::core ::{
2024-05-03 09:11:46 +02:00
Background , Color , Font , Pixels , Point , Rectangle , Size , Transformation ,
2024-04-03 21:07:54 +02:00
} ;
2025-02-21 01:22:56 +01:00
use crate ::graphics ::text ::{ Editor , Paragraph } ;
2025-10-24 17:23:40 +02:00
use crate ::graphics ::{ Shell , Viewport } ;
2020-05-28 01:37:59 +02:00
/// A [`wgpu`] graphics renderer for [`iced`].
///
/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
2021-12-23 09:34:37 +02:00
/// [`iced`]: https://github.com/iced-rs/iced
2024-04-03 21:07:54 +02:00
pub struct Renderer {
2025-03-24 01:28:39 +01:00
engine : Engine ,
2024-04-03 21:07:54 +02:00
default_font : Font ,
default_text_size : Pixels ,
layers : layer ::Stack ,
2025-03-24 01:28:39 +01:00
quad : quad ::State ,
triangle : triangle ::State ,
text : text ::State ,
2024-05-08 13:22:22 +02:00
text_viewport : text ::Viewport ,
2024-04-05 23:59:21 +02:00
2025-03-24 01:28:39 +01:00
#[ cfg(any(feature = " svg " , feature = " image " )) ]
image : image ::State ,
2024-04-03 21:07:54 +02:00
// TODO: Centralize all the image feature handling
#[ cfg(any(feature = " svg " , feature = " image " )) ]
2024-05-07 15:50:18 +02:00
image_cache : std ::cell ::RefCell < image ::Cache > ,
2025-03-24 01:28:39 +01:00
staging_belt : wgpu ::util ::StagingBelt ,
2024-04-03 21:07:54 +02:00
}
impl Renderer {
2024-04-08 15:35:54 +02:00
pub fn new (
2025-03-24 01:28:39 +01:00
engine : Engine ,
2024-04-08 15:35:54 +02:00
default_font : Font ,
default_text_size : Pixels ,
) -> Self {
2024-04-03 21:07:54 +02:00
Self {
2024-04-08 15:35:54 +02:00
default_font ,
default_text_size ,
2024-04-03 21:07:54 +02:00
layers : layer ::Stack ::new ( ) ,
2025-03-24 01:28:39 +01:00
quad : quad ::State ::new ( ) ,
triangle : triangle ::State ::new (
& engine . device ,
& engine . triangle_pipeline ,
) ,
text : text ::State ::new ( ) ,
text_viewport : engine . text_pipeline . create_viewport ( & engine . device ) ,
#[ cfg(any(feature = " svg " , feature = " image " )) ]
image : image ::State ::new ( ) ,
2024-04-05 23:59:21 +02:00
2024-04-03 21:07:54 +02:00
#[ cfg(any(feature = " svg " , feature = " image " )) ]
2025-10-24 17:23:40 +02:00
image_cache : std ::cell ::RefCell ::new ( engine . create_image_cache ( ) ) ,
2025-03-24 01:28:39 +01:00
// TODO: Resize belt smartly (?)
// It would be great if the `StagingBelt` API exposed methods
// for introspection to detect when a resize may be worth it.
staging_belt : wgpu ::util ::StagingBelt ::new (
buffer ::MAX_WRITE_SIZE as u64 ,
2024-05-07 15:50:18 +02:00
) ,
2025-03-24 01:28:39 +01:00
engine ,
}
}
fn draw (
& mut self ,
clear_color : Option < Color > ,
target : & wgpu ::TextureView ,
viewport : & Viewport ,
) -> wgpu ::CommandEncoder {
let mut encoder = self . engine . device . create_command_encoder (
& wgpu ::CommandEncoderDescriptor {
label : Some ( " iced_wgpu encoder " ) ,
} ,
) ;
self . prepare ( & mut encoder , viewport ) ;
self . render ( & mut encoder , target , clear_color , viewport ) ;
2025-03-24 19:51:18 +01:00
self . quad . trim ( ) ;
self . triangle . trim ( ) ;
self . text . trim ( ) ;
2025-03-24 01:28:39 +01:00
2025-11-25 09:36:25 +01:00
// TODO: Provide window id (?)
self . engine . trim ( ) ;
2025-03-24 19:47:25 +01:00
2025-03-24 01:28:39 +01:00
#[ cfg(any(feature = " svg " , feature = " image " )) ]
{
2025-03-24 19:51:18 +01:00
self . image . trim ( ) ;
2025-03-24 01:28:39 +01:00
self . image_cache . borrow_mut ( ) . trim ( ) ;
2024-04-03 21:07:54 +02:00
}
2025-03-24 01:28:39 +01:00
encoder
2024-04-03 21:07:54 +02:00
}
2024-05-09 12:32:25 +02:00
pub fn present (
2024-04-03 21:07:54 +02:00
& mut self ,
clear_color : Option < Color > ,
2025-03-24 01:28:39 +01:00
_format : wgpu ::TextureFormat ,
2024-04-03 21:07:54 +02:00
frame : & wgpu ::TextureView ,
viewport : & Viewport ,
2025-03-24 01:28:39 +01:00
) -> wgpu ::SubmissionIndex {
let encoder = self . draw ( clear_color , frame , viewport ) ;
2024-04-05 23:59:21 +02:00
2025-03-24 01:28:39 +01:00
self . staging_belt . finish ( ) ;
let submission = self . engine . queue . submit ( [ encoder . finish ( ) ] ) ;
self . staging_belt . recall ( ) ;
submission
}
2024-05-06 12:17:43 +02:00
2025-03-24 01:28:39 +01:00
/// Renders the current surface to an offscreen buffer.
///
/// Returns RGBA bytes of the texture data.
pub fn screenshot (
& mut self ,
viewport : & Viewport ,
background_color : Color ,
) -> Vec < u8 > {
#[ derive(Clone, Copy, Debug) ]
struct BufferDimensions {
width : u32 ,
height : u32 ,
unpadded_bytes_per_row : usize ,
padded_bytes_per_row : usize ,
}
impl BufferDimensions {
fn new ( size : Size < u32 > ) -> Self {
let unpadded_bytes_per_row = size . width as usize * 4 ; //slice of buffer per row; always RGBA
let alignment = wgpu ::COPY_BYTES_PER_ROW_ALIGNMENT as usize ; //256
let padded_bytes_per_row_padding = ( alignment
- unpadded_bytes_per_row % alignment )
% alignment ;
let padded_bytes_per_row =
unpadded_bytes_per_row + padded_bytes_per_row_padding ;
Self {
width : size . width ,
height : size . height ,
unpadded_bytes_per_row ,
padded_bytes_per_row ,
}
}
}
let dimensions = BufferDimensions ::new ( viewport . physical_size ( ) ) ;
let texture_extent = wgpu ::Extent3d {
width : dimensions . width ,
height : dimensions . height ,
depth_or_array_layers : 1 ,
} ;
let texture =
self . engine . device . create_texture ( & wgpu ::TextureDescriptor {
label : Some ( " iced_wgpu.offscreen.source_texture " ) ,
size : texture_extent ,
mip_level_count : 1 ,
sample_count : 1 ,
dimension : wgpu ::TextureDimension ::D2 ,
format : self . engine . format ,
usage : wgpu ::TextureUsages ::RENDER_ATTACHMENT
| wgpu ::TextureUsages ::COPY_SRC
| wgpu ::TextureUsages ::TEXTURE_BINDING ,
view_formats : & [ ] ,
} ) ;
let view = texture . create_view ( & wgpu ::TextureViewDescriptor ::default ( ) ) ;
2025-03-24 19:36:02 +01:00
let mut encoder = self . draw ( Some ( background_color ) , & view , viewport ) ;
2025-03-24 01:28:39 +01:00
let texture = crate ::color ::convert (
& self . engine . device ,
& mut encoder ,
texture ,
if graphics ::color ::GAMMA_CORRECTION {
wgpu ::TextureFormat ::Rgba8UnormSrgb
} else {
wgpu ::TextureFormat ::Rgba8Unorm
} ,
) ;
let output_buffer =
self . engine . device . create_buffer ( & wgpu ::BufferDescriptor {
label : Some ( " iced_wgpu.offscreen.output_texture_buffer " ) ,
size : ( dimensions . padded_bytes_per_row
* dimensions . height as usize ) as u64 ,
usage : wgpu ::BufferUsages ::MAP_READ
| wgpu ::BufferUsages ::COPY_DST ,
mapped_at_creation : false ,
} ) ;
encoder . copy_texture_to_buffer (
texture . as_image_copy ( ) ,
wgpu ::TexelCopyBufferInfo {
buffer : & output_buffer ,
layout : wgpu ::TexelCopyBufferLayout {
offset : 0 ,
bytes_per_row : Some ( dimensions . padded_bytes_per_row as u32 ) ,
rows_per_image : None ,
} ,
} ,
texture_extent ,
) ;
self . staging_belt . finish ( ) ;
let index = self . engine . queue . submit ( [ encoder . finish ( ) ] ) ;
self . staging_belt . recall ( ) ;
let slice = output_buffer . slice ( .. ) ;
slice . map_async ( wgpu ::MapMode ::Read , | _ | { } ) ;
2025-10-31 14:11:20 +01:00
let _ = self . engine . device . poll ( wgpu ::PollType ::Wait {
submission_index : Some ( index ) ,
timeout : None ,
} ) ;
2025-03-24 01:28:39 +01:00
let mapped_buffer = slice . get_mapped_range ( ) ;
mapped_buffer . chunks ( dimensions . padded_bytes_per_row ) . fold (
vec! [ ] ,
| mut acc , row | {
acc . extend ( & row [ .. dimensions . unpadded_bytes_per_row ] ) ;
acc
} ,
)
2024-04-03 21:07:54 +02:00
}
fn prepare (
& mut self ,
encoder : & mut wgpu ::CommandEncoder ,
2024-04-05 00:40:39 +02:00
viewport : & Viewport ,
2024-04-03 21:07:54 +02:00
) {
2025-09-02 23:29:22 +02:00
let scale_factor = viewport . scale_factor ( ) ;
2024-04-05 00:40:39 +02:00
2025-03-24 01:28:39 +01:00
self . text_viewport
. update ( & self . engine . queue , viewport . physical_size ( ) ) ;
2024-05-08 13:22:22 +02:00
2025-01-26 03:53:18 +01:00
let physical_bounds = Rectangle ::< f32 > ::from ( Rectangle ::with_size (
viewport . physical_size ( ) ,
) ) ;
2025-08-18 20:51:57 +02:00
self . layers . merge ( ) ;
for layer in self . layers . iter ( ) {
2025-10-25 22:53:44 +02:00
let clip_bounds = layer . bounds * scale_factor ;
2025-01-26 03:53:18 +01:00
if physical_bounds
2025-10-25 22:53:44 +02:00
. intersection ( & clip_bounds )
2025-01-26 03:53:18 +01:00
. and_then ( Rectangle ::snap )
. is_none ( )
{
continue ;
}
2024-04-05 23:59:21 +02:00
if ! layer . quads . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let prepare_span = debug ::prepare ( debug ::Primitive ::Quad ) ;
2025-03-24 01:28:39 +01:00
self . quad . prepare (
& self . engine . quad_pipeline ,
& self . engine . device ,
& mut self . staging_belt ,
2024-04-05 23:59:21 +02:00
encoder ,
& layer . quads ,
viewport . projection ( ) ,
scale_factor ,
) ;
2025-04-09 21:50:21 +02:00
prepare_span . finish ( ) ;
2024-04-05 23:59:21 +02:00
}
if ! layer . triangles . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let prepare_span = debug ::prepare ( debug ::Primitive ::Triangle ) ;
2025-03-24 01:28:39 +01:00
self . triangle . prepare (
& self . engine . triangle_pipeline ,
& self . engine . device ,
& mut self . staging_belt ,
2024-04-05 23:59:21 +02:00
encoder ,
& layer . triangles ,
2024-04-07 18:45:30 +02:00
Transformation ::scale ( scale_factor ) ,
viewport . physical_size ( ) ,
2024-04-05 23:59:21 +02:00
) ;
2025-04-09 21:50:21 +02:00
prepare_span . finish ( ) ;
2024-04-05 23:59:21 +02:00
}
2024-04-08 15:04:35 +02:00
if ! layer . primitives . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let prepare_span = debug ::prepare ( debug ::Primitive ::Shader ) ;
2025-03-24 01:28:39 +01:00
let mut primitive_storage = self
. engine
. primitive_storage
. write ( )
. expect ( " Write primitive storage " ) ;
2024-04-08 15:04:35 +02:00
for instance in & layer . primitives {
instance . primitive . prepare (
2025-09-06 20:23:31 +02:00
& mut primitive_storage ,
2025-03-24 01:28:39 +01:00
& self . engine . device ,
& self . engine . queue ,
self . engine . format ,
2024-04-08 15:04:35 +02:00
& instance . bounds ,
viewport ,
) ;
}
2025-04-09 21:50:21 +02:00
prepare_span . finish ( ) ;
2024-04-08 15:04:35 +02:00
}
2024-04-05 23:59:21 +02:00
#[ cfg(any(feature = " svg " , feature = " image " )) ]
if ! layer . images . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let prepare_span = debug ::prepare ( debug ::Primitive ::Image ) ;
2025-03-24 01:28:39 +01:00
self . image . prepare (
& self . engine . image_pipeline ,
& self . engine . device ,
& mut self . staging_belt ,
2024-04-05 23:59:21 +02:00
encoder ,
2024-05-06 12:14:42 +02:00
& mut self . image_cache . borrow_mut ( ) ,
2024-04-05 23:59:21 +02:00
& layer . images ,
viewport . projection ( ) ,
scale_factor ,
) ;
2025-04-09 21:50:21 +02:00
prepare_span . finish ( ) ;
2024-04-03 21:07:54 +02:00
}
2024-08-03 16:23:30 +02:00
if ! layer . text . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let prepare_span = debug ::prepare ( debug ::Primitive ::Text ) ;
2025-03-24 01:28:39 +01:00
self . text . prepare (
& self . engine . text_pipeline ,
& self . engine . device ,
& self . engine . queue ,
2024-08-03 16:23:30 +02:00
& self . text_viewport ,
encoder ,
& layer . text ,
layer . bounds ,
Transformation ::scale ( scale_factor ) ,
) ;
2025-04-09 21:50:21 +02:00
prepare_span . finish ( ) ;
2024-08-03 16:23:30 +02:00
}
2024-04-03 21:07:54 +02:00
}
}
fn render (
& mut self ,
encoder : & mut wgpu ::CommandEncoder ,
frame : & wgpu ::TextureView ,
clear_color : Option < Color > ,
2024-04-05 00:40:39 +02:00
viewport : & Viewport ,
2024-04-03 21:07:54 +02:00
) {
use std ::mem ::ManuallyDrop ;
let mut render_pass = ManuallyDrop ::new ( encoder . begin_render_pass (
& wgpu ::RenderPassDescriptor {
label : Some ( " iced_wgpu render pass " ) ,
color_attachments : & [ Some ( wgpu ::RenderPassColorAttachment {
view : frame ,
2025-07-22 02:50:42 +02:00
depth_slice : None ,
2024-04-03 21:07:54 +02:00
resolve_target : None ,
ops : wgpu ::Operations {
load : match clear_color {
Some ( background_color ) = > wgpu ::LoadOp ::Clear ( {
let [ r , g , b , a ] =
graphics ::color ::pack ( background_color )
. components ( ) ;
wgpu ::Color {
r : f64 ::from ( r ) ,
g : f64 ::from ( g ) ,
b : f64 ::from ( b ) ,
a : f64 ::from ( a ) ,
}
} ) ,
None = > wgpu ::LoadOp ::Load ,
} ,
store : wgpu ::StoreOp ::Store ,
} ,
} ) ] ,
depth_stencil_attachment : None ,
timestamp_writes : None ,
occlusion_query_set : None ,
} ,
) ) ;
let mut quad_layer = 0 ;
let mut mesh_layer = 0 ;
let mut text_layer = 0 ;
#[ cfg(any(feature = " svg " , feature = " image " )) ]
let mut image_layer = 0 ;
2025-09-02 23:29:22 +02:00
let scale_factor = viewport . scale_factor ( ) ;
2024-04-05 00:40:39 +02:00
let physical_bounds = Rectangle ::< f32 > ::from ( Rectangle ::with_size (
viewport . physical_size ( ) ,
) ) ;
2024-04-05 23:59:21 +02:00
let scale = Transformation ::scale ( scale_factor ) ;
2024-04-03 21:07:54 +02:00
2024-04-05 23:59:21 +02:00
for layer in self . layers . iter ( ) {
let Some ( physical_bounds ) =
2025-02-14 23:14:13 +01:00
physical_bounds . intersection ( & ( layer . bounds * scale_factor ) )
2024-04-05 23:59:21 +02:00
else {
continue ;
} ;
2024-04-05 00:40:39 +02:00
2024-04-08 15:04:35 +02:00
let Some ( scissor_rect ) = physical_bounds . snap ( ) else {
continue ;
} ;
2024-04-05 00:40:39 +02:00
2024-04-05 23:59:21 +02:00
if ! layer . quads . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let render_span = debug ::render ( debug ::Primitive ::Quad ) ;
2025-03-24 01:28:39 +01:00
self . quad . render (
& self . engine . quad_pipeline ,
2024-04-05 23:59:21 +02:00
quad_layer ,
scissor_rect ,
& layer . quads ,
& mut render_pass ,
) ;
2025-04-09 21:50:21 +02:00
render_span . finish ( ) ;
2024-04-05 00:40:39 +02:00
2024-04-05 23:59:21 +02:00
quad_layer + = 1 ;
}
2024-04-03 21:07:54 +02:00
2024-04-05 23:59:21 +02:00
if ! layer . triangles . is_empty ( ) {
let _ = ManuallyDrop ::into_inner ( render_pass ) ;
2025-04-09 21:50:21 +02:00
let render_span = debug ::render ( debug ::Primitive ::Triangle ) ;
2025-03-24 01:28:39 +01:00
mesh_layer + = self . triangle . render (
& self . engine . triangle_pipeline ,
2024-04-05 23:59:21 +02:00
encoder ,
frame ,
mesh_layer ,
& layer . triangles ,
physical_bounds ,
scale ,
) ;
2025-04-09 21:50:21 +02:00
render_span . finish ( ) ;
2024-04-05 23:59:21 +02:00
render_pass = ManuallyDrop ::new ( encoder . begin_render_pass (
& wgpu ::RenderPassDescriptor {
label : Some ( " iced_wgpu render pass " ) ,
color_attachments : & [ Some (
wgpu ::RenderPassColorAttachment {
view : frame ,
2025-07-22 02:50:42 +02:00
depth_slice : None ,
2024-04-05 23:59:21 +02:00
resolve_target : None ,
ops : wgpu ::Operations {
load : wgpu ::LoadOp ::Load ,
store : wgpu ::StoreOp ::Store ,
2024-04-03 21:07:54 +02:00
} ,
2024-04-05 23:59:21 +02:00
} ,
) ] ,
depth_stencil_attachment : None ,
timestamp_writes : None ,
occlusion_query_set : None ,
} ,
) ) ;
}
2024-04-08 15:04:35 +02:00
if ! layer . primitives . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let render_span = debug ::render ( debug ::Primitive ::Shader ) ;
2024-04-08 15:04:35 +02:00
2025-03-24 01:28:39 +01:00
let primitive_storage = self
. engine
. primitive_storage
. read ( )
. expect ( " Read primitive storage " ) ;
2025-09-06 21:17:38 +02:00
let mut need_render = Vec ::new ( ) ;
2024-04-08 15:04:35 +02:00
for instance in & layer . primitives {
2025-09-07 04:55:45 +02:00
let bounds = instance . bounds * scale ;
2024-04-08 15:04:35 +02:00
if let Some ( clip_bounds ) = ( instance . bounds * scale )
. intersection ( & physical_bounds )
. and_then ( Rectangle ::snap )
{
2025-09-07 04:55:45 +02:00
render_pass . set_viewport (
bounds . x ,
bounds . y ,
bounds . width ,
bounds . height ,
0.0 ,
1.0 ,
) ;
render_pass . set_scissor_rect (
clip_bounds . x ,
clip_bounds . y ,
clip_bounds . width ,
clip_bounds . height ,
2025-09-06 21:17:38 +02:00
) ;
2025-09-07 04:55:45 +02:00
let drawn = instance
. primitive
. draw ( & primitive_storage , & mut render_pass ) ;
2025-09-06 21:17:38 +02:00
if ! drawn {
need_render . push ( ( instance , clip_bounds ) ) ;
}
}
}
2025-09-07 04:55:45 +02:00
render_pass . set_viewport (
0.0 ,
0.0 ,
viewport . physical_width ( ) as f32 ,
viewport . physical_height ( ) as f32 ,
0.0 ,
1.0 ,
) ;
render_pass . set_scissor_rect (
0 ,
0 ,
viewport . physical_width ( ) ,
viewport . physical_height ( ) ,
) ;
2025-09-06 21:17:38 +02:00
if ! need_render . is_empty ( ) {
let _ = ManuallyDrop ::into_inner ( render_pass ) ;
for ( instance , clip_bounds ) in need_render {
2024-04-08 15:04:35 +02:00
instance . primitive . render (
2025-03-24 01:28:39 +01:00
& primitive_storage ,
2025-09-06 20:23:31 +02:00
encoder ,
2024-04-08 15:04:35 +02:00
frame ,
& clip_bounds ,
) ;
}
2025-09-06 21:17:38 +02:00
render_pass = ManuallyDrop ::new ( encoder . begin_render_pass (
& wgpu ::RenderPassDescriptor {
label : Some ( " iced_wgpu render pass " ) ,
color_attachments : & [ Some (
wgpu ::RenderPassColorAttachment {
view : frame ,
depth_slice : None ,
resolve_target : None ,
ops : wgpu ::Operations {
load : wgpu ::LoadOp ::Load ,
store : wgpu ::StoreOp ::Store ,
} ,
} ,
) ] ,
depth_stencil_attachment : None ,
timestamp_writes : None ,
occlusion_query_set : None ,
} ,
) ) ;
2024-04-08 15:04:35 +02:00
}
2025-04-09 21:50:21 +02:00
render_span . finish ( ) ;
2024-04-08 15:04:35 +02:00
}
2024-04-05 23:59:21 +02:00
#[ cfg(any(feature = " svg " , feature = " image " )) ]
if ! layer . images . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let render_span = debug ::render ( debug ::Primitive ::Image ) ;
2025-03-24 01:28:39 +01:00
self . image . render (
& self . engine . image_pipeline ,
2024-04-05 23:59:21 +02:00
image_layer ,
scissor_rect ,
& mut render_pass ,
) ;
2025-04-09 21:50:21 +02:00
render_span . finish ( ) ;
2024-04-05 23:59:21 +02:00
image_layer + = 1 ;
2024-04-03 21:07:54 +02:00
}
2024-08-03 16:23:30 +02:00
if ! layer . text . is_empty ( ) {
2025-04-09 21:50:21 +02:00
let render_span = debug ::render ( debug ::Primitive ::Text ) ;
2025-03-24 01:28:39 +01:00
text_layer + = self . text . render (
& self . engine . text_pipeline ,
2024-08-03 16:23:30 +02:00
& self . text_viewport ,
text_layer ,
& layer . text ,
scissor_rect ,
& mut render_pass ,
) ;
2025-04-09 21:50:21 +02:00
render_span . finish ( ) ;
2024-08-03 16:23:30 +02:00
}
2024-04-03 21:07:54 +02:00
}
let _ = ManuallyDrop ::into_inner ( render_pass ) ;
2025-04-29 23:01:45 +02:00
debug ::layers_rendered ( | | {
self . layers
. iter ( )
. filter ( | layer | {
! layer . is_empty ( )
& & physical_bounds
. intersection ( & ( layer . bounds * scale_factor ) )
. is_some_and ( | viewport | viewport . snap ( ) . is_some ( ) )
} )
. count ( )
} ) ;
2024-04-03 21:07:54 +02:00
}
}
impl core ::Renderer for Renderer {
fn start_layer ( & mut self , bounds : Rectangle ) {
2024-04-05 23:59:21 +02:00
self . layers . push_clip ( bounds ) ;
2024-04-03 21:07:54 +02:00
}
2024-04-09 22:25:16 +02:00
fn end_layer ( & mut self ) {
2024-04-03 21:07:54 +02:00
self . layers . pop_clip ( ) ;
}
fn start_transformation ( & mut self , transformation : Transformation ) {
self . layers . push_transformation ( transformation ) ;
}
2024-04-09 22:25:16 +02:00
fn end_transformation ( & mut self ) {
2024-04-03 21:07:54 +02:00
self . layers . pop_transformation ( ) ;
}
fn fill_quad (
& mut self ,
quad : core ::renderer ::Quad ,
background : impl Into < Background > ,
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
layer . draw_quad ( quad , background . into ( ) , transformation ) ;
2024-04-03 21:07:54 +02:00
}
2025-08-17 00:58:37 +02:00
fn reset ( & mut self , new_bounds : Rectangle ) {
self . layers . reset ( new_bounds ) ;
2024-04-03 21:07:54 +02:00
}
2025-10-25 00:07:13 +02:00
fn allocate_image (
& mut self ,
_handle : & core ::image ::Handle ,
2025-10-28 21:19:25 +01:00
_callback : impl FnOnce ( Result < core ::image ::Allocation , core ::image ::Error > )
+ Send
+ 'static ,
2025-10-25 00:07:13 +02:00
) {
#[ cfg(feature = " image " ) ]
self . image_cache
. get_mut ( )
. allocate_image ( _handle , _callback ) ;
}
2024-04-03 21:07:54 +02:00
}
impl core ::text ::Renderer for Renderer {
type Font = Font ;
type Paragraph = Paragraph ;
type Editor = Editor ;
const ICON_FONT : Font = Font ::with_name ( " Iced-Icons " ) ;
const CHECKMARK_ICON : char = '\u{f00c}' ;
const ARROW_DOWN_ICON : char = '\u{e800}' ;
fn default_font ( & self ) -> Self ::Font {
self . default_font
}
fn default_size ( & self ) -> Pixels {
self . default_text_size
}
fn fill_paragraph (
& mut self ,
text : & Self ::Paragraph ,
position : Point ,
color : Color ,
clip_bounds : Rectangle ,
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
layer . draw_paragraph (
text ,
position ,
color ,
clip_bounds ,
transformation ,
) ;
2024-04-03 21:07:54 +02:00
}
fn fill_editor (
& mut self ,
editor : & Self ::Editor ,
position : Point ,
color : Color ,
clip_bounds : Rectangle ,
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
layer . draw_editor ( editor , position , color , clip_bounds , transformation ) ;
2024-04-03 21:07:54 +02:00
}
fn fill_text (
& mut self ,
text : core ::Text ,
position : Point ,
color : Color ,
clip_bounds : Rectangle ,
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
layer . draw_text ( text , position , color , clip_bounds , transformation ) ;
2024-04-03 21:07:54 +02:00
}
}
#[ cfg(feature = " image " ) ]
impl core ::image ::Renderer for Renderer {
type Handle = core ::image ::Handle ;
2025-10-28 21:19:25 +01:00
fn load_image (
& self ,
handle : & Self ::Handle ,
) -> Result < core ::image ::Allocation , core ::image ::Error > {
self . image_cache . borrow_mut ( ) . load_image (
& self . engine . device ,
& self . engine . queue ,
handle ,
)
}
fn measure_image ( & self , handle : & Self ::Handle ) -> Option < core ::Size < u32 > > {
2024-05-06 12:14:42 +02:00
self . image_cache . borrow_mut ( ) . measure_image ( handle )
2024-04-03 21:07:54 +02:00
}
2025-10-28 19:44:46 +01:00
fn draw_image (
& mut self ,
image : core ::Image ,
bounds : Rectangle ,
clip_bounds : Rectangle ,
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
2025-10-28 19:44:46 +01:00
layer . draw_raster ( image , bounds , clip_bounds , transformation ) ;
2024-04-03 21:07:54 +02:00
}
}
#[ cfg(feature = " svg " ) ]
impl core ::svg ::Renderer for Renderer {
2024-05-10 20:08:09 +02:00
fn measure_svg ( & self , handle : & core ::svg ::Handle ) -> core ::Size < u32 > {
2024-05-06 12:14:42 +02:00
self . image_cache . borrow_mut ( ) . measure_svg ( handle )
2024-04-03 21:07:54 +02:00
}
2025-10-28 19:44:46 +01:00
fn draw_svg (
& mut self ,
svg : core ::Svg ,
bounds : Rectangle ,
clip_bounds : Rectangle ,
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
2025-10-28 19:44:46 +01:00
layer . draw_svg ( svg , bounds , clip_bounds , transformation ) ;
2024-04-03 21:07:54 +02:00
}
}
impl graphics ::mesh ::Renderer for Renderer {
fn draw_mesh ( & mut self , mesh : graphics ::Mesh ) {
2025-02-03 11:53:45 +01:00
debug_assert! (
! mesh . indices ( ) . is_empty ( ) ,
" Mesh must not have empty indices "
) ;
debug_assert! (
2025-09-19 18:22:45 +02:00
mesh . indices ( ) . len ( ) . is_multiple_of ( 3 ) ,
2025-02-03 11:53:45 +01:00
" Mesh indices length must be a multiple of 3 "
) ;
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
layer . draw_mesh ( mesh , transformation ) ;
2024-04-03 21:07:54 +02:00
}
}
#[ cfg(feature = " geometry " ) ]
impl graphics ::geometry ::Renderer for Renderer {
type Geometry = Geometry ;
type Frame = geometry ::Frame ;
2025-08-17 22:31:58 +02:00
fn new_frame ( & self , bounds : Rectangle ) -> Self ::Frame {
geometry ::Frame ::new ( bounds )
2024-04-03 21:07:54 +02:00
}
fn draw_geometry ( & mut self , geometry : Self ::Geometry ) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
2024-04-03 21:07:54 +02:00
match geometry {
2024-08-04 03:28:43 +02:00
Geometry ::Live {
meshes ,
images ,
text ,
} = > {
2024-04-09 22:25:16 +02:00
layer . draw_mesh_group ( meshes , transformation ) ;
2024-08-04 03:28:43 +02:00
for image in images {
2024-08-04 04:30:12 +02:00
layer . draw_image ( image , transformation ) ;
2024-08-04 03:28:43 +02:00
}
2024-04-09 22:25:16 +02:00
layer . draw_text_group ( text , transformation ) ;
2024-04-03 21:07:54 +02:00
}
2024-04-05 23:59:21 +02:00
Geometry ::Cached ( cache ) = > {
2024-04-06 03:06:40 +02:00
if let Some ( meshes ) = cache . meshes {
2024-04-09 22:25:16 +02:00
layer . draw_mesh_cache ( meshes , transformation ) ;
2024-04-06 03:06:40 +02:00
}
2024-08-04 03:28:43 +02:00
if let Some ( images ) = cache . images {
2024-08-04 04:30:12 +02:00
for image in images . iter ( ) . cloned ( ) {
2024-08-04 03:28:43 +02:00
layer . draw_image ( image , transformation ) ;
}
}
2024-04-06 03:06:40 +02:00
if let Some ( text ) = cache . text {
2024-04-09 22:25:16 +02:00
layer . draw_text_cache ( text , transformation ) ;
2024-04-06 03:06:40 +02:00
}
2024-04-03 21:07:54 +02:00
}
}
}
}
2024-04-08 15:04:35 +02:00
impl primitive ::Renderer for Renderer {
fn draw_primitive ( & mut self , bounds : Rectangle , primitive : impl Primitive ) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
2025-09-06 20:23:31 +02:00
layer . draw_primitive ( bounds , primitive , transformation ) ;
2024-04-08 15:04:35 +02:00
}
}
2024-04-03 21:07:54 +02:00
impl graphics ::compositor ::Default for crate ::Renderer {
type Compositor = window ::Compositor ;
}
2025-03-24 01:28:39 +01:00
impl renderer ::Headless for Renderer {
async fn new (
default_font : Font ,
default_text_size : Pixels ,
2025-03-24 20:18:24 +01:00
backend : Option < & str > ,
2025-03-24 01:28:39 +01:00
) -> Option < Self > {
2025-03-24 20:18:24 +01:00
if backend . is_some_and ( | backend | backend ! = " wgpu " ) {
return None ;
}
2025-03-24 01:28:39 +01:00
let instance = wgpu ::Instance ::new ( & wgpu ::InstanceDescriptor {
backends : wgpu ::Backends ::from_env ( )
. unwrap_or ( wgpu ::Backends ::PRIMARY ) ,
flags : wgpu ::InstanceFlags ::empty ( ) ,
.. wgpu ::InstanceDescriptor ::default ( )
} ) ;
let adapter = instance
. request_adapter ( & wgpu ::RequestAdapterOptions {
power_preference : wgpu ::PowerPreference ::HighPerformance ,
force_fallback_adapter : false ,
compatible_surface : None ,
} )
2025-07-22 02:50:42 +02:00
. await
. ok ( ) ? ;
2025-03-24 01:28:39 +01:00
let ( device , queue ) = adapter
2025-07-22 02:50:42 +02:00
. request_device ( & wgpu ::DeviceDescriptor {
label : Some ( " iced_wgpu [headless] " ) ,
required_features : wgpu ::Features ::empty ( ) ,
required_limits : wgpu ::Limits {
max_bind_groups : 2 ,
.. wgpu ::Limits ::default ( )
2025-03-24 01:28:39 +01:00
} ,
2025-07-22 02:50:42 +02:00
memory_hints : wgpu ::MemoryHints ::MemoryUsage ,
trace : wgpu ::Trace ::Off ,
2025-10-31 14:11:20 +01:00
experimental_features : wgpu ::ExperimentalFeatures ::disabled ( ) ,
2025-07-22 02:50:42 +02:00
} )
2025-03-24 01:28:39 +01:00
. await
. ok ( ) ? ;
let engine = Engine ::new (
& adapter ,
device ,
queue ,
if graphics ::color ::GAMMA_CORRECTION {
wgpu ::TextureFormat ::Rgba8UnormSrgb
} else {
wgpu ::TextureFormat ::Rgba8Unorm
} ,
Some ( graphics ::Antialiasing ::MSAAx4 ) ,
2025-10-24 17:23:40 +02:00
Shell ::headless ( ) ,
2025-03-24 01:28:39 +01:00
) ;
Some ( Self ::new ( engine , default_font , default_text_size ) )
}
2025-03-24 20:18:24 +01:00
fn name ( & self ) -> String {
" wgpu " . to_owned ( )
}
2025-03-24 01:28:39 +01:00
fn screenshot (
& mut self ,
size : Size < u32 > ,
scale_factor : f32 ,
background_color : Color ,
) -> Vec < u8 > {
self . screenshot (
2025-09-02 23:29:22 +02:00
& Viewport ::with_physical_size ( size , scale_factor ) ,
2025-03-24 01:28:39 +01:00
background_color ,
)
}
}