summaryrefslogtreecommitdiffstats
path: root/wgpu/src
diff options
context:
space:
mode:
Diffstat (limited to 'wgpu/src')
-rw-r--r--wgpu/src/backend.rs288
-rw-r--r--wgpu/src/defaults.rs32
-rw-r--r--wgpu/src/image.rs326
-rw-r--r--wgpu/src/image/atlas.rs92
-rw-r--r--wgpu/src/image/raster.rs4
-rw-r--r--wgpu/src/image/vector.rs18
-rw-r--r--wgpu/src/lib.rs34
-rw-r--r--wgpu/src/primitive.rs100
-rw-r--r--wgpu/src/quad.rs261
-rw-r--r--wgpu/src/renderer.rs509
-rw-r--r--wgpu/src/renderer/widget.rs18
-rw-r--r--wgpu/src/renderer/widget/button.rs94
-rw-r--r--wgpu/src/renderer/widget/checkbox.rs59
-rw-r--r--wgpu/src/renderer/widget/column.rs34
-rw-r--r--wgpu/src/renderer/widget/container.rs49
-rw-r--r--wgpu/src/renderer/widget/image.rs22
-rw-r--r--wgpu/src/renderer/widget/progress_bar.rs54
-rw-r--r--wgpu/src/renderer/widget/radio.rs64
-rw-r--r--wgpu/src/renderer/widget/row.rs34
-rw-r--r--wgpu/src/renderer/widget/scrollable.rs127
-rw-r--r--wgpu/src/renderer/widget/slider.rs106
-rw-r--r--wgpu/src/renderer/widget/space.rs8
-rw-r--r--wgpu/src/renderer/widget/svg.rs22
-rw-r--r--wgpu/src/renderer/widget/text.rs47
-rw-r--r--wgpu/src/renderer/widget/text_input.rs182
-rw-r--r--wgpu/src/settings.rs49
-rw-r--r--wgpu/src/shader/blit.vert6
-rw-r--r--wgpu/src/shader/blit.vert.spvbin1384 -> 1384 bytes
-rw-r--r--wgpu/src/shader/quad.vert5
-rw-r--r--wgpu/src/shader/quad.vert.spvbin3372 -> 3604 bytes
-rw-r--r--wgpu/src/target.rs14
-rw-r--r--wgpu/src/text.rs116
-rw-r--r--wgpu/src/text/font.rs37
-rw-r--r--wgpu/src/text/icons.ttfbin4912 -> 0 bytes
-rw-r--r--wgpu/src/transformation.rs54
-rw-r--r--wgpu/src/triangle.rs369
-rw-r--r--wgpu/src/triangle/msaa.rs77
-rw-r--r--wgpu/src/viewport.rs29
-rw-r--r--wgpu/src/widget.rs31
-rw-r--r--wgpu/src/widget/button.rs5
-rw-r--r--wgpu/src/widget/canvas.rs147
-rw-r--r--wgpu/src/widget/canvas/drawable.rs12
-rw-r--r--wgpu/src/widget/canvas/fill.rs14
-rw-r--r--wgpu/src/widget/canvas/frame.rs310
-rw-r--r--wgpu/src/widget/canvas/layer.rs25
-rw-r--r--wgpu/src/widget/canvas/layer/cache.rs99
-rw-r--r--wgpu/src/widget/canvas/path.rs49
-rw-r--r--wgpu/src/widget/canvas/path/arc.rs44
-rw-r--r--wgpu/src/widget/canvas/path/builder.rs177
-rw-r--r--wgpu/src/widget/canvas/stroke.rs83
-rw-r--r--wgpu/src/widget/canvas/text.rs34
-rw-r--r--wgpu/src/widget/checkbox.rs2
-rw-r--r--wgpu/src/widget/container.rs2
-rw-r--r--wgpu/src/widget/pane_grid.rs31
-rw-r--r--wgpu/src/widget/pick_list.rs9
-rw-r--r--wgpu/src/widget/progress_bar.rs10
-rw-r--r--wgpu/src/widget/qr_code.rs2
-rw-r--r--wgpu/src/widget/radio.rs2
-rw-r--r--wgpu/src/widget/rule.rs10
-rw-r--r--wgpu/src/widget/scrollable.rs2
-rw-r--r--wgpu/src/widget/slider.rs7
-rw-r--r--wgpu/src/widget/text_input.rs5
-rw-r--r--wgpu/src/window.rs6
-rw-r--r--wgpu/src/window/backend.rs113
-rw-r--r--wgpu/src/window/compositor.rs177
-rw-r--r--wgpu/src/window/swap_chain.rs57
66 files changed, 1282 insertions, 3523 deletions
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
new file mode 100644
index 00000000..fccb5ac7
--- /dev/null
+++ b/wgpu/src/backend.rs
@@ -0,0 +1,288 @@
+use crate::quad;
+use crate::text;
+use crate::triangle;
+use crate::{Settings, Transformation};
+use iced_graphics::backend;
+use iced_graphics::font;
+use iced_graphics::layer::Layer;
+use iced_graphics::{Primitive, Viewport};
+use iced_native::mouse;
+use iced_native::{Font, HorizontalAlignment, Size, VerticalAlignment};
+
+#[cfg(any(feature = "image", feature = "svg"))]
+use crate::image;
+
+/// A [`wgpu`] graphics backend for [`iced`].
+///
+/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
+/// [`iced`]: https://github.com/hecrj/iced
+#[derive(Debug)]
+pub struct Backend {
+ quad_pipeline: quad::Pipeline,
+ text_pipeline: text::Pipeline,
+ triangle_pipeline: triangle::Pipeline,
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ image_pipeline: image::Pipeline,
+
+ default_text_size: u16,
+}
+
+impl Backend {
+ /// Creates a new [`Backend`].
+ pub fn new(device: &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,
+ );
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ let image_pipeline = image::Pipeline::new(device, settings.format);
+
+ Self {
+ quad_pipeline,
+ text_pipeline,
+ triangle_pipeline,
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ image_pipeline,
+
+ default_text_size: settings.default_text_size,
+ }
+ }
+
+ /// Draws the provided primitives in the given `TextureView`.
+ ///
+ /// The text provided as overlay will be rendered on top of the primitives.
+ /// This is useful for rendering debug information.
+ pub fn draw<T: AsRef<str>>(
+ &mut self,
+ device: &wgpu::Device,
+ staging_belt: &mut wgpu::util::StagingBelt,
+ encoder: &mut wgpu::CommandEncoder,
+ frame: &wgpu::TextureView,
+ viewport: &Viewport,
+ (primitive, mouse_interaction): &(Primitive, mouse::Interaction),
+ overlay_text: &[T],
+ ) -> mouse::Interaction {
+ log::debug!("Drawing");
+
+ let target_size = viewport.physical_size();
+ let scale_factor = viewport.scale_factor() as f32;
+ let transformation = viewport.projection();
+
+ let mut layers = Layer::generate(primitive, viewport);
+ layers.push(Layer::overlay(overlay_text, viewport));
+
+ for layer in layers {
+ self.flush(
+ device,
+ scale_factor,
+ transformation,
+ &layer,
+ staging_belt,
+ encoder,
+ &frame,
+ target_size.width,
+ target_size.height,
+ );
+ }
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ self.image_pipeline.trim_cache();
+
+ *mouse_interaction
+ }
+
+ fn flush(
+ &mut self,
+ device: &wgpu::Device,
+ scale_factor: f32,
+ transformation: Transformation,
+ layer: &Layer<'_>,
+ staging_belt: &mut wgpu::util::StagingBelt,
+ encoder: &mut wgpu::CommandEncoder,
+ target: &wgpu::TextureView,
+ target_width: u32,
+ target_height: u32,
+ ) {
+ let bounds = (layer.bounds * scale_factor).snap();
+
+ if !layer.quads.is_empty() {
+ self.quad_pipeline.draw(
+ device,
+ staging_belt,
+ encoder,
+ &layer.quads,
+ transformation,
+ scale_factor,
+ bounds,
+ target,
+ );
+ }
+
+ if !layer.meshes.is_empty() {
+ let scaled = transformation
+ * Transformation::scale(scale_factor, scale_factor);
+
+ self.triangle_pipeline.draw(
+ device,
+ staging_belt,
+ encoder,
+ target,
+ target_width,
+ target_height,
+ scaled,
+ scale_factor,
+ &layer.meshes,
+ );
+ }
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ {
+ if !layer.images.is_empty() {
+ let scaled = transformation
+ * Transformation::scale(scale_factor, scale_factor);
+
+ self.image_pipeline.draw(
+ device,
+ staging_belt,
+ encoder,
+ &layer.images,
+ scaled,
+ bounds,
+ target,
+ scale_factor,
+ );
+ }
+ }
+
+ if !layer.text.is_empty() {
+ for text in layer.text.iter() {
+ // Target physical coordinates directly to avoid blurry text
+ let text = wgpu_glyph::Section {
+ // TODO: We `round` here to avoid rerasterizing text when
+ // its position changes slightly. This can make text feel a
+ // bit "jumpy". We may be able to do better once we improve
+ // our text rendering/caching pipeline.
+ screen_position: (
+ (text.bounds.x * scale_factor).round(),
+ (text.bounds.y * scale_factor).round(),
+ ),
+ // 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
+ // scaling when rendering. This would ensure that both
+ // measuring and rendering follow the same layout rules.
+ bounds: (
+ (text.bounds.width * scale_factor).ceil(),
+ (text.bounds.height * scale_factor).ceil(),
+ ),
+ text: vec![wgpu_glyph::Text {
+ text: text.content,
+ scale: wgpu_glyph::ab_glyph::PxScale {
+ x: text.size * scale_factor,
+ y: text.size * scale_factor,
+ },
+ font_id: self.text_pipeline.find_font(text.font),
+ extra: wgpu_glyph::Extra {
+ color: text.color,
+ z: 0.0,
+ },
+ }],
+ layout: wgpu_glyph::Layout::default()
+ .h_align(match text.horizontal_alignment {
+ HorizontalAlignment::Left => {
+ wgpu_glyph::HorizontalAlign::Left
+ }
+ HorizontalAlignment::Center => {
+ wgpu_glyph::HorizontalAlign::Center
+ }
+ HorizontalAlignment::Right => {
+ wgpu_glyph::HorizontalAlign::Right
+ }
+ })
+ .v_align(match text.vertical_alignment {
+ VerticalAlignment::Top => {
+ wgpu_glyph::VerticalAlign::Top
+ }
+ VerticalAlignment::Center => {
+ wgpu_glyph::VerticalAlign::Center
+ }
+ VerticalAlignment::Bottom => {
+ wgpu_glyph::VerticalAlign::Bottom
+ }
+ }),
+ ..Default::default()
+ };
+
+ self.text_pipeline.queue(text);
+ }
+
+ self.text_pipeline.draw_queued(
+ device,
+ staging_belt,
+ encoder,
+ target,
+ transformation,
+ wgpu_glyph::Region {
+ x: bounds.x,
+ y: bounds.y,
+ width: bounds.width,
+ height: bounds.height,
+ },
+ );
+ }
+ }
+}
+
+impl iced_graphics::Backend for Backend {
+ fn trim_measurements(&mut self) {
+ self.text_pipeline.trim_measurement_cache()
+ }
+}
+
+impl backend::Text for Backend {
+ const ICON_FONT: Font = font::ICONS;
+ const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
+ const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON;
+
+ fn default_size(&self) -> u16 {
+ self.default_text_size
+ }
+
+ fn measure(
+ &self,
+ contents: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ ) -> (f32, f32) {
+ self.text_pipeline.measure(contents, size, font, bounds)
+ }
+}
+
+#[cfg(feature = "image")]
+impl backend::Image for Backend {
+ fn dimensions(&self, handle: &iced_native::image::Handle) -> (u32, u32) {
+ self.image_pipeline.dimensions(handle)
+ }
+}
+
+#[cfg(feature = "svg")]
+impl backend::Svg for Backend {
+ fn viewport_dimensions(
+ &self,
+ handle: &iced_native::svg::Handle,
+ ) -> (u32, u32) {
+ self.image_pipeline.viewport_dimensions(handle)
+ }
+}
diff --git a/wgpu/src/defaults.rs b/wgpu/src/defaults.rs
deleted file mode 100644
index 11718a87..00000000
--- a/wgpu/src/defaults.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Use default styling attributes to inherit styles.
-use iced_native::Color;
-
-/// Some default styling attributes.
-#[derive(Debug, Clone, Copy)]
-pub struct Defaults {
- /// Text styling
- pub text: Text,
-}
-
-impl Default for Defaults {
- fn default() -> Defaults {
- Defaults {
- text: Text::default(),
- }
- }
-}
-
-/// Some default text styling attributes.
-#[derive(Debug, Clone, Copy)]
-pub struct Text {
- /// The default color of text
- pub color: Color,
-}
-
-impl Default for Text {
- fn default() -> Text {
- Text {
- color: Color::BLACK,
- }
- }
-}
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index d3603676..c256ca7e 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -9,10 +9,13 @@ mod vector;
use crate::Transformation;
use atlas::Atlas;
+use iced_graphics::layer;
use iced_native::Rectangle;
use std::cell::RefCell;
use std::mem;
+use bytemuck::{Pod, Zeroable};
+
#[cfg(feature = "image")]
use iced_native::image;
@@ -40,6 +43,8 @@ pub struct Pipeline {
impl Pipeline {
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
+ use wgpu::util::DeviceExt;
+
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
@@ -47,50 +52,52 @@ impl Pipeline {
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Linear,
- lod_min_clamp: -100.0,
- lod_max_clamp: 100.0,
- compare_function: wgpu::CompareFunction::Always,
+ ..Default::default()
});
let constant_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- bindings: &[
- wgpu::BindGroupLayoutBinding {
+ label: Some("iced_wgpu::image constants layout"),
+ entries: &[
+ wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
- ty: wgpu::BindingType::UniformBuffer { dynamic: false },
+ ty: wgpu::BindingType::UniformBuffer {
+ dynamic: false,
+ min_binding_size: wgpu::BufferSize::new(
+ mem::size_of::<Uniforms>() as u64,
+ ),
+ },
+ count: None,
},
- wgpu::BindGroupLayoutBinding {
+ wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT,
- ty: wgpu::BindingType::Sampler,
+ ty: wgpu::BindingType::Sampler { comparison: false },
+ count: None,
},
],
});
- let uniforms = Uniforms {
- transform: Transformation::identity().into(),
- };
-
- let uniforms_buffer = device
- .create_buffer_mapped(
- 1,
- wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
- )
- .fill_from_slice(&[uniforms]);
+ let uniforms_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some("iced_wgpu::image uniforms buffer"),
+ size: mem::size_of::<Uniforms>() as u64,
+ usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
+ mapped_at_creation: false,
+ });
let constant_bind_group =
device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::image constants bind group"),
layout: &constant_layout,
- bindings: &[
- wgpu::Binding {
+ entries: &[
+ wgpu::BindGroupEntry {
binding: 0,
- resource: wgpu::BindingResource::Buffer {
- buffer: &uniforms_buffer,
- range: 0..std::mem::size_of::<Uniforms>() as u64,
- },
+ resource: wgpu::BindingResource::Buffer(
+ uniforms_buffer.slice(..),
+ ),
},
- wgpu::Binding {
+ wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
@@ -99,36 +106,38 @@ impl Pipeline {
let texture_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- bindings: &[wgpu::BindGroupLayoutBinding {
+ label: Some("iced_wgpu::image texture atlas layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::SampledTexture {
- multisampled: false,
dimension: wgpu::TextureViewDimension::D2,
+ component_type: wgpu::TextureComponentType::Float,
+ multisampled: false,
},
+ count: None,
}],
});
let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+ label: Some("iced_wgpu::image pipeline layout"),
+ push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout, &texture_layout],
});
- let vs = include_bytes!("shader/image.vert.spv");
- let vs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&vs[..]))
- .expect("Read image vertex shader as SPIR-V"),
- );
+ let vs_module = device.create_shader_module(wgpu::include_spirv!(
+ "shader/image.vert.spv"
+ ));
- let fs = include_bytes!("shader/image.frag.spv");
- let fs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&fs[..]))
- .expect("Read image fragment shader as SPIR-V"),
- );
+ let fs_module = device.create_shader_module(wgpu::include_spirv!(
+ "shader/image.frag.spv"
+ ));
let pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
- layout: &layout,
+ label: Some("iced_wgpu::image pipeline"),
+ layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
@@ -140,9 +149,7 @@ impl Pipeline {
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Cw,
cull_mode: wgpu::CullMode::None,
- depth_bias: 0,
- depth_bias_slope_scale: 0.0,
- depth_bias_clamp: 0.0,
+ ..Default::default()
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
@@ -160,72 +167,83 @@ impl Pipeline {
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
- index_format: wgpu::IndexFormat::Uint16,
- vertex_buffers: &[
- wgpu::VertexBufferDescriptor {
- stride: mem::size_of::<Vertex>() as u64,
- step_mode: wgpu::InputStepMode::Vertex,
- attributes: &[wgpu::VertexAttributeDescriptor {
- shader_location: 0,
- format: wgpu::VertexFormat::Float2,
- offset: 0,
- }],
- },
- wgpu::VertexBufferDescriptor {
- stride: mem::size_of::<Instance>() as u64,
- step_mode: wgpu::InputStepMode::Instance,
- attributes: &[
- wgpu::VertexAttributeDescriptor {
- shader_location: 1,
+ vertex_state: wgpu::VertexStateDescriptor {
+ index_format: wgpu::IndexFormat::Uint16,
+ vertex_buffers: &[
+ wgpu::VertexBufferDescriptor {
+ stride: mem::size_of::<Vertex>() as u64,
+ step_mode: wgpu::InputStepMode::Vertex,
+ attributes: &[wgpu::VertexAttributeDescriptor {
+ shader_location: 0,
format: wgpu::VertexFormat::Float2,
offset: 0,
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 2,
- format: wgpu::VertexFormat::Float2,
- offset: 4 * 2,
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 3,
- format: wgpu::VertexFormat::Float2,
- offset: 4 * 4,
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 4,
- format: wgpu::VertexFormat::Float2,
- offset: 4 * 6,
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 5,
- format: wgpu::VertexFormat::Uint,
- offset: 4 * 8,
- },
- ],
- },
- ],
+ }],
+ },
+ wgpu::VertexBufferDescriptor {
+ stride: mem::size_of::<Instance>() as u64,
+ step_mode: wgpu::InputStepMode::Instance,
+ attributes: &[
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 1,
+ format: wgpu::VertexFormat::Float2,
+ offset: 0,
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 2,
+ format: wgpu::VertexFormat::Float2,
+ offset: 4 * 2,
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 3,
+ format: wgpu::VertexFormat::Float2,
+ offset: 4 * 4,
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 4,
+ format: wgpu::VertexFormat::Float2,
+ offset: 4 * 6,
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 5,
+ format: wgpu::VertexFormat::Uint,
+ offset: 4 * 8,
+ },
+ ],
+ },
+ ],
+ },
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
- let vertices = device
- .create_buffer_mapped(QUAD_VERTS.len(), wgpu::BufferUsage::VERTEX)
- .fill_from_slice(&QUAD_VERTS);
+ let vertices =
+ device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: Some("iced_wgpu::image vertex buffer"),
+ contents: bytemuck::cast_slice(&QUAD_VERTS),
+ usage: wgpu::BufferUsage::VERTEX,
+ });
- let indices = device
- .create_buffer_mapped(QUAD_INDICES.len(), wgpu::BufferUsage::INDEX)
- .fill_from_slice(&QUAD_INDICES);
+ let indices =
+ device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: Some("iced_wgpu::image index buffer"),
+ contents: bytemuck::cast_slice(&QUAD_INDICES),
+ usage: wgpu::BufferUsage::INDEX,
+ });
let instances = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some("iced_wgpu::image instance buffer"),
size: mem::size_of::<Instance>() as u64 * Instance::MAX as u64,
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
+ mapped_at_creation: false,
});
let texture_atlas = Atlas::new(device);
let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::image texture atlas bind group"),
layout: &texture_layout,
- bindings: &[wgpu::Binding {
+ entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(
&texture_atlas.view(),
@@ -271,9 +289,10 @@ impl Pipeline {
pub fn draw(
&mut self,
- device: &mut wgpu::Device,
+ device: &wgpu::Device,
+ staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
- images: &[Image],
+ images: &[layer::Image],
transformation: Transformation,
bounds: Rectangle<u32>,
target: &wgpu::TextureView,
@@ -288,31 +307,48 @@ impl Pipeline {
let mut vector_cache = self.vector_cache.borrow_mut();
for image in images {
- match &image.handle {
+ match &image {
#[cfg(feature = "image")]
- Handle::Raster(handle) => {
+ layer::Image::Raster { handle, bounds } => {
if let Some(atlas_entry) = raster_cache.upload(
handle,
device,
encoder,
&mut self.texture_atlas,
) {
- add_instances(image, atlas_entry, instances);
+ add_instances(
+ [bounds.x, bounds.y],
+ [bounds.width, bounds.height],
+ atlas_entry,
+ instances,
+ );
}
}
+ #[cfg(not(feature = "image"))]
+ layer::Image::Raster { .. } => {}
+
#[cfg(feature = "svg")]
- Handle::Vector(handle) => {
+ layer::Image::Vector { handle, bounds } => {
+ let size = [bounds.width, bounds.height];
+
if let Some(atlas_entry) = vector_cache.upload(
handle,
- image.size,
+ size,
_scale,
device,
encoder,
&mut self.texture_atlas,
) {
- add_instances(image, atlas_entry, instances);
+ add_instances(
+ [bounds.x, bounds.y],
+ size,
+ atlas_entry,
+ instances,
+ );
}
}
+ #[cfg(not(feature = "svg"))]
+ layer::Image::Vector { .. } => {}
}
}
@@ -327,8 +363,9 @@ impl Pipeline {
self.texture =
device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::image texture atlas bind group"),
layout: &self.texture_layout,
- bindings: &[wgpu::Binding {
+ entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(
&self.texture_atlas.view(),
@@ -339,23 +376,20 @@ impl Pipeline {
self.texture_version = texture_version;
}
- let uniforms_buffer = device
- .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
- .fill_from_slice(&[Uniforms {
- transform: transformation.into(),
- }]);
-
- encoder.copy_buffer_to_buffer(
- &uniforms_buffer,
- 0,
- &self.uniforms,
- 0,
- std::mem::size_of::<Uniforms>() as u64,
- );
+ {
+ let mut uniforms_buffer = staging_belt.write_buffer(
+ encoder,
+ &self.uniforms,
+ 0,
+ wgpu::BufferSize::new(mem::size_of::<Uniforms>() as u64)
+ .unwrap(),
+ device,
+ );
- let instances_buffer = device
- .create_buffer_mapped(instances.len(), wgpu::BufferUsage::COPY_SRC)
- .fill_from_slice(&instances);
+ uniforms_buffer.copy_from_slice(bytemuck::bytes_of(&Uniforms {
+ transform: transformation.into(),
+ }));
+ }
let mut i = 0;
let total = instances.len();
@@ -364,27 +398,30 @@ impl Pipeline {
let end = (i + Instance::MAX).min(total);
let amount = end - i;
- encoder.copy_buffer_to_buffer(
- &instances_buffer,
- (i * std::mem::size_of::<Instance>()) as u64,
+ let mut instances_buffer = staging_belt.write_buffer(
+ encoder,
&self.instances,
0,
- (amount * std::mem::size_of::<Instance>()) as u64,
+ wgpu::BufferSize::new(
+ (amount * std::mem::size_of::<Instance>()) as u64,
+ )
+ .unwrap(),
+ device,
);
+ instances_buffer.copy_from_slice(bytemuck::cast_slice(
+ &instances[i..i + amount],
+ ));
+
let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: target,
resolve_target: None,
- load_op: wgpu::LoadOp::Load,
- store_op: wgpu::StoreOp::Store,
- clear_color: wgpu::Color {
- r: 0.0,
- g: 0.0,
- b: 0.0,
- a: 0.0,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Load,
+ store: true,
},
},
],
@@ -394,11 +431,9 @@ impl Pipeline {
render_pass.set_pipeline(&self.pipeline);
render_pass.set_bind_group(0, &self.constants, &[]);
render_pass.set_bind_group(1, &self.texture, &[]);
- render_pass.set_index_buffer(&self.indices, 0);
- render_pass.set_vertex_buffers(
- 0,
- &[(&self.vertices, 0), (&self.instances, 0)],
- );
+ render_pass.set_index_buffer(self.indices.slice(..));
+ render_pass.set_vertex_buffer(0, self.vertices.slice(..));
+ render_pass.set_vertex_buffer(1, self.instances.slice(..));
render_pass.set_scissor_rect(
bounds.x,
@@ -426,22 +461,8 @@ impl Pipeline {
}
}
-pub struct Image {
- pub handle: Handle,
- pub position: [f32; 2],
- pub size: [f32; 2],
-}
-
-pub enum Handle {
- #[cfg(feature = "image")]
- Raster(image::Handle),
-
- #[cfg(feature = "svg")]
- Vector(svg::Handle),
-}
-
#[repr(C)]
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Zeroable, Pod)]
pub struct Vertex {
_position: [f32; 2],
}
@@ -464,7 +485,7 @@ const QUAD_VERTS: [Vertex; 4] = [
];
#[repr(C)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Zeroable, Pod)]
struct Instance {
_position: [f32; 2],
_size: [f32; 2],
@@ -478,28 +499,29 @@ impl Instance {
}
#[repr(C)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Zeroable, Pod)]
struct Uniforms {
transform: [f32; 16],
}
fn add_instances(
- image: &Image,
+ image_position: [f32; 2],
+ image_size: [f32; 2],
entry: &atlas::Entry,
instances: &mut Vec<Instance>,
) {
match entry {
atlas::Entry::Contiguous(allocation) => {
- add_instance(image.position, image.size, allocation, instances);
+ add_instance(image_position, image_size, allocation, instances);
}
atlas::Entry::Fragmented { fragments, size } => {
- let scaling_x = image.size[0] / size.0 as f32;
- let scaling_y = image.size[1] / size.1 as f32;
+ let scaling_x = image_size[0] / size.0 as f32;
+ let scaling_y = image_size[1] / size.1 as f32;
for fragment in fragments {
let allocation = &fragment.allocation;
- let [x, y] = image.position;
+ let [x, y] = image_position;
let (fragment_x, fragment_y) = fragment.position;
let (fragment_width, fragment_height) = allocation.size();
diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index 86a5ff49..660ebe44 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -28,8 +28,8 @@ impl Atlas {
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
+ label: Some("iced_wgpu::image texture atlas"),
size: extent,
- array_layer_count: 2,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
@@ -39,12 +39,15 @@ impl Atlas {
| wgpu::TextureUsage::SAMPLED,
});
- let texture_view = texture.create_default_view();
+ let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
+ dimension: Some(wgpu::TextureViewDimension::D2Array),
+ ..Default::default()
+ });
Atlas {
texture,
texture_view,
- layers: vec![Layer::Empty, Layer::Empty],
+ layers: vec![Layer::Empty],
}
}
@@ -56,17 +59,16 @@ impl Atlas {
self.layers.len()
}
- pub fn upload<C>(
+ pub fn upload(
&mut self,
width: u32,
height: u32,
- data: &[C],
+ data: &[u8],
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
- ) -> Option<Entry>
- where
- C: Copy + 'static,
- {
+ ) -> Option<Entry> {
+ use wgpu::util::DeviceExt;
+
let entry = {
let current_size = self.layers.len();
let entry = self.allocate(width, height)?;
@@ -80,9 +82,31 @@ impl Atlas {
log::info!("Allocated atlas entry: {:?}", entry);
- let buffer = device
- .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
- .fill_from_slice(data);
+ // It is a webgpu requirement that:
+ // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0
+ // So we calculate padded_width by rounding width up to the next
+ // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT.
+ let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
+ let padding = (align - (4 * width) % align) % align;
+ let padded_width = (4 * width + padding) as usize;
+ let padded_data_size = padded_width * height as usize;
+
+ let mut padded_data = vec![0; padded_data_size];
+
+ for row in 0..height as usize {
+ let offset = row * padded_width;
+
+ padded_data[offset..offset + 4 * width as usize].copy_from_slice(
+ &data[row * 4 * width as usize..(row + 1) * 4 * width as usize],
+ )
+ }
+
+ let buffer =
+ device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: Some("iced_wgpu::image staging buffer"),
+ contents: &padded_data,
+ usage: wgpu::BufferUsage::COPY_SRC,
+ });
match &entry {
Entry::Contiguous(allocation) => {
@@ -90,6 +114,7 @@ impl Atlas {
&buffer,
width,
height,
+ padding,
0,
&allocation,
encoder,
@@ -98,12 +123,13 @@ impl Atlas {
Entry::Fragmented { fragments, .. } => {
for fragment in fragments {
let (x, y) = fragment.position;
- let offset = (y * width + x) as usize * 4;
+ let offset = (y * padded_width as u32 + 4 * x) as usize;
self.upload_allocation(
&buffer,
width,
height,
+ padding,
offset,
&fragment.allocation,
encoder,
@@ -256,6 +282,7 @@ impl Atlas {
buffer: &wgpu::Buffer,
image_width: u32,
image_height: u32,
+ padding: u32,
offset: usize,
allocation: &Allocation,
encoder: &mut wgpu::CommandEncoder,
@@ -273,18 +300,19 @@ impl Atlas {
encoder.copy_buffer_to_texture(
wgpu::BufferCopyView {
buffer,
- offset: offset as u64,
- row_pitch: 4 * image_width,
- image_height,
+ layout: wgpu::TextureDataLayout {
+ offset: offset as u64,
+ bytes_per_row: 4 * image_width + padding,
+ rows_per_image: image_height,
+ },
},
wgpu::TextureCopyView {
texture: &self.texture,
- array_layer: layer as u32,
mip_level: 0,
origin: wgpu::Origin3d {
- x: x as f32,
- y: y as f32,
- z: 0.0,
+ x,
+ y,
+ z: layer as u32,
},
},
extent,
@@ -302,12 +330,12 @@ impl Atlas {
}
let new_texture = device.create_texture(&wgpu::TextureDescriptor {
+ label: Some("iced_wgpu::image texture atlas"),
size: wgpu::Extent3d {
width: SIZE,
height: SIZE,
- depth: 1,
+ depth: self.layers.len() as u32,
},
- array_layer_count: self.layers.len() as u32,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
@@ -329,22 +357,20 @@ impl Atlas {
encoder.copy_texture_to_texture(
wgpu::TextureCopyView {
texture: &self.texture,
- array_layer: i as u32,
mip_level: 0,
origin: wgpu::Origin3d {
- x: 0.0,
- y: 0.0,
- z: 0.0,
+ x: 0,
+ y: 0,
+ z: i as u32,
},
},
wgpu::TextureCopyView {
texture: &new_texture,
- array_layer: i as u32,
mip_level: 0,
origin: wgpu::Origin3d {
- x: 0.0,
- y: 0.0,
- z: 0.0,
+ x: 0,
+ y: 0,
+ z: i as u32,
},
},
wgpu::Extent3d {
@@ -356,6 +382,10 @@ impl Atlas {
}
self.texture = new_texture;
- self.texture_view = self.texture.create_default_view();
+ self.texture_view =
+ self.texture.create_view(&wgpu::TextureViewDescriptor {
+ dimension: Some(wgpu::TextureViewDimension::D2Array),
+ ..Default::default()
+ });
}
}
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 4f69df8c..25607dab 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -43,14 +43,14 @@ impl Cache {
let memory = match handle.data() {
image::Data::Path(path) => {
if let Ok(image) = ::image::open(path) {
- Memory::Host(image.to_bgra())
+ Memory::Host(image.to_bgra8())
} else {
Memory::NotFound
}
}
image::Data::Bytes(bytes) => {
if let Ok(image) = ::image::load_from_memory(&bytes) {
- Memory::Host(image.to_bgra())
+ Memory::Host(image.to_bgra8())
} else {
Memory::Invalid
}
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index bae0f82f..95df2e99 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -45,9 +45,19 @@ impl Cache {
let opt = resvg::Options::default();
- let svg = match resvg::usvg::Tree::from_file(handle.path(), &opt.usvg) {
- Ok(tree) => Svg::Loaded(tree),
- Err(_) => Svg::NotFound,
+ let svg = match handle.data() {
+ svg::Data::Path(path) => {
+ match resvg::usvg::Tree::from_file(path, &opt.usvg) {
+ Ok(tree) => Svg::Loaded(tree),
+ Err(_) => Svg::NotFound,
+ }
+ }
+ svg::Data::Bytes(bytes) => {
+ match resvg::usvg::Tree::from_data(&bytes, &opt.usvg) {
+ Ok(tree) => Svg::Loaded(tree),
+ Err(_) => Svg::NotFound,
+ }
+ }
};
let _ = self.svgs.insert(handle.id(), svg);
@@ -107,7 +117,7 @@ impl Cache {
let allocation = texture_atlas.upload(
width,
height,
- canvas.get_data(),
+ bytemuck::cast_slice(canvas.get_data()),
device,
encoder,
)?;
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 4e0cbc60..a4c2ac0e 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -1,6 +1,6 @@
//! A [`wgpu`] renderer for [`iced_native`].
//!
-//! ![`iced_wgpu` crate graph](https://github.com/hecrj/iced/blob/cae26cb7bc627f4a5b3bcf1cd023a0c552e8c65e/docs/graphs/wgpu.png?raw=true)
+//! ![The native path of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//!
//! For now, it is the default renderer of [Iced] in native platforms.
//!
@@ -11,8 +11,9 @@
//! Currently, `iced_wgpu` supports the following primitives:
//! - Text, which is rendered using [`wgpu_glyph`]. No shaping at all.
//! - Quads or rectangles, with rounded borders and a solid background color.
-//! - Images, lazily loaded from the filesystem.
//! - Clip areas, useful to implement scrollables or hide overflowing content.
+//! - Images and SVG, loaded from memory or the file system.
+//! - Meshes of triangles, useful to draw geometry freely.
//!
//! [Iced]: https://github.com/hecrj/iced
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
@@ -22,36 +23,37 @@
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
-#![forbid(unsafe_code)]
+#![deny(unsafe_code)]
#![forbid(rust_2018_idioms)]
-pub mod defaults;
+#![cfg_attr(docsrs, feature(doc_cfg))]
+
pub mod settings;
pub mod triangle;
pub mod widget;
pub mod window;
-mod primitive;
+mod backend;
mod quad;
-mod renderer;
-mod target;
mod text;
-mod transformation;
-mod viewport;
+pub use iced_graphics::{
+ Antialiasing, Color, Defaults, Error, Primitive, Viewport,
+};
pub use wgpu;
-pub use defaults::Defaults;
-pub use primitive::Primitive;
-pub use renderer::Renderer;
+pub use backend::Backend;
pub use settings::Settings;
-pub use target::Target;
-pub use viewport::Viewport;
#[doc(no_inline)]
pub use widget::*;
-pub(crate) use quad::Quad;
-pub(crate) use transformation::Transformation;
+pub(crate) use iced_graphics::Transformation;
#[cfg(any(feature = "image", feature = "svg"))]
mod image;
+
+/// A [`wgpu`] graphics renderer for [`iced`].
+///
+/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
+/// [`iced`]: https://github.com/hecrj/iced
+pub type Renderer = iced_graphics::Renderer<Backend>;
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
deleted file mode 100644
index 46d9e624..00000000
--- a/wgpu/src/primitive.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-use iced_native::{
- image, svg, Background, Color, Font, HorizontalAlignment, Point, Rectangle,
- Vector, VerticalAlignment,
-};
-
-use crate::triangle;
-use std::sync::Arc;
-
-/// A rendering primitive.
-#[derive(Debug, Clone)]
-pub enum Primitive {
- /// An empty primitive
- None,
- /// A group of primitives
- Group {
- /// The primitives of the group
- primitives: Vec<Primitive>,
- },
- /// A text primitive
- Text {
- /// The contents of the text
- content: String,
- /// The bounds of the text
- bounds: Rectangle,
- /// The color of the text
- color: Color,
- /// The size of the text
- size: f32,
- /// The font of the text
- font: Font,
- /// The horizontal alignment of the text
- horizontal_alignment: HorizontalAlignment,
- /// The vertical alignment of the text
- vertical_alignment: VerticalAlignment,
- },
- /// A quad primitive
- Quad {
- /// The bounds of the quad
- bounds: Rectangle,
- /// The background of the quad
- background: Background,
- /// The border radius of the quad
- border_radius: u16,
- /// The border width of the quad
- border_width: u16,
- /// The border color of the quad
- border_color: Color,
- },
- /// An image primitive
- Image {
- /// The handle of the image
- handle: image::Handle,
- /// The bounds of the image
- bounds: Rectangle,
- },
- /// An SVG primitive
- Svg {
- /// The path of the SVG file
- handle: svg::Handle,
-
- /// The bounds of the viewport
- bounds: Rectangle,
- },
- /// A clip primitive
- Clip {
- /// The bounds of the clip
- bounds: Rectangle,
- /// The offset transformation of the clip
- offset: Vector<u32>,
- /// The content of the clip
- content: Box<Primitive>,
- },
- /// A low-level primitive to render a mesh of triangles.
- ///
- /// It can be used to render many kinds of geometry freely.
- Mesh2D {
- /// The top-left coordinate of the mesh
- origin: Point,
-
- /// The vertex and index buffers of the mesh
- buffers: triangle::Mesh2D,
- },
- /// A cached primitive.
- ///
- /// This can be useful if you are implementing a widget where primitive
- /// generation is expensive.
- Cached {
- /// The origin of the coordinate system of the cached primitives
- origin: Point,
-
- /// The cached primitive
- cache: Arc<Primitive>,
- },
-}
-
-impl Default for Primitive {
- fn default() -> Primitive {
- Primitive::None
- }
-}
diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs
index 9047080d..24d20cfa 100644
--- a/wgpu/src/quad.rs
+++ b/wgpu/src/quad.rs
@@ -1,7 +1,10 @@
use crate::Transformation;
+use iced_graphics::layer;
use iced_native::Rectangle;
+use bytemuck::{Pod, Zeroable};
use std::mem;
+use wgpu::util::DeviceExt;
#[derive(Debug)]
pub struct Pipeline {
@@ -14,57 +17,58 @@ pub struct Pipeline {
}
impl Pipeline {
- pub fn new(
- device: &mut wgpu::Device,
- format: wgpu::TextureFormat,
- ) -> Pipeline {
+ pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Pipeline {
let constant_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- bindings: &[wgpu::BindGroupLayoutBinding {
+ label: Some("iced_wgpu::quad uniforms layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
- ty: wgpu::BindingType::UniformBuffer { dynamic: false },
+ ty: wgpu::BindingType::UniformBuffer {
+ dynamic: false,
+ min_binding_size: wgpu::BufferSize::new(
+ mem::size_of::<Uniforms>() as u64,
+ ),
+ },
+ count: None,
}],
});
- let constants_buffer = device
- .create_buffer_mapped(
- 1,
- wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
- )
- .fill_from_slice(&[Uniforms::default()]);
+ let constants_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some("iced_wgpu::quad uniforms buffer"),
+ size: mem::size_of::<Uniforms>() as u64,
+ usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
+ mapped_at_creation: false,
+ });
let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::quad uniforms bind group"),
layout: &constant_layout,
- bindings: &[wgpu::Binding {
+ entries: &[wgpu::BindGroupEntry {
binding: 0,
- resource: wgpu::BindingResource::Buffer {
- buffer: &constants_buffer,
- range: 0..std::mem::size_of::<Uniforms>() as u64,
- },
+ resource: wgpu::BindingResource::Buffer(
+ constants_buffer.slice(..),
+ ),
}],
});
let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+ label: Some("iced_wgpu::quad pipeline layout"),
+ push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout],
});
- let vs = include_bytes!("shader/quad.vert.spv");
- let vs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&vs[..]))
- .expect("Read quad vertex shader as SPIR-V"),
- );
+ let vs_module = device
+ .create_shader_module(wgpu::include_spirv!("shader/quad.vert.spv"));
- let fs = include_bytes!("shader/quad.frag.spv");
- let fs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&fs[..]))
- .expect("Read quad fragment shader as SPIR-V"),
- );
+ let fs_module = device
+ .create_shader_module(wgpu::include_spirv!("shader/quad.frag.spv"));
let pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
- layout: &layout,
+ label: Some("iced_wgpu::quad pipeline"),
+ layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
@@ -76,9 +80,7 @@ impl Pipeline {
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Cw,
cull_mode: wgpu::CullMode::None,
- depth_bias: 0,
- depth_bias_slope_scale: 0.0,
- depth_bias_clamp: 0.0,
+ ..Default::default()
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
@@ -96,70 +98,80 @@ impl Pipeline {
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
- index_format: wgpu::IndexFormat::Uint16,
- vertex_buffers: &[
- wgpu::VertexBufferDescriptor {
- stride: mem::size_of::<Vertex>() as u64,
- step_mode: wgpu::InputStepMode::Vertex,
- attributes: &[wgpu::VertexAttributeDescriptor {
- shader_location: 0,
- format: wgpu::VertexFormat::Float2,
- offset: 0,
- }],
- },
- wgpu::VertexBufferDescriptor {
- stride: mem::size_of::<Quad>() as u64,
- step_mode: wgpu::InputStepMode::Instance,
- attributes: &[
- wgpu::VertexAttributeDescriptor {
- shader_location: 1,
+ vertex_state: wgpu::VertexStateDescriptor {
+ index_format: wgpu::IndexFormat::Uint16,
+ vertex_buffers: &[
+ wgpu::VertexBufferDescriptor {
+ stride: mem::size_of::<Vertex>() as u64,
+ step_mode: wgpu::InputStepMode::Vertex,
+ attributes: &[wgpu::VertexAttributeDescriptor {
+ shader_location: 0,
format: wgpu::VertexFormat::Float2,
offset: 0,
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 2,
- format: wgpu::VertexFormat::Float2,
- offset: 4 * 2,
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 3,
- format: wgpu::VertexFormat::Float4,
- offset: 4 * (2 + 2),
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 4,
- format: wgpu::VertexFormat::Float4,
- offset: 4 * (2 + 2 + 4),
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 5,
- format: wgpu::VertexFormat::Float,
- offset: 4 * (2 + 2 + 4 + 4),
- },
- wgpu::VertexAttributeDescriptor {
- shader_location: 6,
- format: wgpu::VertexFormat::Float,
- offset: 4 * (2 + 2 + 4 + 4 + 1),
- },
- ],
- },
- ],
+ }],
+ },
+ wgpu::VertexBufferDescriptor {
+ stride: mem::size_of::<layer::Quad>() as u64,
+ step_mode: wgpu::InputStepMode::Instance,
+ attributes: &[
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 1,
+ format: wgpu::VertexFormat::Float2,
+ offset: 0,
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 2,
+ format: wgpu::VertexFormat::Float2,
+ offset: 4 * 2,
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 3,
+ format: wgpu::VertexFormat::Float4,
+ offset: 4 * (2 + 2),
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 4,
+ format: wgpu::VertexFormat::Float4,
+ offset: 4 * (2 + 2 + 4),
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 5,
+ format: wgpu::VertexFormat::Float,
+ offset: 4 * (2 + 2 + 4 + 4),
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 6,
+ format: wgpu::VertexFormat::Float,
+ offset: 4 * (2 + 2 + 4 + 4 + 1),
+ },
+ ],
+ },
+ ],
+ },
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
- let vertices = device
- .create_buffer_mapped(QUAD_VERTS.len(), wgpu::BufferUsage::VERTEX)
- .fill_from_slice(&QUAD_VERTS);
+ let vertices =
+ device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: Some("iced_wgpu::quad vertex buffer"),
+ contents: bytemuck::cast_slice(&QUAD_VERTS),
+ usage: wgpu::BufferUsage::VERTEX,
+ });
- let indices = device
- .create_buffer_mapped(QUAD_INDICES.len(), wgpu::BufferUsage::INDEX)
- .fill_from_slice(&QUAD_INDICES);
+ let indices =
+ device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
+ label: Some("iced_wgpu::quad index buffer"),
+ contents: bytemuck::cast_slice(&QUAD_INDICES),
+ usage: wgpu::BufferUsage::INDEX,
+ });
let instances = device.create_buffer(&wgpu::BufferDescriptor {
- size: mem::size_of::<Quad>() as u64 * Quad::MAX as u64,
+ label: Some("iced_wgpu::quad instance buffer"),
+ size: mem::size_of::<layer::Quad>() as u64 * MAX_INSTANCES as u64,
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
+ mapped_at_creation: false,
});
Pipeline {
@@ -174,9 +186,10 @@ impl Pipeline {
pub fn draw(
&mut self,
- device: &mut wgpu::Device,
+ device: &wgpu::Device,
+ staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
- instances: &[Quad],
+ instances: &[layer::Quad],
transformation: Transformation,
scale: f32,
bounds: Rectangle<u32>,
@@ -184,37 +197,38 @@ impl Pipeline {
) {
let uniforms = Uniforms::new(transformation, scale);
- let constants_buffer = device
- .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
- .fill_from_slice(&[uniforms]);
+ {
+ let mut constants_buffer = staging_belt.write_buffer(
+ encoder,
+ &self.constants_buffer,
+ 0,
+ wgpu::BufferSize::new(mem::size_of::<Uniforms>() as u64)
+ .unwrap(),
+ device,
+ );
- encoder.copy_buffer_to_buffer(
- &constants_buffer,
- 0,
- &self.constants_buffer,
- 0,
- std::mem::size_of::<Uniforms>() as u64,
- );
+ constants_buffer.copy_from_slice(bytemuck::bytes_of(&uniforms));
+ }
let mut i = 0;
let total = instances.len();
while i < total {
- let end = (i + Quad::MAX).min(total);
+ let end = (i + MAX_INSTANCES).min(total);
let amount = end - i;
- let instance_buffer = device
- .create_buffer_mapped(amount, wgpu::BufferUsage::COPY_SRC)
- .fill_from_slice(&instances[i..end]);
+ let instance_bytes = bytemuck::cast_slice(&instances[i..end]);
- encoder.copy_buffer_to_buffer(
- &instance_buffer,
- 0,
+ let mut instance_buffer = staging_belt.write_buffer(
+ encoder,
&self.instances,
0,
- (mem::size_of::<Quad>() * amount) as u64,
+ wgpu::BufferSize::new(instance_bytes.len() as u64).unwrap(),
+ device,
);
+ instance_buffer.copy_from_slice(instance_bytes);
+
{
let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@@ -222,13 +236,9 @@ impl Pipeline {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: target,
resolve_target: None,
- load_op: wgpu::LoadOp::Load,
- store_op: wgpu::StoreOp::Store,
- clear_color: wgpu::Color {
- r: 0.0,
- g: 0.0,
- b: 0.0,
- a: 0.0,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Load,
+ store: true,
},
},
],
@@ -237,11 +247,9 @@ impl Pipeline {
render_pass.set_pipeline(&self.pipeline);
render_pass.set_bind_group(0, &self.constants, &[]);
- render_pass.set_index_buffer(&self.indices, 0);
- render_pass.set_vertex_buffers(
- 0,
- &[(&self.vertices, 0), (&self.instances, 0)],
- );
+ render_pass.set_index_buffer(self.indices.slice(..));
+ render_pass.set_vertex_buffer(0, self.vertices.slice(..));
+ render_pass.set_vertex_buffer(1, self.instances.slice(..));
render_pass.set_scissor_rect(
bounds.x,
bounds.y,
@@ -257,13 +265,13 @@ impl Pipeline {
);
}
- i += Quad::MAX;
+ i += MAX_INSTANCES;
}
}
}
#[repr(C)]
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Zeroable, Pod)]
pub struct Vertex {
_position: [f32; 2],
}
@@ -285,23 +293,10 @@ const QUAD_VERTS: [Vertex; 4] = [
},
];
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub struct Quad {
- pub position: [f32; 2],
- pub scale: [f32; 2],
- pub color: [f32; 4],
- pub border_color: [f32; 4],
- pub border_radius: f32,
- pub border_width: f32,
-}
-
-impl Quad {
- const MAX: usize = 100_000;
-}
+const MAX_INSTANCES: usize = 100_000;
#[repr(C)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Zeroable, Pod)]
struct Uniforms {
transform: [f32; 16],
scale: f32,
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
deleted file mode 100644
index c06af339..00000000
--- a/wgpu/src/renderer.rs
+++ /dev/null
@@ -1,509 +0,0 @@
-use crate::{
- quad, text, triangle, Defaults, Primitive, Quad, Settings, Target,
- Transformation,
-};
-
-#[cfg(any(feature = "image", feature = "svg"))]
-use crate::image::{self, Image};
-
-use iced_native::{
- layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector,
- Widget,
-};
-
-mod widget;
-
-/// A [`wgpu`] renderer.
-///
-/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
-#[derive(Debug)]
-pub struct Renderer {
- quad_pipeline: quad::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>,
- quads: Vec<Quad>,
- meshes: Vec<(Point, &'a triangle::Mesh2D)>,
- text: Vec<wgpu_glyph::Section<'a>>,
-
- #[cfg(any(feature = "image", feature = "svg"))]
- images: Vec<Image>,
-}
-
-impl<'a> Layer<'a> {
- pub fn new(bounds: Rectangle<u32>) -> Self {
- Self {
- bounds,
- quads: Vec::new(),
- text: Vec::new(),
- meshes: Vec::new(),
-
- #[cfg(any(feature = "image", feature = "svg"))]
- images: Vec::new(),
- }
- }
-}
-
-impl Renderer {
- /// 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,
- );
-
- #[cfg(any(feature = "image", feature = "svg"))]
- let image_pipeline = image::Pipeline::new(device, settings.format);
-
- Self {
- quad_pipeline,
- text_pipeline,
- triangle_pipeline,
-
- #[cfg(any(feature = "image", feature = "svg"))]
- image_pipeline,
- }
- }
-
- /// 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],
- ) -> MouseCursor {
- log::debug!("Drawing");
-
- let (width, height) = target.viewport.dimensions();
- let scale_factor = scale_factor as f32;
- let transformation = target.viewport.transformation();
-
- let mut layers = Vec::new();
-
- layers.push(Layer::new(Rectangle {
- x: 0,
- y: 0,
- width: u32::from(width),
- height: u32::from(height),
- }));
-
- self.draw_primitive(Vector::new(0.0, 0.0), primitive, &mut layers);
- self.draw_overlay(overlay, &mut layers);
-
- for layer in layers {
- self.flush(
- device,
- scale_factor,
- transformation,
- &layer,
- encoder,
- target.texture,
- width,
- height,
- );
- }
-
- #[cfg(any(feature = "image", feature = "svg"))]
- self.image_pipeline.trim_cache();
-
- *mouse_cursor
- }
-
- fn draw_primitive<'a>(
- &mut self,
- translation: Vector,
- primitive: &'a Primitive,
- layers: &mut Vec<Layer<'a>>,
- ) {
- match primitive {
- Primitive::None => {}
- Primitive::Group { primitives } => {
- // TODO: Inspect a bit and regroup (?)
- for primitive in primitives {
- self.draw_primitive(translation, primitive, layers)
- }
- }
- Primitive::Text {
- content,
- bounds,
- size,
- color,
- font,
- horizontal_alignment,
- vertical_alignment,
- } => {
- let x = match horizontal_alignment {
- iced_native::HorizontalAlignment::Left => bounds.x,
- iced_native::HorizontalAlignment::Center => {
- bounds.x + bounds.width / 2.0
- }
- iced_native::HorizontalAlignment::Right => {
- bounds.x + bounds.width
- }
- };
-
- let y = match vertical_alignment {
- iced_native::VerticalAlignment::Top => bounds.y,
- iced_native::VerticalAlignment::Center => {
- bounds.y + bounds.height / 2.0
- }
- iced_native::VerticalAlignment::Bottom => {
- bounds.y + bounds.height
- }
- };
-
- let layer = layers.last_mut().unwrap();
-
- layer.text.push(wgpu_glyph::Section {
- text: &content,
- screen_position: (x + translation.x, y + translation.y),
- bounds: (bounds.width, bounds.height),
- scale: wgpu_glyph::Scale { x: *size, y: *size },
- color: color.into_linear(),
- font_id: self.text_pipeline.find_font(*font),
- layout: wgpu_glyph::Layout::default()
- .h_align(match horizontal_alignment {
- iced_native::HorizontalAlignment::Left => {
- wgpu_glyph::HorizontalAlign::Left
- }
- iced_native::HorizontalAlignment::Center => {
- wgpu_glyph::HorizontalAlign::Center
- }
- iced_native::HorizontalAlignment::Right => {
- wgpu_glyph::HorizontalAlign::Right
- }
- })
- .v_align(match vertical_alignment {
- iced_native::VerticalAlignment::Top => {
- wgpu_glyph::VerticalAlign::Top
- }
- iced_native::VerticalAlignment::Center => {
- wgpu_glyph::VerticalAlign::Center
- }
- iced_native::VerticalAlignment::Bottom => {
- wgpu_glyph::VerticalAlign::Bottom
- }
- }),
- ..Default::default()
- })
- }
- Primitive::Quad {
- bounds,
- background,
- border_radius,
- border_width,
- border_color,
- } => {
- let layer = layers.last_mut().unwrap();
-
- // TODO: Move some of these computations to the GPU (?)
- layer.quads.push(Quad {
- position: [
- bounds.x + translation.x,
- bounds.y + translation.y,
- ],
- scale: [bounds.width, bounds.height],
- color: match background {
- 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::Mesh2D { origin, buffers } => {
- let layer = layers.last_mut().unwrap();
-
- layer.meshes.push((*origin + translation, buffers));
- }
- Primitive::Clip {
- bounds,
- offset,
- content,
- } => {
- let layer = layers.last_mut().unwrap();
-
- let layer_bounds: Rectangle<f32> = layer.bounds.into();
-
- let clip = Rectangle {
- x: bounds.x + translation.x,
- y: bounds.y + translation.y,
- ..*bounds
- };
-
- // Only draw visible content
- if let Some(clip_bounds) = layer_bounds.intersection(&clip) {
- let clip_layer = Layer::new(clip_bounds.into());
- let new_layer = Layer::new(layer.bounds);
-
- layers.push(clip_layer);
- self.draw_primitive(
- translation
- - Vector::new(offset.x as f32, offset.y as f32),
- content,
- layers,
- );
- layers.push(new_layer);
- }
- }
-
- Primitive::Cached { origin, cache } => {
- self.draw_primitive(
- translation + Vector::new(origin.x, origin.y),
- &cache,
- layers,
- );
- }
-
- #[cfg(feature = "image")]
- Primitive::Image { handle, bounds } => {
- let layer = layers.last_mut().unwrap();
-
- layer.images.push(Image {
- handle: image::Handle::Raster(handle.clone()),
- position: [
- bounds.x + translation.x,
- bounds.y + translation.y,
- ],
- size: [bounds.width, bounds.height],
- });
- }
- #[cfg(not(feature = "image"))]
- Primitive::Image { .. } => {}
-
- #[cfg(feature = "svg")]
- Primitive::Svg { handle, bounds } => {
- let layer = layers.last_mut().unwrap();
-
- layer.images.push(Image {
- handle: image::Handle::Vector(handle.clone()),
- position: [
- bounds.x + translation.x,
- bounds.y + translation.y,
- ],
- size: [bounds.width, bounds.height],
- });
- }
- #[cfg(not(feature = "svg"))]
- Primitive::Svg { .. } => {}
- }
- }
-
- fn draw_overlay<'a, T: AsRef<str>>(
- &mut self,
- lines: &'a [T],
- layers: &mut Vec<Layer<'a>>,
- ) {
- let first = layers.first().unwrap();
- let mut overlay = Layer::new(first.bounds);
-
- let font_id = self.text_pipeline.overlay_font();
- let scale = wgpu_glyph::Scale { x: 20.0, y: 20.0 };
-
- for (i, line) in lines.iter().enumerate() {
- overlay.text.push(wgpu_glyph::Section {
- text: line.as_ref(),
- screen_position: (11.0, 11.0 + 25.0 * i as f32),
- color: [0.9, 0.9, 0.9, 1.0],
- scale,
- font_id,
- ..wgpu_glyph::Section::default()
- });
-
- overlay.text.push(wgpu_glyph::Section {
- text: line.as_ref(),
- screen_position: (10.0, 10.0 + 25.0 * i as f32),
- color: [0.0, 0.0, 0.0, 1.0],
- scale,
- font_id,
- ..wgpu_glyph::Section::default()
- });
- }
-
- layers.push(overlay);
- }
-
- fn flush(
- &mut self,
- 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 * scale_factor;
-
- if layer.meshes.len() > 0 {
- let scaled = transformation
- * Transformation::scale(scale_factor, scale_factor);
-
- self.triangle_pipeline.draw(
- device,
- encoder,
- target,
- target_width,
- target_height,
- scaled,
- &layer.meshes,
- bounds,
- );
- }
-
- if layer.quads.len() > 0 {
- self.quad_pipeline.draw(
- device,
- encoder,
- &layer.quads,
- transformation,
- scale_factor,
- bounds,
- target,
- );
- }
-
- #[cfg(any(feature = "image", feature = "svg"))]
- {
- if layer.images.len() > 0 {
- let scaled = transformation
- * Transformation::scale(scale_factor, scale_factor);
-
- self.image_pipeline.draw(
- device,
- encoder,
- &layer.images,
- scaled,
- bounds,
- target,
- scale_factor,
- );
- }
- }
-
- if layer.text.len() > 0 {
- for text in layer.text.iter() {
- // Target physical coordinates directly to avoid blurry text
- let text = wgpu_glyph::Section {
- // TODO: We `round` here to avoid rerasterizing text when
- // its position changes slightly. This can make text feel a
- // bit "jumpy". We may be able to do better once we improve
- // our text rendering/caching pipeline.
- screen_position: (
- (text.screen_position.0 * scale_factor).round(),
- (text.screen_position.1 * scale_factor).round(),
- ),
- // 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
- // scaling when rendering. This would ensure that both
- // measuring and rendering follow the same layout rules.
- bounds: (
- (text.bounds.0 * scale_factor).ceil(),
- (text.bounds.1 * scale_factor).ceil(),
- ),
- scale: wgpu_glyph::Scale {
- x: text.scale.x * scale_factor,
- y: text.scale.y * scale_factor,
- },
- ..*text
- };
-
- self.text_pipeline.queue(text);
- }
-
- self.text_pipeline.draw_queued(
- device,
- encoder,
- target,
- transformation,
- wgpu_glyph::Region {
- x: bounds.x,
- y: bounds.y,
- width: bounds.width,
- height: bounds.height,
- },
- );
- }
- }
-}
-
-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, limits);
-
- self.text_pipeline.clear_measurement_cache();
-
- node
- }
-}
-
-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, defaults, layout, cursor_position);
-
- explain_layout(layout, color, &mut primitives);
- primitives.push(primitive);
-
- (Primitive::Group { primitives }, cursor)
- }
-}
-
-fn explain_layout(
- layout: Layout<'_>,
- color: Color,
- primitives: &mut Vec<Primitive>,
-) {
- primitives.push(Primitive::Quad {
- bounds: layout.bounds(),
- 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() {
- explain_layout(child, color, primitives);
- }
-}
diff --git a/wgpu/src/renderer/widget.rs b/wgpu/src/renderer/widget.rs
deleted file mode 100644
index 84f908e7..00000000
--- a/wgpu/src/renderer/widget.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-mod button;
-mod checkbox;
-mod column;
-mod container;
-mod progress_bar;
-mod radio;
-mod row;
-mod scrollable;
-mod slider;
-mod space;
-mod text;
-mod text_input;
-
-#[cfg(feature = "svg")]
-mod svg;
-
-#[cfg(feature = "image")]
-mod image;
diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs
deleted file mode 100644
index 0de5bf5c..00000000
--- a/wgpu/src/renderer/widget/button.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer};
-use iced_native::{
- Background, Color, Element, Layout, MouseCursor, Point, Rectangle, Vector,
-};
-
-impl iced_native::button::Renderer for Renderer {
- const DEFAULT_PADDING: u16 = 5;
-
- type Style = Box<dyn StyleSheet>;
-
- fn draw<Message>(
- &mut self,
- defaults: &Defaults,
- bounds: Rectangle,
- cursor_position: Point,
- is_disabled: bool,
- is_pressed: bool,
- style: &Box<dyn StyleSheet>,
- content: &Element<'_, Message, Self>,
- content_layout: Layout<'_>,
- ) -> Self::Output {
- let is_mouse_over = bounds.contains(cursor_position);
-
- let styling = if is_disabled {
- style.disabled()
- } else if is_mouse_over {
- if is_pressed {
- style.pressed()
- } else {
- style.hovered()
- }
- } else {
- style.active()
- };
-
- let (content, _) = content.draw(
- self,
- &Defaults {
- text: defaults::Text {
- color: styling.text_color,
- },
- ..*defaults
- },
- content_layout,
- cursor_position,
- );
-
- (
- if styling.background.is_some() || styling.border_width > 0 {
- let background = Primitive::Quad {
- bounds,
- background: styling
- .background
- .unwrap_or(Background::Color(Color::TRANSPARENT)),
- border_radius: styling.border_radius,
- border_width: styling.border_width,
- border_color: styling.border_color,
- };
-
- if styling.shadow_offset == Vector::default() {
- Primitive::Group {
- primitives: vec![background, content],
- }
- } else {
- // TODO: Implement proper shadow support
- let shadow = Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + styling.shadow_offset.x,
- y: bounds.y + styling.shadow_offset.y,
- ..bounds
- },
- background: Background::Color(
- [0.0, 0.0, 0.0, 0.5].into(),
- ),
- border_radius: styling.border_radius,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- };
-
- Primitive::Group {
- primitives: vec![shadow, background, content],
- }
- }
- } else {
- content
- },
- if is_mouse_over {
- MouseCursor::Pointer
- } else {
- MouseCursor::OutOfBounds
- },
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs
deleted file mode 100644
index 1a0585d3..00000000
--- a/wgpu/src/renderer/widget/checkbox.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use crate::{checkbox::StyleSheet, Primitive, Renderer};
-use iced_native::{
- checkbox, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment,
-};
-
-impl checkbox::Renderer for Renderer {
- type Style = Box<dyn StyleSheet>;
-
- const DEFAULT_SIZE: u16 = 20;
- const DEFAULT_SPACING: u16 = 15;
-
- fn draw(
- &mut self,
- bounds: Rectangle,
- is_checked: bool,
- is_mouse_over: bool,
- (label, _): Self::Output,
- style_sheet: &Self::Style,
- ) -> Self::Output {
- let style = if is_mouse_over {
- style_sheet.hovered(is_checked)
- } else {
- style_sheet.active(is_checked)
- };
-
- let checkbox = Primitive::Quad {
- bounds,
- background: style.background,
- border_radius: style.border_radius,
- border_width: style.border_width,
- border_color: style.border_color,
- };
-
- (
- Primitive::Group {
- primitives: if is_checked {
- let check = Primitive::Text {
- content: crate::text::CHECKMARK_ICON.to_string(),
- font: crate::text::BUILTIN_ICONS,
- size: bounds.height * 0.7,
- bounds: bounds,
- color: style.checkmark_color,
- horizontal_alignment: HorizontalAlignment::Center,
- vertical_alignment: VerticalAlignment::Center,
- };
-
- vec![checkbox, check, label]
- } else {
- vec![checkbox, label]
- },
- },
- if is_mouse_over {
- MouseCursor::Pointer
- } else {
- MouseCursor::OutOfBounds
- },
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/column.rs b/wgpu/src/renderer/widget/column.rs
deleted file mode 100644
index 95a7463a..00000000
--- a/wgpu/src/renderer/widget/column.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{column, Element, Layout, MouseCursor, Point};
-
-impl column::Renderer for Renderer {
- fn draw<Message>(
- &mut self,
- defaults: &Self::Defaults,
- content: &[Element<'_, Message, Self>],
- layout: Layout<'_>,
- cursor_position: Point,
- ) -> Self::Output {
- let mut mouse_cursor = MouseCursor::OutOfBounds;
-
- (
- Primitive::Group {
- primitives: content
- .iter()
- .zip(layout.children())
- .map(|(child, layout)| {
- let (primitive, new_mouse_cursor) =
- child.draw(self, defaults, layout, cursor_position);
-
- if new_mouse_cursor > mouse_cursor {
- mouse_cursor = new_mouse_cursor;
- }
-
- primitive
- })
- .collect(),
- },
- mouse_cursor,
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/container.rs b/wgpu/src/renderer/widget/container.rs
deleted file mode 100644
index 2d4d1db8..00000000
--- a/wgpu/src/renderer/widget/container.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use crate::{container, defaults, Defaults, Primitive, Renderer};
-use iced_native::{Background, Color, Element, Layout, Point, Rectangle};
-
-impl iced_native::container::Renderer for Renderer {
- type Style = Box<dyn container::StyleSheet>;
-
- fn draw<Message>(
- &mut self,
- defaults: &Defaults,
- bounds: Rectangle,
- cursor_position: Point,
- style_sheet: &Self::Style,
- content: &Element<'_, Message, Self>,
- content_layout: Layout<'_>,
- ) -> Self::Output {
- let style = style_sheet.style();
-
- let defaults = Defaults {
- text: defaults::Text {
- color: style.text_color.unwrap_or(defaults.text.color),
- },
- ..*defaults
- };
-
- let (content, mouse_cursor) =
- content.draw(self, &defaults, content_layout, cursor_position);
-
- if style.background.is_some() || style.border_width > 0 {
- let quad = Primitive::Quad {
- bounds,
- background: style
- .background
- .unwrap_or(Background::Color(Color::TRANSPARENT)),
- border_radius: style.border_radius,
- border_width: style.border_width,
- border_color: style.border_color,
- };
-
- (
- Primitive::Group {
- primitives: vec![quad, content],
- },
- mouse_cursor,
- )
- } else {
- (content, mouse_cursor)
- }
- }
-}
diff --git a/wgpu/src/renderer/widget/image.rs b/wgpu/src/renderer/widget/image.rs
deleted file mode 100644
index 70dc5d97..00000000
--- a/wgpu/src/renderer/widget/image.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{image, Layout, MouseCursor};
-
-impl image::Renderer for Renderer {
- fn dimensions(&self, handle: &image::Handle) -> (u32, u32) {
- self.image_pipeline.dimensions(handle)
- }
-
- fn draw(
- &mut self,
- handle: image::Handle,
- layout: Layout<'_>,
- ) -> Self::Output {
- (
- Primitive::Image {
- handle,
- bounds: layout.bounds(),
- },
- MouseCursor::OutOfBounds,
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/progress_bar.rs b/wgpu/src/renderer/widget/progress_bar.rs
deleted file mode 100644
index 34e33276..00000000
--- a/wgpu/src/renderer/widget/progress_bar.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use crate::{progress_bar::StyleSheet, Primitive, Renderer};
-use iced_native::{progress_bar, Color, MouseCursor, Rectangle};
-
-impl progress_bar::Renderer for Renderer {
- type Style = Box<dyn StyleSheet>;
-
- const DEFAULT_HEIGHT: u16 = 30;
-
- fn draw(
- &self,
- bounds: Rectangle,
- range: std::ops::RangeInclusive<f32>,
- value: f32,
- style_sheet: &Self::Style,
- ) -> Self::Output {
- let style = style_sheet.style();
-
- let (range_start, range_end) = range.into_inner();
- let active_progress_width = bounds.width
- * ((value - range_start) / (range_end - range_start).max(1.0));
-
- let background = Primitive::Group {
- primitives: vec![Primitive::Quad {
- bounds: Rectangle { ..bounds },
- background: style.background,
- border_radius: style.border_radius,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- }],
- };
-
- (
- if active_progress_width > 0.0 {
- let bar = Primitive::Quad {
- bounds: Rectangle {
- width: active_progress_width,
- ..bounds
- },
- background: style.bar,
- border_radius: style.border_radius,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- };
-
- Primitive::Group {
- primitives: vec![background, bar],
- }
- } else {
- background
- },
- MouseCursor::OutOfBounds,
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/radio.rs b/wgpu/src/renderer/widget/radio.rs
deleted file mode 100644
index 564f066b..00000000
--- a/wgpu/src/renderer/widget/radio.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use crate::{radio::StyleSheet, Primitive, Renderer};
-use iced_native::{radio, Background, Color, MouseCursor, Rectangle};
-
-const SIZE: f32 = 28.0;
-const DOT_SIZE: f32 = SIZE / 2.0;
-
-impl radio::Renderer for Renderer {
- type Style = Box<dyn StyleSheet>;
-
- fn default_size(&self) -> u32 {
- SIZE as u32
- }
-
- fn draw(
- &mut self,
- bounds: Rectangle,
- is_selected: bool,
- is_mouse_over: bool,
- (label, _): Self::Output,
- style_sheet: &Self::Style,
- ) -> Self::Output {
- let style = if is_mouse_over {
- style_sheet.hovered()
- } else {
- style_sheet.active()
- };
-
- let radio = Primitive::Quad {
- bounds,
- background: style.background,
- border_radius: (SIZE / 2.0) as u16,
- border_width: style.border_width,
- border_color: style.border_color,
- };
-
- (
- Primitive::Group {
- primitives: if is_selected {
- let radio_circle = Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + DOT_SIZE / 2.0,
- y: bounds.y + DOT_SIZE / 2.0,
- width: bounds.width - DOT_SIZE,
- height: bounds.height - DOT_SIZE,
- },
- background: Background::Color(style.dot_color),
- border_radius: (DOT_SIZE / 2.0) as u16,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- };
-
- vec![radio, radio_circle, label]
- } else {
- vec![radio, label]
- },
- },
- if is_mouse_over {
- MouseCursor::Pointer
- } else {
- MouseCursor::OutOfBounds
- },
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/row.rs b/wgpu/src/renderer/widget/row.rs
deleted file mode 100644
index bd9f1a04..00000000
--- a/wgpu/src/renderer/widget/row.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{row, Element, Layout, MouseCursor, Point};
-
-impl row::Renderer for Renderer {
- fn draw<Message>(
- &mut self,
- defaults: &Self::Defaults,
- children: &[Element<'_, Message, Self>],
- layout: Layout<'_>,
- cursor_position: Point,
- ) -> Self::Output {
- let mut mouse_cursor = MouseCursor::OutOfBounds;
-
- (
- Primitive::Group {
- primitives: children
- .iter()
- .zip(layout.children())
- .map(|(child, layout)| {
- let (primitive, new_mouse_cursor) =
- child.draw(self, defaults, layout, cursor_position);
-
- if new_mouse_cursor > mouse_cursor {
- mouse_cursor = new_mouse_cursor;
- }
-
- primitive
- })
- .collect(),
- },
- mouse_cursor,
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs
deleted file mode 100644
index bfee7411..00000000
--- a/wgpu/src/renderer/widget/scrollable.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{
- scrollable, Background, Color, MouseCursor, Rectangle, Vector,
-};
-
-const SCROLLBAR_WIDTH: u16 = 10;
-const SCROLLBAR_MARGIN: u16 = 2;
-
-impl scrollable::Renderer for Renderer {
- type Style = Box<dyn iced_style::scrollable::StyleSheet>;
-
- fn scrollbar(
- &self,
- bounds: Rectangle,
- content_bounds: Rectangle,
- offset: u32,
- ) -> Option<scrollable::Scrollbar> {
- if content_bounds.height > bounds.height {
- let scrollbar_bounds = Rectangle {
- x: bounds.x + bounds.width
- - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN),
- y: bounds.y,
- width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN),
- height: bounds.height,
- };
-
- let ratio = bounds.height / content_bounds.height;
- let scrollbar_height = bounds.height * ratio;
- let y_offset = offset as f32 * ratio;
-
- let scroller_bounds = Rectangle {
- x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN),
- y: scrollbar_bounds.y + y_offset,
- width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN),
- height: scrollbar_height,
- };
-
- Some(scrollable::Scrollbar {
- bounds: scrollbar_bounds,
- scroller: scrollable::Scroller {
- bounds: scroller_bounds,
- },
- })
- } else {
- None
- }
- }
-
- fn draw(
- &mut self,
- state: &scrollable::State,
- bounds: Rectangle,
- _content_bounds: Rectangle,
- is_mouse_over: bool,
- is_mouse_over_scrollbar: bool,
- scrollbar: Option<scrollable::Scrollbar>,
- offset: u32,
- style_sheet: &Self::Style,
- (content, mouse_cursor): Self::Output,
- ) -> Self::Output {
- let clip = Primitive::Clip {
- bounds,
- offset: Vector::new(0, offset),
- content: Box::new(content),
- };
-
- (
- if let Some(scrollbar) = scrollbar {
- let style = if state.is_scroller_grabbed() {
- style_sheet.dragging()
- } else if is_mouse_over_scrollbar {
- style_sheet.hovered()
- } else {
- style_sheet.active()
- };
-
- let is_scrollbar_visible =
- style.background.is_some() || style.border_width > 0;
-
- let scroller = if is_mouse_over
- || state.is_scroller_grabbed()
- || is_scrollbar_visible
- {
- Primitive::Quad {
- bounds: scrollbar.scroller.bounds,
- background: Background::Color(style.scroller.color),
- border_radius: style.scroller.border_radius,
- border_width: style.scroller.border_width,
- border_color: style.scroller.border_color,
- }
- } else {
- Primitive::None
- };
-
- let scrollbar = if is_scrollbar_visible {
- Primitive::Quad {
- bounds: Rectangle {
- x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN),
- width: scrollbar.bounds.width
- - f32::from(2 * SCROLLBAR_MARGIN),
- ..scrollbar.bounds
- },
- background: style
- .background
- .unwrap_or(Background::Color(Color::TRANSPARENT)),
- border_radius: style.border_radius,
- border_width: style.border_width,
- border_color: style.border_color,
- }
- } else {
- Primitive::None
- };
-
- Primitive::Group {
- primitives: vec![clip, scrollbar, scroller],
- }
- } else {
- clip
- },
- if is_mouse_over_scrollbar || state.is_scroller_grabbed() {
- MouseCursor::Idle
- } else {
- mouse_cursor
- },
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/slider.rs b/wgpu/src/renderer/widget/slider.rs
deleted file mode 100644
index c8ebd0da..00000000
--- a/wgpu/src/renderer/widget/slider.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-use crate::{
- slider::{HandleShape, StyleSheet},
- Primitive, Renderer,
-};
-use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle};
-
-const HANDLE_HEIGHT: f32 = 22.0;
-
-impl slider::Renderer for Renderer {
- type Style = Box<dyn StyleSheet>;
-
- fn height(&self) -> u32 {
- 30
- }
-
- fn draw(
- &mut self,
- bounds: Rectangle,
- cursor_position: Point,
- range: std::ops::RangeInclusive<f32>,
- value: f32,
- is_dragging: bool,
- style_sheet: &Self::Style,
- ) -> Self::Output {
- let is_mouse_over = bounds.contains(cursor_position);
-
- let style = if is_dragging {
- style_sheet.dragging()
- } else if is_mouse_over {
- style_sheet.hovered()
- } else {
- style_sheet.active()
- };
-
- let rail_y = bounds.y + (bounds.height / 2.0).round();
-
- let (rail_top, rail_bottom) = (
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x,
- y: rail_y,
- width: bounds.width,
- height: 2.0,
- },
- background: Background::Color(style.rail_colors.0),
- border_radius: 0,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- },
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x,
- y: rail_y + 2.0,
- width: bounds.width,
- height: 2.0,
- },
- background: Background::Color(style.rail_colors.1),
- border_radius: 0,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- },
- );
-
- let (range_start, range_end) = range.into_inner();
-
- let (handle_width, handle_height, handle_border_radius) =
- match style.handle.shape {
- HandleShape::Circle { radius } => {
- (f32::from(radius * 2), f32::from(radius * 2), radius)
- }
- HandleShape::Rectangle {
- width,
- border_radius,
- } => (f32::from(width), HANDLE_HEIGHT, border_radius),
- };
-
- let handle_offset = (bounds.width - handle_width)
- * ((value - range_start) / (range_end - range_start).max(1.0));
-
- let handle = Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + handle_offset.round(),
- y: rail_y - handle_height / 2.0,
- width: handle_width,
- height: handle_height,
- },
- background: Background::Color(style.handle.color),
- border_radius: handle_border_radius,
- border_width: style.handle.border_width,
- border_color: style.handle.border_color,
- };
-
- (
- Primitive::Group {
- primitives: vec![rail_top, rail_bottom, handle],
- },
- if is_dragging {
- MouseCursor::Grabbing
- } else if is_mouse_over {
- MouseCursor::Grab
- } else {
- MouseCursor::OutOfBounds
- },
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/space.rs b/wgpu/src/renderer/widget/space.rs
deleted file mode 100644
index 28e05437..00000000
--- a/wgpu/src/renderer/widget/space.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{space, MouseCursor, Rectangle};
-
-impl space::Renderer for Renderer {
- fn draw(&mut self, _bounds: Rectangle) -> Self::Output {
- (Primitive::None, MouseCursor::OutOfBounds)
- }
-}
diff --git a/wgpu/src/renderer/widget/svg.rs b/wgpu/src/renderer/widget/svg.rs
deleted file mode 100644
index 67bc3fe1..00000000
--- a/wgpu/src/renderer/widget/svg.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{svg, Layout, MouseCursor};
-
-impl svg::Renderer for Renderer {
- fn dimensions(&self, handle: &svg::Handle) -> (u32, u32) {
- self.image_pipeline.viewport_dimensions(handle)
- }
-
- fn draw(
- &mut self,
- handle: svg::Handle,
- layout: Layout<'_>,
- ) -> Self::Output {
- (
- Primitive::Svg {
- handle,
- bounds: layout.bounds(),
- },
- MouseCursor::OutOfBounds,
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs
deleted file mode 100644
index 33e549cd..00000000
--- a/wgpu/src/renderer/widget/text.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use crate::{Primitive, Renderer};
-use iced_native::{
- text, Color, Font, HorizontalAlignment, MouseCursor, Rectangle, Size,
- VerticalAlignment,
-};
-
-use std::f32;
-
-impl text::Renderer for Renderer {
- const DEFAULT_SIZE: u16 = 20;
-
- fn measure(
- &self,
- content: &str,
- size: u16,
- font: Font,
- bounds: Size,
- ) -> (f32, f32) {
- self.text_pipeline
- .measure(content, f32::from(size), font, bounds)
- }
-
- fn draw(
- &mut self,
- defaults: &Self::Defaults,
- bounds: Rectangle,
- content: &str,
- size: u16,
- font: Font,
- color: Option<Color>,
- horizontal_alignment: HorizontalAlignment,
- vertical_alignment: VerticalAlignment,
- ) -> Self::Output {
- (
- Primitive::Text {
- content: content.to_string(),
- size: f32::from(size),
- bounds,
- color: color.unwrap_or(defaults.text.color),
- font,
- horizontal_alignment,
- vertical_alignment,
- },
- MouseCursor::OutOfBounds,
- )
- }
-}
diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs
deleted file mode 100644
index e2a1b3a9..00000000
--- a/wgpu/src/renderer/widget/text_input.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-use crate::{text_input::StyleSheet, Primitive, Renderer};
-
-use iced_native::{
- text_input, Background, Color, Font, HorizontalAlignment, MouseCursor,
- Point, Rectangle, Size, Vector, VerticalAlignment,
-};
-use std::f32;
-
-impl text_input::Renderer for Renderer {
- type Style = Box<dyn StyleSheet>;
-
- fn default_size(&self) -> u16 {
- // TODO: Make this configurable
- 20
- }
-
- fn measure_value(&self, value: &str, size: u16, font: Font) -> f32 {
- let (mut width, _) = self.text_pipeline.measure(
- value,
- f32::from(size),
- font,
- Size::INFINITY,
- );
-
- let spaces_at_the_end = value.len() - value.trim_end().len();
-
- if spaces_at_the_end > 0 {
- let space_width = self.text_pipeline.space_width(size as f32);
- width += spaces_at_the_end as f32 * space_width;
- }
-
- width
- }
-
- fn offset(
- &self,
- text_bounds: Rectangle,
- size: u16,
- value: &text_input::Value,
- state: &text_input::State,
- font: Font,
- ) -> f32 {
- if state.is_focused() {
- let (_, offset) = measure_cursor_and_scroll_offset(
- self,
- text_bounds,
- value,
- size,
- state.cursor_position(value),
- font,
- );
-
- offset
- } else {
- 0.0
- }
- }
-
- fn draw(
- &mut self,
- bounds: Rectangle,
- text_bounds: Rectangle,
- cursor_position: Point,
- size: u16,
- font: Font,
- placeholder: &str,
- value: &text_input::Value,
- state: &text_input::State,
- style_sheet: &Self::Style,
- ) -> Self::Output {
- let is_mouse_over = bounds.contains(cursor_position);
-
- let style = if state.is_focused() {
- style_sheet.focused()
- } else if is_mouse_over {
- style_sheet.hovered()
- } else {
- style_sheet.active()
- };
-
- let input = Primitive::Quad {
- bounds,
- background: style.background,
- border_radius: style.border_radius,
- border_width: style.border_width,
- border_color: style.border_color,
- };
-
- let text = value.to_string();
-
- let text_value = Primitive::Text {
- content: if text.is_empty() {
- placeholder.to_string()
- } else {
- text.clone()
- },
- color: if text.is_empty() {
- style_sheet.placeholder_color()
- } else {
- style_sheet.value_color()
- }
- .into(),
- font,
- bounds: Rectangle {
- width: f32::INFINITY,
- ..text_bounds
- },
- size: f32::from(size),
- horizontal_alignment: HorizontalAlignment::Left,
- vertical_alignment: VerticalAlignment::Center,
- };
-
- let (contents_primitive, offset) = if state.is_focused() {
- let (text_value_width, offset) = measure_cursor_and_scroll_offset(
- self,
- text_bounds,
- value,
- size,
- state.cursor_position(value),
- font,
- );
-
- let cursor = Primitive::Quad {
- bounds: Rectangle {
- x: text_bounds.x + text_value_width,
- y: text_bounds.y,
- width: 1.0,
- height: text_bounds.height,
- },
- background: Background::Color(style_sheet.value_color()),
- border_radius: 0,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- };
-
- (
- Primitive::Group {
- primitives: vec![text_value, cursor],
- },
- Vector::new(offset as u32, 0),
- )
- } else {
- (text_value, Vector::new(0, 0))
- };
-
- let contents = Primitive::Clip {
- bounds: text_bounds,
- offset,
- content: Box::new(contents_primitive),
- };
-
- (
- Primitive::Group {
- primitives: vec![input, contents],
- },
- if is_mouse_over {
- MouseCursor::Text
- } else {
- MouseCursor::OutOfBounds
- },
- )
- }
-}
-
-fn measure_cursor_and_scroll_offset(
- renderer: &Renderer,
- text_bounds: Rectangle,
- value: &text_input::Value,
- size: u16,
- cursor_index: usize,
- font: Font,
-) -> (f32, f32) {
- use iced_native::text_input::Renderer;
-
- let text_before_cursor = value.until(cursor_index).to_string();
-
- let text_value_width =
- renderer.measure_value(&text_before_cursor, size, font);
- let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0);
-
- (text_value_width, offset)
-}
diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs
index f946ce0d..26763e22 100644
--- a/wgpu/src/settings.rs
+++ b/wgpu/src/settings.rs
@@ -1,22 +1,31 @@
-//! Configure a [`Renderer`].
-//!
-//! [`Renderer`]: struct.Renderer.html
+//! Configure a renderer.
+pub use crate::Antialiasing;
-/// The settings of a [`Renderer`].
+/// The settings of a [`Backend`].
///
-/// [`Renderer`]: ../struct.Renderer.html
+/// [`Backend`]: crate::Backend
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Settings {
- /// The output format of the [`Renderer`].
+ /// The output format of the [`Backend`].
///
- /// [`Renderer`]: ../struct.Renderer.html
+ /// [`Backend`]: crate::Backend
pub format: wgpu::TextureFormat,
+ /// The present mode of the [`Backend`].
+ ///
+ /// [`Backend`]: crate::Backend
+ pub present_mode: wgpu::PresentMode,
+
/// The bytes of the font that will be used by default.
///
/// If `None` is provided, a default system font will be chosen.
pub default_font: Option<&'static [u8]>,
+ /// The default size of text.
+ ///
+ /// By default, it will be set to 20.
+ pub default_text_size: u16,
+
/// The antialiasing strategy that will be used for triangle primitives.
pub antialiasing: Option<Antialiasing>,
}
@@ -25,32 +34,10 @@ impl Default for Settings {
fn default() -> Settings {
Settings {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
+ present_mode: wgpu::PresentMode::Mailbox,
default_font: None,
+ default_text_size: 20,
antialiasing: None,
}
}
}
-
-/// An antialiasing strategy.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Antialiasing {
- /// Multisample AA with 2 samples
- MSAAx2,
- /// Multisample AA with 4 samples
- MSAAx4,
- /// Multisample AA with 8 samples
- MSAAx8,
- /// Multisample AA with 16 samples
- MSAAx16,
-}
-
-impl Antialiasing {
- pub(crate) fn sample_count(&self) -> u32 {
- match self {
- Antialiasing::MSAAx2 => 2,
- Antialiasing::MSAAx4 => 4,
- Antialiasing::MSAAx8 => 8,
- Antialiasing::MSAAx16 => 16,
- }
- }
-}
diff --git a/wgpu/src/shader/blit.vert b/wgpu/src/shader/blit.vert
index 1c081b9e..899cd39d 100644
--- a/wgpu/src/shader/blit.vert
+++ b/wgpu/src/shader/blit.vert
@@ -3,12 +3,12 @@
layout(location = 0) out vec2 o_Uv;
const vec2 positions[6] = vec2[6](
- vec2(-1.0, -1.0),
vec2(-1.0, 1.0),
- vec2(1.0, 1.0),
vec2(-1.0, -1.0),
vec2(1.0, -1.0),
- vec2(1.0, 1.0)
+ vec2(-1.0, 1.0),
+ vec2(1.0, 1.0),
+ vec2(1.0, -1.0)
);
const vec2 uvs[6] = vec2[6](
diff --git a/wgpu/src/shader/blit.vert.spv b/wgpu/src/shader/blit.vert.spv
index ad697d48..e0b436ce 100644
--- a/wgpu/src/shader/blit.vert.spv
+++ b/wgpu/src/shader/blit.vert.spv
Binary files differ
diff --git a/wgpu/src/shader/quad.vert b/wgpu/src/shader/quad.vert
index 1d9a4fd2..09a278b1 100644
--- a/wgpu/src/shader/quad.vert
+++ b/wgpu/src/shader/quad.vert
@@ -24,6 +24,11 @@ void main() {
vec2 p_Pos = i_Pos * u_Scale;
vec2 p_Scale = i_Scale * u_Scale;
+ float i_BorderRadius = min(
+ i_BorderRadius,
+ min(i_Scale.x, i_Scale.y) / 2.0
+ );
+
mat4 i_Transform = mat4(
vec4(p_Scale.x + 1.0, 0.0, 0.0, 0.0),
vec4(0.0, p_Scale.y + 1.0, 0.0, 0.0),
diff --git a/wgpu/src/shader/quad.vert.spv b/wgpu/src/shader/quad.vert.spv
index 7059b51b..fa71ba1e 100644
--- a/wgpu/src/shader/quad.vert.spv
+++ b/wgpu/src/shader/quad.vert.spv
Binary files differ
diff --git a/wgpu/src/target.rs b/wgpu/src/target.rs
deleted file mode 100644
index 1e72c0c3..00000000
--- a/wgpu/src/target.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use crate::Viewport;
-
-/// A rendering target.
-#[derive(Debug)]
-pub struct Target<'a> {
- /// The texture where graphics will be rendered.
- pub texture: &'a wgpu::TextureView,
-
- /// The viewport of the target.
- ///
- /// Most of the time, you will want this to match the dimensions of the
- /// texture.
- pub viewport: &'a Viewport,
-}
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index c5670102..78999cf8 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -1,84 +1,71 @@
-mod font;
-
use crate::Transformation;
-
+use iced_graphics::font;
use std::{cell::RefCell, collections::HashMap};
-
-pub const BUILTIN_ICONS: iced_native::Font = iced_native::Font::External {
- name: "iced_wgpu icons",
- bytes: include_bytes!("text/icons.ttf"),
-};
-
-pub const CHECKMARK_ICON: char = '\u{F00C}';
-
-const FALLBACK_FONT: &[u8] = include_bytes!("../fonts/Lato-Regular.ttf");
+use wgpu_glyph::ab_glyph;
#[derive(Debug)]
pub struct Pipeline {
- draw_brush: RefCell<wgpu_glyph::GlyphBrush<'static, ()>>,
+ draw_brush: RefCell<wgpu_glyph::GlyphBrush<()>>,
draw_font_map: RefCell<HashMap<String, wgpu_glyph::FontId>>,
-
- measure_brush: RefCell<glyph_brush::GlyphBrush<'static, ()>>,
+ measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
}
impl Pipeline {
pub fn new(
- device: &mut wgpu::Device,
+ device: &wgpu::Device,
format: wgpu::TextureFormat,
default_font: Option<&[u8]>,
) -> Self {
- // TODO: Font customization
- let font_source = font::Source::new();
+ let default_font = default_font.map(|slice| slice.to_vec());
- let default_font =
- default_font.map(|slice| slice.to_vec()).unwrap_or_else(|| {
- font_source
+ // TODO: Font customization
+ #[cfg(feature = "default_system_font")]
+ let default_font = {
+ default_font.or_else(|| {
+ font::Source::new()
.load(&[font::Family::SansSerif, font::Family::Serif])
- .unwrap_or_else(|_| FALLBACK_FONT.to_vec())
- });
-
- let load_glyph_brush = |font: Vec<u8>| {
- let builder =
- wgpu_glyph::GlyphBrushBuilder::using_fonts_bytes(vec![
- font.clone()
- ])?;
-
- Ok((
- builder,
- glyph_brush::GlyphBrushBuilder::using_font_bytes(font).build(),
- ))
+ .ok()
+ })
};
- let (brush_builder, measure_brush) = load_glyph_brush(default_font)
- .unwrap_or_else(|_: wgpu_glyph::rusttype::Error| {
- log::warn!("System font failed to load. Falling back to embedded font...");
+ let default_font =
+ default_font.unwrap_or_else(|| font::FALLBACK.to_vec());
- load_glyph_brush(FALLBACK_FONT.to_vec()).expect("Load fallback font")
+ let font = ab_glyph::FontArc::try_from_vec(default_font)
+ .unwrap_or_else(|_| {
+ log::warn!(
+ "System font failed to load. Falling back to \
+ embedded font..."
+ );
+
+ ab_glyph::FontArc::try_from_slice(font::FALLBACK)
+ .expect("Load fallback font")
});
- let draw_brush = brush_builder
- .initial_cache_size((2048, 2048))
- .build(device, format);
+ let draw_brush =
+ wgpu_glyph::GlyphBrushBuilder::using_font(font.clone())
+ .initial_cache_size((2048, 2048))
+ .draw_cache_multithread(false) // TODO: Expose as a configuration flag
+ .build(device, format);
+
+ let measure_brush =
+ glyph_brush::GlyphBrushBuilder::using_font(font).build();
Pipeline {
draw_brush: RefCell::new(draw_brush),
draw_font_map: RefCell::new(HashMap::new()),
-
measure_brush: RefCell::new(measure_brush),
}
}
- pub fn overlay_font(&self) -> wgpu_glyph::FontId {
- wgpu_glyph::FontId(0)
- }
-
pub fn queue(&mut self, section: wgpu_glyph::Section<'_>) {
self.draw_brush.borrow_mut().queue(section);
}
pub fn draw_queued(
&mut self,
- device: &mut wgpu::Device,
+ device: &wgpu::Device,
+ staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
transformation: Transformation,
@@ -88,6 +75,7 @@ impl Pipeline {
.borrow_mut()
.draw_queued_with_transform_and_scissoring(
device,
+ staging_belt,
encoder,
target,
transformation.into(),
@@ -108,10 +96,13 @@ impl Pipeline {
let wgpu_glyph::FontId(font_id) = self.find_font(font);
let section = wgpu_glyph::Section {
- text: content,
- scale: wgpu_glyph::Scale { x: size, y: size },
bounds: (bounds.width, bounds.height),
- font_id: wgpu_glyph::FontId(font_id),
+ text: vec![wgpu_glyph::Text {
+ text: content,
+ scale: size.into(),
+ font_id: wgpu_glyph::FontId(font_id),
+ extra: wgpu_glyph::Extra::default(),
+ }],
..Default::default()
};
@@ -124,21 +115,7 @@ impl Pipeline {
}
}
- pub fn space_width(&self, size: f32) -> f32 {
- use wgpu_glyph::GlyphCruncher;
-
- let glyph_brush = self.measure_brush.borrow();
-
- // TODO: Select appropriate font
- let font = &glyph_brush.fonts()[0];
-
- font.glyph(' ')
- .scaled(wgpu_glyph::Scale { x: size, y: size })
- .h_metrics()
- .advance_width
- }
-
- pub fn clear_measurement_cache(&mut self) {
+ pub fn trim_measurement_cache(&mut self) {
// TODO: We should probably use a `GlyphCalculator` for this. However,
// it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
// This makes stuff quite inconvenient. A manual method for trimming the
@@ -170,11 +147,12 @@ impl Pipeline {
return *font_id;
}
- // TODO: Find a way to share font data
- let _ = self.measure_brush.borrow_mut().add_font_bytes(bytes);
+ let font = ab_glyph::FontArc::try_from_slice(bytes)
+ .expect("Load font");
+
+ let _ = self.measure_brush.borrow_mut().add_font(font.clone());
- let font_id =
- self.draw_brush.borrow_mut().add_font_bytes(bytes);
+ let font_id = self.draw_brush.borrow_mut().add_font(font);
let _ = self
.draw_font_map
diff --git a/wgpu/src/text/font.rs b/wgpu/src/text/font.rs
deleted file mode 100644
index 7346ccdb..00000000
--- a/wgpu/src/text/font.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-pub use font_kit::{
- error::SelectionError as LoadError, family_name::FamilyName as Family,
-};
-
-pub struct Source {
- raw: font_kit::source::SystemSource,
-}
-
-impl Source {
- pub fn new() -> Self {
- Source {
- raw: font_kit::source::SystemSource::new(),
- }
- }
-
- pub fn load(&self, families: &[Family]) -> Result<Vec<u8>, LoadError> {
- let font = self.raw.select_best_match(
- families,
- &font_kit::properties::Properties::default(),
- )?;
-
- match font {
- font_kit::handle::Handle::Path { path, .. } => {
- use std::io::Read;
-
- let mut buf = Vec::new();
- let mut reader = std::fs::File::open(path).expect("Read font");
- let _ = reader.read_to_end(&mut buf);
-
- Ok(buf)
- }
- font_kit::handle::Handle::Memory { bytes, .. } => {
- Ok(bytes.as_ref().clone())
- }
- }
- }
-}
diff --git a/wgpu/src/text/icons.ttf b/wgpu/src/text/icons.ttf
deleted file mode 100644
index 1c832f86..00000000
--- a/wgpu/src/text/icons.ttf
+++ /dev/null
Binary files differ
diff --git a/wgpu/src/transformation.rs b/wgpu/src/transformation.rs
deleted file mode 100644
index 666696f3..00000000
--- a/wgpu/src/transformation.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use glam::{Mat4, Vec3, Vec4};
-use std::ops::Mul;
-
-/// A 2D transformation matrix.
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Transformation(Mat4);
-
-impl Transformation {
- /// Get the identity transformation.
- pub fn identity() -> Transformation {
- Transformation(Mat4::identity())
- }
-
- /// Creates an orthographic projection.
- #[rustfmt::skip]
- pub fn orthographic(width: u32, height: u32) -> Transformation {
- Transformation(Mat4::from_cols(
- Vec4::new(2.0 / width as f32, 0.0, 0.0, 0.0),
- Vec4::new(0.0, 2.0 / height as f32, 0.0, 0.0),
- Vec4::new(0.0, 0.0, -1.0, 0.0),
- Vec4::new(-1.0, -1.0, 0.0, 1.0)
- ))
- }
-
- /// Creates a translate transformation.
- pub fn translate(x: f32, y: f32) -> Transformation {
- Transformation(Mat4::from_translation(Vec3::new(x, y, 0.0)))
- }
-
- /// Creates a scale transformation.
- pub fn scale(x: f32, y: f32) -> Transformation {
- Transformation(Mat4::from_scale(Vec3::new(x, y, 1.0)))
- }
-}
-
-impl Mul for Transformation {
- type Output = Self;
-
- fn mul(self, rhs: Self) -> Self {
- Transformation(self.0 * rhs.0)
- }
-}
-
-impl AsRef<[f32; 16]> for Transformation {
- fn as_ref(&self) -> &[f32; 16] {
- self.0.as_ref()
- }
-}
-
-impl From<Transformation> for [f32; 16] {
- fn from(t: Transformation) -> [f32; 16] {
- t.as_ref().clone()
- }
-}
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index 51a6f954..61a771d8 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -1,18 +1,23 @@
//! Draw meshes of triangles.
use crate::{settings, Transformation};
-use iced_native::{Point, Rectangle};
+use iced_graphics::layer;
+
+use bytemuck::{Pod, Zeroable};
use std::mem;
+pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
+
mod msaa;
-const UNIFORM_BUFFER_SIZE: usize = 100;
-const VERTEX_BUFFER_SIZE: usize = 100_000;
-const INDEX_BUFFER_SIZE: usize = 100_000;
+const UNIFORM_BUFFER_SIZE: usize = 50;
+const VERTEX_BUFFER_SIZE: usize = 10_000;
+const INDEX_BUFFER_SIZE: usize = 10_000;
#[derive(Debug)]
pub(crate) struct Pipeline {
pipeline: wgpu::RenderPipeline,
blit: Option<msaa::Blit>,
+ constants_layout: wgpu::BindGroupLayout,
constants: wgpu::BindGroup,
uniforms_buffer: Buffer<Uniforms>,
vertex_buffer: Buffer<Vertex2D>,
@@ -21,6 +26,7 @@ pub(crate) struct Pipeline {
#[derive(Debug)]
struct Buffer<T> {
+ label: &'static str,
raw: wgpu::Buffer,
size: usize,
usage: wgpu::BufferUsage,
@@ -29,16 +35,20 @@ struct Buffer<T> {
impl<T> Buffer<T> {
pub fn new(
+ label: &'static str,
device: &wgpu::Device,
size: usize,
usage: wgpu::BufferUsage,
) -> Self {
let raw = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some(label),
size: (std::mem::size_of::<T>() * size) as u64,
usage,
+ mapped_at_creation: false,
});
Buffer {
+ label,
raw,
size,
usage,
@@ -46,34 +56,48 @@ impl<T> Buffer<T> {
}
}
- pub fn ensure_capacity(&mut self, device: &wgpu::Device, size: usize) {
- if self.size < size {
+ pub fn expand(&mut self, device: &wgpu::Device, size: usize) -> bool {
+ let needs_resize = self.size < size;
+
+ if needs_resize {
self.raw = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some(self.label),
size: (std::mem::size_of::<T>() * size) as u64,
usage: self.usage,
+ mapped_at_creation: false,
});
self.size = size;
}
+
+ needs_resize
}
}
impl Pipeline {
pub fn new(
- device: &mut wgpu::Device,
+ device: &wgpu::Device,
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Pipeline {
- let constant_layout =
+ let constants_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- bindings: &[wgpu::BindGroupLayoutBinding {
+ label: Some("iced_wgpu::triangle uniforms layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
- ty: wgpu::BindingType::UniformBuffer { dynamic: true },
+ ty: wgpu::BindingType::UniformBuffer {
+ dynamic: true,
+ min_binding_size: wgpu::BufferSize::new(
+ mem::size_of::<Uniforms>() as u64,
+ ),
+ },
+ count: None,
}],
});
let constants_buffer = Buffer::new(
+ "iced_wgpu::triangle uniforms buffer",
device,
UNIFORM_BUFFER_SIZE,
wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
@@ -81,36 +105,37 @@ impl Pipeline {
let constant_bind_group =
device.create_bind_group(&wgpu::BindGroupDescriptor {
- layout: &constant_layout,
- bindings: &[wgpu::Binding {
+ label: Some("iced_wgpu::triangle uniforms bind group"),
+ layout: &constants_layout,
+ entries: &[wgpu::BindGroupEntry {
binding: 0,
- resource: wgpu::BindingResource::Buffer {
- buffer: &constants_buffer.raw,
- range: 0..std::mem::size_of::<Uniforms>() as u64,
- },
+ resource: wgpu::BindingResource::Buffer(
+ constants_buffer
+ .raw
+ .slice(0..std::mem::size_of::<Uniforms>() as u64),
+ ),
}],
});
let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
- bind_group_layouts: &[&constant_layout],
+ label: Some("iced_wgpu::triangle pipeline layout"),
+ push_constant_ranges: &[],
+ bind_group_layouts: &[&constants_layout],
});
- let vs = include_bytes!("shader/triangle.vert.spv");
- let vs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&vs[..]))
- .expect("Read triangle vertex shader as SPIR-V"),
- );
+ let vs_module = device.create_shader_module(wgpu::include_spirv!(
+ "shader/triangle.vert.spv"
+ ));
- let fs = include_bytes!("shader/triangle.frag.spv");
- let fs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&fs[..]))
- .expect("Read triangle fragment shader as SPIR-V"),
- );
+ let fs_module = device.create_shader_module(wgpu::include_spirv!(
+ "shader/triangle.frag.spv"
+ ));
let pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
- layout: &layout,
+ label: Some("iced_wgpu::triangle pipeline"),
+ layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
@@ -122,9 +147,7 @@ impl Pipeline {
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Cw,
cull_mode: wgpu::CullMode::None,
- depth_bias: 0,
- depth_bias_slope_scale: 0.0,
- depth_bias_clamp: 0.0,
+ ..Default::default()
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
@@ -142,28 +165,30 @@ impl Pipeline {
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
- index_format: wgpu::IndexFormat::Uint32,
- vertex_buffers: &[wgpu::VertexBufferDescriptor {
- stride: mem::size_of::<Vertex2D>() as u64,
- step_mode: wgpu::InputStepMode::Vertex,
- attributes: &[
- // Position
- wgpu::VertexAttributeDescriptor {
- shader_location: 0,
- format: wgpu::VertexFormat::Float2,
- offset: 0,
- },
- // Color
- wgpu::VertexAttributeDescriptor {
- shader_location: 1,
- format: wgpu::VertexFormat::Float4,
- offset: 4 * 2,
- },
- ],
- }],
- sample_count: antialiasing
- .map(|a| a.sample_count())
- .unwrap_or(1),
+ vertex_state: wgpu::VertexStateDescriptor {
+ index_format: wgpu::IndexFormat::Uint32,
+ vertex_buffers: &[wgpu::VertexBufferDescriptor {
+ stride: mem::size_of::<Vertex2D>() as u64,
+ step_mode: wgpu::InputStepMode::Vertex,
+ attributes: &[
+ // Position
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 0,
+ format: wgpu::VertexFormat::Float2,
+ offset: 0,
+ },
+ // Color
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 1,
+ format: wgpu::VertexFormat::Float4,
+ offset: 4 * 2,
+ },
+ ],
+ }],
+ },
+ sample_count: u32::from(
+ antialiasing.map(|a| a.sample_count()).unwrap_or(1),
+ ),
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
@@ -171,14 +196,17 @@ impl Pipeline {
Pipeline {
pipeline,
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
+ constants_layout,
constants: constant_bind_group,
uniforms_buffer: constants_buffer,
vertex_buffer: Buffer::new(
+ "iced_wgpu::triangle vertex buffer",
device,
VERTEX_BUFFER_SIZE,
wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
),
index_buffer: Buffer::new(
+ "iced_wgpu::triangle index buffer",
device,
INDEX_BUFFER_SIZE,
wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST,
@@ -188,30 +216,50 @@ impl Pipeline {
pub fn draw(
&mut self,
- device: &mut wgpu::Device,
+ device: &wgpu::Device,
+ staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
target_width: u32,
target_height: u32,
transformation: Transformation,
- meshes: &[(Point, &Mesh2D)],
- bounds: Rectangle<u32>,
+ scale_factor: f32,
+ meshes: &[layer::Mesh<'_>],
) {
// This looks a bit crazy, but we are just counting how many vertices
// and indices we will need to handle.
// TODO: Improve readability
let (total_vertices, total_indices) = meshes
.iter()
- .map(|(_, mesh)| (mesh.vertices.len(), mesh.indices.len()))
+ .map(|layer::Mesh { buffers, .. }| {
+ (buffers.vertices.len(), buffers.indices.len())
+ })
.fold((0, 0), |(total_v, total_i), (v, i)| {
(total_v + v, total_i + i)
});
// Then we ensure the current buffers are big enough, resizing if
// necessary
- self.uniforms_buffer.ensure_capacity(device, meshes.len());
- self.vertex_buffer.ensure_capacity(device, total_vertices);
- self.index_buffer.ensure_capacity(device, total_indices);
+ let _ = self.vertex_buffer.expand(device, total_vertices);
+ let _ = self.index_buffer.expand(device, total_indices);
+
+ // If the uniforms buffer is resized, then we need to recreate its
+ // bind group.
+ if self.uniforms_buffer.expand(device, meshes.len()) {
+ self.constants =
+ device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::triangle uniforms buffer"),
+ layout: &self.constants_layout,
+ entries: &[wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::Buffer(
+ self.uniforms_buffer.raw.slice(
+ 0..std::mem::size_of::<Uniforms>() as u64,
+ ),
+ ),
+ }],
+ });
+ }
let mut uniforms: Vec<Uniforms> = Vec::with_capacity(meshes.len());
let mut offsets: Vec<(
@@ -223,73 +271,85 @@ impl Pipeline {
let mut last_index = 0;
// We upload everything upfront
- for (origin, mesh) in meshes {
- let transform = Uniforms {
- transform: (transformation
- * Transformation::translate(origin.x, origin.y))
- .into(),
- };
-
- let vertex_buffer = device
- .create_buffer_mapped(
- mesh.vertices.len(),
- wgpu::BufferUsage::COPY_SRC,
- )
- .fill_from_slice(&mesh.vertices);
-
- let index_buffer = device
- .create_buffer_mapped(
- mesh.indices.len(),
- wgpu::BufferUsage::COPY_SRC,
- )
- .fill_from_slice(&mesh.indices);
-
- encoder.copy_buffer_to_buffer(
- &vertex_buffer,
- 0,
- &self.vertex_buffer.raw,
- (std::mem::size_of::<Vertex2D>() * last_vertex) as u64,
- (std::mem::size_of::<Vertex2D>() * mesh.vertices.len()) as u64,
- );
+ for mesh in meshes {
+ let transform = (transformation
+ * Transformation::translate(mesh.origin.x, mesh.origin.y))
+ .into();
+
+ let vertices = bytemuck::cast_slice(&mesh.buffers.vertices);
+ let indices = bytemuck::cast_slice(&mesh.buffers.indices);
+
+ match (
+ wgpu::BufferSize::new(vertices.len() as u64),
+ wgpu::BufferSize::new(indices.len() as u64),
+ ) {
+ (Some(vertices_size), Some(indices_size)) => {
+ {
+ let mut vertex_buffer = staging_belt.write_buffer(
+ encoder,
+ &self.vertex_buffer.raw,
+ (std::mem::size_of::<Vertex2D>() * last_vertex)
+ as u64,
+ vertices_size,
+ device,
+ );
+
+ vertex_buffer.copy_from_slice(vertices);
+ }
+
+ {
+ let mut index_buffer = staging_belt.write_buffer(
+ encoder,
+ &self.index_buffer.raw,
+ (std::mem::size_of::<u32>() * last_index) as u64,
+ indices_size,
+ device,
+ );
+
+ index_buffer.copy_from_slice(indices);
+ }
+
+ uniforms.push(transform);
+ offsets.push((
+ last_vertex as u64,
+ last_index as u64,
+ mesh.buffers.indices.len(),
+ ));
+
+ last_vertex += mesh.buffers.vertices.len();
+ last_index += mesh.buffers.indices.len();
+ }
+ _ => {}
+ }
+ }
+
+ let uniforms = bytemuck::cast_slice(&uniforms);
- encoder.copy_buffer_to_buffer(
- &index_buffer,
+ if let Some(uniforms_size) =
+ wgpu::BufferSize::new(uniforms.len() as u64)
+ {
+ let mut uniforms_buffer = staging_belt.write_buffer(
+ encoder,
+ &self.uniforms_buffer.raw,
0,
- &self.index_buffer.raw,
- (std::mem::size_of::<u32>() * last_index) as u64,
- (std::mem::size_of::<u32>() * mesh.indices.len()) as u64,
+ uniforms_size,
+ device,
);
- uniforms.push(transform);
- offsets.push((
- last_vertex as u64,
- last_index as u64,
- mesh.indices.len(),
- ));
-
- last_vertex += mesh.vertices.len();
- last_index += mesh.indices.len();
+ uniforms_buffer.copy_from_slice(uniforms);
}
- let uniforms_buffer = device
- .create_buffer_mapped(uniforms.len(), wgpu::BufferUsage::COPY_SRC)
- .fill_from_slice(&uniforms);
-
- encoder.copy_buffer_to_buffer(
- &uniforms_buffer,
- 0,
- &self.uniforms_buffer.raw,
- 0,
- (std::mem::size_of::<Uniforms>() * uniforms.len()) as u64,
- );
-
{
- let (attachment, resolve_target, load_op) =
+ let (attachment, resolve_target, load) =
if let Some(blit) = &mut self.blit {
let (attachment, resolve_target) =
blit.targets(device, target_width, target_height);
- (attachment, Some(resolve_target), wgpu::LoadOp::Clear)
+ (
+ attachment,
+ Some(resolve_target),
+ wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
+ )
} else {
(target, None, wgpu::LoadOp::Load)
};
@@ -300,47 +360,43 @@ impl Pipeline {
wgpu::RenderPassColorAttachmentDescriptor {
attachment,
resolve_target,
- load_op,
- store_op: wgpu::StoreOp::Store,
- clear_color: wgpu::Color {
- r: 0.0,
- g: 0.0,
- b: 0.0,
- a: 0.0,
- },
+ ops: wgpu::Operations { load, store: true },
},
],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.pipeline);
- render_pass.set_scissor_rect(
- bounds.x,
- bounds.y,
- bounds.width,
- bounds.height,
- );
for (i, (vertex_offset, index_offset, indices)) in
offsets.into_iter().enumerate()
{
+ let clip_bounds = (meshes[i].clip_bounds * scale_factor).snap();
+
+ render_pass.set_scissor_rect(
+ clip_bounds.x,
+ clip_bounds.y,
+ clip_bounds.width,
+ clip_bounds.height,
+ );
+
render_pass.set_bind_group(
0,
&self.constants,
- &[(std::mem::size_of::<Uniforms>() * i) as u64],
+ &[(std::mem::size_of::<Uniforms>() * i) as u32],
);
render_pass.set_index_buffer(
- &self.index_buffer.raw,
- index_offset * std::mem::size_of::<u32>() as u64,
+ self.index_buffer
+ .raw
+ .slice(index_offset * mem::size_of::<u32>() as u64..),
);
- render_pass.set_vertex_buffers(
+ render_pass.set_vertex_buffer(
0,
- &[(
- &self.vertex_buffer.raw,
- vertex_offset * std::mem::size_of::<Vertex2D>() as u64,
- )],
+ self.vertex_buffer.raw.slice(
+ vertex_offset * mem::size_of::<Vertex2D>() as u64..,
+ ),
);
render_pass.draw_indexed(0..indices as u32, 0, 0..1);
@@ -354,38 +410,31 @@ impl Pipeline {
}
#[repr(C)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Zeroable, Pod)]
struct Uniforms {
transform: [f32; 16],
+ // We need to align this to 256 bytes to please `wgpu`...
+ // TODO: Be smarter and stop wasting memory!
+ _padding_a: [f32; 32],
+ _padding_b: [f32; 16],
}
impl Default for Uniforms {
fn default() -> Self {
Self {
transform: *Transformation::identity().as_ref(),
+ _padding_a: [0.0; 32],
+ _padding_b: [0.0; 16],
}
}
}
-/// A two-dimensional vertex with some color in __linear__ RGBA.
-#[repr(C)]
-#[derive(Copy, Clone, Debug)]
-pub struct Vertex2D {
- /// The vertex position
- pub position: [f32; 2],
- /// The vertex color in __linear__ RGBA.
- pub color: [f32; 4],
-}
-
-/// A set of [`Vertex2D`] and indices representing a list of triangles.
-///
-/// [`Vertex2D`]: struct.Vertex2D.html
-#[derive(Clone, Debug)]
-pub struct Mesh2D {
- /// The vertices of the mesh
- pub vertices: Vec<Vertex2D>,
- /// The list of vertex indices that defines the triangles of the mesh.
- ///
- /// Therefore, this list should always have a length that is a multiple of 3.
- pub indices: Vec<u32>,
+impl From<Transformation> for Uniforms {
+ fn from(transformation: Transformation) -> Uniforms {
+ Self {
+ transform: transformation.into(),
+ _padding_a: [0.0; 32],
+ _padding_b: [0.0; 16],
+ }
+ }
}
diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs
index 7ccfb062..db86f748 100644
--- a/wgpu/src/triangle/msaa.rs
+++ b/wgpu/src/triangle/msaa.rs
@@ -23,24 +23,25 @@ impl Blit {
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Linear,
- lod_min_clamp: -100.0,
- lod_max_clamp: 100.0,
- compare_function: wgpu::CompareFunction::Always,
+ ..Default::default()
});
let constant_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- bindings: &[wgpu::BindGroupLayoutBinding {
+ label: Some("iced_wgpu::triangle:msaa uniforms layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
- ty: wgpu::BindingType::Sampler,
+ ty: wgpu::BindingType::Sampler { comparison: false },
+ count: None,
}],
});
let constant_bind_group =
device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::triangle::msaa uniforms bind group"),
layout: &constant_layout,
- bindings: &[wgpu::Binding {
+ entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Sampler(&sampler),
}],
@@ -48,36 +49,38 @@ impl Blit {
let texture_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- bindings: &[wgpu::BindGroupLayoutBinding {
+ label: Some("iced_wgpu::triangle::msaa texture layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::SampledTexture {
- multisampled: false,
dimension: wgpu::TextureViewDimension::D2,
+ component_type: wgpu::TextureComponentType::Float,
+ multisampled: false,
},
+ count: None,
}],
});
let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+ label: Some("iced_wgpu::triangle::msaa pipeline layout"),
+ push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout, &texture_layout],
});
- let vs = include_bytes!("../shader/blit.vert.spv");
- let vs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&vs[..]))
- .expect("Read blit vertex shader as SPIR-V"),
- );
+ let vs_module = device.create_shader_module(wgpu::include_spirv!(
+ "../shader/blit.vert.spv"
+ ));
- let fs = include_bytes!("../shader/blit.frag.spv");
- let fs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&fs[..]))
- .expect("Read blit fragment shader as SPIR-V"),
- );
+ let fs_module = device.create_shader_module(wgpu::include_spirv!(
+ "../shader/blit.frag.spv"
+ ));
let pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
- layout: &layout,
+ label: Some("iced_wgpu::triangle::msaa pipeline"),
+ layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
@@ -89,9 +92,7 @@ impl Blit {
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Cw,
cull_mode: wgpu::CullMode::None,
- depth_bias: 0,
- depth_bias_slope_scale: 0.0,
- depth_bias_clamp: 0.0,
+ ..Default::default()
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
@@ -109,8 +110,10 @@ impl Blit {
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
- index_format: wgpu::IndexFormat::Uint16,
- vertex_buffers: &[],
+ vertex_state: wgpu::VertexStateDescriptor {
+ index_format: wgpu::IndexFormat::Uint16,
+ vertex_buffers: &[],
+ },
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
@@ -120,7 +123,7 @@ impl Blit {
format,
pipeline,
constants: constant_bind_group,
- texture_layout: texture_layout,
+ texture_layout,
sample_count: antialiasing.sample_count(),
targets: None,
}
@@ -173,13 +176,9 @@ impl Blit {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: target,
resolve_target: None,
- load_op: wgpu::LoadOp::Load,
- store_op: wgpu::StoreOp::Store,
- clear_color: wgpu::Color {
- r: 0.0,
- g: 0.0,
- b: 0.0,
- a: 0.0,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Load,
+ store: true,
},
},
],
@@ -222,8 +221,8 @@ impl Targets {
};
let attachment = device.create_texture(&wgpu::TextureDescriptor {
+ label: Some("iced_wgpu::triangle::msaa attachment"),
size: extent,
- array_layer_count: 1,
mip_level_count: 1,
sample_count,
dimension: wgpu::TextureDimension::D2,
@@ -232,8 +231,8 @@ impl Targets {
});
let resolve = device.create_texture(&wgpu::TextureDescriptor {
+ label: Some("iced_wgpu::triangle::msaa resolve target"),
size: extent,
- array_layer_count: 1,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
@@ -242,12 +241,16 @@ impl Targets {
| wgpu::TextureUsage::SAMPLED,
});
- let attachment = attachment.create_default_view();
- let resolve = resolve.create_default_view();
+ let attachment =
+ attachment.create_view(&wgpu::TextureViewDescriptor::default());
+
+ let resolve =
+ resolve.create_view(&wgpu::TextureViewDescriptor::default());
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::triangle::msaa texture bind group"),
layout: texture_layout,
- bindings: &[wgpu::Binding {
+ entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&resolve),
}],
diff --git a/wgpu/src/viewport.rs b/wgpu/src/viewport.rs
deleted file mode 100644
index 66242468..00000000
--- a/wgpu/src/viewport.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use crate::Transformation;
-
-/// A viewing region for displaying computer graphics.
-#[derive(Debug)]
-pub struct Viewport {
- width: u32,
- height: u32,
- transformation: Transformation,
-}
-
-impl Viewport {
- /// Creates a new [`Viewport`] with the given dimensions.
- pub fn new(width: u32, height: u32) -> Viewport {
- Viewport {
- width,
- height,
- transformation: Transformation::orthographic(width, height),
- }
- }
-
- /// Returns the dimensions of the [`Viewport`].
- pub fn dimensions(&self) -> (u32, u32) {
- (self.width, self.height)
- }
-
- pub(crate) fn transformation(&self) -> Transformation {
- self.transformation
- }
-}
diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs
index 73cce7e2..177ae1b6 100644
--- a/wgpu/src/widget.rs
+++ b/wgpu/src/widget.rs
@@ -7,11 +7,16 @@
//! ```
//! use iced_wgpu::{button, Button};
//! ```
+use crate::Renderer;
+
pub mod button;
pub mod checkbox;
pub mod container;
+pub mod pane_grid;
+pub mod pick_list;
pub mod progress_bar;
pub mod radio;
+pub mod rule;
pub mod scrollable;
pub mod slider;
pub mod text_input;
@@ -23,10 +28,16 @@ pub use checkbox::Checkbox;
#[doc(no_inline)]
pub use container::Container;
#[doc(no_inline)]
+pub use pane_grid::PaneGrid;
+#[doc(no_inline)]
+pub use pick_list::PickList;
+#[doc(no_inline)]
pub use progress_bar::ProgressBar;
#[doc(no_inline)]
pub use radio::Radio;
#[doc(no_inline)]
+pub use rule::Rule;
+#[doc(no_inline)]
pub use scrollable::Scrollable;
#[doc(no_inline)]
pub use slider::Slider;
@@ -34,8 +45,28 @@ pub use slider::Slider;
pub use text_input::TextInput;
#[cfg(feature = "canvas")]
+#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
pub mod canvas;
#[cfg(feature = "canvas")]
#[doc(no_inline)]
pub use canvas::Canvas;
+
+#[cfg(feature = "qr_code")]
+#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))]
+pub mod qr_code;
+
+#[cfg(feature = "qr_code")]
+#[doc(no_inline)]
+pub use qr_code::QRCode;
+
+pub use iced_native::Space;
+
+/// A container that distributes its contents vertically.
+pub type Column<'a, Message> = iced_native::Column<'a, Message, Renderer>;
+
+/// A container that distributes its contents horizontally.
+pub type Row<'a, Message> = iced_native::Row<'a, Message, Renderer>;
+
+/// A paragraph of text.
+pub type Text = iced_native::Text<Renderer>;
diff --git a/wgpu/src/widget/button.rs b/wgpu/src/widget/button.rs
index b738c55e..fc729cd5 100644
--- a/wgpu/src/widget/button.rs
+++ b/wgpu/src/widget/button.rs
@@ -1,13 +1,10 @@
//! Allow your users to perform actions by pressing a button.
//!
//! A [`Button`] has some local [`State`].
-//!
-//! [`Button`]: type.Button.html
-//! [`State`]: struct.State.html
use crate::Renderer;
+pub use iced_graphics::button::{Style, StyleSheet};
pub use iced_native::button::State;
-pub use iced_style::button::{Style, StyleSheet};
/// A widget that produces a message when clicked.
///
diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs
index 3a9605c9..399dd19c 100644
--- a/wgpu/src/widget/canvas.rs
+++ b/wgpu/src/widget/canvas.rs
@@ -3,149 +3,4 @@
//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a
//! [`Frame`]. It can be used for animation, data visualization, game graphics,
//! and more!
-//!
-//! [`Canvas`]: struct.Canvas.html
-//! [`Frame`]: struct.Frame.html
-use crate::{Defaults, Primitive, Renderer};
-
-use iced_native::{
- layout, Element, Hasher, Layout, Length, MouseCursor, Point, Size, Widget,
-};
-use std::hash::Hash;
-
-pub mod layer;
-pub mod path;
-
-mod drawable;
-mod fill;
-mod frame;
-mod stroke;
-mod text;
-
-pub use drawable::Drawable;
-pub use fill::Fill;
-pub use frame::Frame;
-pub use layer::Layer;
-pub use path::Path;
-pub use stroke::{LineCap, LineJoin, Stroke};
-pub use text::Text;
-
-/// A widget capable of drawing 2D graphics.
-///
-/// A [`Canvas`] may contain multiple layers. A [`Layer`] is drawn using the
-/// painter's algorithm. In other words, layers will be drawn on top of each in
-/// the same order they are pushed into the [`Canvas`].
-///
-/// [`Canvas`]: struct.Canvas.html
-/// [`Layer`]: layer/trait.Layer.html
-#[derive(Debug)]
-pub struct Canvas<'a> {
- width: Length,
- height: Length,
- layers: Vec<Box<dyn Layer + 'a>>,
-}
-
-impl<'a> Canvas<'a> {
- const DEFAULT_SIZE: u16 = 100;
-
- /// Creates a new [`Canvas`] with no layers.
- ///
- /// [`Canvas`]: struct.Canvas.html
- pub fn new() -> Self {
- Canvas {
- width: Length::Units(Self::DEFAULT_SIZE),
- height: Length::Units(Self::DEFAULT_SIZE),
- layers: Vec::new(),
- }
- }
-
- /// Sets the width of the [`Canvas`].
- ///
- /// [`Canvas`]: struct.Canvas.html
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
- self
- }
-
- /// Sets the height of the [`Canvas`].
- ///
- /// [`Canvas`]: struct.Canvas.html
- pub fn height(mut self, height: Length) -> Self {
- self.height = height;
- self
- }
-
- /// Adds a [`Layer`] to the [`Canvas`].
- ///
- /// It will be drawn on top of previous layers.
- ///
- /// [`Layer`]: layer/trait.Layer.html
- /// [`Canvas`]: struct.Canvas.html
- pub fn push(mut self, layer: impl Layer + 'a) -> Self {
- self.layers.push(Box::new(layer));
- self
- }
-}
-
-impl<'a, Message> Widget<Message, Renderer> for Canvas<'a> {
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
- }
-
- fn layout(
- &self,
- _renderer: &Renderer,
- limits: &layout::Limits,
- ) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
- }
-
- fn draw(
- &self,
- _renderer: &mut Renderer,
- _defaults: &Defaults,
- layout: Layout<'_>,
- _cursor_position: Point,
- ) -> (Primitive, MouseCursor) {
- let bounds = layout.bounds();
- let origin = Point::new(bounds.x, bounds.y);
- let size = Size::new(bounds.width, bounds.height);
-
- (
- Primitive::Group {
- primitives: self
- .layers
- .iter()
- .map(|layer| Primitive::Cached {
- origin,
- cache: layer.draw(size),
- })
- .collect(),
- },
- MouseCursor::Idle,
- )
- }
-
- fn hash_layout(&self, state: &mut Hasher) {
- std::any::TypeId::of::<Canvas<'static>>().hash(state);
-
- self.width.hash(state);
- self.height.hash(state);
- }
-}
-
-impl<'a, Message> From<Canvas<'a>> for Element<'a, Message, Renderer>
-where
- Message: 'static,
-{
- fn from(canvas: Canvas<'a>) -> Element<'a, Message, Renderer> {
- Element::new(canvas)
- }
-}
+pub use iced_graphics::canvas::*;
diff --git a/wgpu/src/widget/canvas/drawable.rs b/wgpu/src/widget/canvas/drawable.rs
deleted file mode 100644
index 6c74071c..00000000
--- a/wgpu/src/widget/canvas/drawable.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use crate::canvas::Frame;
-
-/// A type that can be drawn on a [`Frame`].
-///
-/// [`Frame`]: struct.Frame.html
-pub trait Drawable {
- /// Draws the [`Drawable`] on the given [`Frame`].
- ///
- /// [`Drawable`]: trait.Drawable.html
- /// [`Frame`]: struct.Frame.html
- fn draw(&self, frame: &mut Frame);
-}
diff --git a/wgpu/src/widget/canvas/fill.rs b/wgpu/src/widget/canvas/fill.rs
deleted file mode 100644
index 5ce24cf3..00000000
--- a/wgpu/src/widget/canvas/fill.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use iced_native::Color;
-
-/// The style used to fill geometry.
-#[derive(Debug, Clone, Copy)]
-pub enum Fill {
- /// Fill with a color.
- Color(Color),
-}
-
-impl Default for Fill {
- fn default() -> Fill {
- Fill::Color(Color::BLACK)
- }
-}
diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs
deleted file mode 100644
index 7d7ce06a..00000000
--- a/wgpu/src/widget/canvas/frame.rs
+++ /dev/null
@@ -1,310 +0,0 @@
-use iced_native::{Point, Rectangle, Size, Vector};
-
-use crate::{
- canvas::{Fill, Path, Stroke, Text},
- triangle, Primitive,
-};
-
-/// The frame of a [`Canvas`].
-///
-/// [`Canvas`]: struct.Canvas.html
-#[derive(Debug)]
-pub struct Frame {
- width: f32,
- height: f32,
- buffers: lyon::tessellation::VertexBuffers<triangle::Vertex2D, u32>,
- primitives: Vec<Primitive>,
- transforms: Transforms,
-}
-
-#[derive(Debug)]
-struct Transforms {
- previous: Vec<Transform>,
- current: Transform,
-}
-
-#[derive(Debug, Clone, Copy)]
-struct Transform {
- raw: lyon::math::Transform,
- is_identity: bool,
-}
-
-impl Frame {
- /// Creates a new empty [`Frame`] with the given dimensions.
- ///
- /// The default coordinate system of a [`Frame`] has its origin at the
- /// top-left corner of its bounds.
- ///
- /// [`Frame`]: struct.Frame.html
- pub fn new(width: f32, height: f32) -> Frame {
- Frame {
- width,
- height,
- buffers: lyon::tessellation::VertexBuffers::new(),
- primitives: Vec::new(),
- transforms: Transforms {
- previous: Vec::new(),
- current: Transform {
- raw: lyon::math::Transform::identity(),
- is_identity: true,
- },
- },
- }
- }
-
- /// Returns the width of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn width(&self) -> f32 {
- self.width
- }
-
- /// Returns the width of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn height(&self) -> f32 {
- self.height
- }
-
- /// Returns the dimensions of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn size(&self) -> Size {
- Size::new(self.width, self.height)
- }
-
- /// Returns the coordinate of the center of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn center(&self) -> Point {
- Point::new(self.width / 2.0, self.height / 2.0)
- }
-
- /// Draws the given [`Path`] on the [`Frame`] by filling it with the
- /// provided style.
- ///
- /// [`Path`]: path/struct.Path.html
- /// [`Frame`]: struct.Frame.html
- pub fn fill(&mut self, path: &Path, fill: Fill) {
- use lyon::tessellation::{
- BuffersBuilder, FillOptions, FillTessellator,
- };
-
- let mut buffers = BuffersBuilder::new(
- &mut self.buffers,
- FillVertex(match fill {
- Fill::Color(color) => color.into_linear(),
- }),
- );
-
- let mut tessellator = FillTessellator::new();
-
- let result = if self.transforms.current.is_identity {
- tessellator.tessellate_path(
- path.raw(),
- &FillOptions::default(),
- &mut buffers,
- )
- } else {
- let path = path.transformed(&self.transforms.current.raw);
-
- tessellator.tessellate_path(
- path.raw(),
- &FillOptions::default(),
- &mut buffers,
- )
- };
-
- let _ = result.expect("Tessellate path");
- }
-
- /// Draws the stroke of the given [`Path`] on the [`Frame`] with the
- /// provided style.
- ///
- /// [`Path`]: path/struct.Path.html
- /// [`Frame`]: struct.Frame.html
- pub fn stroke(&mut self, path: &Path, stroke: Stroke) {
- use lyon::tessellation::{
- BuffersBuilder, StrokeOptions, StrokeTessellator,
- };
-
- let mut buffers = BuffersBuilder::new(
- &mut self.buffers,
- StrokeVertex(stroke.color.into_linear()),
- );
-
- let mut tessellator = StrokeTessellator::new();
-
- let mut options = StrokeOptions::default();
- options.line_width = stroke.width;
- options.start_cap = stroke.line_cap.into();
- options.end_cap = stroke.line_cap.into();
- options.line_join = stroke.line_join.into();
-
- let result = if self.transforms.current.is_identity {
- tessellator.tessellate_path(path.raw(), &options, &mut buffers)
- } else {
- let path = path.transformed(&self.transforms.current.raw);
-
- tessellator.tessellate_path(path.raw(), &options, &mut buffers)
- };
-
- let _ = result.expect("Stroke path");
- }
-
- /// Draws the characters of the given [`Text`] on the [`Frame`], filling
- /// them with the given color.
- ///
- /// __Warning:__ Text currently does not work well with rotations and scale
- /// transforms! The position will be correctly transformed, but the
- /// resulting glyphs will not be rotated or scaled properly.
- ///
- /// Additionally, all text will be rendered on top of all the layers of
- /// a [`Canvas`]. Therefore, it is currently only meant to be used for
- /// overlays, which is the most common use case.
- ///
- /// Support for vectorial text is planned, and should address all these
- /// limitations.
- ///
- /// [`Text`]: struct.Text.html
- /// [`Frame`]: struct.Frame.html
- pub fn fill_text(&mut self, text: Text) {
- use std::f32;
-
- let position = if self.transforms.current.is_identity {
- text.position
- } else {
- let transformed = self.transforms.current.raw.transform_point(
- lyon::math::Point::new(text.position.x, text.position.y),
- );
-
- Point::new(transformed.x, transformed.y)
- };
-
- // TODO: Use vectorial text instead of primitive
- self.primitives.push(Primitive::Text {
- content: text.content,
- bounds: Rectangle {
- x: position.x,
- y: position.y,
- width: f32::INFINITY,
- height: f32::INFINITY,
- },
- color: text.color,
- size: text.size,
- font: text.font,
- horizontal_alignment: text.horizontal_alignment,
- vertical_alignment: text.vertical_alignment,
- });
- }
-
- /// Stores the current transform of the [`Frame`] and executes the given
- /// drawing operations, restoring the transform afterwards.
- ///
- /// This method is useful to compose transforms and perform drawing
- /// operations in different coordinate systems.
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) {
- self.transforms.previous.push(self.transforms.current);
-
- f(self);
-
- self.transforms.current = self.transforms.previous.pop().unwrap();
- }
-
- /// Applies a translation to the current transform of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn translate(&mut self, translation: Vector) {
- self.transforms.current.raw = self
- .transforms
- .current
- .raw
- .pre_translate(lyon::math::Vector::new(
- translation.x,
- translation.y,
- ));
- self.transforms.current.is_identity = false;
- }
-
- /// Applies a rotation to the current transform of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn rotate(&mut self, angle: f32) {
- self.transforms.current.raw = self
- .transforms
- .current
- .raw
- .pre_rotate(lyon::math::Angle::radians(-angle));
- self.transforms.current.is_identity = false;
- }
-
- /// Applies a scaling to the current transform of the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- #[inline]
- pub fn scale(&mut self, scale: f32) {
- self.transforms.current.raw =
- self.transforms.current.raw.pre_scale(scale, scale);
- self.transforms.current.is_identity = false;
- }
-
- /// Produces the primitive representing everything drawn on the [`Frame`].
- ///
- /// [`Frame`]: struct.Frame.html
- pub fn into_primitive(mut self) -> Primitive {
- self.primitives.push(Primitive::Mesh2D {
- origin: Point::ORIGIN,
- buffers: triangle::Mesh2D {
- vertices: self.buffers.vertices,
- indices: self.buffers.indices,
- },
- });
-
- Primitive::Group {
- primitives: self.primitives,
- }
- }
-}
-
-struct FillVertex([f32; 4]);
-
-impl lyon::tessellation::FillVertexConstructor<triangle::Vertex2D>
- for FillVertex
-{
- fn new_vertex(
- &mut self,
- position: lyon::math::Point,
- _attributes: lyon::tessellation::FillAttributes<'_>,
- ) -> triangle::Vertex2D {
- triangle::Vertex2D {
- position: [position.x, position.y],
- color: self.0,
- }
- }
-}
-
-struct StrokeVertex([f32; 4]);
-
-impl lyon::tessellation::StrokeVertexConstructor<triangle::Vertex2D>
- for StrokeVertex
-{
- fn new_vertex(
- &mut self,
- position: lyon::math::Point,
- _attributes: lyon::tessellation::StrokeAttributes<'_, '_>,
- ) -> triangle::Vertex2D {
- triangle::Vertex2D {
- position: [position.x, position.y],
- color: self.0,
- }
- }
-}
diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs
deleted file mode 100644
index a46b7fb1..00000000
--- a/wgpu/src/widget/canvas/layer.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-//! Produce, store, and reuse geometry.
-mod cache;
-
-pub use cache::Cache;
-
-use crate::Primitive;
-use iced_native::Size;
-
-use std::sync::Arc;
-
-/// A layer that can be presented at a [`Canvas`].
-///
-/// [`Canvas`]: ../struct.Canvas.html
-pub trait Layer: std::fmt::Debug {
- /// Draws the [`Layer`] in the given bounds and produces a [`Primitive`] as
- /// a result.
- ///
- /// The [`Layer`] may choose to store the produced [`Primitive`] locally and
- /// only recompute it when the bounds change, its contents change, or is
- /// otherwise explicitly cleared by other means.
- ///
- /// [`Layer`]: trait.Layer.html
- /// [`Primitive`]: ../../../enum.Primitive.html
- fn draw(&self, bounds: Size) -> Arc<Primitive>;
-}
diff --git a/wgpu/src/widget/canvas/layer/cache.rs b/wgpu/src/widget/canvas/layer/cache.rs
deleted file mode 100644
index 6b69f01e..00000000
--- a/wgpu/src/widget/canvas/layer/cache.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use crate::{
- canvas::{Drawable, Frame, Layer},
- Primitive,
-};
-
-use iced_native::Size;
-use std::{cell::RefCell, marker::PhantomData, sync::Arc};
-
-/// A simple cache that stores generated geometry to avoid recomputation.
-///
-/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer
-/// change or it is explicitly cleared.
-///
-/// [`Layer`]: ../trait.Layer.html
-/// [`Cached`]: struct.Cached.html
-#[derive(Debug)]
-pub struct Cache<T: Drawable> {
- input: PhantomData<T>,
- state: RefCell<State>,
-}
-
-#[derive(Debug)]
-enum State {
- Empty,
- Filled {
- bounds: Size,
- primitive: Arc<Primitive>,
- },
-}
-
-impl<T> Cache<T>
-where
- T: Drawable + std::fmt::Debug,
-{
- /// Creates a new empty [`Cache`].
- ///
- /// [`Cache`]: struct.Cache.html
- pub fn new() -> Self {
- Cache {
- input: PhantomData,
- state: RefCell::new(State::Empty),
- }
- }
-
- /// Clears the cache, forcing a redraw the next time it is used.
- ///
- /// [`Cached`]: struct.Cached.html
- pub fn clear(&mut self) {
- *self.state.borrow_mut() = State::Empty;
- }
-
- /// Binds the [`Cache`] with some data, producing a [`Layer`] that can be
- /// added to a [`Canvas`].
- ///
- /// [`Cache`]: struct.Cache.html
- /// [`Layer`]: ../trait.Layer.html
- /// [`Canvas`]: ../../struct.Canvas.html
- pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a {
- Bind {
- cache: self,
- input: input,
- }
- }
-}
-
-#[derive(Debug)]
-struct Bind<'a, T: Drawable> {
- cache: &'a Cache<T>,
- input: &'a T,
-}
-
-impl<'a, T> Layer for Bind<'a, T>
-where
- T: Drawable + std::fmt::Debug,
-{
- fn draw(&self, current_bounds: Size) -> Arc<Primitive> {
- use std::ops::Deref;
-
- if let State::Filled { bounds, primitive } =
- self.cache.state.borrow().deref()
- {
- if *bounds == current_bounds {
- return primitive.clone();
- }
- }
-
- let mut frame = Frame::new(current_bounds.width, current_bounds.height);
- self.input.draw(&mut frame);
-
- let primitive = Arc::new(frame.into_primitive());
-
- *self.cache.state.borrow_mut() = State::Filled {
- bounds: current_bounds,
- primitive: primitive.clone(),
- };
-
- primitive
- }
-}
diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs
deleted file mode 100644
index 15c2e853..00000000
--- a/wgpu/src/widget/canvas/path.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-//! Build different kinds of 2D shapes.
-pub mod arc;
-
-mod builder;
-
-pub use arc::Arc;
-pub use builder::Builder;
-
-/// An immutable set of points that may or may not be connected.
-///
-/// A single [`Path`] can represent different kinds of 2D shapes!
-///
-/// [`Path`]: struct.Path.html
-#[derive(Debug, Clone)]
-pub struct Path {
- raw: lyon::path::Path,
-}
-
-impl Path {
- /// Creates a new [`Path`] with the provided closure.
- ///
- /// Use the [`Builder`] to configure your [`Path`].
- ///
- /// [`Path`]: struct.Path.html
- /// [`Builder`]: struct.Builder.html
- pub fn new(f: impl FnOnce(&mut Builder)) -> Self {
- let mut builder = Builder::new();
-
- // TODO: Make it pure instead of side-effect-based (?)
- f(&mut builder);
-
- builder.build()
- }
-
- #[inline]
- pub(crate) fn raw(&self) -> &lyon::path::Path {
- &self.raw
- }
-
- #[inline]
- pub(crate) fn transformed(
- &self,
- transform: &lyon::math::Transform,
- ) -> Path {
- Path {
- raw: self.raw.transformed(transform),
- }
- }
-}
diff --git a/wgpu/src/widget/canvas/path/arc.rs b/wgpu/src/widget/canvas/path/arc.rs
deleted file mode 100644
index 343191f1..00000000
--- a/wgpu/src/widget/canvas/path/arc.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-//! Build and draw curves.
-use iced_native::{Point, Vector};
-
-/// A segment of a differentiable curve.
-#[derive(Debug, Clone, Copy)]
-pub struct Arc {
- /// The center of the arc.
- pub center: Point,
- /// The radius of the arc.
- pub radius: f32,
- /// The start of the segment's angle, clockwise rotation.
- pub start_angle: f32,
- /// The end of the segment's angle, clockwise rotation.
- pub end_angle: f32,
-}
-
-/// An elliptical [`Arc`].
-///
-/// [`Arc`]: struct.Arc.html
-#[derive(Debug, Clone, Copy)]
-pub struct Elliptical {
- /// The center of the arc.
- pub center: Point,
- /// The radii of the arc's ellipse, defining its axes.
- pub radii: Vector,
- /// The rotation of the arc's ellipse.
- pub rotation: f32,
- /// The start of the segment's angle, clockwise rotation.
- pub start_angle: f32,
- /// The end of the segment's angle, clockwise rotation.
- pub end_angle: f32,
-}
-
-impl From<Arc> for Elliptical {
- fn from(arc: Arc) -> Elliptical {
- Elliptical {
- center: arc.center,
- radii: Vector::new(arc.radius, arc.radius),
- rotation: 0.0,
- start_angle: arc.start_angle,
- end_angle: arc.end_angle,
- }
- }
-}
diff --git a/wgpu/src/widget/canvas/path/builder.rs b/wgpu/src/widget/canvas/path/builder.rs
deleted file mode 100644
index a013149e..00000000
--- a/wgpu/src/widget/canvas/path/builder.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-use crate::canvas::path::{arc, Arc, Path};
-
-use iced_native::{Point, Size};
-use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder};
-
-/// A [`Path`] builder.
-///
-/// Once a [`Path`] is built, it can no longer be mutated.
-///
-/// [`Path`]: struct.Path.html
-#[allow(missing_debug_implementations)]
-pub struct Builder {
- raw: lyon::path::builder::SvgPathBuilder<lyon::path::Builder>,
-}
-
-impl Builder {
- /// Creates a new [`Builder`].
- ///
- /// [`Builder`]: struct.Builder.html
- pub fn new() -> Builder {
- Builder {
- raw: lyon::path::Path::builder().with_svg(),
- }
- }
-
- /// Moves the starting point of a new sub-path to the given `Point`.
- #[inline]
- pub fn move_to(&mut self, point: Point) {
- let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y));
- }
-
- /// Connects the last point in the [`Path`] to the given `Point` with a
- /// straight line.
- ///
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn line_to(&mut self, point: Point) {
- let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y));
- }
-
- /// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in
- /// a clockwise direction.
- ///
- /// [`Arc`]: struct.Arc.html
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn arc(&mut self, arc: Arc) {
- self.ellipse(arc.into());
- }
-
- /// Adds a circular arc to the [`Path`] with the given control points and
- /// radius.
- ///
- /// The arc is connected to the previous point by a straight line, if
- /// necessary.
- ///
- /// [`Path`]: struct.Path.html
- pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) {
- use lyon::{math, path};
-
- let a = math::Point::new(a.x, a.y);
-
- if self.raw.current_position() != a {
- let _ = self.raw.line_to(a);
- }
-
- let _ = self.raw.arc_to(
- math::Vector::new(radius, radius),
- math::Angle::radians(0.0),
- path::ArcFlags::default(),
- math::Point::new(b.x, b.y),
- );
- }
-
- /// Adds an [`Ellipse`] to the [`Path`] using a clockwise direction.
- ///
- /// [`Ellipse`]: struct.Arc.html
- /// [`Path`]: struct.Path.html
- pub fn ellipse(&mut self, arc: arc::Elliptical) {
- use lyon::{geom, math};
-
- let arc = geom::Arc {
- center: math::Point::new(arc.center.x, arc.center.y),
- radii: math::Vector::new(arc.radii.x, arc.radii.y),
- x_rotation: math::Angle::radians(arc.rotation),
- start_angle: math::Angle::radians(arc.start_angle),
- sweep_angle: math::Angle::radians(arc.end_angle),
- };
-
- let _ = self.raw.move_to(arc.sample(0.0));
-
- arc.for_each_quadratic_bezier(&mut |curve| {
- let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to);
- });
- }
-
- /// Adds a cubic Bézier curve to the [`Path`] given its two control points
- /// and its end point.
- ///
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn bezier_curve_to(
- &mut self,
- control_a: Point,
- control_b: Point,
- to: Point,
- ) {
- use lyon::math;
-
- let _ = self.raw.cubic_bezier_to(
- math::Point::new(control_a.x, control_a.y),
- math::Point::new(control_b.x, control_b.y),
- math::Point::new(to.x, to.y),
- );
- }
-
- /// Adds a quadratic Bézier curve to the [`Path`] given its control point
- /// and its end point.
- ///
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn quadratic_curve_to(&mut self, control: Point, to: Point) {
- use lyon::math;
-
- let _ = self.raw.quadratic_bezier_to(
- math::Point::new(control.x, control.y),
- math::Point::new(to.x, to.y),
- );
- }
-
- /// Adds a rectangle to the [`Path`] given its top-left corner coordinate
- /// and its `Size`.
- ///
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn rectangle(&mut self, p: Point, size: Size) {
- self.move_to(p);
- self.line_to(Point::new(p.x + size.width, p.y));
- self.line_to(Point::new(p.x + size.width, p.y + size.height));
- self.line_to(Point::new(p.x, p.y + size.height));
- self.close();
- }
-
- /// Adds a circle to the [`Path`] given its center coordinate and its
- /// radius.
- ///
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn circle(&mut self, center: Point, radius: f32) {
- self.arc(Arc {
- center,
- radius,
- start_angle: 0.0,
- end_angle: 2.0 * std::f32::consts::PI,
- });
- }
-
- /// Closes the current sub-path in the [`Path`] with a straight line to
- /// the starting point.
- ///
- /// [`Path`]: struct.Path.html
- #[inline]
- pub fn close(&mut self) {
- self.raw.close()
- }
-
- /// Builds the [`Path`] of this [`Builder`].
- ///
- /// [`Path`]: struct.Path.html
- /// [`Builder`]: struct.Builder.html
- #[inline]
- pub fn build(self) -> Path {
- Path {
- raw: self.raw.build(),
- }
- }
-}
diff --git a/wgpu/src/widget/canvas/stroke.rs b/wgpu/src/widget/canvas/stroke.rs
deleted file mode 100644
index 46d669c4..00000000
--- a/wgpu/src/widget/canvas/stroke.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use iced_native::Color;
-
-/// The style of a stroke.
-#[derive(Debug, Clone, Copy)]
-pub struct Stroke {
- /// The color of the stroke.
- pub color: Color,
- /// The distance between the two edges of the stroke.
- pub width: f32,
- /// The shape to be used at the end of open subpaths when they are stroked.
- pub line_cap: LineCap,
- /// The shape to be used at the corners of paths or basic shapes when they
- /// are stroked.
- pub line_join: LineJoin,
-}
-
-impl Default for Stroke {
- fn default() -> Stroke {
- Stroke {
- color: Color::BLACK,
- width: 1.0,
- line_cap: LineCap::default(),
- line_join: LineJoin::default(),
- }
- }
-}
-
-/// The shape used at the end of open subpaths when they are stroked.
-#[derive(Debug, Clone, Copy)]
-pub enum LineCap {
- /// The stroke for each sub-path does not extend beyond its two endpoints.
- Butt,
- /// At the end of each sub-path, the shape representing the stroke will be
- /// extended by a square.
- Square,
- /// At the end of each sub-path, the shape representing the stroke will be
- /// extended by a semicircle.
- Round,
-}
-
-impl Default for LineCap {
- fn default() -> LineCap {
- LineCap::Butt
- }
-}
-
-impl From<LineCap> for lyon::tessellation::LineCap {
- fn from(line_cap: LineCap) -> lyon::tessellation::LineCap {
- match line_cap {
- LineCap::Butt => lyon::tessellation::LineCap::Butt,
- LineCap::Square => lyon::tessellation::LineCap::Square,
- LineCap::Round => lyon::tessellation::LineCap::Round,
- }
- }
-}
-
-/// The shape used at the corners of paths or basic shapes when they are
-/// stroked.
-#[derive(Debug, Clone, Copy)]
-pub enum LineJoin {
- /// A sharp corner.
- Miter,
- /// A round corner.
- Round,
- /// A bevelled corner.
- Bevel,
-}
-
-impl Default for LineJoin {
- fn default() -> LineJoin {
- LineJoin::Miter
- }
-}
-
-impl From<LineJoin> for lyon::tessellation::LineJoin {
- fn from(line_join: LineJoin) -> lyon::tessellation::LineJoin {
- match line_join {
- LineJoin::Miter => lyon::tessellation::LineJoin::Miter,
- LineJoin::Round => lyon::tessellation::LineJoin::Round,
- LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel,
- }
- }
-}
diff --git a/wgpu/src/widget/canvas/text.rs b/wgpu/src/widget/canvas/text.rs
deleted file mode 100644
index d1cf1a0f..00000000
--- a/wgpu/src/widget/canvas/text.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use iced_native::{Color, Font, HorizontalAlignment, Point, VerticalAlignment};
-
-/// A bunch of text that can be drawn to a canvas
-#[derive(Debug, Clone)]
-pub struct Text {
- /// The contents of the text
- pub content: String,
- /// The position where to begin drawing the text (top-left corner coordinates)
- pub position: Point,
- /// The color of the text
- pub color: Color,
- /// The size of the text
- pub size: f32,
- /// The font of the text
- pub font: Font,
- /// The horizontal alignment of the text
- pub horizontal_alignment: HorizontalAlignment,
- /// The vertical alignment of the text
- pub vertical_alignment: VerticalAlignment,
-}
-
-impl Default for Text {
- fn default() -> Text {
- Text {
- content: String::new(),
- position: Point::ORIGIN,
- color: Color::BLACK,
- size: 16.0,
- font: Font::Default,
- horizontal_alignment: HorizontalAlignment::Left,
- vertical_alignment: VerticalAlignment::Top,
- }
- }
-}
diff --git a/wgpu/src/widget/checkbox.rs b/wgpu/src/widget/checkbox.rs
index da0d7a84..d27d77cc 100644
--- a/wgpu/src/widget/checkbox.rs
+++ b/wgpu/src/widget/checkbox.rs
@@ -1,7 +1,7 @@
//! Show toggle controls using checkboxes.
use crate::Renderer;
-pub use iced_style::checkbox::{Style, StyleSheet};
+pub use iced_graphics::checkbox::{Style, StyleSheet};
/// A box that can be checked.
///
diff --git a/wgpu/src/widget/container.rs b/wgpu/src/widget/container.rs
index 9a93a246..bc26cef2 100644
--- a/wgpu/src/widget/container.rs
+++ b/wgpu/src/widget/container.rs
@@ -1,7 +1,7 @@
//! Decorate content and apply alignment.
use crate::Renderer;
-pub use iced_style::container::{Style, StyleSheet};
+pub use iced_graphics::container::{Style, StyleSheet};
/// An element decorating some content.
///
diff --git a/wgpu/src/widget/pane_grid.rs b/wgpu/src/widget/pane_grid.rs
new file mode 100644
index 00000000..c26dde48
--- /dev/null
+++ b/wgpu/src/widget/pane_grid.rs
@@ -0,0 +1,31 @@
+//! Let your users split regions of your application and organize layout dynamically.
+//!
+//! [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish)
+//!
+//! # Example
+//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
+//! drag and drop, and hotkey support.
+//!
+//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
+use crate::Renderer;
+
+pub use iced_native::pane_grid::{
+ Axis, Configuration, Direction, DragEvent, Node, Pane, ResizeEvent, Split,
+ State,
+};
+
+/// A collection of panes distributed using either vertical or horizontal splits
+/// to completely fill the space available.
+///
+/// [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish)
+///
+/// This is an alias of an `iced_native` pane grid with an `iced_wgpu::Renderer`.
+pub type PaneGrid<'a, Message> = iced_native::PaneGrid<'a, Message, Renderer>;
+
+/// The content of a [`Pane`].
+pub type Content<'a, Message> =
+ iced_native::pane_grid::Content<'a, Message, Renderer>;
+
+/// The title bar of a [`Pane`].
+pub type TitleBar<'a, Message> =
+ iced_native::pane_grid::TitleBar<'a, Message, Renderer>;
diff --git a/wgpu/src/widget/pick_list.rs b/wgpu/src/widget/pick_list.rs
new file mode 100644
index 00000000..fccc68c9
--- /dev/null
+++ b/wgpu/src/widget/pick_list.rs
@@ -0,0 +1,9 @@
+//! Display a dropdown list of selectable values.
+pub use iced_native::pick_list::State;
+
+pub use iced_graphics::overlay::menu::Style as Menu;
+pub use iced_graphics::pick_list::{Style, StyleSheet};
+
+/// A widget allowing the selection of a single value from a list of options.
+pub type PickList<'a, T, Message> =
+ iced_native::PickList<'a, T, Message, crate::Renderer>;
diff --git a/wgpu/src/widget/progress_bar.rs b/wgpu/src/widget/progress_bar.rs
index 34450b5e..45a25d00 100644
--- a/wgpu/src/widget/progress_bar.rs
+++ b/wgpu/src/widget/progress_bar.rs
@@ -1,12 +1,10 @@
-//! Allow your users to perform actions by pressing a button.
+//! Allow your users to visually track the progress of a computation.
//!
-//! A [`Button`] has some local [`State`].
-//!
-//! [`Button`]: type.Button.html
-//! [`State`]: struct.State.html
+//! A [`ProgressBar`] has a range of possible values and a current value,
+//! as well as a length, height and style.
use crate::Renderer;
-pub use iced_style::progress_bar::{Style, StyleSheet};
+pub use iced_graphics::progress_bar::{Style, StyleSheet};
/// A bar that displays progress.
///
diff --git a/wgpu/src/widget/qr_code.rs b/wgpu/src/widget/qr_code.rs
new file mode 100644
index 00000000..7b1c2408
--- /dev/null
+++ b/wgpu/src/widget/qr_code.rs
@@ -0,0 +1,2 @@
+//! Encode and display information in a QR code.
+pub use iced_graphics::qr_code::*;
diff --git a/wgpu/src/widget/radio.rs b/wgpu/src/widget/radio.rs
index 6e5cf042..0b843d1f 100644
--- a/wgpu/src/widget/radio.rs
+++ b/wgpu/src/widget/radio.rs
@@ -1,7 +1,7 @@
//! Create choices using radio buttons.
use crate::Renderer;
-pub use iced_style::radio::{Style, StyleSheet};
+pub use iced_graphics::radio::{Style, StyleSheet};
/// A circular button representing a choice.
///
diff --git a/wgpu/src/widget/rule.rs b/wgpu/src/widget/rule.rs
new file mode 100644
index 00000000..3f7bc67a
--- /dev/null
+++ b/wgpu/src/widget/rule.rs
@@ -0,0 +1,10 @@
+//! Display a horizontal or vertical rule for dividing content.
+
+use crate::Renderer;
+
+pub use iced_graphics::rule::{FillMode, Style, StyleSheet};
+
+/// Display a horizontal or vertical rule for dividing content.
+///
+/// This is an alias of an `iced_native` rule with an `iced_wgpu::Renderer`.
+pub type Rule = iced_native::Rule<Renderer>;
diff --git a/wgpu/src/widget/scrollable.rs b/wgpu/src/widget/scrollable.rs
index 1d236105..fabb4318 100644
--- a/wgpu/src/widget/scrollable.rs
+++ b/wgpu/src/widget/scrollable.rs
@@ -1,8 +1,8 @@
//! Navigate an endless amount of content with a scrollbar.
use crate::Renderer;
+pub use iced_graphics::scrollable::{Scrollbar, Scroller, StyleSheet};
pub use iced_native::scrollable::State;
-pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
/// A widget that can vertically display an infinite amount of content
/// with a scrollbar.
diff --git a/wgpu/src/widget/slider.rs b/wgpu/src/widget/slider.rs
index 4e47978f..9a269858 100644
--- a/wgpu/src/widget/slider.rs
+++ b/wgpu/src/widget/slider.rs
@@ -1,16 +1,13 @@
//! Display an interactive selector of a single value from a range of values.
//!
//! A [`Slider`] has some local [`State`].
-//!
-//! [`Slider`]: struct.Slider.html
-//! [`State`]: struct.State.html
use crate::Renderer;
+pub use iced_graphics::slider::{Handle, HandleShape, Style, StyleSheet};
pub use iced_native::slider::State;
-pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet};
/// An horizontal bar and a handle that selects a single value from a range of
/// values.
///
/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`.
-pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Renderer>;
+pub type Slider<'a, T, Message> = iced_native::Slider<'a, T, Message, Renderer>;
diff --git a/wgpu/src/widget/text_input.rs b/wgpu/src/widget/text_input.rs
index 260fe3a6..db18b1cc 100644
--- a/wgpu/src/widget/text_input.rs
+++ b/wgpu/src/widget/text_input.rs
@@ -1,13 +1,10 @@
//! Display fields that can be filled with text.
//!
//! A [`TextInput`] has some local [`State`].
-//!
-//! [`TextInput`]: struct.TextInput.html
-//! [`State`]: struct.State.html
use crate::Renderer;
+pub use iced_graphics::text_input::{Style, StyleSheet};
pub use iced_native::text_input::State;
-pub use iced_style::text_input::{Style, StyleSheet};
/// A field that can be filled with text.
///
diff --git a/wgpu/src/window.rs b/wgpu/src/window.rs
index b7adad82..aac5fb9e 100644
--- a/wgpu/src/window.rs
+++ b/wgpu/src/window.rs
@@ -1,6 +1,4 @@
//! Display rendering results on windows.
-mod backend;
-mod swap_chain;
+mod compositor;
-pub use backend::Backend;
-pub use swap_chain::SwapChain;
+pub use compositor::Compositor;
diff --git a/wgpu/src/window/backend.rs b/wgpu/src/window/backend.rs
deleted file mode 100644
index 5b269f36..00000000
--- a/wgpu/src/window/backend.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use crate::{window::SwapChain, Renderer, Settings, Target};
-
-use iced_native::MouseCursor;
-use raw_window_handle::HasRawWindowHandle;
-
-/// A window graphics backend for iced powered by `wgpu`.
-#[derive(Debug)]
-pub struct Backend {
- device: wgpu::Device,
- queue: wgpu::Queue,
- format: wgpu::TextureFormat,
-}
-
-impl iced_native::window::Backend for Backend {
- type Settings = Settings;
- type Renderer = Renderer;
- type Surface = wgpu::Surface;
- type SwapChain = SwapChain;
-
- fn new(settings: Self::Settings) -> (Backend, Renderer) {
- let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
- power_preference: if settings.antialiasing.is_none() {
- wgpu::PowerPreference::Default
- } else {
- wgpu::PowerPreference::HighPerformance
- },
- backends: wgpu::BackendBit::all(),
- })
- .expect("Request adapter");
-
- let (mut device, queue) =
- adapter.request_device(&wgpu::DeviceDescriptor {
- extensions: wgpu::Extensions {
- anisotropic_filtering: false,
- },
- limits: wgpu::Limits { max_bind_groups: 2 },
- });
-
- let renderer = Renderer::new(&mut device, settings);
-
- (
- Backend {
- device,
- queue,
- format: settings.format,
- },
- renderer,
- )
- }
-
- fn create_surface<W: HasRawWindowHandle>(
- &mut self,
- window: &W,
- ) -> wgpu::Surface {
- wgpu::Surface::create(window)
- }
-
- fn create_swap_chain(
- &mut self,
- surface: &Self::Surface,
- width: u32,
- height: u32,
- ) -> SwapChain {
- SwapChain::new(&self.device, surface, self.format, width, height)
- }
-
- fn draw<T: AsRef<str>>(
- &mut self,
- renderer: &mut Self::Renderer,
- swap_chain: &mut SwapChain,
- output: &<Self::Renderer as iced_native::Renderer>::Output,
- scale_factor: f64,
- overlay: &[T],
- ) -> MouseCursor {
- let (frame, viewport) = swap_chain.next_frame();
-
- let mut encoder = self.device.create_command_encoder(
- &wgpu::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 mouse_cursor = renderer.draw(
- &mut self.device,
- &mut encoder,
- Target {
- texture: &frame.view,
- viewport,
- },
- output,
- scale_factor,
- overlay,
- );
-
- self.queue.submit(&[encoder.finish()]);
-
- mouse_cursor
- }
-}
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
new file mode 100644
index 00000000..492efb42
--- /dev/null
+++ b/wgpu/src/window/compositor.rs
@@ -0,0 +1,177 @@
+use crate::{Backend, Color, Error, Renderer, Settings, Viewport};
+
+use futures::task::SpawnExt;
+use iced_native::{futures, mouse};
+use raw_window_handle::HasRawWindowHandle;
+
+/// A window graphics backend for iced powered by `wgpu`.
+#[allow(missing_debug_implementations)]
+pub struct Compositor {
+ settings: Settings,
+ instance: wgpu::Instance,
+ device: wgpu::Device,
+ queue: wgpu::Queue,
+ staging_belt: wgpu::util::StagingBelt,
+ local_pool: futures::executor::LocalPool,
+}
+
+impl Compositor {
+ const CHUNK_SIZE: u64 = 10 * 1024;
+
+ /// Requests a new [`Compositor`] with the given [`Settings`].
+ ///
+ /// Returns `None` if no compatible graphics adapter could be found.
+ pub async fn request(settings: Settings) -> Option<Self> {
+ let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
+
+ let adapter = instance
+ .request_adapter(&wgpu::RequestAdapterOptions {
+ power_preference: if settings.antialiasing.is_none() {
+ wgpu::PowerPreference::Default
+ } else {
+ wgpu::PowerPreference::HighPerformance
+ },
+ compatible_surface: None,
+ })
+ .await?;
+
+ let (device, queue) = adapter
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ features: wgpu::Features::empty(),
+ limits: wgpu::Limits {
+ max_bind_groups: 2,
+ ..wgpu::Limits::default()
+ },
+ shader_validation: false,
+ },
+ None,
+ )
+ .await
+ .ok()?;
+
+ let staging_belt = wgpu::util::StagingBelt::new(Self::CHUNK_SIZE);
+ let local_pool = futures::executor::LocalPool::new();
+
+ Some(Compositor {
+ instance,
+ settings,
+ device,
+ queue,
+ staging_belt,
+ local_pool,
+ })
+ }
+
+ /// Creates a new rendering [`Backend`] for this [`Compositor`].
+ pub fn create_backend(&self) -> Backend {
+ Backend::new(&self.device, self.settings)
+ }
+}
+
+impl iced_graphics::window::Compositor for Compositor {
+ type Settings = Settings;
+ type Renderer = Renderer;
+ type Surface = wgpu::Surface;
+ type SwapChain = wgpu::SwapChain;
+
+ fn new(settings: Self::Settings) -> Result<(Self, Renderer), Error> {
+ let compositor = futures::executor::block_on(Self::request(settings))
+ .ok_or(Error::AdapterNotFound)?;
+
+ let backend = compositor.create_backend();
+
+ Ok((compositor, Renderer::new(backend)))
+ }
+
+ fn create_surface<W: HasRawWindowHandle>(
+ &mut self,
+ window: &W,
+ ) -> wgpu::Surface {
+ #[allow(unsafe_code)]
+ unsafe {
+ self.instance.create_surface(window)
+ }
+ }
+
+ fn create_swap_chain(
+ &mut self,
+ surface: &Self::Surface,
+ width: u32,
+ height: u32,
+ ) -> Self::SwapChain {
+ self.device.create_swap_chain(
+ surface,
+ &wgpu::SwapChainDescriptor {
+ usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
+ format: self.settings.format,
+ present_mode: self.settings.present_mode,
+ width,
+ height,
+ },
+ )
+ }
+
+ fn draw<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ swap_chain: &mut Self::SwapChain,
+ viewport: &Viewport,
+ background_color: Color,
+ output: &<Self::Renderer as iced_native::Renderer>::Output,
+ overlay: &[T],
+ ) -> mouse::Interaction {
+ let frame = swap_chain.get_current_frame().expect("Next frame");
+
+ let mut encoder = self.device.create_command_encoder(
+ &wgpu::CommandEncoderDescriptor {
+ label: Some("iced_wgpu encoder"),
+ },
+ );
+
+ let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
+ attachment: &frame.output.view,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Clear({
+ let [r, g, b, a] = background_color.into_linear();
+
+ wgpu::Color {
+ r: f64::from(r),
+ g: f64::from(g),
+ b: f64::from(b),
+ a: f64::from(a),
+ }
+ }),
+ store: true,
+ },
+ }],
+ depth_stencil_attachment: None,
+ });
+
+ let mouse_interaction = renderer.backend_mut().draw(
+ &mut self.device,
+ &mut self.staging_belt,
+ &mut encoder,
+ &frame.output.view,
+ viewport,
+ output,
+ overlay,
+ );
+
+ // Submit work
+ self.staging_belt.finish();
+ self.queue.submit(Some(encoder.finish()));
+
+ // Recall staging buffers
+ self.local_pool
+ .spawner()
+ .spawn(self.staging_belt.recall())
+ .expect("Recall staging belt");
+
+ self.local_pool.run_until_stalled();
+
+ mouse_interaction
+ }
+}
diff --git a/wgpu/src/window/swap_chain.rs b/wgpu/src/window/swap_chain.rs
deleted file mode 100644
index 4ca2901b..00000000
--- a/wgpu/src/window/swap_chain.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use crate::Viewport;
-
-/// The rendering target of a window.
-///
-/// It represents a series of virtual framebuffers with a scale factor.
-#[derive(Debug)]
-pub struct SwapChain {
- raw: wgpu::SwapChain,
- viewport: Viewport,
-}
-
-impl SwapChain {}
-
-impl SwapChain {
- /// Creates a new [`SwapChain`] for the given surface.
- ///
- /// [`SwapChain`]: struct.SwapChain.html
- pub fn new(
- device: &wgpu::Device,
- surface: &wgpu::Surface,
- format: wgpu::TextureFormat,
- width: u32,
- height: u32,
- ) -> SwapChain {
- SwapChain {
- raw: new_swap_chain(surface, format, width, height, device),
- viewport: Viewport::new(width, height),
- }
- }
-
- /// Returns the next frame of the [`SwapChain`] alongside its [`Viewport`].
- ///
- /// [`SwapChain`]: struct.SwapChain.html
- /// [`Viewport`]: ../struct.Viewport.html
- pub fn next_frame(&mut self) -> (wgpu::SwapChainOutput<'_>, &Viewport) {
- (self.raw.get_next_texture(), &self.viewport)
- }
-}
-
-fn new_swap_chain(
- surface: &wgpu::Surface,
- format: wgpu::TextureFormat,
- width: u32,
- height: u32,
- device: &wgpu::Device,
-) -> wgpu::SwapChain {
- device.create_swap_chain(
- &surface,
- &wgpu::SwapChainDescriptor {
- usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
- format,
- width,
- height,
- present_mode: wgpu::PresentMode::Vsync,
- },
- )
-}