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
) ]
2023-05-11 17:28:51 +02:00
#![ cfg_attr(docsrs, feature(doc_auto_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 ;
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 ;
2024-04-05 23:59:21 +02: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 ;
use crate ::core ::{
2024-05-03 09:11:46 +02:00
Background , Color , Font , Pixels , Point , Rectangle , Size , Transformation ,
Vector ,
2024-04-03 21:07:54 +02:00
} ;
use crate ::graphics ::text ::{ Editor , Paragraph } ;
use crate ::graphics ::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
#[ allow(missing_debug_implementations) ]
pub struct Renderer {
default_font : Font ,
default_text_size : Pixels ,
layers : layer ::Stack ,
2024-04-05 23:59:21 +02:00
triangle_storage : triangle ::Storage ,
text_storage : text ::Storage ,
2024-04-03 21:07:54 +02:00
// TODO: Centralize all the image feature handling
#[ cfg(any(feature = " svg " , feature = " image " )) ]
image_cache : image ::cache ::Shared ,
}
impl Renderer {
2024-04-08 15:35:54 +02:00
pub fn new (
_engine : & Engine ,
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 ( ) ,
2024-04-05 23:59:21 +02:00
triangle_storage : triangle ::Storage ::new ( ) ,
text_storage : text ::Storage ::new ( ) ,
2024-04-03 21:07:54 +02:00
#[ cfg(any(feature = " svg " , feature = " image " )) ]
image_cache : _engine . image_cache ( ) . clone ( ) ,
}
}
pub fn present < T : AsRef < str > > (
& mut self ,
engine : & mut Engine ,
device : & wgpu ::Device ,
queue : & wgpu ::Queue ,
encoder : & mut wgpu ::CommandEncoder ,
clear_color : Option < Color > ,
format : wgpu ::TextureFormat ,
frame : & wgpu ::TextureView ,
viewport : & Viewport ,
overlay : & [ T ] ,
) {
2024-04-03 22:57:28 +02:00
self . draw_overlay ( overlay , viewport ) ;
2024-04-05 00:40:39 +02:00
self . prepare ( engine , device , queue , format , encoder , viewport ) ;
2024-04-07 18:45:30 +02:00
self . render ( engine , encoder , frame , clear_color , viewport ) ;
2024-04-05 23:59:21 +02:00
self . triangle_storage . trim ( ) ;
self . text_storage . trim ( ) ;
2024-04-03 21:07:54 +02:00
}
fn prepare (
& mut self ,
engine : & mut Engine ,
device : & wgpu ::Device ,
queue : & wgpu ::Queue ,
_format : wgpu ::TextureFormat ,
encoder : & mut wgpu ::CommandEncoder ,
2024-04-05 00:40:39 +02:00
viewport : & Viewport ,
2024-04-03 21:07:54 +02:00
) {
2024-04-05 00:40:39 +02:00
let scale_factor = viewport . scale_factor ( ) as f32 ;
2024-04-03 21:07:54 +02:00
for layer in self . layers . iter_mut ( ) {
2024-04-05 23:59:21 +02:00
if ! layer . quads . is_empty ( ) {
engine . quad_pipeline . prepare (
device ,
encoder ,
& mut engine . staging_belt ,
& layer . quads ,
viewport . projection ( ) ,
scale_factor ,
) ;
}
if ! layer . triangles . is_empty ( ) {
engine . triangle_pipeline . prepare (
device ,
encoder ,
& mut engine . staging_belt ,
& mut self . triangle_storage ,
& 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
) ;
}
2024-04-08 15:04:35 +02:00
if ! layer . primitives . is_empty ( ) {
for instance in & layer . primitives {
instance . primitive . prepare (
device ,
queue ,
engine . format ,
& mut engine . primitive_storage ,
& instance . bounds ,
viewport ,
) ;
}
}
2024-04-05 23:59:21 +02:00
if ! layer . text . is_empty ( ) {
engine . text_pipeline . prepare (
device ,
queue ,
encoder ,
& mut self . text_storage ,
& layer . text ,
layer . bounds ,
Transformation ::scale ( scale_factor ) ,
viewport . physical_size ( ) ,
) ;
}
#[ cfg(any(feature = " svg " , feature = " image " )) ]
if ! layer . images . is_empty ( ) {
engine . image_pipeline . prepare (
device ,
encoder ,
& mut engine . staging_belt ,
& layer . images ,
viewport . projection ( ) ,
scale_factor ,
) ;
2024-04-03 21:07:54 +02:00
}
}
}
fn render (
& mut self ,
engine : & mut Engine ,
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 ,
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 ;
2024-04-05 00:40:39 +02:00
let scale_factor = viewport . scale_factor ( ) as f32 ;
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 ) =
physical_bounds . intersection ( & ( layer . bounds * scale ) )
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 ( ) {
engine . quad_pipeline . render (
quad_layer ,
scissor_rect ,
& layer . quads ,
& mut render_pass ,
) ;
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 ) ;
mesh_layer + = engine . triangle_pipeline . render (
encoder ,
frame ,
& self . triangle_storage ,
mesh_layer ,
& layer . triangles ,
physical_bounds ,
scale ,
) ;
render_pass = ManuallyDrop ::new ( encoder . begin_render_pass (
& wgpu ::RenderPassDescriptor {
label : Some ( " iced_wgpu render pass " ) ,
color_attachments : & [ Some (
wgpu ::RenderPassColorAttachment {
view : frame ,
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 ( ) {
let _ = ManuallyDrop ::into_inner ( render_pass ) ;
for instance in & layer . primitives {
if let Some ( clip_bounds ) = ( instance . bounds * scale )
. intersection ( & physical_bounds )
. and_then ( Rectangle ::snap )
{
instance . primitive . render (
encoder ,
& engine . primitive_storage ,
frame ,
& clip_bounds ,
) ;
}
}
render_pass = ManuallyDrop ::new ( encoder . begin_render_pass (
& wgpu ::RenderPassDescriptor {
label : Some ( " iced_wgpu render pass " ) ,
color_attachments : & [ Some (
wgpu ::RenderPassColorAttachment {
view : frame ,
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-05 23:59:21 +02:00
if ! layer . text . is_empty ( ) {
text_layer + = engine . text_pipeline . render (
& self . text_storage ,
text_layer ,
& layer . text ,
scissor_rect ,
& mut render_pass ,
) ;
}
#[ cfg(any(feature = " svg " , feature = " image " )) ]
if ! layer . images . is_empty ( ) {
engine . image_pipeline . render (
image_layer ,
scissor_rect ,
& mut render_pass ,
) ;
image_layer + = 1 ;
2024-04-03 21:07:54 +02:00
}
}
let _ = ManuallyDrop ::into_inner ( render_pass ) ;
}
2024-04-03 22:57:28 +02:00
fn draw_overlay (
& mut self ,
overlay : & [ impl AsRef < str > ] ,
viewport : & Viewport ,
) {
use crate ::core ::alignment ;
use crate ::core ::text ::Renderer as _ ;
use crate ::core ::Renderer as _ ;
self . with_layer (
Rectangle ::with_size ( viewport . logical_size ( ) ) ,
| renderer | {
for ( i , line ) in overlay . iter ( ) . enumerate ( ) {
let text = crate ::core ::Text {
content : line . as_ref ( ) . to_owned ( ) ,
bounds : viewport . logical_size ( ) ,
size : Pixels ( 20.0 ) ,
line_height : core ::text ::LineHeight ::default ( ) ,
font : Font ::MONOSPACE ,
horizontal_alignment : alignment ::Horizontal ::Left ,
vertical_alignment : alignment ::Vertical ::Top ,
shaping : core ::text ::Shaping ::Basic ,
} ;
renderer . fill_text (
text . clone ( ) ,
Point ::new ( 11.0 , 11.0 + 25.0 * i as f32 ) ,
Color ::new ( 0.9 , 0.9 , 0.9 , 1.0 ) ,
Rectangle ::with_size ( Size ::INFINITY ) ,
) ;
renderer . fill_text (
text ,
Point ::new ( 11.0 , 11.0 + 25.0 * i as f32 )
+ Vector ::new ( - 1.0 , - 1.0 ) ,
Color ::BLACK ,
Rectangle ::with_size ( Size ::INFINITY ) ,
) ;
}
} ,
) ;
}
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
}
fn clear ( & mut self ) {
self . layers . clear ( ) ;
}
}
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 ;
fn measure_image ( & self , handle : & Self ::Handle ) -> Size < u32 > {
self . image_cache . lock ( ) . measure_image ( handle )
}
fn draw_image (
& mut self ,
handle : Self ::Handle ,
filter_method : core ::image ::FilterMethod ,
bounds : Rectangle ,
2024-05-03 09:11:46 +02:00
rotation : core ::Radians ,
2024-04-03 21:07:54 +02:00
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
2024-05-02 13:15:17 +02:00
layer . draw_image (
handle ,
filter_method ,
bounds ,
transformation ,
rotation ,
) ;
2024-04-03 21:07:54 +02:00
}
}
#[ cfg(feature = " svg " ) ]
impl core ::svg ::Renderer for Renderer {
fn measure_svg ( & self , handle : & core ::svg ::Handle ) -> Size < u32 > {
self . image_cache . lock ( ) . measure_svg ( handle )
}
fn draw_svg (
& mut self ,
handle : core ::svg ::Handle ,
color_filter : Option < Color > ,
bounds : Rectangle ,
2024-05-03 09:11:46 +02:00
rotation : core ::Radians ,
2024-04-03 21:07:54 +02:00
) {
2024-04-09 22:25:16 +02:00
let ( layer , transformation ) = self . layers . current_mut ( ) ;
2024-05-02 15:21:22 +02:00
layer . draw_svg ( handle , color_filter , bounds , transformation , rotation ) ;
2024-04-03 21:07:54 +02:00
}
}
impl graphics ::mesh ::Renderer for Renderer {
fn draw_mesh ( & mut self , mesh : graphics ::Mesh ) {
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 ;
fn new_frame ( & self , size : Size ) -> Self ::Frame {
geometry ::Frame ::new ( size )
}
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-04-05 23:59:21 +02:00
Geometry ::Live { meshes , text } = > {
2024-04-09 22:25:16 +02:00
layer . draw_mesh_group ( meshes , transformation ) ;
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
}
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 ( ) ;
layer . draw_primitive ( bounds , Box ::new ( 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 ;
}