diff options
Diffstat (limited to 'wgpu/src/renderer.rs')
| -rw-r--r-- | wgpu/src/renderer.rs | 308 |
1 files changed, 171 insertions, 137 deletions
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index f27a4b8a..1da19b1a 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -1,37 +1,41 @@ -use crate::{quad, text, Image, Primitive, Quad, Transformation}; -use iced_native::{ - renderer::{Debugger, Windowed}, - Background, Color, Layout, MouseCursor, Point, Rectangle, Vector, Widget, +use crate::{ + quad, text, triangle, Defaults, Primitive, Quad, Settings, Target, + Transformation, }; -use wgpu::{ - Adapter, BackendBit, CommandEncoderDescriptor, Device, DeviceDescriptor, - Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions, +#[cfg(any(feature = "image", feature = "svg"))] +use crate::image::{self, Image}; + +use iced_native::{ + layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector, + Widget, }; +use std::sync::Arc; -mod target; mod widget; -pub use target::Target; - /// A [`wgpu`] renderer. /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs #[derive(Debug)] pub struct Renderer { - device: Device, - queue: Queue, quad_pipeline: quad::Pipeline, - image_pipeline: crate::image::Pipeline, text_pipeline: text::Pipeline, + triangle_pipeline: triangle::Pipeline, + + #[cfg(any(feature = "image", feature = "svg"))] + image_pipeline: image::Pipeline, } struct Layer<'a> { bounds: Rectangle<u32>, offset: Vector<u32>, quads: Vec<Quad>, - images: Vec<Image>, + meshes: Vec<(Point, Arc<triangle::Mesh2D>)>, text: Vec<wgpu_glyph::Section<'a>>, + + #[cfg(any(feature = "image", feature = "svg"))] + images: Vec<Image>, } impl<'a> Layer<'a> { @@ -40,72 +44,62 @@ impl<'a> Layer<'a> { bounds, offset, quads: Vec::new(), - images: Vec::new(), text: Vec::new(), + meshes: Vec::new(), + + #[cfg(any(feature = "image", feature = "svg"))] + images: Vec::new(), } } } impl Renderer { - fn new() -> Self { - let adapter = Adapter::request(&RequestAdapterOptions { - power_preference: PowerPreference::LowPower, - backends: BackendBit::all(), - }) - .expect("Request adapter"); - - let (mut device, queue) = adapter.request_device(&DeviceDescriptor { - extensions: Extensions { - anisotropic_filtering: false, - }, - limits: Limits { max_bind_groups: 2 }, - }); + /// Creates a new [`Renderer`]. + /// + /// [`Renderer`]: struct.Renderer.html + pub fn new(device: &mut wgpu::Device, settings: Settings) -> Self { + let text_pipeline = + text::Pipeline::new(device, settings.format, settings.default_font); + let quad_pipeline = quad::Pipeline::new(device, settings.format); + let triangle_pipeline = triangle::Pipeline::new( + device, + settings.format, + settings.antialiasing, + ); - let text_pipeline = text::Pipeline::new(&mut device); - let quad_pipeline = quad::Pipeline::new(&mut device); - let image_pipeline = crate::image::Pipeline::new(&mut device); + #[cfg(any(feature = "image", feature = "svg"))] + let image_pipeline = image::Pipeline::new(device, settings.format); Self { - device, - queue, quad_pipeline, - image_pipeline, text_pipeline, + triangle_pipeline, + + #[cfg(any(feature = "image", feature = "svg"))] + image_pipeline, } } - fn draw<T: AsRef<str>>( + /// Draws the provided primitives in the given [`Target`]. + /// + /// The text provided as overlay will be renderer on top of the primitives. + /// This is useful for rendering debug information. + /// + /// [`Target`]: struct.Target.html + pub fn draw<T: AsRef<str>>( &mut self, + device: &mut wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + target: Target<'_>, (primitive, mouse_cursor): &(Primitive, MouseCursor), + scale_factor: f64, overlay: &[T], - target: &mut Target, ) -> MouseCursor { log::debug!("Drawing"); - let (width, height) = target.dimensions(); - let dpi = target.dpi(); - let transformation = target.transformation(); - let frame = target.next_frame(); - - let mut encoder = self - .device - .create_command_encoder(&CommandEncoderDescriptor { todo: 0 }); - - let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { - attachment: &frame.view, - resolve_target: None, - load_op: wgpu::LoadOp::Clear, - store_op: wgpu::StoreOp::Store, - clear_color: wgpu::Color { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, - }, - }], - depth_stencil_attachment: None, - }); + let (width, height) = target.viewport.dimensions(); + let scale_factor = scale_factor as f32; + let transformation = target.viewport.transformation(); let mut layers = Vec::new(); @@ -123,10 +117,20 @@ impl Renderer { self.draw_overlay(overlay, &mut layers); for layer in layers { - self.flush(dpi, transformation, &layer, &mut encoder, &frame.view); + self.flush( + device, + scale_factor, + transformation, + &layer, + encoder, + target.texture, + width, + height, + ); } - self.queue.submit(&[encoder.finish()]); + #[cfg(any(feature = "image", feature = "svg"))] + self.image_pipeline.trim_cache(); *mouse_cursor } @@ -215,6 +219,8 @@ impl Renderer { bounds, background, border_radius, + border_width, + border_color, } => { // TODO: Move some of this computations to the GPU (?) layer.quads.push(Quad { @@ -227,39 +233,30 @@ impl Renderer { Background::Color(color) => color.into_linear(), }, border_radius: *border_radius as f32, + border_width: *border_width as f32, + border_color: border_color.into_linear(), }); } - Primitive::Image { path, bounds } => { - layer.images.push(Image { - path: path.clone(), - position: [bounds.x, bounds.y], - scale: [bounds.width, bounds.height], - }); + Primitive::Mesh2D { origin, buffers } => { + layer.meshes.push((*origin, buffers.clone())); } Primitive::Clip { bounds, offset, content, } => { - let x = bounds.x - layer.offset.x as f32; - let y = bounds.y - layer.offset.y as f32; - let width = (bounds.width + x).min(bounds.width); - let height = (bounds.height + y).min(bounds.height); - - // Only draw visible content on-screen - // TODO: Also, check for parent layer bounds to avoid further - // drawing in some circumstances. - if width > 0.0 && height > 0.0 { - let clip_layer = Layer::new( - Rectangle { - x: x.max(0.0).floor() as u32, - y: y.max(0.0).floor() as u32, - width: width.ceil() as u32, - height: height.ceil() as u32, - }, - layer.offset + *offset, - ); + let layer_bounds: Rectangle<f32> = layer.bounds.into(); + let clip = Rectangle { + x: bounds.x - layer.offset.x as f32, + y: bounds.y - layer.offset.y as f32, + ..*bounds + }; + + // Only draw visible content + if let Some(clip_bounds) = layer_bounds.intersection(&clip) { + let clip_layer = + Layer::new(clip_bounds.into(), layer.offset + *offset); let new_layer = Layer::new(layer.bounds, layer.offset); layers.push(clip_layer); @@ -267,6 +264,28 @@ impl Renderer { layers.push(new_layer); } } + + #[cfg(feature = "image")] + Primitive::Image { handle, bounds } => { + layer.images.push(Image { + handle: image::Handle::Raster(handle.clone()), + position: [bounds.x, bounds.y], + size: [bounds.width, bounds.height], + }); + } + #[cfg(not(feature = "image"))] + Primitive::Image { .. } => {} + + #[cfg(feature = "svg")] + Primitive::Svg { handle, bounds } => { + layer.images.push(Image { + handle: image::Handle::Vector(handle.clone()), + position: [bounds.x, bounds.y], + size: [bounds.width, bounds.height], + }); + } + #[cfg(not(feature = "svg"))] + Primitive::Svg { .. } => {} } } @@ -306,42 +325,69 @@ impl Renderer { fn flush( &mut self, - dpi: f32, + device: &mut wgpu::Device, + scale_factor: f32, transformation: Transformation, layer: &Layer<'_>, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, + target_width: u32, + target_height: u32, ) { - let bounds = layer.bounds * dpi; + let bounds = layer.bounds * scale_factor; + + if layer.meshes.len() > 0 { + let translated = transformation + * Transformation::scale(scale_factor, scale_factor) + * Transformation::translate( + -(layer.offset.x as f32), + -(layer.offset.y as f32), + ); + + self.triangle_pipeline.draw( + device, + encoder, + target, + target_width, + target_height, + translated, + &layer.meshes, + bounds, + ); + } if layer.quads.len() > 0 { self.quad_pipeline.draw( - &mut self.device, + device, encoder, &layer.quads, transformation, - dpi, + scale_factor, bounds, target, ); } - if layer.images.len() > 0 { - let translated_and_scaled = transformation - * Transformation::scale(dpi, dpi) - * Transformation::translate( - -(layer.offset.x as f32), - -(layer.offset.y as f32), - ); + #[cfg(any(feature = "image", feature = "svg"))] + { + if layer.images.len() > 0 { + let translated_and_scaled = transformation + * Transformation::scale(scale_factor, scale_factor) + * Transformation::translate( + -(layer.offset.x as f32), + -(layer.offset.y as f32), + ); - self.image_pipeline.draw( - &mut self.device, - encoder, - &layer.images, - translated_and_scaled, - bounds, - target, - ); + self.image_pipeline.draw( + device, + encoder, + &layer.images, + translated_and_scaled, + bounds, + target, + scale_factor, + ); + } } if layer.text.len() > 0 { @@ -353,25 +399,25 @@ impl Renderer { // bit "jumpy". We may be able to do better once we improve // our text rendering/caching pipeline. screen_position: ( - (text.screen_position.0 * dpi).round(), - (text.screen_position.1 * dpi).round(), + (text.screen_position.0 * scale_factor).round(), + (text.screen_position.1 * scale_factor).round(), ), - // TODO: Fix precision issues with some DPI factors. + // TODO: Fix precision issues with some scale factors. // // The `ceil` here can cause some words to render on the // same line when they should not. // // Ideally, `wgpu_glyph` should be able to compute layout // using logical positions, and then apply the proper - // DPI scaling. This would ensure that both measuring and - // rendering follow the same layout rules. + // scaling when rendering. This would ensure that both + // measuring and rendering follow the same layout rules. bounds: ( - (text.bounds.0 * dpi).ceil(), - (text.bounds.1 * dpi).ceil(), + (text.bounds.0 * scale_factor).ceil(), + (text.bounds.1 * scale_factor).ceil(), ), scale: wgpu_glyph::Scale { - x: text.scale.x * dpi, - y: text.scale.y * dpi, + x: text.scale.x * scale_factor, + y: text.scale.y * scale_factor, }, ..*text }; @@ -380,7 +426,7 @@ impl Renderer { } self.text_pipeline.draw_queued( - &mut self.device, + device, encoder, target, transformation, @@ -397,12 +443,14 @@ impl Renderer { impl iced_native::Renderer for Renderer { type Output = (Primitive, MouseCursor); + type Defaults = Defaults; fn layout<'a, Message>( &mut self, element: &iced_native::Element<'a, Message, Self>, + limits: &iced_native::layout::Limits, ) -> iced_native::layout::Node { - let node = element.layout(self, &iced_native::layout::Limits::NONE); + let node = element.layout(self, limits); self.text_pipeline.clear_measurement_cache(); @@ -410,33 +458,18 @@ impl iced_native::Renderer for Renderer { } } -impl Windowed for Renderer { - type Target = Target; - - fn new() -> Self { - Self::new() - } - - fn draw<T: AsRef<str>>( - &mut self, - output: &Self::Output, - overlay: &[T], - target: &mut Target, - ) -> MouseCursor { - self.draw(output, overlay, target) - } -} - -impl Debugger for Renderer { +impl layout::Debugger for Renderer { fn explain<Message>( &mut self, + defaults: &Defaults, widget: &dyn Widget<Message, Self>, layout: Layout<'_>, cursor_position: Point, color: Color, ) -> Self::Output { let mut primitives = Vec::new(); - let (primitive, cursor) = widget.draw(self, layout, cursor_position); + let (primitive, cursor) = + widget.draw(self, defaults, layout, cursor_position); explain_layout(layout, color, &mut primitives); primitives.push(primitive); @@ -450,11 +483,12 @@ fn explain_layout( color: Color, primitives: &mut Vec<Primitive>, ) { - // TODO: Draw borders instead primitives.push(Primitive::Quad { bounds: layout.bounds(), - background: Background::Color([0.0, 0.0, 0.0, 0.05].into()), + background: Background::Color(Color::TRANSPARENT), border_radius: 0, + border_width: 1, + border_color: [0.6, 0.6, 0.6, 0.5].into(), }); for child in layout.children() { |
