diff options
author | 2022-10-04 18:24:46 -0700 | |
---|---|---|
committer | 2022-10-04 18:24:46 -0700 | |
commit | 6e7b3ced0b1daf368e44e181ecdb4ae529877eb6 (patch) | |
tree | e530025c737d509b640172d595cff0a0809f5a40 | |
parent | 5d0fffc626928177239336757507b986b081b878 (diff) | |
download | iced-6e7b3ced0b1daf368e44e181ecdb4ae529877eb6.tar.gz iced-6e7b3ced0b1daf368e44e181ecdb4ae529877eb6.tar.bz2 iced-6e7b3ced0b1daf368e44e181ecdb4ae529877eb6.zip |
Reworked wgpu buffers, updated glow side to have proper transform location storage, attempting to fix visibility modifiers, implemented some of the feedback received in initial PR.
Diffstat (limited to '')
-rw-r--r-- | examples/arc/src/main.rs | 4 | ||||
-rw-r--r-- | examples/clock/src/main.rs | 6 | ||||
-rw-r--r-- | examples/modern_art/src/main.rs | 7 | ||||
-rw-r--r-- | examples/solar_system/src/main.rs | 4 | ||||
-rw-r--r-- | glow/src/backend.rs | 2 | ||||
-rw-r--r-- | glow/src/shader/common/gradient.frag | 1 | ||||
-rw-r--r-- | glow/src/triangle.rs | 126 | ||||
-rw-r--r-- | glow/src/triangle/gradient.rs | 92 | ||||
-rw-r--r-- | glow/src/triangle/solid.rs | 62 | ||||
-rw-r--r-- | graphics/src/gradient.rs | 2 | ||||
-rw-r--r-- | graphics/src/layer.rs | 32 | ||||
-rw-r--r-- | graphics/src/widget/canvas.rs | 4 | ||||
-rw-r--r-- | graphics/src/widget/canvas/fill.rs | 14 | ||||
-rw-r--r-- | graphics/src/widget/canvas/stroke.rs | 14 | ||||
-rw-r--r-- | wgpu/src/backend.rs | 2 | ||||
-rw-r--r-- | wgpu/src/buffers/buffer.rs | 141 | ||||
-rw-r--r-- | wgpu/src/buffers/dynamic_buffers.rs | 3 | ||||
-rw-r--r-- | wgpu/src/triangle.rs | 303 | ||||
-rw-r--r-- | wgpu/src/triangle/gradient.rs | 6 | ||||
-rw-r--r-- | wgpu/src/triangle/solid.rs | 15 |
20 files changed, 417 insertions, 423 deletions
diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs index 6029a69c..bc7e49c6 100644 --- a/examples/arc/src/main.rs +++ b/examples/arc/src/main.rs @@ -2,7 +2,7 @@ use std::{f32::consts::PI, time::Instant}; use iced::executor; use iced::widget::canvas::{ - self, Cache, Canvas, Cursor, Geometry, Path, Stroke, StrokeStyle, + self, Cache, Canvas, Cursor, Geometry, Path, Stroke, Style, }; use iced::{ Application, Command, Element, Length, Point, Rectangle, Settings, @@ -114,7 +114,7 @@ impl<Message> canvas::Program<Message> for Arc { frame.stroke( &path, Stroke { - style: StrokeStyle::Solid(palette.text), + style: Style::Solid(palette.text), width: 10.0, ..Stroke::default() }, diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 51f25a3f..06ed44f0 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -1,6 +1,6 @@ use iced::executor; use iced::widget::canvas::{ - Cache, Cursor, Geometry, LineCap, Path, Stroke, StrokeStyle, + Cache, Cursor, Geometry, LineCap, Path, Stroke, Style, }; use iced::widget::{canvas, container}; use iced::{ @@ -111,7 +111,7 @@ impl<Message> canvas::Program<Message> for Clock { let thin_stroke = || -> Stroke { Stroke { width, - style: StrokeStyle::Solid(Color::WHITE), + style: Style::Solid(Color::WHITE), line_cap: LineCap::Round, ..Stroke::default() } @@ -120,7 +120,7 @@ impl<Message> canvas::Program<Message> for Clock { let wide_stroke = || -> Stroke { Stroke { width: width * 3.0, - style: StrokeStyle::Solid(Color::WHITE), + style: Style::Solid(Color::WHITE), line_cap: LineCap::Round, ..Stroke::default() } diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs index c7945012..238c9a0f 100644 --- a/examples/modern_art/src/main.rs +++ b/examples/modern_art/src/main.rs @@ -1,5 +1,5 @@ use rand::{Rng, thread_rng}; -use crate::canvas::{Cursor, FillStyle, Geometry}; +use crate::canvas::{Cursor, Geometry}; use iced::widget::canvas::{Cache, Fill, Frame}; use iced::widget::{canvas, Canvas}; use iced::Settings; @@ -8,6 +8,7 @@ use iced::{ Renderer, Size, Theme, }; use iced_graphics::gradient::Gradient; +use iced_graphics::widget::canvas::Style; fn main() -> iced::Result { ModernArt::run(Settings { @@ -120,7 +121,7 @@ fn generate_box(frame: &mut Frame, bounds: Size) -> bool { top_left, size, Fill { - style: FillStyle::Solid(random_color()), + style: Style::Solid(random_color()), .. Default::default() } ); @@ -129,7 +130,7 @@ fn generate_box(frame: &mut Frame, bounds: Size) -> bool { top_left, size, Fill { - style: FillStyle::Gradient(&gradient( + style: Style::Gradient(&gradient( top_left, Point::new(top_left.x + size.width, top_left.y + size.height) )), diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index fcd20561..8d713ce0 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -19,7 +19,7 @@ use iced::{ }; use std::time::Instant; -use crate::canvas::StrokeStyle; +use crate::canvas::Style; pub fn main() -> iced::Result { SolarSystem::run(Settings { @@ -179,7 +179,7 @@ impl<Message> canvas::Program<Message> for State { frame.stroke( &orbit, Stroke { - style: StrokeStyle::Solid(Color::from_rgba8(0, 153, 255, 0.1)), + style: Style::Solid(Color::from_rgba8(0, 153, 255, 0.1)), width: 1.0, line_dash: canvas::LineDash { offset: 0, diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 6fc4fb38..7333d513 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -99,7 +99,7 @@ impl Backend { ); } - if !layer.meshes.0.is_empty() { + if !layer.meshes.is_empty() { let scaled = transformation * Transformation::scale(scale_factor, scale_factor); diff --git a/glow/src/shader/common/gradient.frag b/glow/src/shader/common/gradient.frag index 588f63e0..1afb557d 100644 --- a/glow/src/shader/common/gradient.frag +++ b/glow/src/shader/common/gradient.frag @@ -23,6 +23,7 @@ uniform uint color_stops_size; uniform float color_stop_offsets[MAX_STOPS]; uniform vec4 color_stop_colors[MAX_STOPS]; +//TODO: rewrite without branching to make ALUs happy void main() { vec2 gradient_vec = vec2(gradient_end - gradient_start); vec2 current_vec = vec2(raw_position.xy - gradient_start); diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs index 85d873fe..f16f8af4 100644 --- a/glow/src/triangle.rs +++ b/glow/src/triangle.rs @@ -2,23 +2,22 @@ mod gradient; mod solid; -use crate::program::{self, Shader}; -use crate::Transformation; +use crate::{program, Transformation}; use glow::HasContext; -use iced_graphics::layer::{Mesh, Meshes}; +use iced_graphics::layer::{attribute_count_of, Mesh}; use iced_graphics::shader; use std::marker::PhantomData; use crate::triangle::gradient::GradientProgram; use crate::triangle::solid::SolidProgram; pub use iced_graphics::triangle::{Mesh2D, Vertex2D}; +use shader::Shader; #[derive(Debug)] pub(crate) struct Pipeline { vertex_array: <glow::Context as HasContext>::VertexArray, vertices: Buffer<Vertex2D>, indices: Buffer<u32>, - current_transform: Transformation, programs: TrianglePrograms, } @@ -68,7 +67,6 @@ impl Pipeline { vertex_array, vertices, indices, - current_transform: Transformation::identity(), programs: TrianglePrograms { solid: SolidProgram::new(gl, shader_version), gradient: GradientProgram::new(gl, shader_version), @@ -78,7 +76,7 @@ impl Pipeline { pub fn draw( &mut self, - meshes: &Meshes<'_>, + meshes: &[Mesh<'_>], gl: &glow::Context, target_height: u32, transformation: Transformation, @@ -90,8 +88,8 @@ impl Pipeline { gl.bind_vertex_array(Some(self.vertex_array)) } - //count the total number of vertices & indices we need to handle for all meshes - let (total_vertices, total_indices) = meshes.attribute_count(); + //count the total amount of vertices & indices we need to handle + let (total_vertices, total_indices) = attribute_count_of(meshes); // Then we ensure the current attribute buffers are big enough, resizing if necessary unsafe { @@ -100,25 +98,25 @@ impl Pipeline { } // We upload all the vertices and indices upfront - let mut last_vertex = 0; - let mut last_index = 0; + let mut vertex_offset = 0; + let mut index_offset = 0; - for Mesh { buffers, .. } in meshes.0.iter() { + for mesh in meshes { unsafe { gl.buffer_sub_data_u8_slice( glow::ARRAY_BUFFER, - (last_vertex * std::mem::size_of::<Vertex2D>()) as i32, - bytemuck::cast_slice(&buffers.vertices), + (vertex_offset * std::mem::size_of::<Vertex2D>()) as i32, + bytemuck::cast_slice(&mesh.buffers.vertices), ); gl.buffer_sub_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, - (last_index * std::mem::size_of::<u32>()) as i32, - bytemuck::cast_slice(&buffers.indices), + (index_offset * std::mem::size_of::<u32>()) as i32, + bytemuck::cast_slice(&mesh.buffers.indices), ); - last_vertex += buffers.vertices.len(); - last_index += buffers.indices.len(); + vertex_offset += mesh.buffers.vertices.len(); + index_offset += mesh.buffers.indices.len(); } } @@ -126,22 +124,11 @@ impl Pipeline { let mut last_vertex = 0; let mut last_index = 0; - for (index, Mesh { - buffers, - origin, - clip_bounds, - shader, - }) in meshes.0.iter().enumerate() - { - let transform = - transformation * Transformation::translate(origin.x, origin.y); - - if index == 0 { - //set initial transform uniform for both programs - self.programs.set_transforms(gl, transform); - } + for mesh in meshes { + let transform = transformation + * Transformation::translate(mesh.origin.x, mesh.origin.y); - let clip_bounds = (*clip_bounds * scale_factor).snap(); + let clip_bounds = (mesh.clip_bounds * scale_factor).snap(); unsafe { gl.scissor( @@ -152,25 +139,25 @@ impl Pipeline { clip_bounds.height as i32, ); - let t = if self.current_transform != transform { - self.current_transform = transform; - Some(transform) - } else { - None - }; - - self.use_with_shader(gl, shader, t); + match mesh.shader { + Shader::Solid(color) => { + self.programs.solid.use_program(gl, &color, &transform); + } + Shader::Gradient(gradient) => { + self.programs.gradient.use_program(gl, &gradient, &transform); + } + } gl.draw_elements_base_vertex( glow::TRIANGLES, - buffers.indices.len() as i32, + mesh.buffers.indices.len() as i32, glow::UNSIGNED_INT, (last_index * std::mem::size_of::<u32>()) as i32, last_vertex as i32, ); - last_vertex += buffers.vertices.len(); - last_index += buffers.indices.len(); + last_vertex += mesh.buffers.vertices.len(); + last_index += mesh.buffers.indices.len(); } } @@ -180,31 +167,6 @@ impl Pipeline { gl.disable(glow::MULTISAMPLE); } } - - fn use_with_shader( - &mut self, - gl: &glow::Context, - shader: &shader::Shader, - transform: Option<Transformation>, - ) { - match shader { - shader::Shader::Solid(color) => { - unsafe { gl.use_program(Some(self.programs.solid.program)) } - self.programs.solid.set_uniforms(gl, color, transform); - } - shader::Shader::Gradient(gradient) => { - unsafe { gl.use_program(Some(self.programs.gradient.program)) } - self.programs.gradient.set_uniforms(gl, gradient, transform); - } - } - } -} - -impl TrianglePrograms { - pub fn set_transforms(&self, gl: &glow::Context, transform: Transformation) { - update_transform(gl, self.solid.program, Some(transform)); - update_transform(gl, self.gradient.program, Some(transform)); - } } /// A simple shader program. Uses [`triangle.vert`] for its vertex shader and only binds position @@ -215,14 +177,14 @@ pub(super) fn simple_triangle_program( fragment_shader: &'static str, ) -> <glow::Context as HasContext>::Program { unsafe { - let vertex_shader = Shader::vertex( + let vertex_shader = program::Shader::vertex( gl, shader_version, include_str!("shader/common/triangle.vert"), ); let fragment_shader = - Shader::fragment(gl, shader_version, fragment_shader); + program::Shader::fragment(gl, shader_version, fragment_shader); program::create( gl, @@ -232,23 +194,17 @@ pub(super) fn simple_triangle_program( } } -pub(super) fn update_transform( +pub fn set_transform( gl: &glow::Context, - program: <glow::Context as HasContext>::Program, - transform: Option<Transformation> + location: <glow::Context as HasContext>::UniformLocation, + transform: Transformation, ) { - if let Some(t) = transform { - let transform_location = - unsafe { gl.get_uniform_location(program, "u_Transform") } - .expect("Get transform location."); - - unsafe { - gl.uniform_matrix_4_f32_slice( - Some(&transform_location), - false, - t.as_ref(), - ); - } + unsafe { + gl.uniform_matrix_4_f32_slice( + Some(&location), + false, + transform.as_ref() + ); } } diff --git a/glow/src/triangle/gradient.rs b/glow/src/triangle/gradient.rs index d1b10d77..547871e2 100644 --- a/glow/src/triangle/gradient.rs +++ b/glow/src/triangle/gradient.rs @@ -1,18 +1,42 @@ use crate::program::Version; -use crate::triangle::{simple_triangle_program, update_transform}; +use crate::triangle::{simple_triangle_program, set_transform}; use glow::{Context, HasContext, NativeProgram}; +use iced_graphics::gradient::Linear; use iced_graphics::gradient::Gradient; -use iced_graphics::widget::canvas::gradient::Linear; use iced_graphics::Transformation; #[derive(Debug)] -pub(super) struct GradientProgram { - pub(super) program: <Context as HasContext>::Program, - pub(super) uniform_data: GradientUniformData, +pub struct GradientProgram { + pub program: <Context as HasContext>::Program, + pub uniform_data: GradientUniformData, +} + +#[derive(Debug)] +pub struct GradientUniformData { + gradient: Gradient, + transform: Transformation, + uniform_locations: GradientUniformLocations, +} + +#[derive(Debug)] +struct GradientUniformLocations { + gradient_start_location: <Context as HasContext>::UniformLocation, + gradient_end_location: <Context as HasContext>::UniformLocation, + color_stops_size_location: <Context as HasContext>::UniformLocation, + //currently the maximum number of stops is 64 due to needing to allocate the + //memory for the array of stops with a const value in GLSL + color_stops_locations: [ColorStopLocation; 64], + transform_location: <Context as HasContext>::UniformLocation, +} + +#[derive(Copy, Debug, Clone)] +struct ColorStopLocation { + color: <Context as HasContext>::UniformLocation, + offset: <Context as HasContext>::UniformLocation, } impl GradientProgram { - pub(super) fn new(gl: &Context, shader_version: &Version) -> Self { + pub fn new(gl: &Context, shader_version: &Version) -> Self { let program = simple_triangle_program( gl, shader_version, @@ -25,15 +49,17 @@ impl GradientProgram { } } - pub(super) fn set_uniforms<'a>( + pub fn write_uniforms( &mut self, gl: &Context, gradient: &Gradient, - transform: Option<Transformation>, + transform: &Transformation, ) { - update_transform(gl, self.program, transform); + if transform != &self.uniform_data.transform { + set_transform(gl, self.uniform_data.uniform_locations.transform_location, *transform); + } - if &self.uniform_data.current_gradient != gradient { + if &self.uniform_data.gradient != gradient { match gradient { Gradient::Linear(linear) => { let gradient_start: [f32; 2] = (linear.start).into(); @@ -104,31 +130,17 @@ impl GradientProgram { } } - self.uniform_data.current_gradient = gradient.clone(); + self.uniform_data.gradient = gradient.clone(); } } -} - -#[derive(Debug)] -pub(super) struct GradientUniformData { - current_gradient: Gradient, - uniform_locations: GradientUniformLocations, -} -#[derive(Debug)] -struct GradientUniformLocations { - gradient_start_location: <Context as HasContext>::UniformLocation, - gradient_end_location: <Context as HasContext>::UniformLocation, - color_stops_size_location: <Context as HasContext>::UniformLocation, - //currently the maximum number of stops is 64 due to needing to allocate the - //memory for the array of stops with a const value in GLSL - color_stops_locations: [ColorStopLocation; 64], -} + pub fn use_program(&mut self, gl: &glow::Context, gradient: &Gradient, transform: &Transformation) { + unsafe { + gl.use_program(Some(self.program)) + } -#[derive(Copy, Debug, Clone)] -struct ColorStopLocation { - color: <Context as HasContext>::UniformLocation, - offset: <Context as HasContext>::UniformLocation, + self.write_uniforms(gl, gradient, transform); + } } impl GradientUniformData { @@ -153,10 +165,7 @@ impl GradientUniformData { &format!("color_stop_offsets[{}]", index), ) } - .expect(&format!( - "Gradient - Color stop offset with index {}", - index - )); + .expect("Gradient - Color stop offset location."); let color = unsafe { gl.get_uniform_location( @@ -164,25 +173,28 @@ impl GradientUniformData { &format!("color_stop_colors[{}]", index), ) } - .expect(&format!( - "Gradient - Color stop colors with index {}", - index - )); + .expect("Gradient - Color stop color location."); ColorStopLocation { color, offset } }); + let transform_location = + unsafe { gl.get_uniform_location(program, "u_Transform") } + .expect("Get transform location."); + GradientUniformData { - current_gradient: Gradient::Linear(Linear { + gradient: Gradient::Linear(Linear { start: Default::default(), end: Default::default(), color_stops: vec![], }), + transform: Transformation::identity(), uniform_locations: GradientUniformLocations { gradient_start_location, gradient_end_location, color_stops_size_location, color_stops_locations, + transform_location, }, } } diff --git a/glow/src/triangle/solid.rs b/glow/src/triangle/solid.rs index 3a33cea8..d5b73eb9 100644 --- a/glow/src/triangle/solid.rs +++ b/glow/src/triangle/solid.rs @@ -1,13 +1,38 @@ use crate::program::Version; -use crate::triangle::{simple_triangle_program, update_transform}; +use crate::triangle::{set_transform, simple_triangle_program}; use crate::Color; use glow::{Context, HasContext, NativeProgram}; use iced_graphics::Transformation; #[derive(Debug)] pub struct SolidProgram { - pub(crate) program: <Context as HasContext>::Program, - pub(crate) uniform_data: SolidUniformData, + program: <Context as HasContext>::Program, + uniform_data: SolidUniformData, +} + +#[derive(Debug)] +pub(crate) struct SolidUniformData { + pub color: Color, + pub color_location: <Context as HasContext>::UniformLocation, + pub transform: Transformation, + pub transform_location: <Context as HasContext>::UniformLocation, +} + +impl SolidUniformData { + fn new(gl: &Context, program: NativeProgram) -> Self { + Self { + color: Color::TRANSPARENT, + color_location: unsafe { + gl.get_uniform_location(program, "color") + } + .expect("Solid - Color uniform location."), + transform: Transformation::identity(), + transform_location: unsafe { + gl.get_uniform_location(program, "u_Transform") + } + .expect("Get transform location."), + } + } } impl SolidProgram { @@ -24,15 +49,17 @@ impl SolidProgram { } } - pub fn set_uniforms<'a>( + pub fn write_uniforms( &mut self, gl: &Context, color: &Color, - transform: Option<Transformation>, + transform: &Transformation, ) { - update_transform(gl, self.program, transform); + if transform != &self.uniform_data.transform { + set_transform(gl, self.uniform_data.transform_location, *transform) + } - if &self.uniform_data.color != color { + if color != &self.uniform_data.color { unsafe { gl.uniform_4_f32( Some(&self.uniform_data.color_location), @@ -46,22 +73,11 @@ impl SolidProgram { self.uniform_data.color = *color; } } -} - -#[derive(Debug)] -pub(crate) struct SolidUniformData { - pub color: Color, - pub color_location: <Context as HasContext>::UniformLocation, -} -impl SolidUniformData { - fn new(gl: &Context, program: NativeProgram) -> Self { - Self { - color: Color::TRANSPARENT, - color_location: unsafe { - gl.get_uniform_location(program, "color") - } - .expect("Solid - Color uniform location."), + pub fn use_program(&mut self, gl: &glow::Context, color: &Color, transform: &Transformation) { + unsafe { + gl.use_program(Some(self.program)) } + self.write_uniforms(gl, color, transform) } -} +}
\ No newline at end of file diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index fa57842b..0c394e8b 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -1,6 +1,6 @@ //! For creating a Gradient. use iced_native::Color; -use crate::gradient::linear::Linear; +pub use crate::gradient::linear::Linear; use crate::Point; #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index b7731922..096c50dc 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -19,7 +19,7 @@ pub struct Layer<'a> { pub quads: Vec<Quad>, /// The triangle meshes of the [`Layer`]. - pub meshes: Meshes<'a>, + pub meshes: Vec<Mesh<'a>>, /// The text of the [`Layer`]. pub text: Vec<Text<'a>>, @@ -34,7 +34,7 @@ impl<'a> Layer<'a> { Self { bounds, quads: Vec::new(), - meshes: Meshes(Vec::new()), + meshes: Vec::new(), text: Vec::new(), images: Vec::new(), } @@ -174,7 +174,7 @@ impl<'a> Layer<'a> { // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { - layer.meshes.0.push( + layer.meshes.push( Mesh { origin: Point::new(translation.x, translation.y), buffers, @@ -335,20 +335,14 @@ unsafe impl bytemuck::Zeroable for Quad {} #[allow(unsafe_code)] unsafe impl bytemuck::Pod for Quad {} -#[derive(Debug)] -/// A collection of meshes. -pub struct Meshes<'a>(pub Vec<Mesh<'a>>); - -impl<'a> Meshes<'a> { - /// Returns the number of total vertices & total indices of all [`Mesh`]es. - pub fn attribute_count(&self) -> (usize, usize) { - self.0 - .iter() - .map(|Mesh { buffers, .. }| { - (buffers.vertices.len(), buffers.indices.len()) - }) - .fold((0, 0), |(total_v, total_i), (v, i)| { - (total_v + v, total_i + i) - }) - } +/// Returns the number of total vertices & total indices of all [`Mesh`]es. +pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> (usize, usize) { + meshes + .iter() + .map(|Mesh { buffers, .. }| { + (buffers.vertices.len(), buffers.indices.len()) + }) + .fold((0, 0), |(total_v, total_i), (v, i)| { + (total_v + v, total_i + i) + }) }
\ No newline at end of file diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 95c962af..f6929e97 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -19,12 +19,12 @@ mod text; pub use cache::Cache; pub use cursor::Cursor; pub use event::Event; -pub use fill::{Fill, FillRule, FillStyle}; +pub use fill::{Fill, FillRule, Style}; pub use frame::Frame; pub use geometry::Geometry; pub use path::Path; pub use program::Program; -pub use stroke::{LineCap, LineDash, LineJoin, Stroke, StrokeStyle}; +pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use text::Text; use crate::{Backend, Primitive, Renderer}; diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index 60029e03..6f10505c 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -8,7 +8,7 @@ pub struct Fill<'a> { /// The color or gradient of the fill. /// /// By default, it is set to [`FillStyle::Solid`] `BLACK`. - pub style: FillStyle<'a>, + pub style: Style<'a>, /// The fill rule defines how to determine what is inside and what is /// outside of a shape. @@ -24,7 +24,7 @@ pub struct Fill<'a> { impl <'a> Default for Fill<'a> { fn default() -> Fill<'a> { Fill { - style: FillStyle::Solid(Color::BLACK), + style: Style::Solid(Color::BLACK), rule: FillRule::NonZero, } } @@ -33,7 +33,7 @@ impl <'a> Default for Fill<'a> { impl<'a> From<Color> for Fill<'a> { fn from(color: Color) -> Fill<'a> { Fill { - style: FillStyle::Solid(color), + style: Style::Solid(color), ..Fill::default() } } @@ -41,18 +41,18 @@ impl<'a> From<Color> for Fill<'a> { /// The color or gradient of a [`Fill`]. #[derive(Debug, Clone)] -pub enum FillStyle<'a> { +pub enum Style<'a> { /// A solid color Solid(Color), /// A color gradient Gradient(&'a Gradient), } -impl <'a> Into<Shader> for FillStyle<'a> { +impl <'a> Into<Shader> for Style<'a> { fn into(self) -> Shader { match self { - FillStyle::Solid(color) => Shader::Solid(color), - FillStyle::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => Shader::Solid(color), + Style::Gradient(gradient) => gradient.clone().into() } } } diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index ed82f189..7ce5ff1d 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -8,7 +8,7 @@ pub struct Stroke<'a> { /// The color or gradient of the stroke. /// /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. - pub style: StrokeStyle<'a>, + pub style: Style<'a>, /// 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. @@ -24,7 +24,7 @@ impl<'a> Stroke<'a> { /// Sets the color of the [`Stroke`]. pub fn with_color(self, color: Color) -> Self { Stroke { - style: StrokeStyle::Solid(color), + style: Style::Solid(color), ..self } } @@ -48,7 +48,7 @@ impl<'a> Stroke<'a> { impl<'a> Default for Stroke<'a> { fn default() -> Self { Stroke { - style: StrokeStyle::Solid(Color::BLACK), + style: Style::Solid(Color::BLACK), width: 1.0, line_cap: LineCap::default(), line_join: LineJoin::default(), @@ -59,18 +59,18 @@ impl<'a> Default for Stroke<'a> { /// The color or gradient of a [`Stroke`]. #[derive(Debug, Clone, Copy)] -pub enum StrokeStyle<'a> { +pub enum Style<'a> { /// A solid color Solid(Color), /// A color gradient Gradient(&'a Gradient), } -impl <'a> Into<Shader> for StrokeStyle<'a> { +impl <'a> Into<Shader> for Style<'a> { fn into(self) -> Shader { match self { - StrokeStyle::Solid(color) => Shader::Solid(color), - StrokeStyle::Gradient(gradient) => gradient.clone().into() + Style::Solid(color) => Shader::Solid(color), + Style::Gradient(gradient) => gradient.clone().into() } } } diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index fd688004..9295a491 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -132,7 +132,7 @@ impl Backend { ); } - if !layer.meshes.0.is_empty() { + if !layer.meshes.is_empty() { let scaled = transformation * Transformation::scale(scale_factor, scale_factor); diff --git a/wgpu/src/buffers/buffer.rs b/wgpu/src/buffers/buffer.rs index dae3b038..a44120d3 100644 --- a/wgpu/src/buffers/buffer.rs +++ b/wgpu/src/buffers/buffer.rs @@ -1,91 +1,124 @@ //! Utilities for static buffer operations. +use bytemuck::{Pod, Zeroable}; +use std::marker::PhantomData; +use std::mem; + +//128 triangles/indices +const DEFAULT_STATIC_BUFFER_COUNT: wgpu::BufferAddress = 128; /// A generic buffer struct useful for items which have no alignment requirements /// (e.g. Vertex, Index buffers) and are set once and never changed until destroyed. -/// -/// This buffer is mapped to the GPU on creation, so must be initialized with the correct capacity. #[derive(Debug)] -pub(crate) struct StaticBuffer { - //stored sequentially per mesh iteration +pub(crate) struct StaticBuffer<T> { + //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer offsets: Vec<wgpu::BufferAddress>, + label: &'static str, + usages: wgpu::BufferUsages, gpu: wgpu::Buffer, //the static size of the buffer size: wgpu::BufferAddress, + _data: PhantomData<T>, } -impl StaticBuffer { +impl<T: Pod + Zeroable> StaticBuffer<T> { + /// Initialize a new static buffer. pub fn new( device: &wgpu::Device, label: &'static str, - size: u64, - usage: wgpu::BufferUsages, - total_offsets: usize, + usages: wgpu::BufferUsages, ) -> Self { + let size = (mem::size_of::<T>() as u64) * DEFAULT_STATIC_BUFFER_COUNT; + Self { - offsets: Vec::with_capacity(total_offsets), - gpu: device.create_buffer(&wgpu::BufferDescriptor { - label: Some(label), - size, - usage, - mapped_at_creation: true, - }), + offsets: Vec::new(), + label, + usages, + gpu: Self::gpu_buffer(device, label, size, usages), size, + _data: Default::default(), } } - /// Resolves pending write operations & unmaps buffer from host memory. - pub fn flush(&self) { - (&self.gpu).unmap(); + fn gpu_buffer( + device: &wgpu::Device, + label: &'static str, + size: wgpu::BufferAddress, + usage: wgpu::BufferUsages, + ) -> wgpu::Buffer { + device.create_buffer(&wgpu::BufferDescriptor { + label: Some(label), + size, + usage, + mapped_at_creation: false, + }) } - /// Returns whether or not the buffer needs to be recreated. This can happen whenever the mesh - /// data is re-submitted. - pub fn needs_recreate(&self, new_size: usize) -> bool { - self.size != new_size as u64 - } + /// Returns whether or not the buffer needs to be recreated. This can happen whenever mesh data + /// changes & a redraw is requested. + pub fn recreate_if_needed( + &mut self, + device: &wgpu::Device, + new_count: usize, + ) -> bool { + let size = + wgpu::BufferAddress::from((mem::size_of::<T>() * new_count) as u64); - /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. - pub fn write(&mut self, offset: u64, content: &[u8]) { - //offset has to be divisible by 8 for alignment reasons - let actual_offset = if offset % 8 != 0 { - offset + 4 + if self.size <= size { + self.offsets.clear(); + self.size = size; + self.gpu = Self::gpu_buffer(device, self.label, size, self.usages); + true } else { - offset - }; + false + } + } - let mut buffer = self - .gpu - .slice(actual_offset..(actual_offset + content.len() as u64)) - .get_mapped_range_mut(); - buffer.copy_from_slice(content); - self.offsets.push(actual_offset); + /// Writes the current vertex data to the gpu buffer if it is currently writable with a memcpy & + /// stores its offset. + /// + /// This will return either the offset of the written bytes, or `None` if the GPU buffer is not + /// currently writable. + pub fn write( + &mut self, + device: &wgpu::Device, + staging_belt: &mut wgpu::util::StagingBelt, + encoder: &mut wgpu::CommandEncoder, + offset: u64, + content: &[T], + ) -> u64 { + let bytes = bytemuck::cast_slice(content); + let bytes_size = bytes.len() as u64; + + if let Some(buffer_size) = wgpu::BufferSize::new(bytes_size as u64) { + //offset has to be divisible by 8 for alignment reasons + let actual_offset = if offset % 8 != 0 { offset + 4 } else { offset }; + + let mut buffer = staging_belt.write_buffer( + encoder, + &self.gpu, + actual_offset, + buffer_size, + device, + ); + + buffer.copy_from_slice(bytes); + + self.offsets.push(actual_offset); + } + + bytes_size } fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { self.offsets .get(index) - .expect(&format!("Offset index {} is not in range.", index)) + .expect("Offset at index does not exist.") } /// Returns the slice calculated from the offset stored at the given index. - /// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index + /// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index /// 1 that we stored earlier when writing. - pub fn slice_from_index<T>( - &self, - index: usize, - ) -> wgpu::BufferSlice<'_> { + pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { self.gpu.slice(self.offset_at(index)..) } } - -/// Returns true if the current buffer doesn't exist & needs to be created, or if it's too small -/// for the new content. -pub(crate) fn needs_recreate( - buffer: &Option<StaticBuffer>, - new_size: usize, -) -> bool { - match buffer { - None => true, - Some(buf) => buf.needs_recreate(new_size), - } -} diff --git a/wgpu/src/buffers/dynamic_buffers.rs b/wgpu/src/buffers/dynamic_buffers.rs index d81529ce..75cc202c 100644 --- a/wgpu/src/buffers/dynamic_buffers.rs +++ b/wgpu/src/buffers/dynamic_buffers.rs @@ -50,7 +50,6 @@ impl DynamicBufferType { } } -//TODO think about making cpu & gpu buffers optional pub(crate) struct DynamicBuffer<T: ShaderType> { offsets: Vec<wgpu::DynamicOffset>, cpu: DynamicBufferType, @@ -183,7 +182,7 @@ impl<T: ShaderType + WriteInto> DynamicBuffer<T> { let offset = self .offsets .get(index) - .expect(&format!("Index {} not found in offsets.", index)) + .expect("Index not found in offsets.") .clone(); offset diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index f1770e9a..df5e3132 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -3,11 +3,11 @@ use crate::{settings, Transformation}; use core::fmt; use std::fmt::Formatter; -use iced_graphics::layer::Meshes; +use iced_graphics::layer::{attribute_count_of, Mesh}; use iced_graphics::shader::Shader; use iced_graphics::Size; -use crate::buffers::buffer::{needs_recreate, StaticBuffer}; +use crate::buffers::buffer::StaticBuffer; use crate::triangle::gradient::GradientPipeline; use crate::triangle::solid::SolidPipeline; pub use iced_graphics::triangle::{Mesh2D, Vertex2D}; @@ -20,10 +20,9 @@ mod solid; #[derive(Debug)] pub(crate) struct Pipeline { blit: Option<msaa::Blit>, - // these are optional so we don't allocate any memory to the GPU if - // application has no triangle meshes. - vertex_buffer: Option<StaticBuffer>, - index_buffer: Option<StaticBuffer>, + vertex_buffer: StaticBuffer<Vertex2D>, + index_buffer: StaticBuffer<u32>, + index_strides: Vec<u32>, pipelines: TrianglePipelines, } @@ -69,8 +68,17 @@ impl Pipeline { ) -> Pipeline { Pipeline { blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), - vertex_buffer: None, - index_buffer: None, + vertex_buffer: StaticBuffer::new( + device, + "iced_wgpu::triangle vertex buffer", + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ), + index_buffer: StaticBuffer::new( + device, + "iced_wgpu::triangle vertex buffer", + wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, + ), + index_strides: Vec::new(), pipelines: TrianglePipelines { solid: SolidPipeline::new(device, format, antialiasing), gradient: GradientPipeline::new(device, format, antialiasing), @@ -88,177 +96,152 @@ impl Pipeline { target_size: Size<u32>, transformation: Transformation, scale_factor: f32, - meshes: &Meshes<'_>, + meshes: &[Mesh<'_>], ) { - //count the total number of vertices & indices we need to handle - let (total_vertices, total_indices) = meshes.attribute_count(); + //count the total amount of vertices & indices we need to handle + let (total_vertices, total_indices) = attribute_count_of(meshes); + + // Then we ensure the current attribute buffers are big enough, resizing if necessary + // with wgpu this means recreating the buffer. + + //We are not currently using the return value of these functions as we have no system in + //place to calculate mesh diff, or to know whether or not that would be more performant for + //the majority of use cases. Therefore we will write GPU data every frame (for now). + let _ = self.vertex_buffer.recreate_if_needed(device, total_vertices); + let _ = self.index_buffer.recreate_if_needed(device, total_indices); + + //prepare dynamic buffers & data store for writing + self.index_strides.clear(); + self.pipelines.clear(); + + let mut vertex_offset = 0; + let mut index_offset = 0; - //Only create buffers if they need to be re-sized or don't exist - if needs_recreate(&self.vertex_buffer, total_vertices) { - //mapped to GPU at creation with total vertices - self.vertex_buffer = Some(StaticBuffer::new( + for mesh in meshes { + let transform = transformation + * Transformation::translate(mesh.origin.x, mesh.origin.y); + + //write to both buffers + let new_vertex_offset = self.vertex_buffer.write( device, - "iced_wgpu::triangle vertex buffer", - //TODO: a more reasonable default to prevent frequent resizing calls - // before this was 10_000 - (std::mem::size_of::<Vertex2D>() * total_vertices) as u64, - wgpu::BufferUsages::VERTEX, - meshes.0.len(), - )) - } + staging_belt, + encoder, + vertex_offset, + &mesh.buffers.vertices, + ); - if needs_recreate(&self.index_buffer, total_indices) { - //mapped to GPU at creation with total indices - self.index_buffer = Some(StaticBuffer::new( + let new_index_offset = self.index_buffer.write( device, - "iced_wgpu::triangle index buffer", - //TODO: a more reasonable default to prevent frequent resizing calls - // before this was 10_000 - (std::mem::size_of::<Vertex2D>() * total_indices) as u64, - wgpu::BufferUsages::INDEX, - meshes.0.len(), - )); - } + staging_belt, + encoder, + index_offset, + &mesh.buffers.indices, + ); - if let Some(vertex_buffer) = &mut self.vertex_buffer { - if let Some(index_buffer) = &mut self.index_buffer { - let mut offset_v = 0; - let mut offset_i = 0; - //TODO: store this more efficiently - let mut indices_lengths = Vec::with_capacity(meshes.0.len()); - - //iterate through meshes to write all attribute data - for mesh in meshes.0.iter() { - let transform = transformation - * Transformation::translate( - mesh.origin.x, - mesh.origin.y, - ); + vertex_offset = vertex_offset + new_vertex_offset; + index_offset = index_offset + new_index_offset; - let vertices = bytemuck::cast_slice(&mesh.buffers.vertices); - let indices = bytemuck::cast_slice(&mesh.buffers.indices); - - //TODO: it's (probably) more efficient to reduce this write command and - // iterate first and then upload - vertex_buffer.write(offset_v, vertices); - index_buffer.write(offset_i, indices); - - offset_v += vertices.len() as u64; - offset_i += indices.len() as u64; - indices_lengths.push(mesh.buffers.indices.len()); - - match mesh.shader { - Shader::Solid(color) => { - self.pipelines.solid.push(transform, color); - } - Shader::Gradient(gradient) => { - self.pipelines.gradient.push(transform, gradient); - } - } + self.index_strides.push(mesh.buffers.indices.len() as u32); + + //push uniform data to CPU buffers + match mesh.shader { + Shader::Solid(color) => { + self.pipelines.solid.push(transform, color); } + Shader::Gradient(gradient) => { + self.pipelines.gradient.push(transform, gradient); + } + } + } - //done writing to gpu buffer, unmap from host memory since we don't need it - //anymore - vertex_buffer.flush(); - index_buffer.flush(); - - //resize & memcpy uniforms from CPU buffers to GPU buffers for all pipelines - self.pipelines.write(device, staging_belt, encoder); - - //configure the render pass now that the data is uploaded to the GPU - { - //configure antialiasing pass - let (attachment, resolve_target, load) = - if let Some(blit) = &mut self.blit { - let (attachment, resolve_target) = blit.targets( - device, - target_size.width, - target_size.height, - ); - - ( - attachment, - Some(resolve_target), - wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - ) - } else { - (target, None, wgpu::LoadOp::Load) - }; - - let mut render_pass = encoder.begin_render_pass( - &wgpu::RenderPassDescriptor { - label: Some("iced_wgpu::triangle render pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: attachment, - resolve_target, - ops: wgpu::Operations { load, store: true }, - }, - )], - depth_stencil_attachment: None, + //write uniform data to GPU + self.pipelines.write(device, staging_belt, encoder); + + //configure the render pass now that the data is uploaded to the GPU + { + //configure antialiasing pass + let (attachment, resolve_target, load) = if let Some(blit) = + &mut self.blit + { + let (attachment, resolve_target) = + blit.targets(device, target_size.width, target_size.height); + + ( + attachment, + Some(resolve_target), + wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + ) + } else { + (target, None, wgpu::LoadOp::Load) + }; + + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("iced_wgpu::triangle render pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: attachment, + resolve_target, + ops: wgpu::Operations { load, store: true }, }, - ); - - //TODO: do this a better way; store it in the respective pipelines perhaps - // to be more readable - let mut num_solids = 0; - let mut num_gradients = 0; - - //TODO: try to avoid this extra iteration if possible - for index in 0..meshes.0.len() { - let clip_bounds = - (meshes.0[index].clip_bounds * scale_factor).snap(); - - render_pass.set_scissor_rect( - clip_bounds.x, - clip_bounds.y, - clip_bounds.width, - clip_bounds.height, - ); - - match meshes.0[index].shader { - Shader::Solid(_) => { - self.pipelines.solid.configure_render_pass( - &mut render_pass, - num_solids, - ); - num_solids += 1; - } - Shader::Gradient(_) => { - self.pipelines.gradient.configure_render_pass( - &mut render_pass, - num_gradients, - ); - num_gradients += 1; - } - } - - render_pass.set_index_buffer( - index_buffer.slice_from_index::<u32>(index), - wgpu::IndexFormat::Uint32, + )], + depth_stencil_attachment: None, + }); + + //TODO I can't figure out a clean way to encapsulate these into their appropriate + // structs without displeasing the borrow checker due to the lifetime requirements of + // render_pass & using a mutable reference to each pipeline in a loop... + let mut num_solids = 0; + let mut num_gradients = 0; + + for (index, mesh) in meshes.iter().enumerate() { + let clip_bounds = (mesh.clip_bounds * scale_factor).snap(); + + render_pass.set_scissor_rect( + clip_bounds.x, + clip_bounds.y, + clip_bounds.width, + clip_bounds.height, + ); + + match mesh.shader { + Shader::Solid(_) => { + self.pipelines.solid.configure_render_pass( + &mut render_pass, + num_solids, ); - - render_pass.set_vertex_buffer( - 0, - vertex_buffer.slice_from_index::<Vertex2D>(index), - ); - - render_pass.draw_indexed( - 0..(indices_lengths[index] as u32), - 0, - 0..1, + num_solids += 1; + } + Shader::Gradient(_) => { + self.pipelines.gradient.configure_render_pass( + &mut render_pass, + num_gradients, ); + num_gradients += 1; } - } + }; + + render_pass.set_vertex_buffer( + 0, + self.vertex_buffer.slice_from_index(index), + ); + + render_pass.set_index_buffer( + self.index_buffer.slice_from_index(index), + wgpu::IndexFormat::Uint32, + ); + + render_pass.draw_indexed( + 0..(self.index_strides[index] as u32), + 0, + 0..1, + ); } } if let Some(blit) = &mut self.blit { blit.draw(encoder, target); } - - //cleanup - self.pipelines.clear(); } } diff --git a/wgpu/src/triangle/gradient.rs b/wgpu/src/triangle/gradient.rs index 471b204c..15b6b7e0 100644 --- a/wgpu/src/triangle/gradient.rs +++ b/wgpu/src/triangle/gradient.rs @@ -253,13 +253,13 @@ impl GradientPipeline { pub fn configure_render_pass<'a>( &'a self, render_pass: &mut wgpu::RenderPass<'a>, - index: usize, + count: usize, ) { render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group( 0, &self.bind_group, - &[self.uniform_buffer.offset_at_index(index)], - ); + &[self.uniform_buffer.offset_at_index(count)], + ) } } diff --git a/wgpu/src/triangle/solid.rs b/wgpu/src/triangle/solid.rs index a3cbd72b..e7e9098a 100644 --- a/wgpu/src/triangle/solid.rs +++ b/wgpu/src/triangle/solid.rs @@ -8,15 +8,15 @@ use encase::ShaderType; use glam::Vec4; use iced_graphics::Transformation; -pub(super) struct SolidPipeline { +pub struct SolidPipeline { pipeline: wgpu::RenderPipeline, - pub(super) buffer: DynamicBuffer<SolidUniforms>, + pub(crate) buffer: DynamicBuffer<SolidUniforms>, bind_group_layout: wgpu::BindGroupLayout, bind_group: wgpu::BindGroup, } #[derive(Debug, Clone, Copy, ShaderType)] -pub(super) struct SolidUniforms { +pub struct SolidUniforms { transform: glam::Mat4, color: Vec4, } @@ -156,14 +156,13 @@ impl SolidPipeline { pub fn configure_render_pass<'a>( &'a self, render_pass: &mut wgpu::RenderPass<'a>, - index: usize, + count: usize, ) { render_pass.set_pipeline(&self.pipeline); - render_pass.set_bind_group( 0, &self.bind_group, - &[self.buffer.offset_at_index(index)], - ); + &[self.buffer.offset_at_index(count)], + ) } -}
\ No newline at end of file +} |