diff options
Diffstat (limited to 'wgpu/src')
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`]. //! -//!  +//!  //! //! 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 Binary files differindex ad697d48..e0b436ce 100644 --- a/wgpu/src/shader/blit.vert.spv +++ b/wgpu/src/shader/blit.vert.spv 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 Binary files differindex 7059b51b..fa71ba1e 100644 --- a/wgpu/src/shader/quad.vert.spv +++ b/wgpu/src/shader/quad.vert.spv 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 Binary files differdeleted file mode 100644 index 1c832f86..00000000 --- a/wgpu/src/text/icons.ttf +++ /dev/null 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. +//! +//! [](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. +/// +/// [](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, - }, - ) -} |