summaryrefslogtreecommitdiffstats
path: root/wgpu/src/lib.rs
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-04-03 21:07:54 +0200
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-04-03 21:07:54 +0200
commitb05e61f5c8ae61c9f3c7cc08cded53901ebbccfd (patch)
tree3d35a011d94d4936f09b5a9be4031358a09c60da /wgpu/src/lib.rs
parent99a904112ca111f2ab0e60e30b6c369741b1653b (diff)
downloadiced-b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd.tar.gz
iced-b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd.tar.bz2
iced-b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd.zip
Redesign `iced_wgpu` layering architecture
Diffstat (limited to 'wgpu/src/lib.rs')
-rw-r--r--wgpu/src/lib.rs578
1 files changed, 570 insertions, 8 deletions
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index b00e5c3c..0e173e0a 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -22,8 +22,8 @@
)]
#![forbid(rust_2018_idioms)]
#![deny(
- missing_debug_implementations,
- missing_docs,
+ // missing_debug_implementations,
+ //missing_docs,
unsafe_code,
unused_results,
rustdoc::broken_intra_doc_links
@@ -37,13 +37,21 @@ pub mod window;
#[cfg(feature = "geometry")]
pub mod geometry;
-mod backend;
mod buffer;
mod color;
+mod engine;
mod quad;
mod text;
mod triangle;
+#[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;
+
use buffer::Buffer;
pub use iced_graphics as graphics;
@@ -51,16 +59,570 @@ pub use iced_graphics::core;
pub use wgpu;
-pub use backend::Backend;
-pub use layer::Layer;
+pub use engine::Engine;
+pub use layer::{Layer, LayerMut};
pub use primitive::Primitive;
pub use settings::Settings;
-#[cfg(any(feature = "image", feature = "svg"))]
-mod image;
+#[cfg(feature = "geometry")]
+pub use geometry::Geometry;
+
+use crate::core::{
+ Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
+};
+use crate::graphics::text::{Editor, Paragraph};
+use crate::graphics::Viewport;
+
+use std::borrow::Cow;
/// A [`wgpu`] graphics renderer for [`iced`].
///
/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
/// [`iced`]: https://github.com/iced-rs/iced
-pub type Renderer = iced_graphics::Renderer<Backend>;
+#[allow(missing_debug_implementations)]
+pub struct Renderer {
+ default_font: Font,
+ default_text_size: Pixels,
+ layers: layer::Stack,
+
+ // TODO: Centralize all the image feature handling
+ #[cfg(any(feature = "svg", feature = "image"))]
+ image_cache: image::cache::Shared,
+}
+
+impl Renderer {
+ pub fn new(settings: Settings, _engine: &Engine) -> Self {
+ Self {
+ default_font: settings.default_font,
+ default_text_size: settings.default_text_size,
+ layers: layer::Stack::new(),
+
+ #[cfg(any(feature = "svg", feature = "image"))]
+ image_cache: _engine.image_cache().clone(),
+ }
+ }
+
+ pub fn draw_primitive(&mut self, _primitive: Primitive) {}
+
+ 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],
+ ) {
+ let target_size = viewport.physical_size();
+ let scale_factor = viewport.scale_factor() as f32;
+ let transformation = viewport.projection();
+
+ for line in overlay {
+ println!("{}", line.as_ref());
+ }
+
+ self.prepare(
+ engine,
+ device,
+ queue,
+ format,
+ encoder,
+ scale_factor,
+ target_size,
+ transformation,
+ );
+
+ self.render(
+ engine,
+ device,
+ encoder,
+ frame,
+ clear_color,
+ scale_factor,
+ target_size,
+ );
+ }
+
+ fn prepare(
+ &mut self,
+ engine: &mut Engine,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ _format: wgpu::TextureFormat,
+ encoder: &mut wgpu::CommandEncoder,
+ scale_factor: f32,
+ target_size: Size<u32>,
+ transformation: Transformation,
+ ) {
+ for layer in self.layers.iter_mut() {
+ match layer {
+ LayerMut::Live(live) => {
+ if !live.quads.is_empty() {
+ engine.quad_pipeline.prepare_batch(
+ device,
+ encoder,
+ &mut engine.staging_belt,
+ &live.quads,
+ transformation,
+ scale_factor,
+ );
+ }
+
+ if !live.meshes.is_empty() {
+ engine.triangle_pipeline.prepare_batch(
+ device,
+ encoder,
+ &mut engine.staging_belt,
+ &live.meshes,
+ transformation
+ * Transformation::scale(scale_factor),
+ );
+ }
+
+ if !live.text.is_empty() {
+ engine.text_pipeline.prepare_batch(
+ device,
+ queue,
+ encoder,
+ &live.text,
+ live.bounds.unwrap_or(Rectangle::with_size(
+ Size::INFINITY,
+ )),
+ scale_factor,
+ target_size,
+ );
+ }
+
+ #[cfg(any(feature = "svg", feature = "image"))]
+ if !live.images.is_empty() {
+ engine.image_pipeline.prepare(
+ device,
+ encoder,
+ &mut engine.staging_belt,
+ &live.images,
+ transformation,
+ scale_factor,
+ );
+ }
+ }
+ LayerMut::Cached(mut cached) => {
+ if !cached.quads.is_empty() {
+ engine.quad_pipeline.prepare_cache(
+ device,
+ encoder,
+ &mut engine.staging_belt,
+ &mut cached.quads,
+ transformation,
+ scale_factor,
+ );
+ }
+
+ if !cached.meshes.is_empty() {
+ engine.triangle_pipeline.prepare_cache(
+ device,
+ encoder,
+ &mut engine.staging_belt,
+ &mut cached.meshes,
+ transformation
+ * Transformation::scale(scale_factor),
+ );
+ }
+
+ if !cached.text.is_empty() {
+ let bounds = cached
+ .bounds
+ .unwrap_or(Rectangle::with_size(Size::INFINITY));
+
+ engine.text_pipeline.prepare_cache(
+ device,
+ queue,
+ encoder,
+ &mut cached.text,
+ bounds,
+ scale_factor,
+ target_size,
+ );
+ }
+
+ #[cfg(any(feature = "svg", feature = "image"))]
+ if !cached.images.is_empty() {
+ engine.image_pipeline.prepare(
+ device,
+ encoder,
+ &mut engine.staging_belt,
+ &cached.images,
+ transformation,
+ scale_factor,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ fn render(
+ &mut self,
+ engine: &mut Engine,
+ device: &wgpu::Device,
+ encoder: &mut wgpu::CommandEncoder,
+ frame: &wgpu::TextureView,
+ clear_color: Option<Color>,
+ scale_factor: f32,
+ target_size: Size<u32>,
+ ) {
+ 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;
+
+ // TODO: Can we avoid collecting here?
+ let layers: Vec<_> = self.layers.iter().collect();
+
+ for layer in &layers {
+ match layer {
+ Layer::Live(live) => {
+ let bounds = live
+ .bounds
+ .map(|bounds| bounds * scale_factor)
+ .map(Rectangle::snap)
+ .unwrap_or(Rectangle::with_size(target_size));
+
+ if !live.quads.is_empty() {
+ engine.quad_pipeline.render_batch(
+ quad_layer,
+ bounds,
+ &live.quads,
+ &mut render_pass,
+ );
+
+ quad_layer += 1;
+ }
+
+ if !live.meshes.is_empty() {
+ let _ = ManuallyDrop::into_inner(render_pass);
+
+ engine.triangle_pipeline.render_batch(
+ device,
+ encoder,
+ frame,
+ mesh_layer,
+ target_size,
+ &live.meshes,
+ bounds,
+ scale_factor,
+ );
+
+ mesh_layer += 1;
+
+ 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,
+ },
+ ));
+ }
+
+ if !live.text.is_empty() {
+ engine.text_pipeline.render_batch(
+ text_layer,
+ bounds,
+ &mut render_pass,
+ );
+
+ text_layer += 1;
+ }
+
+ #[cfg(any(feature = "svg", feature = "image"))]
+ if !live.images.is_empty() {
+ engine.image_pipeline.render(
+ image_layer,
+ bounds,
+ &mut render_pass,
+ );
+
+ image_layer += 1;
+ }
+ }
+ Layer::Cached(cached) => {
+ let bounds = cached
+ .bounds
+ .map(|bounds| bounds * scale_factor)
+ .map(Rectangle::snap)
+ .unwrap_or(Rectangle::with_size(target_size));
+
+ if !cached.quads.is_empty() {
+ engine.quad_pipeline.render_cache(
+ &cached.quads,
+ bounds,
+ &mut render_pass,
+ );
+ }
+
+ if !cached.meshes.is_empty() {
+ let _ = ManuallyDrop::into_inner(render_pass);
+
+ engine.triangle_pipeline.render_cache(
+ device,
+ encoder,
+ frame,
+ target_size,
+ &cached.meshes,
+ bounds,
+ scale_factor,
+ );
+
+ 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,
+ },
+ ));
+ }
+
+ if !cached.text.is_empty() {
+ engine.text_pipeline.render_cache(
+ &cached.text,
+ bounds,
+ &mut render_pass,
+ );
+ }
+
+ #[cfg(any(feature = "svg", feature = "image"))]
+ if !cached.images.is_empty() {
+ engine.image_pipeline.render(
+ image_layer,
+ bounds,
+ &mut render_pass,
+ );
+
+ image_layer += 1;
+ }
+ }
+ }
+ }
+
+ let _ = ManuallyDrop::into_inner(render_pass);
+ }
+}
+
+impl core::Renderer for Renderer {
+ fn start_layer(&mut self, bounds: Rectangle) {
+ self.layers.push_clip(Some(bounds));
+ }
+
+ fn end_layer(&mut self, _bounds: Rectangle) {
+ self.layers.pop_clip();
+ }
+
+ fn start_transformation(&mut self, transformation: Transformation) {
+ self.layers.push_transformation(transformation);
+ }
+
+ fn end_transformation(&mut self, _transformation: Transformation) {
+ self.layers.pop_transformation();
+ }
+
+ fn fill_quad(
+ &mut self,
+ quad: core::renderer::Quad,
+ background: impl Into<Background>,
+ ) {
+ self.layers.draw_quad(quad, background.into());
+ }
+
+ 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 load_font(&mut self, font: Cow<'static, [u8]>) {
+ graphics::text::font_system()
+ .write()
+ .expect("Write font system")
+ .load_font(font);
+
+ // TODO: Invalidate buffer cache
+ }
+
+ fn fill_paragraph(
+ &mut self,
+ text: &Self::Paragraph,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ self.layers
+ .draw_paragraph(text, position, color, clip_bounds);
+ }
+
+ fn fill_editor(
+ &mut self,
+ editor: &Self::Editor,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ self.layers
+ .draw_editor(editor, position, color, clip_bounds);
+ }
+
+ fn fill_text(
+ &mut self,
+ text: core::Text,
+ position: Point,
+ color: Color,
+ clip_bounds: Rectangle,
+ ) {
+ self.layers.draw_text(text, position, color, clip_bounds);
+ }
+}
+
+#[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,
+ ) {
+ self.layers.draw_image(handle, filter_method, bounds);
+ }
+}
+
+#[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,
+ ) {
+ self.layers.draw_svg(handle, color_filter, bounds);
+ }
+}
+
+impl graphics::mesh::Renderer for Renderer {
+ fn draw_mesh(&mut self, mesh: graphics::Mesh) {
+ self.layers.draw_mesh(mesh);
+ }
+}
+
+#[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) {
+ match geometry {
+ Geometry::Live(layers) => {
+ for layer in layers {
+ self.layers.draw_layer(layer);
+ }
+ }
+ Geometry::Cached(layers) => {
+ for layer in layers.as_ref() {
+ self.layers.draw_cached_layer(layer);
+ }
+ }
+ }
+ }
+}
+
+impl graphics::compositor::Default for crate::Renderer {
+ type Compositor = window::Compositor;
+}