summaryrefslogtreecommitdiffstats
path: root/wgpu/src/renderer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'wgpu/src/renderer.rs')
-rw-r--r--wgpu/src/renderer.rs308
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() {